diff options
author | George Hazan <ghazan@miranda.im> | 2022-07-24 16:44:24 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2022-07-24 16:44:24 +0300 |
commit | e55d071e5485a937efd427d159b76c208cccdcce (patch) | |
tree | 48d09dc5cf1df2581fd6471b5ccf1560015fa3a4 /protocols/Tox | |
parent | f36629f67153bc500c828cf51de31988122a1024 (diff) |
fixes #3118 (Update toxcore to 0.2.18)
Diffstat (limited to 'protocols/Tox')
108 files changed, 23784 insertions, 5479 deletions
diff --git a/protocols/Tox/libtox/docs/CHANGELOG.md b/protocols/Tox/libtox/docs/CHANGELOG.md index d3cb6d8d65..85da062d93 100644 --- a/protocols/Tox/libtox/docs/CHANGELOG.md +++ b/protocols/Tox/libtox/docs/CHANGELOG.md @@ -1,9 +1,79 @@ + +## v0.2.16 + +### Merged PRs: + +- [#2069](https://github.com/TokTok/c-toxcore/pull/2069) chore: Simplify and speed up nacl build using toxchat/nacl. +- [#2066](https://github.com/TokTok/c-toxcore/pull/2066) test: Add a profiling script and Dockerfile. +- [#2058](https://github.com/TokTok/c-toxcore/pull/2058) fix: properly deallocate frozen peers +- [#2056](https://github.com/TokTok/c-toxcore/pull/2056) cleanup: Avoid implicit boolean and floating point conversions in decls. +- [#2055](https://github.com/TokTok/c-toxcore/pull/2055) cleanup: Avoid implicit bool conversions in logical operators. +- [#2053](https://github.com/TokTok/c-toxcore/pull/2053) cleanup: Enable tokstyle's `-Wlarge-struct-params`. +- [#2052](https://github.com/TokTok/c-toxcore/pull/2052) fix: Fix return type of functions returning uint64_t. +- [#2049](https://github.com/TokTok/c-toxcore/pull/2049) cleanup: Apply stronger type checks and fix errors. +- [#2047](https://github.com/TokTok/c-toxcore/pull/2047) feat: Improve how we share TCP relays with friends +- [#2046](https://github.com/TokTok/c-toxcore/pull/2046) cleanup: Avoid implicit pointer-to-bool conversion in `if` in toxav. +- [#2043](https://github.com/TokTok/c-toxcore/pull/2043) refactor: Compare pointers in if conditions to nullptr. +- [#2041](https://github.com/TokTok/c-toxcore/pull/2041) fix: file transfer bug introduced in commit 2073d02 +- [#2039](https://github.com/TokTok/c-toxcore/pull/2039) refactor: Add a bit more logging; change WARNING to ERROR. +- [#2036](https://github.com/TokTok/c-toxcore/pull/2036) chore: Add BUILD file for websockify. +- [#2035](https://github.com/TokTok/c-toxcore/pull/2035) chore: fine tune fuzzing settings +- [#2033](https://github.com/TokTok/c-toxcore/pull/2033) cleanup: Add some more error path logging to TCP server code. +- [#2032](https://github.com/TokTok/c-toxcore/pull/2032) chore: update the list of CMake options & Windows Docker build deps +- [#2031](https://github.com/TokTok/c-toxcore/pull/2031) fix: remove bogus asserts in fuzzer harness +- [#2030](https://github.com/TokTok/c-toxcore/pull/2030) chore: expand fuzzing to toxsave +- [#2028](https://github.com/TokTok/c-toxcore/pull/2028) fix: syntax error introduced in 8bf37994fd12acec9e3010437502f478399b99b4 +- [#2027](https://github.com/TokTok/c-toxcore/pull/2027) fix: add continous fuzzing +- [#2026](https://github.com/TokTok/c-toxcore/pull/2026) chore: Fix implicit declaration warning in fuzz build +- [#2025](https://github.com/TokTok/c-toxcore/pull/2025) chore: add continous fuzzing to our CI +- [#2024](https://github.com/TokTok/c-toxcore/pull/2024) perf: Reduce minimal encoding size of packed events. +- [#2023](https://github.com/TokTok/c-toxcore/pull/2023) cleanup: Add wrapper library for msgpack pack functions +- [#2022](https://github.com/TokTok/c-toxcore/pull/2022) cleanup: Split tox_unpack into two smaller libs +- [#2021](https://github.com/TokTok/c-toxcore/pull/2021) chore: Disable non-null attributes by default. +- [#2019](https://github.com/TokTok/c-toxcore/pull/2019) chore: Silence clang compile warnings causing circle-ci/asan to fail +- [#2018](https://github.com/TokTok/c-toxcore/pull/2018) fix: Coverty scan +- [#2016](https://github.com/TokTok/c-toxcore/pull/2016) docs: Add libmsgpack dependency in INSTALL.md +- [#2015](https://github.com/TokTok/c-toxcore/pull/2015) fix: shared toxcore autotools build failing +- [#2013](https://github.com/TokTok/c-toxcore/pull/2013) cleanup: Don't use VLAs for huge allocations. +- [#2011](https://github.com/TokTok/c-toxcore/pull/2011) fix: Conan build link failures +- [#2010](https://github.com/TokTok/c-toxcore/pull/2010) chore: Remove duplicate source file in autotools build. +- [#2008](https://github.com/TokTok/c-toxcore/pull/2008) chore: get skeletons out of the closet +- [#2007](https://github.com/TokTok/c-toxcore/pull/2007) feat: add bash-completion for tox-bootstrapd +- [#2006](https://github.com/TokTok/c-toxcore/pull/2006) cleanup: Add more nonnull and nullable annotations. +- [#2002](https://github.com/TokTok/c-toxcore/pull/2002) cleanup: Add nonnull annotations to function definitions. +- [#2001](https://github.com/TokTok/c-toxcore/pull/2001) chore: Add an undefined behaviour/integer sanitizer build. +- [#1999](https://github.com/TokTok/c-toxcore/pull/1999) chore: Speed up cmake builds with `UNITY_BUILD`. +- [#1996](https://github.com/TokTok/c-toxcore/pull/1996) feat: Add unpacker functions for events structures. +- [#1993](https://github.com/TokTok/c-toxcore/pull/1993) feat: Add binary packing functions for tox events. +- [#1992](https://github.com/TokTok/c-toxcore/pull/1992) chore: Set up an Android CI job +- [#1988](https://github.com/TokTok/c-toxcore/pull/1988) cleanup: Make LAN discovery thread-safe without data races. +- [#1987](https://github.com/TokTok/c-toxcore/pull/1987) cleanup: Comply with new cimple callback rules. +- [#1985](https://github.com/TokTok/c-toxcore/pull/1985) cleanup: Split msi callback array into 1 member per callback +- [#1982](https://github.com/TokTok/c-toxcore/pull/1982) chore: Add an easy way to run autotools and circleci builds locally. +- [#1979](https://github.com/TokTok/c-toxcore/pull/1979) chore: Update readme header +- [#1952](https://github.com/TokTok/c-toxcore/pull/1952) feat: Add async event handling (callbacks) code. +- [#1935](https://github.com/TokTok/c-toxcore/pull/1935) feat: add DHT queries to private API +- [#1668](https://github.com/TokTok/c-toxcore/pull/1668) perf: Take advantage of fast networks for file transfers + +### Closed issues: + +- [#2009](https://github.com/TokTok/c-toxcore/issues/2009) Autotools build fails +- [#2004](https://github.com/TokTok/c-toxcore/issues/2004) Add `nullable` and `nonnull` annotations to all functions. +- [#1998](https://github.com/TokTok/c-toxcore/issues/1998) Large stack allocations +- [#1977](https://github.com/TokTok/c-toxcore/issues/1977) Turn array of callbacks in msi.h into separate callbacks. +- [#1670](https://github.com/TokTok/c-toxcore/issues/1670) Broken link in readme +- [#405](https://github.com/TokTok/c-toxcore/issues/405) Remove all¹ uses of global state in toxcore +- [#340](https://github.com/TokTok/c-toxcore/issues/340) Set up a continuous integration build for Android +- [#236](https://github.com/TokTok/c-toxcore/issues/236) Tox file transfers do not use available bandwidth +- [#128](https://github.com/TokTok/c-toxcore/issues/128) Toxcore should provide an easy, local method for making sure Travis checks will pass + ## v0.2.15 ### Merged PRs: - [#1984](https://github.com/TokTok/c-toxcore/pull/1984) fix: Reduce logging verbosity even more. +- [#1983](https://github.com/TokTok/c-toxcore/pull/1983) chore: Release 0.2.15 - [#1980](https://github.com/TokTok/c-toxcore/pull/1980) fix: Reduce logging verbosity in TCP server. ## v0.2.14 diff --git a/protocols/Tox/libtox/docs/README.md b/protocols/Tox/libtox/docs/README.md index 5bb3a157b6..847ec7cd6f 100644 --- a/protocols/Tox/libtox/docs/README.md +++ b/protocols/Tox/libtox/docs/README.md @@ -1,9 +1,8 @@ #  -**Current build status:** [](https://travis-ci.org/TokTok/c-toxcore) -**Current Coverage:** [](https://coveralls.io/github/TokTok/c-toxcore?branch=master) +**Current Coverage:** [](https://codecov.io/gh/TokTok/c-toxcore) -[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://wiki.tox.chat/Binaries) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md) **|** [**Toxcore's Projects**](https://github.com/TokTok/c-toxcore/projects) +[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://tox.chat/download.html) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md) **IRC Channels:** Users: [#tox@libera.chat](https://web.libera.chat/#tox), Developers: [#toktok@libera.chat](https://web.libera.chat/#toktok) diff --git a/protocols/Tox/libtox/libtox.vcxproj b/protocols/Tox/libtox/libtox.vcxproj index 5f6c46837f..08c8a78f9d 100644 --- a/protocols/Tox/libtox/libtox.vcxproj +++ b/protocols/Tox/libtox/libtox.vcxproj @@ -37,12 +37,93 @@ </Link> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="src\toxcore\announce.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\bin_pack.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\bin_unpack.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\ccompat.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="src\toxcore\crypto_core.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> <ClCompile Include="src\toxcore\DHT.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClCompile Include="src\toxcore\events\conference_connected.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\conference_invite.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\conference_message.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\conference_peer_list_changed.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\conference_peer_name.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\conference_title.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\events_alloc.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\file_chunk_request.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\file_recv.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\file_recv_chunk.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\file_recv_control.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_connection_status.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_lossless_packet.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_lossy_packet.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_message.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_name.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_read_receipt.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_request.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_status.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_status_message.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\friend_typing.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\events\self_connection_status.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\forwarding.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="src\toxcore\friend_connection.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> @@ -52,6 +133,12 @@ <ClCompile Include="src\toxcore\group.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClCompile Include="src\toxcore\group_announce.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\group_moderation.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="src\toxcore\LAN_discovery.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> @@ -103,24 +190,47 @@ <ClCompile Include="src\toxcore\TCP_server.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClCompile Include="src\toxcore\timed_auth.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="src\toxcore\tox.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> <ClCompile Include="src\toxcore\tox_api.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClCompile Include="src\toxcore\tox_dispatch.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\tox_events.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\tox_private.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\toxcore\tox_unpack.c"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="src\toxcore\util.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> <ClCompile Include="src\toxencryptsave\toxencryptsave.c"> <PrecompiledHeader>NotUsing</PrecompiledHeader> </ClCompile> + <ClInclude Include="src\toxcore\announce.h" /> + <ClInclude Include="src\toxcore\attributes.h" /> + <ClInclude Include="src\toxcore\bin_pack.h" /> + <ClInclude Include="src\toxcore\bin_unpack.h" /> <ClInclude Include="src\toxcore\ccompat.h" /> <ClInclude Include="src\toxcore\crypto_core.h" /> <ClInclude Include="src\toxcore\DHT.h" /> + <ClInclude Include="src\toxcore\events\events_alloc.h" /> + <ClInclude Include="src\toxcore\forwarding.h" /> <ClInclude Include="src\toxcore\friend_connection.h" /> <ClInclude Include="src\toxcore\friend_requests.h" /> <ClInclude Include="src\toxcore\group.h" /> + <ClInclude Include="src\toxcore\group_announce.h" /> + <ClInclude Include="src\toxcore\group_moderation.h" /> <ClInclude Include="src\toxcore\LAN_discovery.h" /> <ClInclude Include="src\toxcore\list.h" /> <ClInclude Include="src\toxcore\logger.h" /> @@ -138,8 +248,13 @@ <ClInclude Include="src\toxcore\TCP_common.h" /> <ClInclude Include="src\toxcore\TCP_connection.h" /> <ClInclude Include="src\toxcore\TCP_server.h" /> + <ClInclude Include="src\toxcore\timed_auth.h" /> <ClInclude Include="src\toxcore\tox.h" /> + <ClInclude Include="src\toxcore\tox_dispatch.h" /> + <ClInclude Include="src\toxcore\tox_events.h" /> <ClInclude Include="src\toxcore\tox_private.h" /> + <ClInclude Include="src\toxcore\tox_struct.h" /> + <ClInclude Include="src\toxcore\tox_unpack.h" /> <ClInclude Include="src\toxcore\util.h" /> </ItemGroup> <ItemGroup> diff --git a/protocols/Tox/libtox/libtox.vcxproj.filters b/protocols/Tox/libtox/libtox.vcxproj.filters index df52d9ed37..05b489a663 100644 --- a/protocols/Tox/libtox/libtox.vcxproj.filters +++ b/protocols/Tox/libtox/libtox.vcxproj.filters @@ -15,12 +15,93 @@ </Filter> </ItemGroup> <ItemGroup> + <ClCompile Include="src\toxcore\announce.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\bin_pack.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\bin_unpack.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\ccompat.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="src\toxcore\crypto_core.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="src\toxcore\DHT.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\events\conference_connected.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\conference_invite.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\conference_message.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\conference_peer_list_changed.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\conference_peer_name.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\conference_title.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\events_alloc.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\file_chunk_request.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\file_recv.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\file_recv_chunk.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\file_recv_control.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_connection_status.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_lossless_packet.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_lossy_packet.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_message.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_name.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_read_receipt.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_request.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_status.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_status_message.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\friend_typing.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\events\self_connection_status.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\forwarding.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="src\toxcore\friend_connection.c"> <Filter>Source Files</Filter> </ClCompile> @@ -30,6 +111,12 @@ <ClCompile Include="src\toxcore\group.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\toxcore\group_announce.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\group_moderation.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="src\toxcore\LAN_discovery.c"> <Filter>Source Files</Filter> </ClCompile> @@ -81,12 +168,27 @@ <ClCompile Include="src\toxcore\TCP_server.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\toxcore\timed_auth.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="src\toxcore\tox.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="src\toxcore\tox_api.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\toxcore\tox_dispatch.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\tox_events.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\tox_private.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\toxcore\tox_unpack.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="src\toxcore\util.c"> <Filter>Source Files</Filter> </ClCompile> @@ -95,6 +197,18 @@ </ClCompile> </ItemGroup> <ItemGroup> + <ClInclude Include="src\toxcore\announce.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\attributes.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\bin_pack.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\bin_unpack.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\ccompat.h"> <Filter>Header Files</Filter> </ClInclude> @@ -104,6 +218,12 @@ <ClInclude Include="src\toxcore\DHT.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\toxcore\events\events_alloc.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\forwarding.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\friend_connection.h"> <Filter>Header Files</Filter> </ClInclude> @@ -113,6 +233,12 @@ <ClInclude Include="src\toxcore\group.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\toxcore\group_announce.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\group_moderation.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\LAN_discovery.h"> <Filter>Header Files</Filter> </ClInclude> @@ -164,12 +290,27 @@ <ClInclude Include="src\toxcore\TCP_server.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\toxcore\timed_auth.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\tox.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\toxcore\tox_dispatch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\tox_events.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\tox_private.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\toxcore\tox_struct.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\toxcore\tox_unpack.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="src\toxcore\util.h"> <Filter>Header Files</Filter> </ClInclude> diff --git a/protocols/Tox/libtox/src/third_party/cmp/LICENSE b/protocols/Tox/libtox/src/third_party/cmp/LICENSE new file mode 100644 index 0000000000..8adaa77c83 --- /dev/null +++ b/protocols/Tox/libtox/src/third_party/cmp/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2020 Charles Gunyon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/protocols/Tox/libtox/src/third_party/cmp/README.md b/protocols/Tox/libtox/src/third_party/cmp/README.md new file mode 100644 index 0000000000..a726ceb2a8 --- /dev/null +++ b/protocols/Tox/libtox/src/third_party/cmp/README.md @@ -0,0 +1,223 @@ +# CMP + +[](https://travis-ci.org/camgunz/cmp) [](https://coveralls.io/github/camgunz/cmp?branch=develop) + +CMP is a C implementation of the MessagePack serialization format. It +currently implements version 5 of the [MessagePack +Spec](http://github.com/msgpack/msgpack/blob/master/spec.md). + +CMP's goal is to be lightweight and straightforward, forcing nothing on the +programmer. + +## License + +While I'm a big believer in the GPL, I license CMP under the MIT license. + +## Example Usage + +The following examples use a file as the backend, and are modeled after the +examples included with the msgpack-c project. + +```C +#include <stdio.h> +#include <stdlib.h> + +#include "cmp.h" + +static bool read_bytes(void *data, size_t sz, FILE *fh) { + return fread(data, sizeof(uint8_t), sz, fh) == (sz * sizeof(uint8_t)); +} + +static bool file_reader(cmp_ctx_t *ctx, void *data, size_t limit) { + return read_bytes(data, limit, (FILE *)ctx->buf); +} + +static bool file_skipper(cmp_ctx_t *ctx, size_t count) { + return fseek((FILE *)ctx->buf, count, SEEK_CUR); +} + +static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) { + return fwrite(data, sizeof(uint8_t), count, (FILE *)ctx->buf); +} + +static void error_and_exit(const char *msg) { + fprintf(stderr, "%s\n\n", msg); + exit(EXIT_FAILURE); +} + +int main(void) { + FILE *fh = NULL; + cmp_ctx_t cmp = {0}; + uint32_t array_size = 0; + uint32_t str_size = 0; + char hello[6] = {0}; + char message_pack[12] = {0}; + + fh = fopen("cmp_data.dat", "w+b"); + + if (fh == NULL) { + error_and_exit("Error opening data.dat"); + } + + cmp_init(&cmp, fh, file_reader, file_skipper, file_writer); + + if (!cmp_write_array(&cmp, 2)) { + error_and_exit(cmp_strerror(&cmp)); + } + + if (!cmp_write_str(&cmp, "Hello", 5)) { + error_and_exit(cmp_strerror(&cmp)); + } + + if (!cmp_write_str(&cmp, "MessagePack", 11)) { + error_and_exit(cmp_strerror(&cmp)); + } + + rewind(fh); + + if (!cmp_read_array(&cmp, &array_size)) { + error_and_exit(cmp_strerror(&cmp)); + } + + /* You can read the str byte size and then read str bytes... */ + + if (!cmp_read_str_size(&cmp, &str_size)) { + error_and_exit(cmp_strerror(&cmp)); + } + + if (str_size > (sizeof(hello) - 1)) { + error_and_exit("Packed 'hello' length too long\n"); + } + + if (!read_bytes(hello, str_size, fh)) { + error_and_exit(cmp_strerror(&cmp)); + } + + /* + * ...or you can set the maximum number of bytes to read and do it all in + * one call + */ + + str_size = sizeof(message_pack); + if (!cmp_read_str(&cmp, message_pack, &str_size)) { + error_and_exit(cmp_strerror(&cmp)); + } + + printf("Array Length: %u.\n", array_size); + printf("[\"%s\", \"%s\"]\n", hello, message_pack); + + fclose(fh); + + return EXIT_SUCCESS; +} +``` + +## Advanced Usage + +See the `examples` folder. + +## Fast, Lightweight, Flexible, and Robust + +CMP uses no internal buffers; conversions, encoding and decoding are done on +the fly. + +CMP's source and header file together are ~4k LOC. + +CMP makes no heap allocations. + +CMP uses standardized types rather than declaring its own, and it depends only +on `stdbool.h`, `stdint.h` and `string.h`. + +CMP is written using C89 (ANSI C), aside, of course, from its use of +fixed-width integer types and `bool`. + +On the other hand, CMP's test suite requires C99. + +CMP only requires the programmer supply a read function, a write function, and +an optional skip function. In this way, the programmer can use CMP on memory, +files, sockets, etc. + +CMP is portable. It uses fixed-width integer types, and checks the endianness +of the machine at runtime before swapping bytes (MessagePack is big-endian). + +CMP provides a fairly comprehensive error reporting mechanism modeled after +`errno` and `strerror`. + +CMP is thread aware; while contexts cannot be shared between threads, each +thread may use its own context freely. + +CMP is tested using the MessagePack test suite as well as a large set of custom +test cases. Its small test program is compiled with clang using `-Wall -Werror +-Wextra ...` along with several other flags, and generates no compilation +errors in either clang or GCC. + +CMP's source is written as readably as possible, using explicit, descriptive +variable names and a consistent, clear style. + +CMP's source is written to be as secure as possible. Its testing suite checks +for invalid values, and data is always treated as suspect before it passes +validation. + +CMP's API is designed to be clear, convenient and unsurprising. Strings are +null-terminated, binary data is not, error codes are clear, and so on. + +CMP provides optional backwards compatibility for use with other MessagePack +implementations that only implement version 4 of the spec. + +## Building + +There is no build system for CMP. The programmer can drop `cmp.c` and `cmp.h` +in their source tree and modify as necessary. No special compiler settings are +required to build it, and it generates no compilation errors in either clang or +gcc. + +## Versioning + +CMP's versions are single integers. I don't use semantic versioning because +I don't guarantee that any version is completely compatible with any other. In +general, semantic versioning provides a false sense of security. You should be +evaluating compatibility yourself, not relying on some stranger's versioning +convention. + +## Stability + +I only guarantee stability for versions released on +[the releases page](../../releases). While rare, both `master` and `develop` +branches may have errors or mismatched versions. + +## Backwards Compatibility + +Version 4 of the MessagePack spec has no `BIN` type, and provides no `STR8` +marker. In order to remain backwards compatible with version 4 of MessagePack, +do the following: + +Avoid these functions: + + - `cmp_write_bin` + - `cmp_write_bin_marker` + - `cmp_write_str8_marker` + - `cmp_write_str8` + - `cmp_write_bin8_marker` + - `cmp_write_bin8` + - `cmp_write_bin16_marker` + - `cmp_write_bin16` + - `cmp_write_bin32_marker` + - `cmp_write_bin32` + +Use these functions in lieu of their v5 counterparts: + + - `cmp_write_str_marker_v4` instead of `cmp_write_str_marker` + - `cmp_write_str_v4` instead of `cmp_write_str` + - `cmp_write_object_v4` instead of `cmp_write_object` + +## Disabling Floating Point Operations + +Thanks to [tdragon](https://github.com/tdragon) it's possible to disable +floating point operations in CMP by defining `CMP_NO_FLOAT`. No floating point +functionality will be included. Fair warning: this changes the ABI. + +## Setting Endianness at Compile Time + +CMP will honor `WORDS_BIGENDIAN`. If defined to `0` it will convert data +to/from little-endian format when writing/reading. If defined to `1` it won't. +If not defined, CMP will check at runtime. diff --git a/protocols/Tox/libtox/src/third_party/cmp/cmp.c b/protocols/Tox/libtox/src/third_party/cmp/cmp.c new file mode 100644 index 0000000000..405b5780b2 --- /dev/null +++ b/protocols/Tox/libtox/src/third_party/cmp/cmp.c @@ -0,0 +1,3561 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Charles Gunyon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "cmp.h" + +static const uint32_t version = 20; +static const uint32_t mp_version = 5; + +enum { + POSITIVE_FIXNUM_MARKER = 0x00, + FIXMAP_MARKER = 0x80, + FIXARRAY_MARKER = 0x90, + FIXSTR_MARKER = 0xA0, + NIL_MARKER = 0xC0, + FALSE_MARKER = 0xC2, + TRUE_MARKER = 0xC3, + BIN8_MARKER = 0xC4, + BIN16_MARKER = 0xC5, + BIN32_MARKER = 0xC6, + EXT8_MARKER = 0xC7, + EXT16_MARKER = 0xC8, + EXT32_MARKER = 0xC9, + FLOAT_MARKER = 0xCA, + DOUBLE_MARKER = 0xCB, + U8_MARKER = 0xCC, + U16_MARKER = 0xCD, + U32_MARKER = 0xCE, + U64_MARKER = 0xCF, + S8_MARKER = 0xD0, + S16_MARKER = 0xD1, + S32_MARKER = 0xD2, + S64_MARKER = 0xD3, + FIXEXT1_MARKER = 0xD4, + FIXEXT2_MARKER = 0xD5, + FIXEXT4_MARKER = 0xD6, + FIXEXT8_MARKER = 0xD7, + FIXEXT16_MARKER = 0xD8, + STR8_MARKER = 0xD9, + STR16_MARKER = 0xDA, + STR32_MARKER = 0xDB, + ARRAY16_MARKER = 0xDC, + ARRAY32_MARKER = 0xDD, + MAP16_MARKER = 0xDE, + MAP32_MARKER = 0xDF, + NEGATIVE_FIXNUM_MARKER = 0xE0 +}; + +enum { + FIXARRAY_SIZE = 0xF, + FIXMAP_SIZE = 0xF, + FIXSTR_SIZE = 0x1F +}; + +enum { + ERROR_NONE, + STR_DATA_LENGTH_TOO_LONG_ERROR, + BIN_DATA_LENGTH_TOO_LONG_ERROR, + ARRAY_LENGTH_TOO_LONG_ERROR, + MAP_LENGTH_TOO_LONG_ERROR, + INPUT_VALUE_TOO_LARGE_ERROR, + FIXED_VALUE_WRITING_ERROR, + TYPE_MARKER_READING_ERROR, + TYPE_MARKER_WRITING_ERROR, + DATA_READING_ERROR, + DATA_WRITING_ERROR, + EXT_TYPE_READING_ERROR, + EXT_TYPE_WRITING_ERROR, + INVALID_TYPE_ERROR, + LENGTH_READING_ERROR, + LENGTH_WRITING_ERROR, + SKIP_DEPTH_LIMIT_EXCEEDED_ERROR, + INTERNAL_ERROR, + DISABLED_FLOATING_POINT_ERROR, + ERROR_MAX +}; + +static const char * const cmp_error_messages[ERROR_MAX + 1] = { + "No Error", + "Specified string data length is too long (> 0xFFFFFFFF)", + "Specified binary data length is too long (> 0xFFFFFFFF)", + "Specified array length is too long (> 0xFFFFFFFF)", + "Specified map length is too long (> 0xFFFFFFFF)", + "Input value is too large", + "Error writing fixed value", + "Error reading type marker", + "Error writing type marker", + "Error reading packed data", + "Error writing packed data", + "Error reading ext type", + "Error writing ext type", + "Invalid type", + "Error reading size", + "Error writing size", + "Depth limit exceeded while skipping", + "Internal error", + "Floating point operations disabled", + "Max Error" +}; + +#ifdef WORDS_BIGENDIAN +#define is_bigendian() (WORDS_BIGENDIAN) +#else +static const int32_t _i = 1; +#define is_bigendian() ((*(const char *)&_i) == 0) +#endif + +static uint16_t be16(uint16_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = b[0]; + b[0] = b[1]; + b[1] = swap; + } + + return x; +} + +static int16_t sbe16(int16_t x) { + return (int16_t)be16((uint16_t)x); +} + +static uint32_t be32(uint32_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = b[0]; + b[0] = b[3]; + b[3] = swap; + + swap = b[1]; + b[1] = b[2]; + b[2] = swap; + } + + return x; +} + +static int32_t sbe32(int32_t x) { + return (int32_t)be32((uint32_t)x); +} + +static uint64_t be64(uint64_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap; + + swap = b[0]; + b[0] = b[7]; + b[7] = swap; + + swap = b[1]; + b[1] = b[6]; + b[6] = swap; + + swap = b[2]; + b[2] = b[5]; + b[5] = swap; + + swap = b[3]; + b[3] = b[4]; + b[4] = swap; + } + + return x; +} + +static int64_t sbe64(int64_t x) { + return (int64_t)be64((uint64_t)x); +} + +#ifndef CMP_NO_FLOAT +static float decode_befloat(const char *b) { + float f = 0.; + char *fb = (char *)&f; + + if (!is_bigendian()) { + fb[0] = b[3]; + fb[1] = b[2]; + fb[2] = b[1]; + fb[3] = b[0]; + } + else { + fb[0] = b[0]; + fb[1] = b[1]; + fb[2] = b[2]; + fb[3] = b[3]; + } + + return f; +} + +static double decode_bedouble(const char *b) { + double d = 0.; + char *db = (char *)&d; + + if (!is_bigendian()) { + db[0] = b[7]; + db[1] = b[6]; + db[2] = b[5]; + db[3] = b[4]; + db[4] = b[3]; + db[5] = b[2]; + db[6] = b[1]; + db[7] = b[0]; + } + else { + db[0] = b[0]; + db[1] = b[1]; + db[2] = b[2]; + db[3] = b[3]; + db[4] = b[4]; + db[5] = b[5]; + db[6] = b[6]; + db[7] = b[7]; + } + + return d; +} +#endif /* CMP_NO_FLOAT */ + +static bool read_byte(cmp_ctx_t *ctx, uint8_t *x) { + return ctx->read(ctx, x, sizeof(uint8_t)); +} + +static bool write_byte(cmp_ctx_t *ctx, uint8_t x) { + return (ctx->write(ctx, &x, sizeof(uint8_t)) == (sizeof(uint8_t))); +} + +static bool skip_bytes(cmp_ctx_t *ctx, size_t count) { + if (ctx->skip) { + return ctx->skip(ctx, count); + } + else { + uint8_t floor; + size_t i; + + for (i = 0; i < count; i++) { + if (!ctx->read(ctx, &floor, sizeof(uint8_t))) { + return false; + } + } + + return true; + } +} + +static bool read_type_marker(cmp_ctx_t *ctx, uint8_t *marker) { + if (read_byte(ctx, marker)) { + return true; + } + + ctx->error = TYPE_MARKER_READING_ERROR; + return false; +} + +static bool write_type_marker(cmp_ctx_t *ctx, uint8_t marker) { + if (write_byte(ctx, marker)) + return true; + + ctx->error = TYPE_MARKER_WRITING_ERROR; + return false; +} + +static bool write_fixed_value(cmp_ctx_t *ctx, uint8_t value) { + if (write_byte(ctx, value)) + return true; + + ctx->error = FIXED_VALUE_WRITING_ERROR; + return false; +} + +static bool type_marker_to_cmp_type(uint8_t type_marker, uint8_t *cmp_type) { + if (type_marker <= 0x7F) { + *cmp_type = CMP_TYPE_POSITIVE_FIXNUM; + return true; + } + + if (type_marker <= 0x8F) { + *cmp_type = CMP_TYPE_FIXMAP; + return true; + } + + if (type_marker <= 0x9F) { + *cmp_type = CMP_TYPE_FIXARRAY; + return true; + } + + if (type_marker <= 0xBF) { + *cmp_type = CMP_TYPE_FIXSTR; + return true; + } + + if (type_marker >= 0xE0) { + *cmp_type = CMP_TYPE_NEGATIVE_FIXNUM; + return true; + } + + switch (type_marker) { + case NIL_MARKER: + *cmp_type = CMP_TYPE_NIL; + return true; + case FALSE_MARKER: + *cmp_type = CMP_TYPE_BOOLEAN; + return true; + case TRUE_MARKER: + *cmp_type = CMP_TYPE_BOOLEAN; + return true; + case BIN8_MARKER: + *cmp_type = CMP_TYPE_BIN8; + return true; + case BIN16_MARKER: + *cmp_type = CMP_TYPE_BIN16; + return true; + case BIN32_MARKER: + *cmp_type = CMP_TYPE_BIN32; + return true; + case EXT8_MARKER: + *cmp_type = CMP_TYPE_EXT8; + return true; + case EXT16_MARKER: + *cmp_type = CMP_TYPE_EXT16; + return true; + case EXT32_MARKER: + *cmp_type = CMP_TYPE_EXT32; + return true; + case FLOAT_MARKER: + *cmp_type = CMP_TYPE_FLOAT; + return true; + case DOUBLE_MARKER: + *cmp_type = CMP_TYPE_DOUBLE; + return true; + case U8_MARKER: + *cmp_type = CMP_TYPE_UINT8; + return true; + case U16_MARKER: + *cmp_type = CMP_TYPE_UINT16; + return true; + case U32_MARKER: + *cmp_type = CMP_TYPE_UINT32; + return true; + case U64_MARKER: + *cmp_type = CMP_TYPE_UINT64; + return true; + case S8_MARKER: + *cmp_type = CMP_TYPE_SINT8; + return true; + case S16_MARKER: + *cmp_type = CMP_TYPE_SINT16; + return true; + case S32_MARKER: + *cmp_type = CMP_TYPE_SINT32; + return true; + case S64_MARKER: + *cmp_type = CMP_TYPE_SINT64; + return true; + case FIXEXT1_MARKER: + *cmp_type = CMP_TYPE_FIXEXT1; + return true; + case FIXEXT2_MARKER: + *cmp_type = CMP_TYPE_FIXEXT2; + return true; + case FIXEXT4_MARKER: + *cmp_type = CMP_TYPE_FIXEXT4; + return true; + case FIXEXT8_MARKER: + *cmp_type = CMP_TYPE_FIXEXT8; + return true; + case FIXEXT16_MARKER: + *cmp_type = CMP_TYPE_FIXEXT16; + return true; + case STR8_MARKER: + *cmp_type = CMP_TYPE_STR8; + return true; + case STR16_MARKER: + *cmp_type = CMP_TYPE_STR16; + return true; + case STR32_MARKER: + *cmp_type = CMP_TYPE_STR32; + return true; + case ARRAY16_MARKER: + *cmp_type = CMP_TYPE_ARRAY16; + return true; + case ARRAY32_MARKER: + *cmp_type = CMP_TYPE_ARRAY32; + return true; + case MAP16_MARKER: + *cmp_type = CMP_TYPE_MAP16; + return true; + case MAP32_MARKER: + *cmp_type = CMP_TYPE_MAP32; + return true; + default: + return false; + } +} + +static bool read_type_size(cmp_ctx_t *ctx, uint8_t type_marker, + uint8_t cmp_type, + uint32_t *size) { + uint8_t u8temp = 0; + uint16_t u16temp = 0; + uint32_t u32temp = 0; + + switch (cmp_type) { + case CMP_TYPE_POSITIVE_FIXNUM: + *size = 0; + return true; + case CMP_TYPE_FIXMAP: + *size = (type_marker & FIXMAP_SIZE); + return true; + case CMP_TYPE_FIXARRAY: + *size = (type_marker & FIXARRAY_SIZE); + return true; + case CMP_TYPE_FIXSTR: + *size = (type_marker & FIXSTR_SIZE); + return true; + case CMP_TYPE_NIL: + *size = 0; + return true; + case CMP_TYPE_BOOLEAN: + *size = 0; + return true; + case CMP_TYPE_BIN8: + if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = u8temp; + return true; + case CMP_TYPE_BIN16: + if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = be16(u16temp); + return true; + case CMP_TYPE_BIN32: + if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = be32(u32temp); + return true; + case CMP_TYPE_EXT8: + if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = u8temp; + return true; + case CMP_TYPE_EXT16: + if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = be16(u16temp); + return true; + case CMP_TYPE_EXT32: + if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + *size = be32(u32temp); + return true; + case CMP_TYPE_FLOAT: + *size = 4; + return true; + case CMP_TYPE_DOUBLE: + *size = 8; + return true; + case CMP_TYPE_UINT8: + *size = 1; + return true; + case CMP_TYPE_UINT16: + *size = 2; + return true; + case CMP_TYPE_UINT32: + *size = 4; + return true; + case CMP_TYPE_UINT64: + *size = 8; + return true; + case CMP_TYPE_SINT8: + *size = 1; + return true; + case CMP_TYPE_SINT16: + *size = 2; + return true; + case CMP_TYPE_SINT32: + *size = 4; + return true; + case CMP_TYPE_SINT64: + *size = 8; + return true; + case CMP_TYPE_FIXEXT1: + *size = 1; + return true; + case CMP_TYPE_FIXEXT2: + *size = 2; + return true; + case CMP_TYPE_FIXEXT4: + *size = 4; + return true; + case CMP_TYPE_FIXEXT8: + *size = 8; + return true; + case CMP_TYPE_FIXEXT16: + *size = 16; + return true; + case CMP_TYPE_STR8: + if (!ctx->read(ctx, &u8temp, sizeof(uint8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = u8temp; + return true; + case CMP_TYPE_STR16: + if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be16(u16temp); + return true; + case CMP_TYPE_STR32: + if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be32(u32temp); + return true; + case CMP_TYPE_ARRAY16: + if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be16(u16temp); + return true; + case CMP_TYPE_ARRAY32: + if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be32(u32temp); + return true; + case CMP_TYPE_MAP16: + if (!ctx->read(ctx, &u16temp, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be16(u16temp); + return true; + case CMP_TYPE_MAP32: + if (!ctx->read(ctx, &u32temp, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + *size = be32(u32temp); + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + *size = 0; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +static bool read_obj_data(cmp_ctx_t *ctx, uint8_t type_marker, + cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + obj->as.u8 = type_marker; + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + obj->as.s8 = (int8_t)type_marker; + return true; + case CMP_TYPE_NIL: + obj->as.u8 = 0; + return true; + case CMP_TYPE_BOOLEAN: + switch (type_marker) { + case TRUE_MARKER: + obj->as.boolean = true; + return true; + case FALSE_MARKER: + obj->as.boolean = false; + return true; + default: + break; + } + ctx->error = INTERNAL_ERROR; + return false; + case CMP_TYPE_UINT8: + if (!ctx->read(ctx, &obj->as.u8, sizeof(uint8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + return true; + case CMP_TYPE_UINT16: + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u16 = be16(obj->as.u16); + return true; + case CMP_TYPE_UINT32: + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u32 = be32(obj->as.u32); + return true; + case CMP_TYPE_UINT64: + if (!ctx->read(ctx, &obj->as.u64, sizeof(uint64_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u64 = be64(obj->as.u64); + return true; + case CMP_TYPE_SINT8: + if (!ctx->read(ctx, &obj->as.s8, sizeof(int8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + return true; + case CMP_TYPE_SINT16: + if (!ctx->read(ctx, &obj->as.s16, sizeof(int16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s16 = sbe16(obj->as.s16); + return true; + case CMP_TYPE_SINT32: + if (!ctx->read(ctx, &obj->as.s32, sizeof(int32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s32 = sbe32(obj->as.s32); + return true; + case CMP_TYPE_SINT64: + if (!ctx->read(ctx, &obj->as.s64, sizeof(int64_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s64 = sbe64(obj->as.s64); + return true; + case CMP_TYPE_FLOAT: + { +#ifndef CMP_NO_FLOAT + char bytes[4]; + + if (!ctx->read(ctx, bytes, 4)) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.flt = decode_befloat(bytes); + return true; +#else /* CMP_NO_FLOAT */ + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif /* CMP_NO_FLOAT */ + } + case CMP_TYPE_DOUBLE: + { +#ifndef CMP_NO_FLOAT + char bytes[8]; + + if (!ctx->read(ctx, bytes, 8)) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.dbl = decode_bedouble(bytes); + return true; +#else /* CMP_NO_FLOAT */ + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif /* CMP_NO_FLOAT */ + } + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + return read_type_size(ctx, type_marker, obj->type, &obj->as.bin_size); + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + return read_type_size(ctx, type_marker, obj->type, &obj->as.str_size); + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + return read_type_size(ctx, type_marker, obj->type, &obj->as.array_size); + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + return read_type_size(ctx, type_marker, obj->type, &obj->as.map_size); + case CMP_TYPE_FIXEXT1: + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 1; + return true; + case CMP_TYPE_FIXEXT2: + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 2; + return true; + case CMP_TYPE_FIXEXT4: + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 4; + return true; + case CMP_TYPE_FIXEXT8: + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 8; + return true; + case CMP_TYPE_FIXEXT16: + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 16; + return true; + case CMP_TYPE_EXT8: + if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) { + return false; + } + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + return true; + case CMP_TYPE_EXT16: + if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) { + return false; + } + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + return true; + case CMP_TYPE_EXT32: + if (!read_type_size(ctx, type_marker, obj->type, &obj->as.ext.size)) { + return false; + } + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + return true; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read, + cmp_skipper skip, + cmp_writer write) { + ctx->error = ERROR_NONE; + ctx->buf = buf; + ctx->read = read; + ctx->skip = skip; + ctx->write = write; +} + +uint32_t cmp_version(void) { + return version; +} + +uint32_t cmp_mp_version(void) { + return mp_version; +} + +const char* cmp_strerror(cmp_ctx_t *ctx) { + if (ctx->error > ERROR_NONE && ctx->error < ERROR_MAX) + return cmp_error_messages[ctx->error]; + + return ""; +} + +bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c) { + if (c <= 0x7F) + return write_fixed_value(ctx, c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c) { + if (c >= -32 && c <= -1) + return write_fixed_value(ctx, (uint8_t)c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c) { + if (c >= 0) + return cmp_write_pfix(ctx, (uint8_t)c); + if (c >= -32 && c <= -1) + return cmp_write_nfix(ctx, c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c) { + if (!write_type_marker(ctx, S8_MARKER)) + return false; + + return ctx->write(ctx, &c, sizeof(int8_t)); +} + +bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s) { + if (!write_type_marker(ctx, S16_MARKER)) + return false; + + s = sbe16(s); + + return ctx->write(ctx, &s, sizeof(int16_t)); +} + +bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i) { + if (!write_type_marker(ctx, S32_MARKER)) + return false; + + i = sbe32(i); + + return ctx->write(ctx, &i, sizeof(int32_t)); +} + +bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l) { + if (!write_type_marker(ctx, S64_MARKER)) + return false; + + l = sbe64(l); + + return ctx->write(ctx, &l, sizeof(int64_t)); +} + +bool cmp_write_integer(cmp_ctx_t *ctx, int64_t d) { + if (d >= 0) + return cmp_write_uinteger(ctx, (uint64_t)d); + if (d >= -32) + return cmp_write_nfix(ctx, (int8_t)d); + if (d >= -128) + return cmp_write_s8(ctx, (int8_t)d); + if (d >= -32768) + return cmp_write_s16(ctx, (int16_t)d); + if (d >= (-2147483647 - 1)) + return cmp_write_s32(ctx, (int32_t)d); + + return cmp_write_s64(ctx, d); +} + +bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c) { + return cmp_write_pfix(ctx, c); +} + +bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c) { + if (!write_type_marker(ctx, U8_MARKER)) + return false; + + return ctx->write(ctx, &c, sizeof(uint8_t)); +} + +bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s) { + if (!write_type_marker(ctx, U16_MARKER)) + return false; + + s = be16(s); + + return ctx->write(ctx, &s, sizeof(uint16_t)); +} + +bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i) { + if (!write_type_marker(ctx, U32_MARKER)) + return false; + + i = be32(i); + + return ctx->write(ctx, &i, sizeof(uint32_t)); +} + +bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l) { + if (!write_type_marker(ctx, U64_MARKER)) + return false; + + l = be64(l); + + return ctx->write(ctx, &l, sizeof(uint64_t)); +} + +bool cmp_write_uinteger(cmp_ctx_t *ctx, uint64_t u) { + if (u <= 0x7F) + return cmp_write_pfix(ctx, (uint8_t)u); + if (u <= 0xFF) + return cmp_write_u8(ctx, (uint8_t)u); + if (u <= 0xFFFF) + return cmp_write_u16(ctx, (uint16_t)u); + if (u <= 0xFFFFFFFF) + return cmp_write_u32(ctx, (uint32_t)u); + + return cmp_write_u64(ctx, u); +} + +#ifndef CMP_NO_FLOAT +bool cmp_write_float(cmp_ctx_t *ctx, float f) { + if (!write_type_marker(ctx, FLOAT_MARKER)) + return false; + + /* + * We may need to swap the float's bytes, but we can't just swap them inside + * the float because the swapped bytes may not constitute a valid float. + * Therefore, we have to create a buffer and swap the bytes there. + */ + if (!is_bigendian()) { + char swapped[sizeof(float)]; + char *fbuf = (char *)&f; + size_t i; + + for (i = 0; i < sizeof(float); i++) + swapped[i] = fbuf[sizeof(float) - i - 1]; + + return ctx->write(ctx, swapped, sizeof(float)); + } + + return ctx->write(ctx, &f, sizeof(float)); +} + +bool cmp_write_double(cmp_ctx_t *ctx, double d) { + if (!write_type_marker(ctx, DOUBLE_MARKER)) + return false; + + /* Same deal for doubles */ + if (!is_bigendian()) { + char swapped[sizeof(double)]; + char *dbuf = (char *)&d; + size_t i; + + for (i = 0; i < sizeof(double); i++) + swapped[i] = dbuf[sizeof(double) - i - 1]; + + return ctx->write(ctx, swapped, sizeof(double)); + } + + return ctx->write(ctx, &d, sizeof(double)); +} + +bool cmp_write_decimal(cmp_ctx_t *ctx, double d) { + float f = (float)d; + double df = (double)f; + + if (df == d) + return cmp_write_float(ctx, f); + else + return cmp_write_double(ctx, d); +} +#endif /* CMP_NO_FLOAT */ + +bool cmp_write_nil(cmp_ctx_t *ctx) { + return write_type_marker(ctx, NIL_MARKER); +} + +bool cmp_write_true(cmp_ctx_t *ctx) { + return write_type_marker(ctx, TRUE_MARKER); +} + +bool cmp_write_false(cmp_ctx_t *ctx) { + return write_type_marker(ctx, FALSE_MARKER); +} + +bool cmp_write_bool(cmp_ctx_t *ctx, bool b) { + if (b) + return cmp_write_true(ctx); + + return cmp_write_false(ctx); +} + +bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b) { + if (b) + return cmp_write_true(ctx); + + return cmp_write_false(ctx); +} + +bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXSTR_SIZE) + return write_fixed_value(ctx, FIXSTR_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size) { + if (!cmp_write_fixstr_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size) { + if (!write_type_marker(ctx, STR8_MARKER)) + return false; + + if (ctx->write(ctx, &size, sizeof(uint8_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size) { + if (!cmp_write_str8_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, STR16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size) { + if (!cmp_write_str16_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, STR32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size) { + if (!cmp_write_str32_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr_marker(ctx, (uint8_t)size); + if (size <= 0xFF) + return cmp_write_str8_marker(ctx, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_str16_marker(ctx, (uint16_t)size); + + return cmp_write_str32_marker(ctx, size); +} + +bool cmp_write_str_marker_v4(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr_marker(ctx, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_str16_marker(ctx, (uint16_t)size); + + return cmp_write_str32_marker(ctx, size); +} + +bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr(ctx, data, (uint8_t)size); + if (size <= 0xFF) + return cmp_write_str8(ctx, data, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_str16(ctx, data, (uint16_t)size); + + return cmp_write_str32(ctx, data, size); +} + +bool cmp_write_str_v4(cmp_ctx_t *ctx, const char *data, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr(ctx, data, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_str16(ctx, data, (uint16_t)size); + + return cmp_write_str32(ctx, data, size); +} + +bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size) { + if (!write_type_marker(ctx, BIN8_MARKER)) + return false; + + if (ctx->write(ctx, &size, sizeof(uint8_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size) { + if (!cmp_write_bin8_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, BIN16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size) { + if (!cmp_write_bin16_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, BIN32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size) { + if (!cmp_write_bin32_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size) { + if (size <= 0xFF) + return cmp_write_bin8_marker(ctx, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_bin16_marker(ctx, (uint16_t)size); + + return cmp_write_bin32_marker(ctx, size); +} + +bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size) { + if (size <= 0xFF) + return cmp_write_bin8(ctx, data, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_bin16(ctx, data, (uint16_t)size); + + return cmp_write_bin32(ctx, data, size); +} + +bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXARRAY_SIZE) + return write_fixed_value(ctx, FIXARRAY_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, ARRAY16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, ARRAY32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXARRAY_SIZE) + return cmp_write_fixarray(ctx, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_array16(ctx, (uint16_t)size); + + return cmp_write_array32(ctx, size); +} + +bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXMAP_SIZE) + return write_fixed_value(ctx, FIXMAP_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, MAP16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, MAP32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXMAP_SIZE) + return cmp_write_fixmap(ctx, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_map16(ctx, (uint16_t)size); + + return cmp_write_map32(ctx, size); +} + +bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT1_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext1_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 1)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT2_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext2_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 2)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT4_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext4_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 4)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT8_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext8_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 8)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT16_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext16_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 16)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size) { + if (!write_type_marker(ctx, EXT8_MARKER)) + return false; + + if (!ctx->write(ctx, &size, sizeof(uint8_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size, const void *data) { + if (!cmp_write_ext8_marker(ctx, type, size)) + return false; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size) { + if (!write_type_marker(ctx, EXT16_MARKER)) + return false; + + size = be16(size); + + if (!ctx->write(ctx, &size, sizeof(uint16_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size, const void *data) { + if (!cmp_write_ext16_marker(ctx, type, size)) + return false; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size) { + if (!write_type_marker(ctx, EXT32_MARKER)) + return false; + + size = be32(size); + + if (!ctx->write(ctx, &size, sizeof(uint32_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data) { + if (!cmp_write_ext32_marker(ctx, type, size)) + return false; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size) { + if (size == 1) + return cmp_write_fixext1_marker(ctx, type); + if (size == 2) + return cmp_write_fixext2_marker(ctx, type); + if (size == 4) + return cmp_write_fixext4_marker(ctx, type); + if (size == 8) + return cmp_write_fixext8_marker(ctx, type); + if (size == 16) + return cmp_write_fixext16_marker(ctx, type); + if (size <= 0xFF) + return cmp_write_ext8_marker(ctx, type, (uint8_t)size); + if (size <= 0xFFFF) + return cmp_write_ext16_marker(ctx, type, (uint16_t)size); + + return cmp_write_ext32_marker(ctx, type, size); +} + +bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size, const void *data) { + if (size == 1) + return cmp_write_fixext1(ctx, type, data); + if (size == 2) + return cmp_write_fixext2(ctx, type, data); + if (size == 4) + return cmp_write_fixext4(ctx, type, data); + if (size == 8) + return cmp_write_fixext8(ctx, type, data); + if (size == 16) + return cmp_write_fixext16(ctx, type, data); + if (size <= 0xFF) + return cmp_write_ext8(ctx, type, (uint8_t)size, data); + if (size <= 0xFFFF) + return cmp_write_ext16(ctx, type, (uint16_t)size, data); + + return cmp_write_ext32(ctx, type, size, data); +} + +bool cmp_write_object(cmp_ctx_t *ctx, const cmp_object_t *obj) { + switch(obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + return cmp_write_pfix(ctx, obj->as.u8); + case CMP_TYPE_FIXMAP: + return cmp_write_fixmap(ctx, (uint8_t)obj->as.map_size); + case CMP_TYPE_FIXARRAY: + return cmp_write_fixarray(ctx, (uint8_t)obj->as.array_size); + case CMP_TYPE_FIXSTR: + return cmp_write_fixstr_marker(ctx, (uint8_t)obj->as.str_size); + case CMP_TYPE_NIL: + return cmp_write_nil(ctx); + case CMP_TYPE_BOOLEAN: + if (obj->as.boolean) + return cmp_write_true(ctx); + return cmp_write_false(ctx); + case CMP_TYPE_BIN8: + return cmp_write_bin8_marker(ctx, (uint8_t)obj->as.bin_size); + case CMP_TYPE_BIN16: + return cmp_write_bin16_marker(ctx, (uint16_t)obj->as.bin_size); + case CMP_TYPE_BIN32: + return cmp_write_bin32_marker(ctx, obj->as.bin_size); + case CMP_TYPE_EXT8: + return cmp_write_ext8_marker( + ctx, obj->as.ext.type, (uint8_t)obj->as.ext.size + ); + case CMP_TYPE_EXT16: + return cmp_write_ext16_marker( + ctx, obj->as.ext.type, (uint16_t)obj->as.ext.size + ); + case CMP_TYPE_EXT32: + return cmp_write_ext32_marker(ctx, obj->as.ext.type, obj->as.ext.size); + case CMP_TYPE_FLOAT: +#ifndef CMP_NO_FLOAT + return cmp_write_float(ctx, obj->as.flt); +#else /* CMP_NO_FLOAT */ + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif /* CMP_NO_FLOAT */ + case CMP_TYPE_DOUBLE: +#ifndef CMP_NO_FLOAT + return cmp_write_double(ctx, obj->as.dbl); +#else /* CMP_NO_FLOAT */ + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif + case CMP_TYPE_UINT8: + return cmp_write_u8(ctx, obj->as.u8); + case CMP_TYPE_UINT16: + return cmp_write_u16(ctx, obj->as.u16); + case CMP_TYPE_UINT32: + return cmp_write_u32(ctx, obj->as.u32); + case CMP_TYPE_UINT64: + return cmp_write_u64(ctx, obj->as.u64); + case CMP_TYPE_SINT8: + return cmp_write_s8(ctx, obj->as.s8); + case CMP_TYPE_SINT16: + return cmp_write_s16(ctx, obj->as.s16); + case CMP_TYPE_SINT32: + return cmp_write_s32(ctx, obj->as.s32); + case CMP_TYPE_SINT64: + return cmp_write_s64(ctx, obj->as.s64); + case CMP_TYPE_FIXEXT1: + return cmp_write_fixext1_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT2: + return cmp_write_fixext2_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT4: + return cmp_write_fixext4_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT8: + return cmp_write_fixext8_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT16: + return cmp_write_fixext16_marker(ctx, obj->as.ext.type); + case CMP_TYPE_STR8: + return cmp_write_str8_marker(ctx, (uint8_t)obj->as.str_size); + case CMP_TYPE_STR16: + return cmp_write_str16_marker(ctx, (uint16_t)obj->as.str_size); + case CMP_TYPE_STR32: + return cmp_write_str32_marker(ctx, obj->as.str_size); + case CMP_TYPE_ARRAY16: + return cmp_write_array16(ctx, (uint16_t)obj->as.array_size); + case CMP_TYPE_ARRAY32: + return cmp_write_array32(ctx, obj->as.array_size); + case CMP_TYPE_MAP16: + return cmp_write_map16(ctx, (uint16_t)obj->as.map_size); + case CMP_TYPE_MAP32: + return cmp_write_map32(ctx, obj->as.map_size); + case CMP_TYPE_NEGATIVE_FIXNUM: + return cmp_write_nfix(ctx, obj->as.s8); + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_write_object_v4(cmp_ctx_t *ctx, const cmp_object_t *obj) { + switch(obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + return cmp_write_pfix(ctx, obj->as.u8); + case CMP_TYPE_FIXMAP: + return cmp_write_fixmap(ctx, (uint8_t)obj->as.map_size); + case CMP_TYPE_FIXARRAY: + return cmp_write_fixarray(ctx, (uint8_t)obj->as.array_size); + case CMP_TYPE_FIXSTR: + return cmp_write_fixstr_marker(ctx, (uint8_t)obj->as.str_size); + case CMP_TYPE_NIL: + return cmp_write_nil(ctx); + case CMP_TYPE_BOOLEAN: + if (obj->as.boolean) + return cmp_write_true(ctx); + return cmp_write_false(ctx); + case CMP_TYPE_EXT8: + return cmp_write_ext8_marker(ctx, obj->as.ext.type, (uint8_t)obj->as.ext.size); + case CMP_TYPE_EXT16: + return cmp_write_ext16_marker( + ctx, obj->as.ext.type, (uint16_t)obj->as.ext.size + ); + case CMP_TYPE_EXT32: + return cmp_write_ext32_marker(ctx, obj->as.ext.type, obj->as.ext.size); + case CMP_TYPE_FLOAT: +#ifndef CMP_NO_FLOAT + return cmp_write_float(ctx, obj->as.flt); +#else /* CMP_NO_FLOAT */ + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif + case CMP_TYPE_DOUBLE: +#ifndef CMP_NO_FLOAT + return cmp_write_double(ctx, obj->as.dbl); +#else + ctx->error = DISABLED_FLOATING_POINT_ERROR; + return false; +#endif + case CMP_TYPE_UINT8: + return cmp_write_u8(ctx, obj->as.u8); + case CMP_TYPE_UINT16: + return cmp_write_u16(ctx, obj->as.u16); + case CMP_TYPE_UINT32: + return cmp_write_u32(ctx, obj->as.u32); + case CMP_TYPE_UINT64: + return cmp_write_u64(ctx, obj->as.u64); + case CMP_TYPE_SINT8: + return cmp_write_s8(ctx, obj->as.s8); + case CMP_TYPE_SINT16: + return cmp_write_s16(ctx, obj->as.s16); + case CMP_TYPE_SINT32: + return cmp_write_s32(ctx, obj->as.s32); + case CMP_TYPE_SINT64: + return cmp_write_s64(ctx, obj->as.s64); + case CMP_TYPE_FIXEXT1: + return cmp_write_fixext1_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT2: + return cmp_write_fixext2_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT4: + return cmp_write_fixext4_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT8: + return cmp_write_fixext8_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT16: + return cmp_write_fixext16_marker(ctx, obj->as.ext.type); + case CMP_TYPE_STR16: + return cmp_write_str16_marker(ctx, (uint16_t)obj->as.str_size); + case CMP_TYPE_STR32: + return cmp_write_str32_marker(ctx, obj->as.str_size); + case CMP_TYPE_ARRAY16: + return cmp_write_array16(ctx, (uint16_t)obj->as.array_size); + case CMP_TYPE_ARRAY32: + return cmp_write_array32(ctx, obj->as.array_size); + case CMP_TYPE_MAP16: + return cmp_write_map16(ctx, (uint16_t)obj->as.map_size); + case CMP_TYPE_MAP32: + return cmp_write_map32(ctx, obj->as.map_size); + case CMP_TYPE_NEGATIVE_FIXNUM: + return cmp_write_nfix(ctx, obj->as.s8); + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_POSITIVE_FIXNUM) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.u8; + return true; +} + +bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_NEGATIVE_FIXNUM) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.s8; + return true; +} + +bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + *c = obj.as.s8; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.s8; + return true; +} + +bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *s = obj.as.s16; + return true; +} + +bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *i = obj.as.s32; + return true; +} + +bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT64) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *l = obj.as.s64; + return true; +} + +bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *c = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + if (obj.as.u8 <= 127) { + *c = (int8_t)obj.as.u8; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *s = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *s = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *s = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + if (obj.as.u16 <= 32767) { + *s = (int16_t)obj.as.u16; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *i = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *i = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *i = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + *i = obj.as.u16; + return true; + case CMP_TYPE_SINT32: + *i = obj.as.s32; + return true; + case CMP_TYPE_UINT32: + if (obj.as.u32 <= 2147483647) { + *i = (int32_t)obj.as.u32; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *d = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *d = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *d = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + *d = obj.as.u16; + return true; + case CMP_TYPE_SINT32: + *d = obj.as.s32; + return true; + case CMP_TYPE_UINT32: + *d = obj.as.u32; + return true; + case CMP_TYPE_SINT64: + *d = obj.as.s64; + return true; + case CMP_TYPE_UINT64: + if (obj.as.u64 <= 9223372036854775807) { + *d = (int64_t)obj.as.u64; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_integer(cmp_ctx_t *ctx, int64_t *d) { + return cmp_read_long(ctx, d); +} + +bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c) { + return cmp_read_pfix(ctx, c); +} + +bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.u8; + return true; +} + +bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *s = obj.as.u16; + return true; +} + +bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *i = obj.as.u32; + return true; +} + +bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT64) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *l = obj.as.u64; + return true; +} + +bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *c = obj.as.u8; + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + if (obj.as.s8 >= 0) { + *c = (uint8_t)obj.as.s8; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *s = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *s = obj.as.u16; + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + if (obj.as.s8 >= 0) { + *s = (uint8_t)obj.as.s8; + return true; + } + break; + case CMP_TYPE_SINT16: + if (obj.as.s16 >= 0) { + *s = (uint16_t)obj.as.s16; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *i = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *i = obj.as.u16; + return true; + case CMP_TYPE_UINT32: + *i = obj.as.u32; + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + if (obj.as.s8 >= 0) { + *i = (uint8_t)obj.as.s8; + return true; + } + break; + case CMP_TYPE_SINT16: + if (obj.as.s16 >= 0) { + *i = (uint16_t)obj.as.s16; + return true; + } + break; + case CMP_TYPE_SINT32: + if (obj.as.s32 >= 0) { + *i = (uint32_t)obj.as.s32; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *u = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *u = obj.as.u16; + return true; + case CMP_TYPE_UINT32: + *u = obj.as.u32; + return true; + case CMP_TYPE_UINT64: + *u = obj.as.u64; + return true; + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + if (obj.as.s8 >= 0) { + *u = (uint8_t)obj.as.s8; + return true; + } + break; + case CMP_TYPE_SINT16: + if (obj.as.s16 >= 0) { + *u = (uint16_t)obj.as.s16; + return true; + } + break; + case CMP_TYPE_SINT32: + if (obj.as.s32 >= 0) { + *u = (uint32_t)obj.as.s32; + return true; + } + break; + case CMP_TYPE_SINT64: + if (obj.as.s64 >= 0) { + *u = (uint64_t)obj.as.s64; + return true; + } + break; + default: + break; + } + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u) { + return cmp_read_ulong(ctx, u); +} + +#ifndef CMP_NO_FLOAT +bool cmp_read_float(cmp_ctx_t *ctx, float *f) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FLOAT) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *f = obj.as.flt; + + return true; +} + +bool cmp_read_double(cmp_ctx_t *ctx, double *d) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_DOUBLE) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *d = obj.as.dbl; + + return true; +} + +bool cmp_read_decimal(cmp_ctx_t *ctx, double *d) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FLOAT: + *d = (double)obj.as.flt; + return true; + case CMP_TYPE_DOUBLE: + *d = obj.as.dbl; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} +#endif /* CMP_NO_FLOAT */ + +bool cmp_read_nil(cmp_ctx_t *ctx) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type == CMP_TYPE_NIL) + return true; + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_bool(cmp_ctx_t *ctx, bool *b) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_BOOLEAN) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + if (obj.as.boolean) + *b = true; + else + *b = false; + + return true; +} + +bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_BOOLEAN) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + if (obj.as.boolean) + *b = 1; + else + *b = 0; + + return true; +} + +bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + *size = obj.as.str_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size) { + uint32_t str_size = 0; + + if (!cmp_read_str_size(ctx, &str_size)) + return false; + + if (str_size >= *size) { + *size = str_size; + ctx->error = STR_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, str_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + + data[str_size] = 0; + + *size = str_size; + return true; +} + +bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + *size = obj.as.bin_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size) { + uint32_t bin_size = 0; + + if (!cmp_read_bin_size(ctx, &bin_size)) + return false; + + if (bin_size > *size) { + ctx->error = BIN_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, bin_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + + *size = bin_size; + return true; +} + +bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + *size = obj.as.array_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + *size = obj.as.map_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT1) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext1_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 1)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT2) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext2_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 2)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT4) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext4_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 4)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext8_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 8)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext16_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 16)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = (uint8_t)obj.as.ext.size; + + return true; +} + +bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data) { + if (!cmp_read_ext8_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = (uint16_t)obj.as.ext.size; + + return true; +} + +bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data) { + if (!cmp_read_ext16_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = obj.as.ext.size; + + return true; +} + +bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) { + if (!cmp_read_ext32_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + *type = obj.as.ext.type; + *size = obj.as.ext.size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) { + if (!cmp_read_ext_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj) { + uint8_t type_marker = 0; + + if (!read_type_marker(ctx, &type_marker)) + return false; + + if (!type_marker_to_cmp_type(type_marker, &obj->type)) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + return read_obj_data(ctx, type_marker, obj); +} + +bool cmp_skip_object(cmp_ctx_t *ctx, cmp_object_t *obj) { + uint8_t type_marker = 0; + uint8_t cmp_type; + uint32_t size = 0; + + if (!read_type_marker(ctx, &type_marker)) { + return false; + } + + if (!type_marker_to_cmp_type(type_marker, &cmp_type)) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + obj->type = cmp_type; + + if (!read_obj_data(ctx, type_marker, obj)) { + return false; + } + + ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR; + + return false; + default: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + + if (size) { + switch (cmp_type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + size++; + break; + default: + break; + } + + skip_bytes(ctx, size); + } + } + + return true; +} + +bool cmp_skip_object_flat(cmp_ctx_t *ctx, cmp_object_t *obj) { + size_t element_count = 1; + bool in_container = false; + + while (element_count) { + uint8_t type_marker = 0; + uint8_t cmp_type; + uint32_t size = 0; + + if (!read_type_marker(ctx, &type_marker)) { + return false; + } + + if (!type_marker_to_cmp_type(type_marker, &cmp_type)) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + if (in_container) { + obj->type = cmp_type; + + if (!read_obj_data(ctx, type_marker, obj)) { + return false; + } + + ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR; + return false; + } + + in_container = true; + + break; + default: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + + if (size) { + switch (cmp_type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + size++; + break; + default: + break; + } + + skip_bytes(ctx, size); + } + } + + element_count--; + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += size; + break; + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += ((size_t)size) * 2; + break; + default: + break; + } + } + + return true; +} + +bool cmp_skip_object_no_limit(cmp_ctx_t *ctx) { + size_t element_count = 1; + + while (element_count) { + uint8_t type_marker = 0; + uint8_t cmp_type = 0; + uint32_t size = 0; + + if (!read_type_marker(ctx, &type_marker)) { + return false; + } + + if (!type_marker_to_cmp_type(type_marker, &cmp_type)) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + break; + default: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + + if (size) { + switch (cmp_type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + size++; + break; + default: + break; + } + + skip_bytes(ctx, size); + } + } + + element_count--; + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += size; + break; + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += ((size_t)size) * 2; + break; + default: + break; + } + } + + return true; +} + +bool cmp_skip_object_limit(cmp_ctx_t *ctx, cmp_object_t *obj, uint32_t limit) { + size_t element_count = 1; + uint32_t depth = 0; + + while (element_count) { + uint8_t type_marker = 0; + uint8_t cmp_type; + uint32_t size = 0; + + if (!read_type_marker(ctx, &type_marker)) { + return false; + } + + if (!type_marker_to_cmp_type(type_marker, &cmp_type)) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + depth++; + + if (depth > limit) { + obj->type = cmp_type; + + if (!read_obj_data(ctx, type_marker, obj)) { + return false; + } + + ctx->error = SKIP_DEPTH_LIMIT_EXCEEDED_ERROR; + + return false; + } + + break; + default: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + + if (size) { + switch (cmp_type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + size++; + break; + default: + break; + } + + skip_bytes(ctx, size); + } + } + + element_count--; + + switch (cmp_type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += size; + break; + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + if (!read_type_size(ctx, type_marker, cmp_type, &size)) { + return false; + } + element_count += ((size_t)size) * 2; + break; + default: + break; + } + } + + return true; +} + +bool cmp_object_is_char(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + return true; + default: + return false; + } +} + +bool cmp_object_is_short(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + return true; + default: + return false; + } +} + +bool cmp_object_is_int(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + case CMP_TYPE_SINT32: + return true; + default: + return false; + } +} + +bool cmp_object_is_long(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + case CMP_TYPE_SINT32: + case CMP_TYPE_SINT64: + return true; + default: + return false; + } +} + +bool cmp_object_is_sinteger(const cmp_object_t *obj) { + return cmp_object_is_long(obj); +} + +bool cmp_object_is_uchar(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + return true; + default: + return false; + } +} + +bool cmp_object_is_ushort(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + return true; + case CMP_TYPE_UINT16: + return true; + default: + return false; + } +} + +bool cmp_object_is_uint(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + case CMP_TYPE_UINT16: + case CMP_TYPE_UINT32: + return true; + default: + return false; + } +} + +bool cmp_object_is_ulong(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + case CMP_TYPE_UINT16: + case CMP_TYPE_UINT32: + case CMP_TYPE_UINT64: + return true; + default: + return false; + } +} + +bool cmp_object_is_uinteger(const cmp_object_t *obj) { + return cmp_object_is_ulong(obj); +} + +bool cmp_object_is_float(const cmp_object_t *obj) { + if (obj->type == CMP_TYPE_FLOAT) + return true; + + return false; +} + +bool cmp_object_is_double(const cmp_object_t *obj) { + if (obj->type == CMP_TYPE_DOUBLE) + return true; + + return false; +} + +bool cmp_object_is_nil(const cmp_object_t *obj) { + if (obj->type == CMP_TYPE_NIL) + return true; + + return false; +} + +bool cmp_object_is_bool(const cmp_object_t *obj) { + if (obj->type == CMP_TYPE_BOOLEAN) + return true; + + return false; +} + +bool cmp_object_is_str(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + return true; + default: + return false; + } +} + +bool cmp_object_is_bin(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + return true; + default: + return false; + } +} + +bool cmp_object_is_array(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + return true; + default: + return false; + } +} + +bool cmp_object_is_map(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + return true; + default: + return false; + } +} + +bool cmp_object_is_ext(const cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + return true; + default: + return false; + } +} + +bool cmp_object_as_char(const cmp_object_t *obj, int8_t *c) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *c = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + if (obj->as.u8 <= 127) { + *c = obj->as.s8; + return true; + } + else { + return false; + } + default: + return false; + } +} + +bool cmp_object_as_short(const cmp_object_t *obj, int16_t *s) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *s = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *s = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *s = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + if (obj->as.u16 <= 32767) { + *s = (int16_t)obj->as.u16; + return true; + } + else { + return false; + } + default: + return false; + } +} + +bool cmp_object_as_int(const cmp_object_t *obj, int32_t *i) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *i = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *i = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *i = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + *i = obj->as.u16; + return true; + case CMP_TYPE_SINT32: + *i = obj->as.s32; + return true; + case CMP_TYPE_UINT32: + if (obj->as.u32 <= 2147483647) { + *i = (int32_t)obj->as.u32; + return true; + } + else { + return false; + } + default: + return false; + } +} + +bool cmp_object_as_long(const cmp_object_t *obj, int64_t *d) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *d = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *d = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *d = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + *d = obj->as.u16; + return true; + case CMP_TYPE_SINT32: + *d = obj->as.s32; + return true; + case CMP_TYPE_UINT32: + *d = obj->as.u32; + return true; + case CMP_TYPE_SINT64: + *d = obj->as.s64; + return true; + case CMP_TYPE_UINT64: + if (obj->as.u64 <= 9223372036854775807) { + *d = (int64_t)obj->as.u64; + return true; + } + else { + return false; + } + default: + return false; + } +} + +bool cmp_object_as_sinteger(const cmp_object_t *obj, int64_t *d) { + return cmp_object_as_long(obj, d); +} + +bool cmp_object_as_uchar(const cmp_object_t *obj, uint8_t *c) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *c = obj->as.u8; + return true; + default: + return false; + } +} + +bool cmp_object_as_ushort(const cmp_object_t *obj, uint16_t *s) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *s = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *s = obj->as.u16; + return true; + default: + return false; + } +} + +bool cmp_object_as_uint(const cmp_object_t *obj, uint32_t *i) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *i = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *i = obj->as.u16; + return true; + case CMP_TYPE_UINT32: + *i = obj->as.u32; + return true; + default: + return false; + } +} + +bool cmp_object_as_ulong(const cmp_object_t *obj, uint64_t *u) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *u = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *u = obj->as.u16; + return true; + case CMP_TYPE_UINT32: + *u = obj->as.u32; + return true; + case CMP_TYPE_UINT64: + *u = obj->as.u64; + return true; + default: + return false; + } +} + +bool cmp_object_as_uinteger(const cmp_object_t *obj, uint64_t *u) { + return cmp_object_as_ulong(obj, u); +} + +#ifndef CMP_NO_FLOAT +bool cmp_object_as_float(const cmp_object_t *obj, float *f) { + if (obj->type == CMP_TYPE_FLOAT) { + *f = obj->as.flt; + return true; + } + + return false; +} + +bool cmp_object_as_double(const cmp_object_t *obj, double *d) { + if (obj->type == CMP_TYPE_DOUBLE) { + *d = obj->as.dbl; + return true; + } + + return false; +} +#endif /* CMP_NO_FLOAT */ + +bool cmp_object_as_bool(const cmp_object_t *obj, bool *b) { + if (obj->type == CMP_TYPE_BOOLEAN) { + if (obj->as.boolean) + *b = true; + else + *b = false; + + return true; + } + + return false; +} + +bool cmp_object_as_str(const cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + *size = obj->as.str_size; + return true; + default: + return false; + } +} + +bool cmp_object_as_bin(const cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + *size = obj->as.bin_size; + return true; + default: + return false; + } +} + +bool cmp_object_as_array(const cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + *size = obj->as.array_size; + return true; + default: + return false; + } +} + +bool cmp_object_as_map(const cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + *size = obj->as.map_size; + return true; + default: + return false; + } +} + +bool cmp_object_as_ext(const cmp_object_t *obj, int8_t *type, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + *type = obj->as.ext.type; + *size = obj->as.ext.size; + return true; + default: + return false; + } +} + +bool cmp_object_to_str(cmp_ctx_t *ctx, const cmp_object_t *obj, char *data, + uint32_t buf_size) { + uint32_t str_size = 0; + + switch (obj->type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + str_size = obj->as.str_size; + if (str_size >= buf_size) { + ctx->error = STR_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, str_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + + data[str_size] = 0; + return true; + default: + return false; + } +} + +bool cmp_object_to_bin(cmp_ctx_t *ctx, const cmp_object_t *obj, void *data, + uint32_t buf_size) { + uint32_t bin_size = 0; + + switch (obj->type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + bin_size = obj->as.bin_size; + if (bin_size > buf_size) { + ctx->error = BIN_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, bin_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + return true; + default: + return false; + } +} + +/* vi: set et ts=2 sw=2: */ + diff --git a/protocols/Tox/libtox/src/third_party/cmp/cmp.h b/protocols/Tox/libtox/src/third_party/cmp/cmp.h new file mode 100644 index 0000000000..3fde68dbf0 --- /dev/null +++ b/protocols/Tox/libtox/src/third_party/cmp/cmp.h @@ -0,0 +1,572 @@ +/* +The MIT License (MIT) + +Copyright (c) 2020 Charles Gunyon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef CMP_H_INCLUDED +#define CMP_H_INCLUDED + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct cmp_ctx_s; + +typedef bool (*cmp_reader)(struct cmp_ctx_s *ctx, void *data, size_t limit); +typedef bool (*cmp_skipper)(struct cmp_ctx_s *ctx, size_t count); +typedef size_t (*cmp_writer)(struct cmp_ctx_s *ctx, const void *data, + size_t count); + +enum { + CMP_TYPE_POSITIVE_FIXNUM, /* 0 */ + CMP_TYPE_FIXMAP, /* 1 */ + CMP_TYPE_FIXARRAY, /* 2 */ + CMP_TYPE_FIXSTR, /* 3 */ + CMP_TYPE_NIL, /* 4 */ + CMP_TYPE_BOOLEAN, /* 5 */ + CMP_TYPE_BIN8, /* 6 */ + CMP_TYPE_BIN16, /* 7 */ + CMP_TYPE_BIN32, /* 8 */ + CMP_TYPE_EXT8, /* 9 */ + CMP_TYPE_EXT16, /* 10 */ + CMP_TYPE_EXT32, /* 11 */ + CMP_TYPE_FLOAT, /* 12 */ + CMP_TYPE_DOUBLE, /* 13 */ + CMP_TYPE_UINT8, /* 14 */ + CMP_TYPE_UINT16, /* 15 */ + CMP_TYPE_UINT32, /* 16 */ + CMP_TYPE_UINT64, /* 17 */ + CMP_TYPE_SINT8, /* 18 */ + CMP_TYPE_SINT16, /* 19 */ + CMP_TYPE_SINT32, /* 20 */ + CMP_TYPE_SINT64, /* 21 */ + CMP_TYPE_FIXEXT1, /* 22 */ + CMP_TYPE_FIXEXT2, /* 23 */ + CMP_TYPE_FIXEXT4, /* 24 */ + CMP_TYPE_FIXEXT8, /* 25 */ + CMP_TYPE_FIXEXT16, /* 26 */ + CMP_TYPE_STR8, /* 27 */ + CMP_TYPE_STR16, /* 28 */ + CMP_TYPE_STR32, /* 29 */ + CMP_TYPE_ARRAY16, /* 30 */ + CMP_TYPE_ARRAY32, /* 31 */ + CMP_TYPE_MAP16, /* 32 */ + CMP_TYPE_MAP32, /* 33 */ + CMP_TYPE_NEGATIVE_FIXNUM /* 34 */ +}; + +typedef struct cmp_ext_s { + int8_t type; + uint32_t size; +} cmp_ext_t; + +union cmp_object_data_u { + bool boolean; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; +#ifndef CMP_NO_FLOAT + float flt; + double dbl; +#endif /* CMP_NO_FLOAT */ + uint32_t array_size; + uint32_t map_size; + uint32_t str_size; + uint32_t bin_size; + cmp_ext_t ext; +}; + +typedef struct cmp_ctx_s { + uint8_t error; + void *buf; + cmp_reader read; + cmp_skipper skip; + cmp_writer write; +} cmp_ctx_t; + +typedef struct cmp_object_s { + uint8_t type; + union cmp_object_data_u as; +} cmp_object_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * === Main API + * ============================================================================ + */ + +/* + * Initializes a CMP context + * + * If you don't intend to read, `read` may be NULL, but calling `*read*` + * functions will crash; there is no check. + * + * `skip` may be NULL, in which case skipping functions will use `read`. + * + * If you don't intend to write, `write` may be NULL, but calling `*write*` + * functions will crash; there is no check. + */ +void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read, + cmp_skipper skip, + cmp_writer write); + +/* Returns CMP's version */ +uint32_t cmp_version(void); + +/* Returns the MessagePack version employed by CMP */ +uint32_t cmp_mp_version(void); + +/* Returns a string description of a CMP context's error */ +const char* cmp_strerror(cmp_ctx_t *ctx); + +/* Writes a signed integer to the backend */ +bool cmp_write_integer(cmp_ctx_t *ctx, int64_t d); + +/* Writes an unsigned integer to the backend */ +bool cmp_write_uinteger(cmp_ctx_t *ctx, uint64_t u); + +/* + * Writes a floating-point value (either single or double-precision) to the + * backend + */ +#ifndef CMP_NO_FLOAT +bool cmp_write_decimal(cmp_ctx_t *ctx, double d); +#endif /* CMP_NO_FLOAT */ + +/* Writes NULL to the backend */ +bool cmp_write_nil(cmp_ctx_t *ctx); + +/* Writes true to the backend */ +bool cmp_write_true(cmp_ctx_t *ctx); + +/* Writes false to the backend */ +bool cmp_write_false(cmp_ctx_t *ctx); + +/* Writes a boolean value to the backend */ +bool cmp_write_bool(cmp_ctx_t *ctx, bool b); + +/* + * Writes an unsigned char's value to the backend as a boolean. This is useful + * if you are using a different boolean type in your application. + */ +bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b); + +/* + * Writes a string to the backend; according to the MessagePack spec, this must + * be encoded using UTF-8, but CMP leaves that job up to the programmer. + */ +bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size); + +/* + * Writes a string to the backend. This avoids using the STR8 marker, which + * is unsupported by MessagePack v4, the version implemented by many other + * MessagePack libraries. No encoding is assumed in this case, not that it + * matters. + */ +bool cmp_write_str_v4(cmp_ctx_t *ctx, const char *data, uint32_t size); + +/* + * Writes the string marker to the backend. This is useful if you are writing + * data in chunks instead of a single shot. + */ +bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size); + +/* + * Writes the string marker to the backend. This is useful if you are writing + * data in chunks instead of a single shot. This avoids using the STR8 + * marker, which is unsupported by MessagePack v4, the version implemented by + * many other MessagePack libraries. No encoding is assumed in this case, not + * that it matters. + */ +bool cmp_write_str_marker_v4(cmp_ctx_t *ctx, uint32_t size); + +/* Writes binary data to the backend */ +bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size); + +/* + * Writes the binary data marker to the backend. This is useful if you are + * writing data in chunks instead of a single shot. + */ +bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size); + +/* Writes an array to the backend. */ +bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size); + +/* Writes a map to the backend. */ +bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size); + +/* Writes an extended type to the backend */ +bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size, + const void *data); + +/* + * Writes the extended type marker to the backend. This is useful if you want + * to write the type's data in chunks instead of a single shot. + */ +bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); + +/* Writes an object to the backend */ +bool cmp_write_object(cmp_ctx_t *ctx, const cmp_object_t *obj); + +/* + * Writes an object to the backend. This avoids using the STR8 marker, which + * is unsupported by MessagePack v4, the version implemented by many other + * MessagePack libraries. + */ +bool cmp_write_object_v4(cmp_ctx_t *ctx, const cmp_object_t *obj); + +/* Reads a signed integer that fits inside a signed char */ +bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c); + +/* Reads a signed integer that fits inside a signed short */ +bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s); + +/* Reads a signed integer that fits inside a signed int */ +bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i); + +/* Reads a signed integer that fits inside a signed long */ +bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d); + +/* Reads a signed integer */ +bool cmp_read_integer(cmp_ctx_t *ctx, int64_t *d); + +/* Reads an unsigned integer that fits inside an unsigned char */ +bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c); + +/* Reads an unsigned integer that fits inside an unsigned short */ +bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s); + +/* Reads an unsigned integer that fits inside an unsigned int */ +bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i); + +/* Reads an unsigned integer that fits inside an unsigned long */ +bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u); + +/* Reads an unsigned integer */ +bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u); + +/* + * Reads a floating point value (either single or double-precision) from the + * backend + */ +#ifndef CMP_NO_FLOAT +bool cmp_read_decimal(cmp_ctx_t *ctx, double *d); +#endif /* CMP_NO_FLOAT */ + +/* "Reads" (more like "skips") a NULL value from the backend */ +bool cmp_read_nil(cmp_ctx_t *ctx); + +/* Reads a boolean from the backend */ +bool cmp_read_bool(cmp_ctx_t *ctx, bool *b); + +/* + * Reads a boolean as an unsigned char from the backend; this is useful if your + * application uses a different boolean type. + */ +bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b); + +/* Reads a string's size from the backend */ +bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size); + +/* + * Reads a string from the backend; according to the spec, the string's data + * ought to be encoded using UTF-8, + */ +bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size); + +/* Reads the size of packed binary data from the backend */ +bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads packed binary data from the backend */ +bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size); + +/* Reads an array from the backend */ +bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads a map from the backend */ +bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads the extended type's marker from the backend */ +bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); + +/* Reads an extended type from the backend */ +bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); + +/* Reads an object from the backend */ +bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj); + +/* + * Skips the next object from the backend. If that object is an array or map, + * this function will: + * - If `obj` is not `NULL`, fill in `obj` with that object + * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR` + * - Return `false` + * Otherwise: + * - (Don't touch `obj`) + * - Return `true` + */ +bool cmp_skip_object(cmp_ctx_t *ctx, cmp_object_t *obj); + +/* + * This is similar to `cmp_skip_object`, except it tolerates flat arrays and + * maps. If when skipping such an array or map this function encounters + * another array/map, it will: + * - If `obj` is not `NULL`, fill in `obj` with that (nested) object + * - Set `ctx->error` to `SKIP_DEPTH_LIMIT_EXCEEDED_ERROR` + * - Return `false` + * Otherwise: + * - (Don't touch `obj`) + * - Return `true` + * + * WARNING: This can cause your application to spend an unbounded amount of + * time reading nested data structures. Unless you completely trust + * the data source, you should use `cmp_skip_object`. + */ +bool cmp_skip_object_flat(cmp_ctx_t *ctx, cmp_object_t *obj); + +/* + * This is similar to `cmp_skip_object`, except it will continually skip + * nested data structures. + * + * WARNING: This can cause your application to spend an unbounded amount of + * time reading nested data structures. Unless you completely trust + * the data source, you should use `cmp_skip_object`. + */ +bool cmp_skip_object_no_limit(cmp_ctx_t *ctx); + +/* + * WARNING: THIS FUNCTION IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE + * + * There is no way to track depths across elements without allocation. For + * example, an array constructed as: `[ [] [] [] [] [] [] [] [] [] [] ]` + * should be able to be skipped with `cmp_skip_object_limit(&cmp, &obj, 2)`. + * However, because we cannot track depth across the elements, there's no way + * to reset it after descending down into each element. + * + * This is similar to `cmp_skip_object`, except it tolerates up to `limit` + * levels of nesting. For example, in order to skip an array that contains a + * map, call `cmp_skip_object_limit(ctx, &obj, 2)`. Or in other words, + * `cmp_skip_object(ctx, &obj)` acts similarly to `cmp_skip_object_limit(ctx, + * &obj, 0)` + * + * Specifically, `limit` refers to depth, not breadth. So in order to skip an + * array that contains two arrays that each contain 3 strings, you would call + * `cmp_skip_object_limit(ctx, &obj, 2). In order to skip an array that + * contains 4 arrays that each contain 1 string, you would still call + * `cmp_skip_object_limit(ctx, &obj, 2). + */ +bool cmp_skip_object_limit(cmp_ctx_t *ctx, cmp_object_t *obj, uint32_t limit) +#ifdef __GNUC__ + __attribute__((deprecated)) +#endif +; + +#ifdef _MSC_VER +#pragma deprecated(cmp_skip_object_limit) +#endif + +/* + * ============================================================================ + * === Specific API + * ============================================================================ + */ + +bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c); + +bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c); +bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c); +bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s); +bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i); +bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l); + +bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s); +bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i); +bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l); + +#ifndef CMP_NO_FLOAT +bool cmp_write_float(cmp_ctx_t *ctx, float f); +bool cmp_write_double(cmp_ctx_t *ctx, double d); +#endif /* CMP_NO_FLOAT */ + +bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size); +bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size); +bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size); +bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size); +bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size); + +bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size); +bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size); +bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size); +bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size); + +bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size); + +bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size); + +bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data); + +bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size); +bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size, + const void *data); +bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size); +bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size, + const void *data); +bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); +bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size, + const void *data); + +bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c); + +bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c); +bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c); +bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s); +bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i); +bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l); + +bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s); +bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i); +bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l); + +#ifndef CMP_NO_FLOAT +bool cmp_read_float(cmp_ctx_t *ctx, float *f); +bool cmp_read_double(cmp_ctx_t *ctx, double *d); +#endif /* CMP_NO_FLOAT */ + +bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data); + +bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size); +bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data); +bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size); +bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data); +bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); +bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); + +/* + * ============================================================================ + * === Object API + * ============================================================================ + */ + +bool cmp_object_is_char(const cmp_object_t *obj); +bool cmp_object_is_short(const cmp_object_t *obj); +bool cmp_object_is_int(const cmp_object_t *obj); +bool cmp_object_is_long(const cmp_object_t *obj); +bool cmp_object_is_sinteger(const cmp_object_t *obj); +bool cmp_object_is_uchar(const cmp_object_t *obj); +bool cmp_object_is_ushort(const cmp_object_t *obj); +bool cmp_object_is_uint(const cmp_object_t *obj); +bool cmp_object_is_ulong(const cmp_object_t *obj); +bool cmp_object_is_uinteger(const cmp_object_t *obj); +bool cmp_object_is_float(const cmp_object_t *obj); +bool cmp_object_is_double(const cmp_object_t *obj); +bool cmp_object_is_nil(const cmp_object_t *obj); +bool cmp_object_is_bool(const cmp_object_t *obj); +bool cmp_object_is_str(const cmp_object_t *obj); +bool cmp_object_is_bin(const cmp_object_t *obj); +bool cmp_object_is_array(const cmp_object_t *obj); +bool cmp_object_is_map(const cmp_object_t *obj); +bool cmp_object_is_ext(const cmp_object_t *obj); + +bool cmp_object_as_char(const cmp_object_t *obj, int8_t *c); +bool cmp_object_as_short(const cmp_object_t *obj, int16_t *s); +bool cmp_object_as_int(const cmp_object_t *obj, int32_t *i); +bool cmp_object_as_long(const cmp_object_t *obj, int64_t *d); +bool cmp_object_as_sinteger(const cmp_object_t *obj, int64_t *d); +bool cmp_object_as_uchar(const cmp_object_t *obj, uint8_t *c); +bool cmp_object_as_ushort(const cmp_object_t *obj, uint16_t *s); +bool cmp_object_as_uint(const cmp_object_t *obj, uint32_t *i); +bool cmp_object_as_ulong(const cmp_object_t *obj, uint64_t *u); +bool cmp_object_as_uinteger(const cmp_object_t *obj, uint64_t *u); +bool cmp_object_as_float(const cmp_object_t *obj, float *f); +bool cmp_object_as_double(const cmp_object_t *obj, double *d); +bool cmp_object_as_bool(const cmp_object_t *obj, bool *b); +bool cmp_object_as_str(const cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_bin(const cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_array(const cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_map(const cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_ext(const cmp_object_t *obj, int8_t *type, uint32_t *size); + +bool cmp_object_to_str(cmp_ctx_t *ctx, const cmp_object_t *obj, char *data, uint32_t buf_size); +bool cmp_object_to_bin(cmp_ctx_t *ctx, const cmp_object_t *obj, void *data, uint32_t buf_size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* + * ============================================================================ + * === Backwards compatibility defines + * ============================================================================ + */ + +#define cmp_write_int cmp_write_integer +#define cmp_write_sint cmp_write_integer +#define cmp_write_sinteger cmp_write_integer +#define cmp_write_uint cmp_write_uinteger +#define cmp_read_sinteger cmp_read_integer + +#endif /* CMP_H_INCLUDED */ + +/* vi: set et ts=2 sw=2: */ + diff --git a/protocols/Tox/libtox/src/toxcore/DHT.c b/protocols/Tox/libtox/src/toxcore/DHT.c index 4cc8d2002f..8aa161102b 100644 --- a/protocols/Tox/libtox/src/toxcore/DHT.c +++ b/protocols/Tox/libtox/src/toxcore/DHT.c @@ -13,6 +13,7 @@ #include <string.h> #include "LAN_discovery.h" +#include "ccompat.h" #include "logger.h" #include "mono_time.h" #include "network.h" @@ -67,6 +68,9 @@ struct DHT_Friend { unsigned int num_to_bootstrap; }; +static const DHT_Friend empty_dht_friend = {{0}}; +const Node_format empty_node_format = {{0}}; + typedef struct Cryptopacket_Handler { cryptopacket_handler_cb *function; void *object; @@ -74,10 +78,13 @@ typedef struct Cryptopacket_Handler { struct DHT { const Logger *log; + const Network *ns; Mono_Time *mono_time; + const Random *rng; Networking_Core *net; bool hole_punching_enabled; + bool lan_discovery_enabled; Client_data close_clientlist[LCLIENT_LIST]; uint64_t close_lastgetnodes; @@ -105,6 +112,8 @@ struct DHT { Node_format to_bootstrap[MAX_CLOSE_TO_BOOTSTRAP_NODES]; unsigned int num_to_bootstrap; + + dht_get_nodes_response_cb *get_nodes_response; }; const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend) @@ -168,32 +177,34 @@ const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num) return dht->friends_list[friend_num].public_key; } +non_null() static bool assoc_timeout(uint64_t cur_time, const IPPTsPng *assoc) { return (assoc->timestamp + BAD_NODE_TIMEOUT) <= cur_time; } -/** Converts an IPv4-in-IPv6 to IPv4 and returns the new IP_Port. +/** @brief Converts an IPv4-in-IPv6 to IPv4 and returns the new IP_Port. * * If the ip_port is already IPv4 this function returns a copy of the original ip_port. */ +non_null() static IP_Port ip_port_normalize(const IP_Port *ip_port) { IP_Port res = *ip_port; if (net_family_is_ipv6(res.ip.family) && ipv6_ipv4_in_v6(&res.ip.ip.v6)) { - res.ip.family = net_family_ipv4; + res.ip.family = net_family_ipv4(); res.ip.ip.v4.uint32 = res.ip.ip.v6.uint32[3]; } return res; } -/** Compares pk1 and pk2 with pk. +/** @brief Compares pk1 and pk2 with pk. * - * return 0 if both are same distance. - * return 1 if pk1 is closer. - * return 2 if pk2 is closer. + * @retval 0 if both are same distance. + * @retval 1 if pk1 is closer. + * @retval 2 if pk2 is closer. */ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2) { @@ -213,9 +224,8 @@ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2) return 0; } -/** Return index of first unequal bit number. - */ -static unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) +/** Return index of first unequal bit number between public keys pk1 and pk2. */ +unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) { unsigned int i; unsigned int j = 0; @@ -239,7 +249,8 @@ static unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) return i * 8 + j; } -/** Shared key generations are costly, it is therefore smart to store commonly used +/** + * Shared key generations are costly, it is therefore smart to store commonly used * ones so that they can be re-used later without being computed again. * * If a shared key is already in shared_keys, copy it to shared_key. @@ -256,7 +267,7 @@ void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_ Shared_Key *const key = &shared_keys->keys[index]; if (key->stored) { - if (id_equal(public_key, key->public_key)) { + if (pk_equal(public_key, key->public_key)) { memcpy(shared_key, key->shared_key, CRYPTO_SHARED_KEY_SIZE); ++key->times_requested; key->time_last_requested = mono_time_get(mono_time); @@ -290,7 +301,8 @@ void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_ } } -/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key +/** + * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key * for packets that we receive. */ void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *public_key) @@ -298,7 +310,8 @@ void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *publi get_shared_key(dht->mono_time, &dht->shared_keys_recv, shared_key, dht->self_secret_key, public_key); } -/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key +/** + * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key * for packets that we send. */ void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key) @@ -308,34 +321,46 @@ void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *publi #define CRYPTO_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE * 2 + CRYPTO_NONCE_SIZE) -/** Create a request to peer. - * send_public_key and send_secret_key are the pub/secret keys of the sender. - * recv_public_key is public key of receiver. - * packet must be an array of MAX_CRYPTO_REQUEST_SIZE big. - * Data represents the data we send with the request with length being the length of the data. - * request_id is the id of the request (32 = friend request, 254 = ping request). +/** + * @brief Create a request to peer. * - * return -1 on failure. - * return the length of the created packet on success. + * Packs the data and sender public key and encrypts the packet. + * + * @param[in] send_public_key public key of the sender. + * @param[in] send_secret_key secret key of the sender. + * @param[out] packet an array of @ref MAX_CRYPTO_REQUEST_SIZE big. + * @param[in] recv_public_key public key of the receiver. + * @param[in] data represents the data we send with the request. + * @param[in] data_length the length of the data. + * @param[in] request_id the id of the request (32 = friend request, 254 = ping request). + * + * @attention Constraints: + * @code + * sizeof(packet) >= MAX_CRYPTO_REQUEST_SIZE + * @endcode + * + * @retval -1 on failure. + * @return the length of the created packet on success. */ -int create_request(const uint8_t *send_public_key, const uint8_t *send_secret_key, uint8_t *packet, - const uint8_t *recv_public_key, const uint8_t *data, uint32_t length, uint8_t request_id) +int create_request(const Random *rng, const uint8_t *send_public_key, const uint8_t *send_secret_key, + uint8_t *packet, const uint8_t *recv_public_key, + const uint8_t *data, uint32_t data_length, uint8_t request_id) { - if (!send_public_key || !packet || !recv_public_key || !data) { + if (send_public_key == nullptr || packet == nullptr || recv_public_key == nullptr || data == nullptr) { return -1; } - if (MAX_CRYPTO_REQUEST_SIZE < length + CRYPTO_SIZE + 1 + CRYPTO_MAC_SIZE) { + if (MAX_CRYPTO_REQUEST_SIZE < data_length + CRYPTO_SIZE + 1 + CRYPTO_MAC_SIZE) { return -1; } uint8_t *const nonce = packet + 1 + CRYPTO_PUBLIC_KEY_SIZE * 2; - random_nonce(nonce); - uint8_t temp[MAX_CRYPTO_REQUEST_SIZE]; - memcpy(temp + 1, data, length); + random_nonce(rng, nonce); + uint8_t temp[MAX_CRYPTO_REQUEST_SIZE] = {0}; temp[0] = request_id; - const int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, length + 1, - CRYPTO_SIZE + packet); + memcpy(temp + 1, data, data_length); + const int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, data_length + 1, + packet + CRYPTO_SIZE); if (len == -1) { crypto_memzero(temp, MAX_CRYPTO_REQUEST_SIZE); @@ -350,38 +375,59 @@ int create_request(const uint8_t *send_public_key, const uint8_t *send_secret_ke return len + CRYPTO_SIZE; } -/** Puts the senders public key in the request in public_key, the data from the request - * in data if a friend or ping request was sent to us and returns the length of the data. - * packet is the request packet and length is its length. +/** + * @brief Decrypts and unpacks a DHT request packet. + * + * Puts the senders public key in the request in @p public_key, the data from + * the request in @p data. * - * return -1 if not valid request. + * @param[in] self_public_key public key of the receiver (us). + * @param[in] self_secret_key secret key of the receiver (us). + * @param[out] public_key public key of the sender, copied from the input packet. + * @param[out] data decrypted request data, copied from the input packet, must + * have room for @ref MAX_CRYPTO_REQUEST_SIZE bytes. + * @param[in] packet is the request packet. + * @param[in] packet_length length of the packet. + * + * @attention Constraints: + * @code + * sizeof(data) >= MAX_CRYPTO_REQUEST_SIZE + * @endcode + * + * @retval -1 if not valid request. + * @return the length of the unpacked data. */ int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data, - uint8_t *request_id, const uint8_t *packet, uint16_t length) + uint8_t *request_id, const uint8_t *packet, uint16_t packet_length) { - if (!self_public_key || !public_key || !data || !request_id || !packet) { + if (self_public_key == nullptr || public_key == nullptr || data == nullptr || request_id == nullptr + || packet == nullptr) { return -1; } - if (length <= CRYPTO_SIZE + CRYPTO_MAC_SIZE || length > MAX_CRYPTO_REQUEST_SIZE) { + if (packet_length <= CRYPTO_SIZE + CRYPTO_MAC_SIZE || packet_length > MAX_CRYPTO_REQUEST_SIZE) { return -1; } - if (!id_equal(packet + 1, self_public_key)) { + if (!pk_equal(packet + 1, self_public_key)) { return -1; } memcpy(public_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE); const uint8_t *const nonce = packet + 1 + CRYPTO_PUBLIC_KEY_SIZE * 2; uint8_t temp[MAX_CRYPTO_REQUEST_SIZE]; - int len1 = decrypt_data(public_key, self_secret_key, nonce, - packet + CRYPTO_SIZE, length - CRYPTO_SIZE, temp); + int32_t len1 = decrypt_data(public_key, self_secret_key, nonce, + packet + CRYPTO_SIZE, packet_length - CRYPTO_SIZE, temp); if (len1 == -1 || len1 == 0) { crypto_memzero(temp, MAX_CRYPTO_REQUEST_SIZE); return -1; } + assert(len1 == packet_length - CRYPTO_SIZE - CRYPTO_MAC_SIZE); + // Because coverity can't figure out this equation: + assert(len1 <= MAX_CRYPTO_REQUEST_SIZE - CRYPTO_SIZE - CRYPTO_MAC_SIZE); + request_id[0] = temp[0]; --len1; memcpy(data, temp + 1, len1); @@ -389,11 +435,8 @@ int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_ke return len1; } -#define PACKED_NODE_SIZE_IP4 (1 + SIZE_IP4 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE) -#define PACKED_NODE_SIZE_IP6 (1 + SIZE_IP6 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE) - -/** Return packet size of packed node with ip_family on success. - * Return -1 on failure. +/** @return packet size of packed node with ip_family on success. + * @retval -1 on failure. */ int packed_node_size(Family ip_family) { @@ -409,36 +452,40 @@ int packed_node_size(Family ip_family) } -/** Packs an IP_Port structure into data of max size length. +/** @brief Pack an IP_Port structure into data of max size length. * * Packed_length is the offset of data currently packed. * - * Returns size of packed IP_Port data on success - * Return -1 on failure. + * @return size of packed IP_Port data on success. + * @retval -1 on failure. */ -int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port) +int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port) { if (data == nullptr) { return -1; } bool is_ipv4; - uint8_t net_family; + uint8_t family; if (net_family_is_ipv4(ip_port->ip.family)) { // TODO(irungentoo): use functions to convert endianness is_ipv4 = true; - net_family = TOX_AF_INET; + family = TOX_AF_INET; } else if (net_family_is_tcp_ipv4(ip_port->ip.family)) { is_ipv4 = true; - net_family = TOX_TCP_INET; + family = TOX_TCP_INET; } else if (net_family_is_ipv6(ip_port->ip.family)) { is_ipv4 = false; - net_family = TOX_AF_INET6; + family = TOX_AF_INET6; } else if (net_family_is_tcp_ipv6(ip_port->ip.family)) { is_ipv4 = false; - net_family = TOX_TCP_INET6; + family = TOX_TCP_INET6; } else { + Ip_Ntoa ip_str; + // TODO(iphydf): Find out why we're trying to pack invalid IPs, stop + // doing that, and turn this into an error. + LOGGER_TRACE(logger, "cannot pack invalid IP: %s", net_ip_ntoa(&ip_port->ip, &ip_str)); return -1; } @@ -449,7 +496,7 @@ int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port) return -1; } - data[0] = net_family; + data[0] = family; memcpy(data + 1, &ip_port->ip.ip.v4, SIZE_IP4); memcpy(data + 1 + SIZE_IP4, &ip_port->port, sizeof(uint16_t)); return size; @@ -460,25 +507,41 @@ int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port) return -1; } - data[0] = net_family; + data[0] = family; memcpy(data + 1, &ip_port->ip.ip.v6, SIZE_IP6); memcpy(data + 1 + SIZE_IP6, &ip_port->port, sizeof(uint16_t)); return size; } } -static int dht_create_packet(const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], - const uint8_t *shared_key, const uint8_t type, - const uint8_t *plain, size_t plain_length, uint8_t *packet) +/** @brief Encrypt plain and write resulting DHT packet into packet with max size length. + * + * @return size of packet on success. + * @retval -1 on failure. + */ +int dht_create_packet(const Random *rng, const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], + const uint8_t *shared_key, const uint8_t type, + const uint8_t *plain, size_t plain_length, + uint8_t *packet, size_t length) { - VLA(uint8_t, encrypted, plain_length + CRYPTO_MAC_SIZE); + uint8_t *encrypted = (uint8_t *)malloc(plain_length + CRYPTO_MAC_SIZE); uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + if (encrypted == nullptr) { + return -1; + } + + random_nonce(rng, nonce); const int encrypted_length = encrypt_data_symmetric(shared_key, nonce, plain, plain_length, encrypted); if (encrypted_length == -1) { + free(encrypted); + return -1; + } + + if (length < 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + encrypted_length) { + free(encrypted); return -1; } @@ -487,15 +550,16 @@ static int dht_create_packet(const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE); memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, encrypted, encrypted_length); + free(encrypted); return 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + encrypted_length; } -/** Unpack IP_Port structure from data of max size length into ip_port. +/** @brief Unpack IP_Port structure from data of max size length into ip_port. * * len_processed is the offset of data currently unpacked. * - * Return size of unpacked ip_port on success. - * Return -1 on failure. + * @return size of unpacked ip_port on success. + * @retval -1 on failure. */ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled) { @@ -508,31 +572,29 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool if (data[0] == TOX_AF_INET) { is_ipv4 = true; - host_family = net_family_ipv4; + host_family = net_family_ipv4(); } else if (data[0] == TOX_TCP_INET) { if (!tcp_enabled) { return -1; } is_ipv4 = true; - host_family = net_family_tcp_ipv4; + host_family = net_family_tcp_ipv4(); } else if (data[0] == TOX_AF_INET6) { is_ipv4 = false; - host_family = net_family_ipv6; + host_family = net_family_ipv6(); } else if (data[0] == TOX_TCP_INET6) { if (!tcp_enabled) { return -1; } is_ipv4 = false; - host_family = net_family_tcp_ipv6; + host_family = net_family_tcp_ipv6(); } else { return -1; } - *ip_port = (IP_Port) { - 0 - }; + *ip_port = empty_ip_port; if (is_ipv4) { const uint32_t size = 1 + SIZE_IP4 + sizeof(uint16_t); @@ -559,17 +621,17 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool } } -/** Pack number of nodes into data of maxlength length. +/** @brief Pack number of nodes into data of maxlength length. * - * return length of packed nodes on success. - * return -1 on failure. + * @return length of packed nodes on success. + * @retval -1 on failure. */ -int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number) +int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number) { uint32_t packed_length = 0; for (uint32_t i = 0; i < number && packed_length < length; ++i) { - const int ipp_size = pack_ip_port(data + packed_length, length - packed_length, &nodes[i].ip_port); + const int ipp_size = pack_ip_port(logger, data + packed_length, length - packed_length, &nodes[i].ip_port); if (ipp_size == -1) { return -1; @@ -593,12 +655,12 @@ int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_ return packed_length; } -/** Unpack data of length into nodes of size max_num_nodes. +/** @brief Unpack data of length into nodes of size max_num_nodes. * Put the length of the data processed in processed_data_len. * tcp_enabled sets if TCP nodes are expected (true) or not (false). * - * return number of unpacked nodes on success. - * return -1 on failure. + * @return number of unpacked nodes on success. + * @retval -1 on failure. */ int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed_data_len, const uint8_t *data, uint16_t length, bool tcp_enabled) @@ -629,21 +691,24 @@ int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed assert(increment == PACKED_NODE_SIZE_IP4 || increment == PACKED_NODE_SIZE_IP6); } - if (processed_data_len) { + if (processed_data_len != nullptr) { *processed_data_len = len_processed; } return num; } -/** Find index in an array with public_key equal to pk. +/** @brief Find index in an array with public_key equal to pk. * - * return index or UINT32_MAX if not found. + * @return index or UINT32_MAX if not found. */ +non_null(3) nullable(1) static uint32_t index_of_client_pk(const Client_data *array, uint32_t size, const uint8_t *pk) { + assert(size == 0 || array != nullptr); + for (uint32_t i = 0; i < size; ++i) { - if (id_equal(array[i].public_key, pk)) { + if (pk_equal(array[i].public_key, pk)) { return i; } } @@ -651,10 +716,13 @@ static uint32_t index_of_client_pk(const Client_data *array, uint32_t size, cons return UINT32_MAX; } +non_null(3) nullable(1) static uint32_t index_of_friend_pk(const DHT_Friend *array, uint32_t size, const uint8_t *pk) { + assert(size == 0 || array != nullptr); + for (uint32_t i = 0; i < size; ++i) { - if (id_equal(array[i].public_key, pk)) { + if (pk_equal(array[i].public_key, pk)) { return i; } } @@ -662,10 +730,13 @@ static uint32_t index_of_friend_pk(const DHT_Friend *array, uint32_t size, const return UINT32_MAX; } +non_null(3) nullable(1) static uint32_t index_of_node_pk(const Node_format *array, uint32_t size, const uint8_t *pk) { + assert(size == 0 || array != nullptr); + for (uint32_t i = 0; i < size; ++i) { - if (id_equal(array[i].public_key, pk)) { + if (pk_equal(array[i].public_key, pk)) { return i; } } @@ -673,12 +744,15 @@ static uint32_t index_of_node_pk(const Node_format *array, uint32_t size, const return UINT32_MAX; } -/** Find index of Client_data with ip_port equal to param ip_port. +/** @brief Find index of Client_data with ip_port equal to param ip_port. * - * return index or UINT32_MAX if not found. + * @return index or UINT32_MAX if not found. */ +non_null(3) nullable(1) static uint32_t index_of_client_ip_port(const Client_data *array, uint32_t size, const IP_Port *ip_port) { + assert(size == 0 || array != nullptr); + for (uint32_t i = 0; i < size; ++i) { if ((net_family_is_ipv4(ip_port->ip.family) && ipport_equal(&array[i].assoc4.ip_port, ip_port)) || (net_family_is_ipv6(ip_port->ip.family) && ipport_equal(&array[i].assoc6.ip_port, ip_port))) { @@ -689,8 +763,8 @@ static uint32_t index_of_client_ip_port(const Client_data *array, uint32_t size, return UINT32_MAX; } -/** Update ip_port of client if it's needed. - */ +/** Update ip_port of client if it's needed. */ +non_null() static void update_client(const Logger *log, const Mono_Time *mono_time, int index, Client_data *client, const IP_Port *ip_port) { @@ -708,12 +782,13 @@ static void update_client(const Logger *log, const Mono_Time *mono_time, int ind } if (!ipport_equal(&assoc->ip_port, ip_port)) { - char ip_str[IP_NTOA_LEN]; + Ip_Ntoa ip_str_from; + Ip_Ntoa ip_str_to; LOGGER_TRACE(log, "coipil[%u]: switching ipv%d from %s:%u to %s:%u", index, ip_version, - ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)), + net_ip_ntoa(&assoc->ip_port.ip, &ip_str_from), net_ntohs(assoc->ip_port.port), - ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), + net_ip_ntoa(&ip_port->ip, &ip_str_to), net_ntohs(ip_port->port)); } @@ -725,15 +800,15 @@ static void update_client(const Logger *log, const Mono_Time *mono_time, int ind assoc->timestamp = mono_time_get(mono_time); } -/** Check if client with public_key is already in list of length length. +/** @brief Check if client with public_key is already in list of length length. + * * If it is then set its corresponding timestamp to current time. * If the id is already in the list with a different ip_port, update it. * TODO(irungentoo): Maybe optimize this. - * - * return True(1) or False(0) */ -static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_time, Client_data *list, uint16_t length, - const uint8_t *public_key, const IP_Port *ip_port) +non_null() +static bool client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_time, Client_data *list, uint16_t length, + const uint8_t *public_key, const IP_Port *ip_port) { const uint64_t temp_time = mono_time_get(mono_time); uint32_t index = index_of_client_pk(list, length, public_key); @@ -741,7 +816,7 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti /* if public_key is in list, find it and maybe overwrite ip_port */ if (index != UINT32_MAX) { update_client(log, mono_time, index, &list[index], ip_port); - return 1; + return true; } /* public_key not in list yet: see if we can find an identical ip_port, in @@ -752,7 +827,7 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti index = index_of_client_ip_port(list, length, ip_port); if (index == UINT32_MAX) { - return 0; + return false; } IPPTsPng *assoc; @@ -773,10 +848,9 @@ static int client_or_ip_port_in_list(const Logger *log, const Mono_Time *mono_ti LOGGER_DEBUG(log, "coipil[%u]: switching public_key (ipv%d)", index, ip_version); /* kill the other address, if it was set */ - *assoc = (IPPTsPng) { - 0 - }; - return 1; + const IPPTsPng empty_ipptspng = {{{{0}}}}; + *assoc = empty_ipptspng; + return true; } bool add_to_list(Node_format *nodes_list, uint32_t length, const uint8_t *pk, const IP_Port *ip_port, @@ -802,11 +876,13 @@ bool add_to_list(Node_format *nodes_list, uint32_t length, const uint8_t *pk, co } /** - * helper for get_close_nodes(). argument list is a monster :D + * helper for `get_close_nodes()`. argument list is a monster :D */ +non_null() static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key, Node_format *nodes_list, Family sa_family, const Client_data *client_list, uint32_t client_list_length, - uint32_t *num_nodes_ptr, bool is_LAN) + uint32_t *num_nodes_ptr, bool is_LAN, + bool want_announce) { if (!net_family_is_ipv4(sa_family) && !net_family_is_ipv6(sa_family) && !net_family_is_unspec(sa_family)) { return; @@ -844,11 +920,21 @@ static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key, continue; } +#ifdef CHECK_ANNOUNCE_NODE + + if (want_announce && !client->announce_node) { + continue; + } + +#endif + if (num_nodes < MAX_SENT_NODES) { memcpy(nodes_list[num_nodes].public_key, client->public_key, CRYPTO_PUBLIC_KEY_SIZE); nodes_list[num_nodes].ip_port = ipptp->ip_port; ++num_nodes; } else { + // TODO(zugz): this could be made significantly more efficient by + // using a version of add_to_list which works with a sorted list. add_to_list(nodes_list, MAX_SENT_NODES, client->public_key, &ipptp->ip_port, public_key); } } @@ -856,33 +942,35 @@ static void get_close_nodes_inner(uint64_t cur_time, const uint8_t *public_key, *num_nodes_ptr = num_nodes; } -/** Find MAX_SENT_NODES nodes closest to the public_key for the send nodes request: +/** + * Find MAX_SENT_NODES nodes closest to the public_key for the send nodes request: * put them in the nodes_list and return how many were found. * - * TODO(irungentoo): For the love of based <your favorite deity, in doubt use - * "love"> make this function cleaner and much more efficient. + * want_announce: return only nodes which implement the dht announcements protocol. */ +non_null() static int get_somewhat_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, - Family sa_family, bool is_LAN) + Family sa_family, bool is_LAN, bool want_announce) { uint32_t num_nodes = 0; get_close_nodes_inner(dht->cur_time, public_key, nodes_list, sa_family, - dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN); + dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN, want_announce); for (uint32_t i = 0; i < dht->num_friends; ++i) { get_close_nodes_inner(dht->cur_time, public_key, nodes_list, sa_family, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, - &num_nodes, is_LAN); + &num_nodes, is_LAN, want_announce); } return num_nodes; } int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family, - bool is_LAN) + bool is_LAN, bool want_announce) { memset(nodes_list, 0, MAX_SENT_NODES * sizeof(Node_format)); - return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family, is_LAN); + return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family, + is_LAN, want_announce); } typedef struct DHT_Cmp_Data { @@ -891,6 +979,7 @@ typedef struct DHT_Cmp_Data { Client_data entry; } DHT_Cmp_Data; +non_null() static int dht_cmp_entry(const void *a, const void *b) { const DHT_Cmp_Data *cmp1 = (const DHT_Cmp_Data *)a; @@ -914,38 +1003,147 @@ static int dht_cmp_entry(const void *a, const void *b) return 1; } - const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); + const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); - if (close == 1) { + if (closest == 1) { return 1; } - if (close == 2) { + if (closest == 2) { return -1; } return 0; } -/** Is it ok to store node with public_key in client. +#ifdef CHECK_ANNOUNCE_NODE +non_null() +static void set_announce_node_in_list(Client_data *list, uint32_t list_len, const uint8_t *public_key) +{ + const uint32_t index = index_of_client_pk(list, list_len, public_key); + + if (index != UINT32_MAX) { + list[index].announce_node = true; + } +} + +void set_announce_node(DHT *dht, const uint8_t *public_key) +{ + unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key); + + if (index >= LCLIENT_LENGTH) { + index = LCLIENT_LENGTH - 1; + } + + set_announce_node_in_list(dht->close_clientlist + index * LCLIENT_NODES, LCLIENT_NODES, public_key); + + for (int32_t i = 0; i < dht->num_friends; ++i) { + set_announce_node_in_list(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, public_key); + } +} + +/** @brief Send data search request, searching for a random key. */ +non_null() +static bool send_announce_ping(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port) +{ + uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t)]; + + uint8_t unused_secret_key[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(dht->rng, plain, unused_secret_key); + + const uint64_t ping_id = ping_array_add(dht->dht_ping_array, + dht->mono_time, + dht->rng, + public_key, CRYPTO_PUBLIC_KEY_SIZE); + memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, &ping_id, sizeof(ping_id)); + + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + dht_get_shared_key_sent(dht, shared_key, public_key); + + uint8_t request[1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + sizeof(plain) + CRYPTO_MAC_SIZE]; + + if (dht_create_packet(dht->rng, dht->self_public_key, shared_key, NET_PACKET_DATA_SEARCH_REQUEST, + plain, sizeof(plain), request, sizeof(request)) != sizeof(request)) { + return false; + } + + return sendpacket(dht->net, ip_port, request, sizeof(request)) == sizeof(request); +} + +/** @brief If the response is valid, set the sender as an announce node. */ +non_null(1, 2, 3) nullable(5) +static int handle_data_search_response(void *object, const IP_Port *source, + const uint8_t *packet, uint16_t length, + void *userdata) +{ + DHT *dht = (DHT *) object; + + const int32_t plain_len = (int32_t)length - (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE); + + if (plain_len < (int32_t)(CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t))) { + return 1; + } + + VLA(uint8_t, plain, plain_len); + const uint8_t *public_key = packet + 1; + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + dht_get_shared_key_recv(dht, shared_key, public_key); + + if (decrypt_data_symmetric(shared_key, + packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, + packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, + plain_len + CRYPTO_MAC_SIZE, + plain) != plain_len) { + return 1; + } + + uint64_t ping_id; + memcpy(&ping_id, plain + (plain_len - sizeof(uint64_t)), sizeof(ping_id)); + + uint8_t ping_data[CRYPTO_PUBLIC_KEY_SIZE]; + + if (ping_array_check(dht->dht_ping_array, + dht->mono_time, ping_data, + sizeof(ping_data), ping_id) != sizeof(ping_data)) { + return 1; + } + + if (!pk_equal(ping_data, public_key)) { + return 1; + } + + set_announce_node(dht, public_key); + + return 0; + +} +#endif + +/** @brief Is it ok to store node with public_key in client. * - * return 0 if node can't be stored. - * return 1 if it can. + * return false if node can't be stored. + * return true if it can. */ -static unsigned int store_node_ok(const Client_data *client, uint64_t cur_time, const uint8_t *public_key, - const uint8_t *comp_public_key) +non_null() +static bool store_node_ok(const Client_data *client, uint64_t cur_time, const uint8_t *public_key, + const uint8_t *comp_public_key) { return (assoc_timeout(cur_time, &client->assoc4) && assoc_timeout(cur_time, &client->assoc6)) || id_closest(comp_public_key, client->public_key, public_key) == 2; } +non_null() static void sort_client_list(Client_data *list, uint64_t cur_time, unsigned int length, const uint8_t *comp_public_key) { // Pass comp_public_key to qsort with each Client_data entry, so the // comparison function can use it as the base of comparison. - VLA(DHT_Cmp_Data, cmp_list, length); + DHT_Cmp_Data *cmp_list = (DHT_Cmp_Data *)calloc(length, sizeof(DHT_Cmp_Data)); + + if (cmp_list == nullptr) { + return; + } for (uint32_t i = 0; i < length; ++i) { cmp_list[i].cur_time = cur_time; @@ -958,8 +1156,11 @@ static void sort_client_list(Client_data *list, uint64_t cur_time, unsigned int for (uint32_t i = 0; i < length; ++i) { list[i] = cmp_list[i].entry; } + + free(cmp_list); } +non_null() static void update_client_with_reset(const Mono_Time *mono_time, Client_data *client, const IP_Port *ip_port) { IPPTsPng *ipptp_write = nullptr; @@ -985,19 +1186,22 @@ static void update_client_with_reset(const Mono_Time *mono_time, Client_data *cl memset(ipptp_clear, 0, sizeof(*ipptp_clear)); } -/** Replace a first bad (or empty) node with this one - * or replace a possibly bad node (tests failed or not done yet) - * that is further than any other in the list - * from the comp_public_key - * or replace a good node that is further - * than any other in the list from the comp_public_key - * and further than public_key. +/** + * Replace a first bad (or empty) node with this one + * or replace a possibly bad node (tests failed or not done yet) + * that is further than any other in the list + * from the comp_public_key + * or replace a good node that is further + * than any other in the list from the comp_public_key + * and further than public_key. * * Do not replace any node if the list has no bad or possibly bad nodes - * and all nodes in the list are closer to comp_public_key - * than public_key. + * and all nodes in the list are closer to comp_public_key + * than public_key. * - * returns true when the item was stored, false otherwise */ + * @return true when the item was stored, false otherwise + */ +non_null() static bool replace_all(const DHT *dht, Client_data *list, uint16_t length, @@ -1017,20 +1221,21 @@ static bool replace_all(const DHT *dht, sort_client_list(list, dht->cur_time, length, comp_public_key); Client_data *const client = &list[0]; - id_copy(client->public_key, public_key); + pk_copy(client->public_key, public_key); update_client_with_reset(dht->mono_time, client, ip_port); return true; } -/** Add node to close list. +/** @brief Add node to close list. * * simulate is set to 1 if we want to check if a node can be added to the list without adding it. * - * return -1 on failure. - * return 0 on success. + * return false on failure. + * return true on success. */ -static int add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port, bool simulate) +non_null() +static bool add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port, bool simulate) { unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key); @@ -1049,31 +1254,35 @@ static int add_to_close(DHT *dht, const uint8_t *public_key, const IP_Port *ip_p } if (simulate) { - return 0; + return true; } - id_copy(client->public_key, public_key); + pk_copy(client->public_key, public_key); update_client_with_reset(dht->mono_time, client, ip_port); - return 0; +#ifdef CHECK_ANNOUNCE_NODE + client->announce_node = false; + send_announce_ping(dht, public_key, ip_port); +#endif + return true; } - return -1; + return false; } -/** Return 1 if node can be added to close list, 0 if it can't. - */ +/** Return 1 if node can be added to close list, 0 if it can't. */ bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port) { - return add_to_close(dht, public_key, ip_port, 1) == 0; + return add_to_close(dht, public_key, ip_port, true); } +non_null() static bool is_pk_in_client_list(const Client_data *list, unsigned int client_list_length, uint64_t cur_time, const uint8_t *public_key, const IP_Port *ip_port) { const uint32_t index = index_of_client_pk(list, client_list_length, public_key); if (index == UINT32_MAX) { - return 0; + return false; } const IPPTsPng *assoc = net_family_is_ipv4(ip_port->ip.family) @@ -1083,6 +1292,7 @@ static bool is_pk_in_client_list(const Client_data *list, unsigned int client_li return !assoc_timeout(cur_time, assoc); } +non_null() static bool is_pk_in_close_list(const DHT *dht, const uint8_t *public_key, const IP_Port *ip_port) { unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key); @@ -1095,17 +1305,19 @@ static bool is_pk_in_close_list(const DHT *dht, const uint8_t *public_key, const ip_port); } -/** Check if the node obtained with a get_nodes with public_key should be pinged. +/** @brief Check if the node obtained with a get_nodes with public_key should be pinged. + * * NOTE: for best results call it after addto_lists. * * return false if the node should not be pinged. * return true if it should. */ +non_null() static bool ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port) { bool ret = false; - if (add_to_close(dht, public_key, ip_port, 1) == 0) { + if (add_to_close(dht, public_key, ip_port, true)) { ret = true; } @@ -1161,10 +1373,10 @@ static bool ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_key, cons return ret; } -/** Attempt to add client with ip_port and public_key to the friends client list +/** @brief Attempt to add client with ip_port and public_key to the friends client list * and close_clientlist. * - * returns 1+ if the item is used in any list, 0 else + * @return 1+ if the item is used in any list, 0 else */ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key) { @@ -1179,7 +1391,7 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key public_key, &ipp_copy); /* add_to_close should be called only if !in_list (don't extract to variable) */ - if (in_close_list || add_to_close(dht, public_key, &ipp_copy, 0)) { + if (in_close_list || !add_to_close(dht, public_key, &ipp_copy, false)) { ++used; } @@ -1193,9 +1405,9 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key if (in_list || replace_all(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, public_key, &ipp_copy, dht->friends_list[i].public_key)) { - DHT_Friend *dht_friend = &dht->friends_list[i]; + const DHT_Friend *dht_friend = &dht->friends_list[i]; - if (id_equal(public_key, dht_friend->public_key)) { + if (pk_equal(public_key, dht_friend->public_key)) { friend_foundip = dht_friend; } @@ -1203,12 +1415,12 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key } } - if (!friend_foundip) { + if (friend_foundip == nullptr) { return used; } for (uint32_t i = 0; i < friend_foundip->lock_count; ++i) { - if (friend_foundip->callbacks[i].ip_callback) { + if (friend_foundip->callbacks[i].ip_callback != nullptr) { friend_foundip->callbacks[i].ip_callback(friend_foundip->callbacks[i].data, friend_foundip->callbacks[i].number, &ipp_copy); } @@ -1217,6 +1429,7 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key return used; } +non_null() static bool update_client_data(const Mono_Time *mono_time, Client_data *array, size_t size, const IP_Port *ip_port, const uint8_t *pk, bool node_is_self) { @@ -1245,20 +1458,22 @@ static bool update_client_data(const Mono_Time *mono_time, Client_data *array, s return true; } -/** If public_key is a friend or us, update ret_ip_port +/** + * If public_key is a friend or us, update ret_ip_port * nodepublic_key is the id of the node that sent us this info. */ +non_null() static void returnedip_ports(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *nodepublic_key) { IP_Port ipp_copy = ip_port_normalize(ip_port); - if (id_equal(public_key, dht->self_public_key)) { + if (pk_equal(public_key, dht->self_public_key)) { update_client_data(dht->mono_time, dht->close_clientlist, LCLIENT_LIST, &ipp_copy, nodepublic_key, true); return; } for (uint32_t i = 0; i < dht->num_friends; ++i) { - if (id_equal(public_key, dht->friends_list[i].public_key)) { + if (pk_equal(public_key, dht->friends_list[i].public_key)) { Client_data *const client_list = dht->friends_list[i].client_list; if (update_client_data(dht->mono_time, client_list, MAX_FRIEND_CLIENTS, &ipp_copy, nodepublic_key, false)) { @@ -1271,7 +1486,7 @@ static void returnedip_ports(DHT *dht, const IP_Port *ip_port, const uint8_t *pu bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id) { /* Check if packet is going to be sent to ourself. */ - if (id_equal(public_key, dht->self_public_key)) { + if (pk_equal(public_key, dht->self_public_key)) { return false; } @@ -1281,15 +1496,16 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c memcpy(receiver.public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); receiver.ip_port = *ip_port; - if (pack_nodes(plain_message, sizeof(plain_message), &receiver, 1) == -1) { + if (pack_nodes(dht->log, plain_message, sizeof(plain_message), &receiver, 1) == -1) { return false; } uint64_t ping_id = 0; - ping_id = ping_array_add(dht->dht_ping_array, dht->mono_time, plain_message, sizeof(receiver)); + ping_id = ping_array_add(dht->dht_ping_array, dht->mono_time, dht->rng, plain_message, sizeof(receiver)); if (ping_id == 0) { + LOGGER_ERROR(dht->log, "adding ping id failed"); return false; } @@ -1302,28 +1518,27 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; dht_get_shared_key_sent(dht, shared_key, public_key); - const int len = dht_create_packet(dht->self_public_key, shared_key, NET_PACKET_GET_NODES, - plain, sizeof(plain), data); + const int len = dht_create_packet(dht->rng, + dht->self_public_key, shared_key, NET_PACKET_GET_NODES, + plain, sizeof(plain), data, sizeof(data)); crypto_memzero(shared_key, sizeof(shared_key)); if (len != sizeof(data)) { + LOGGER_ERROR(dht->log, "getnodes packet encryption failed"); return false; } - if (sendpacket(dht->net, ip_port, data, len) > 0) { - return true; - } - - return false; + return sendpacket(dht->net, ip_port, data, len) > 0; } /** Send a send nodes response: message for IPv6 nodes */ +non_null() static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id, const uint8_t *sendback_data, uint16_t length, const uint8_t *shared_encryption_key) { /* Check if packet is going to be sent to ourself. */ - if (id_equal(public_key, dht->self_public_key)) { + if (pk_equal(public_key, dht->self_public_key)) { return -1; } @@ -1335,14 +1550,14 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t Node_format nodes_list[MAX_SENT_NODES]; const uint32_t num_nodes = - get_close_nodes(dht, client_id, nodes_list, net_family_unspec, ip_is_lan(&ip_port->ip)); + get_close_nodes(dht, client_id, nodes_list, net_family_unspec(), ip_is_lan(&ip_port->ip), false); VLA(uint8_t, plain, 1 + node_format_size * MAX_SENT_NODES + length); int nodes_length = 0; - if (num_nodes) { - nodes_length = pack_nodes(plain + 1, node_format_size * MAX_SENT_NODES, nodes_list, num_nodes); + if (num_nodes > 0) { + nodes_length = pack_nodes(dht->log, plain + 1, node_format_size * MAX_SENT_NODES, nodes_list, num_nodes); if (nodes_length <= 0) { return -1; @@ -1355,8 +1570,9 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t const uint32_t crypto_size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE; VLA(uint8_t, data, 1 + nodes_length + length + crypto_size); - const int len = dht_create_packet(dht->self_public_key, shared_encryption_key, NET_PACKET_SEND_NODES_IPV6, - plain, 1 + nodes_length + length, data); + const int len = dht_create_packet(dht->rng, + dht->self_public_key, shared_encryption_key, NET_PACKET_SEND_NODES_IPV6, + plain, 1 + nodes_length + length, data, SIZEOF_VLA(data)); if (len != SIZEOF_VLA(data)) { return -1; @@ -1367,17 +1583,18 @@ static int sendnodes_ipv6(const DHT *dht, const IP_Port *ip_port, const uint8_t #define CRYPTO_NODE_SIZE (CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint64_t)) +non_null() static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { if (length != (CRYPTO_SIZE + CRYPTO_MAC_SIZE + sizeof(uint64_t))) { - return true; + return 1; } DHT *const dht = (DHT *)object; /* Check if packet is from ourself. */ - if (id_equal(packet + 1, dht->self_public_key)) { - return true; + if (pk_equal(packet + 1, dht->self_public_key)) { + return 1; } uint8_t plain[CRYPTO_NODE_SIZE]; @@ -1393,7 +1610,7 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p if (len != CRYPTO_NODE_SIZE) { crypto_memzero(shared_key, sizeof(shared_key)); - return true; + return 1; } sendnodes_ipv6(dht, source, packet + 1, plain, plain + CRYPTO_PUBLIC_KEY_SIZE, sizeof(uint64_t), shared_key); @@ -1402,10 +1619,11 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p crypto_memzero(shared_key, sizeof(shared_key)); - return false; + return 0; } /** Return true if we sent a getnode packet to the peer associated with the supplied info. */ +non_null() static bool sent_getnode_to_node(DHT *dht, const uint8_t *public_key, const IP_Port *node_ip_port, uint64_t ping_id) { uint8_t data[sizeof(Node_format) * 2]; @@ -1420,31 +1638,28 @@ static bool sent_getnode_to_node(DHT *dht, const uint8_t *public_key, const IP_P return false; } - if (!ipport_equal(&test.ip_port, node_ip_port) || !id_equal(test.public_key, public_key)) { - return false; - } - - return true; + return ipport_equal(&test.ip_port, node_ip_port) && pk_equal(test.public_key, public_key); } -static int handle_sendnodes_core(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, - Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out) +non_null() +static bool handle_sendnodes_core(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out) { DHT *const dht = (DHT *)object; const uint32_t cid_size = 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + 1 + sizeof(uint64_t) + CRYPTO_MAC_SIZE; if (length < cid_size) { /* too short */ - return 1; + return false; } const uint32_t data_size = length - cid_size; if (data_size == 0) { - return 1; + return false; } if (data_size > sizeof(Node_format) * MAX_SENT_NODES) { /* invalid length */ - return 1; + return false; } VLA(uint8_t, plain, 1 + data_size + sizeof(uint64_t)); @@ -1460,33 +1675,33 @@ static int handle_sendnodes_core(void *object, const IP_Port *source, const uint crypto_memzero(shared_key, sizeof(shared_key)); if ((unsigned int)len != SIZEOF_VLA(plain)) { - return 1; + return false; } if (plain[0] > size_plain_nodes) { - return 1; + return false; } uint64_t ping_id; memcpy(&ping_id, plain + 1 + data_size, sizeof(ping_id)); if (!sent_getnode_to_node(dht, packet + 1, source, ping_id)) { - return 1; + return false; } uint16_t length_nodes = 0; - const int num_nodes = unpack_nodes(plain_nodes, plain[0], &length_nodes, plain + 1, data_size, 0); + const int num_nodes = unpack_nodes(plain_nodes, plain[0], &length_nodes, plain + 1, data_size, false); if (length_nodes != data_size) { - return 1; + return false; } if (num_nodes != plain[0]) { - return 1; + return false; } if (num_nodes < 0) { - return 1; + return false; } /* store the address the *request* was sent to */ @@ -1494,9 +1709,10 @@ static int handle_sendnodes_core(void *object, const IP_Port *source, const uint *num_nodes_out = num_nodes; - return 0; + return true; } +non_null() static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -1504,7 +1720,7 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint Node_format plain_nodes[MAX_SENT_NODES]; uint32_t num_nodes; - if (handle_sendnodes_core(object, source, packet, length, plain_nodes, MAX_SENT_NODES, &num_nodes)) { + if (!handle_sendnodes_core(object, source, packet, length, plain_nodes, MAX_SENT_NODES, &num_nodes)) { return 1; } @@ -1516,6 +1732,10 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint if (ipport_isset(&plain_nodes[i].ip_port)) { ping_node_from_getnodes_ok(dht, plain_nodes[i].public_key, &plain_nodes[i].ip_port); returnedip_ports(dht, &plain_nodes[i].ip_port, plain_nodes[i].public_key, packet + 1); + + if (dht->get_nodes_response != nullptr) { + dht->get_nodes_response(dht, &plain_nodes[i], userdata); + } } } @@ -1525,6 +1745,7 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint /*----------------------------------------------------------------------------------*/ /*------------------------END of packet handling functions--------------------------*/ +non_null(1) nullable(2, 3, 5) static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback, void *data, int32_t number, uint16_t *lock_count) { @@ -1534,7 +1755,7 @@ static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback dht_friend->callbacks[lock_num].data = data; dht_friend->callbacks[lock_num].number = number; - if (lock_count) { + if (lock_count != nullptr) { *lock_count = lock_num + 1; } } @@ -1564,16 +1785,16 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback, dht->friends_list = temp; DHT_Friend *const dht_friend = &dht->friends_list[dht->num_friends]; - memset(dht_friend, 0, sizeof(DHT_Friend)); + *dht_friend = empty_dht_friend; memcpy(dht_friend->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - dht_friend->nat.nat_ping_id = random_u64(); + dht_friend->nat.nat_ping_id = random_u64(dht->rng); ++dht->num_friends; dht_friend_lock(dht_friend, ip_callback, data, number, lock_count); - dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec, - 1); + dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec(), + true, false); return 0; } @@ -1589,7 +1810,7 @@ int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count) DHT_Friend *const dht_friend = &dht->friends_list[friend_num]; --dht_friend->lock_count; - if (dht_friend->lock_count && lock_count) { /* DHT friend is still in use.*/ + if (dht_friend->lock_count > 0 && lock_count > 0) { /* DHT friend is still in use.*/ --lock_count; dht_friend->callbacks[lock_count].ip_callback = nullptr; dht_friend->callbacks[lock_count].data = nullptr; @@ -1641,7 +1862,7 @@ int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port) const Client_data *const client = &frnd->client_list[client_index]; const IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr }; - for (const IPPTsPng * const *it = assocs; *it; ++it) { + for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) { const IPPTsPng *const assoc = *it; if (!assoc_timeout(dht->cur_time, assoc)) { @@ -1654,6 +1875,7 @@ int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port) } /** returns number of nodes not in kill-timeout */ +non_null() static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, const uint8_t *public_key, Client_data *list, uint32_t list_count, uint32_t *bootstrap_times, bool sortable) { @@ -1661,11 +1883,17 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co const uint64_t temp_time = mono_time_get(dht->mono_time); uint32_t num_nodes = 0; - VLA(Client_data *, client_list, list_count * 2); - VLA(IPPTsPng *, assoc_list, list_count * 2); + Client_data **client_list = (Client_data **)calloc(list_count * 2, sizeof(Client_data *)); + IPPTsPng **assoc_list = (IPPTsPng **)calloc(list_count * 2, sizeof(IPPTsPng *)); unsigned int sort = 0; bool sort_ok = false; + if (client_list == nullptr || assoc_list == nullptr) { + free(assoc_list); + free(client_list); + return 0; + } + for (uint32_t i = 0; i < list_count; ++i) { /* If node is not dead. */ Client_data *client = &list[i]; @@ -1705,12 +1933,12 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co sort_client_list(list, dht->cur_time, list_count, public_key); } - if ((num_nodes != 0) && (mono_time_is_timeout(dht->mono_time, *lastgetnode, GET_NODE_INTERVAL) - || *bootstrap_times < MAX_BOOTSTRAP_TIMES)) { - uint32_t rand_node = random_range_u32(num_nodes); + if (num_nodes > 0 && (mono_time_is_timeout(dht->mono_time, *lastgetnode, GET_NODE_INTERVAL) + || *bootstrap_times < MAX_BOOTSTRAP_TIMES)) { + uint32_t rand_node = random_range_u32(dht->rng, num_nodes); if ((num_nodes - 1) != rand_node) { - rand_node += random_range_u32(num_nodes - (rand_node + 1)); + rand_node += random_range_u32(dht->rng, num_nodes - (rand_node + 1)); } dht_getnodes(dht, &assoc_list[rand_node]->ip_port, client_list[rand_node]->public_key, public_key); @@ -1719,12 +1947,17 @@ static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, co ++*bootstrap_times; } + free(assoc_list); + free(client_list); return not_kill; } -/** Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request - * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list. +/** @brief Ping each client in the "friends" list every PING_INTERVAL seconds. + * + * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good + * node for each "friend" in our "friends" list. */ +non_null() static void do_dht_friends(DHT *dht) { for (size_t i = 0; i < dht->num_friends; ++i) { @@ -1738,13 +1971,15 @@ static void do_dht_friends(DHT *dht) do_ping_and_sendnode_requests(dht, &dht_friend->lastgetnode, dht_friend->public_key, dht_friend->client_list, MAX_FRIEND_CLIENTS, - &dht_friend->bootstrap_times, 1); + &dht_friend->bootstrap_times, true); } } -/** Ping each client in the close nodes list every PING_INTERVAL seconds. +/** @brief Ping each client in the close nodes list every PING_INTERVAL seconds. + * * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list. */ +non_null() static void do_Close(DHT *dht) { for (size_t i = 0; i < dht->num_to_bootstrap; ++i) { @@ -1753,9 +1988,9 @@ static void do_Close(DHT *dht) dht->num_to_bootstrap = 0; - uint8_t not_killed = do_ping_and_sendnode_requests( - dht, &dht->close_lastgetnodes, dht->self_public_key, dht->close_clientlist, LCLIENT_LIST, &dht->close_bootstrap_times, - 0); + const uint8_t not_killed = do_ping_and_sendnode_requests( + dht, &dht->close_lastgetnodes, dht->self_public_key, dht->close_clientlist, LCLIENT_LIST, &dht->close_bootstrap_times, + false); if (not_killed != 0) { return; @@ -1774,22 +2009,27 @@ static void do_Close(DHT *dht) IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr }; - for (IPPTsPng * const *it = assocs; *it; ++it) { + for (IPPTsPng * const *it = assocs; *it != nullptr; ++it) { IPPTsPng *const assoc = *it; - if (assoc->timestamp) { + if (assoc->timestamp != 0) { assoc->timestamp = badonly; } } } } -void dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key) +bool dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key) { - dht_getnodes(dht, ip_port, public_key, dht->self_public_key); + if (pk_equal(public_key, dht->self_public_key)) { + // Bootstrapping off ourselves is ok (onion paths are still set up). + return true; + } + + return dht_getnodes(dht, ip_port, public_key, dht->self_public_key); } -int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, +int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled, uint16_t port, const uint8_t *public_key) { IP_Port ip_port_v64; @@ -1799,12 +2039,12 @@ int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enable if (ipv6enabled) { /* setup for getting BOTH: an IPv6 AND an IPv4 address */ - ip_port_v64.ip.family = net_family_unspec; + ip_port_v64.ip.family = net_family_unspec(); ip_reset(&ip_port_v4.ip); ip_extra = &ip_port_v4.ip; } - if (addr_resolve_or_parse_ip(address, &ip_port_v64.ip, ip_extra)) { + if (addr_resolve_or_parse_ip(dht->ns, address, &ip_port_v64.ip, ip_extra)) { ip_port_v64.port = port; dht_bootstrap(dht, &ip_port_v64, public_key); @@ -1819,18 +2059,19 @@ int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enable return 0; } -/** Send the given packet to node with public_key. +/** @brief Send the given packet to node with public_key. * - * return -1 if failure. + * @return number of bytes sent. + * @retval -1 if failure. */ int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length) { for (uint32_t i = 0; i < LCLIENT_LIST; ++i) { - if (id_equal(public_key, dht->close_clientlist[i].public_key)) { + if (pk_equal(public_key, dht->close_clientlist[i].public_key)) { const Client_data *const client = &dht->close_clientlist[i]; const IPPTsPng *const assocs[] = { &client->assoc6, &client->assoc4, nullptr }; - for (const IPPTsPng * const *it = assocs; *it; ++it) { + for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) { const IPPTsPng *const assoc = *it; if (ip_isset(&assoc->ip_port.ip)) { @@ -1845,13 +2086,15 @@ int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packe return -1; } -/** Puts all the different ips returned by the nodes for a friend_num into array ip_portlist. +/** @brief Puts all the different ips returned by the nodes for a friend_num into array ip_portlist. + * * ip_portlist must be at least MAX_FRIEND_CLIENTS big. * - * return the number of ips returned. - * return 0 if we are connected to friend or if no ips were found. - * return -1 if no such friend. + * @return the number of ips returned. + * @retval 0 if we are connected to friend or if no ips were found. + * @retval -1 if no such friend. */ +non_null() static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_num) { if (friend_num >= dht->num_friends) { @@ -1880,7 +2123,7 @@ static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_n ++num_ipv6s; } - if (id_equal(client->public_key, dht_friend->public_key)) { + if (pk_equal(client->public_key, dht_friend->public_key)) { if (!assoc_timeout(dht->cur_time, &client->assoc6) || !assoc_timeout(dht->cur_time, &client->assoc4)) { return 0; /* direct connectivity */ @@ -1946,6 +2189,7 @@ typedef bool foreach_ip_port_cb(const DHT *dht, const IP_Port *ip_port, uint32_t * @param callback The callback to invoke for each IP/port. * @param userdata Extra pointer passed to the callback. */ +non_null() static uint32_t foreach_ip_port(const DHT *dht, const DHT_Friend *dht_friend, foreach_ip_port_cb *callback, void *userdata) { @@ -1977,6 +2221,7 @@ static uint32_t foreach_ip_port(const DHT *dht, const DHT_Friend *dht_friend, return n; } +non_null() static bool send_packet_to_friend(const DHT *dht, const IP_Port *ip_port, uint32_t *n, void *userdata) { const Packet *packet = (const Packet *)userdata; @@ -1994,8 +2239,8 @@ static bool send_packet_to_friend(const DHT *dht, const IP_Port *ip_port, uint32 /** * Send the following packet to everyone who tells us they are connected to friend_id. * - * return ip for friend. - * return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4). + * @return ip for friend. + * @return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4). */ uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet) { @@ -2019,6 +2264,7 @@ uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet return foreach_ip_port(dht, dht_friend, send_packet_to_friend, &packet_userdata); } +non_null() static bool get_ip_port(const DHT *dht, const IP_Port *ip_port, uint32_t *n, void *userdata) { IP_Port *ip_list = (IP_Port *)userdata; @@ -2027,10 +2273,11 @@ static bool get_ip_port(const DHT *dht, const IP_Port *ip_port, uint32_t *n, voi return true; } -/** Send the following packet to one random person who tells us they are connected to friend_id. +/** @brief Send the following packet to one random person who tells us they are connected to friend_id. * - * return number of nodes the packet was sent to. + * @return number of nodes the packet was sent to. */ +non_null() static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet) { const uint32_t num = index_of_friend_pk(dht->friends_list, dht->num_friends, friend_id); @@ -2049,7 +2296,7 @@ static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, con return 0; } - const uint32_t rand_idx = random_range_u32(n); + const uint32_t rand_idx = random_range_u32(dht->rng, n); const int retval = send_packet(dht->net, &ip_list[rand_idx], *packet); if ((unsigned int)retval == packet->length) { @@ -2062,6 +2309,7 @@ static uint32_t routeone_to_friend(const DHT *dht, const uint8_t *friend_id, con /*----------------------------------------------------------------------------------*/ /*---------------------BEGINNING OF NAT PUNCHING FUNCTIONS--------------------------*/ +non_null() static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping_id, uint8_t type) { uint8_t data[sizeof(uint64_t) + 1]; @@ -2071,8 +2319,8 @@ static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping memcpy(data + 1, &ping_id, sizeof(uint64_t)); /* 254 is NAT ping request packet id */ const int len = create_request( - dht->self_public_key, dht->self_secret_key, packet_data, public_key, data, - sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING); + dht->rng, dht->self_public_key, dht->self_secret_key, packet_data, public_key, + data, sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING); if (len == -1) { return -1; @@ -2096,6 +2344,7 @@ static int send_NATping(const DHT *dht, const uint8_t *public_key, uint64_t ping } /** Handle a received ping request for. */ +non_null() static int handle_NATping(void *object, const IP_Port *source, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length, void *userdata) { @@ -2107,7 +2356,7 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so uint64_t ping_id; memcpy(&ping_id, packet + 1, sizeof(uint64_t)); - uint32_t friendnumber = index_of_friend_pk(dht->friends_list, dht->num_friends, source_pubkey); + const uint32_t friendnumber = index_of_friend_pk(dht->friends_list, dht->num_friends, source_pubkey); if (friendnumber == UINT32_MAX) { return 1; @@ -2124,8 +2373,8 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so if (packet[0] == NAT_PING_RESPONSE) { if (dht_friend->nat.nat_ping_id == ping_id) { - dht_friend->nat.nat_ping_id = random_u64(); - dht_friend->nat.hole_punching = 1; + dht_friend->nat.nat_ping_id = random_u64(dht->rng); + dht_friend->nat.hole_punching = true; return 0; } } @@ -2133,12 +2382,13 @@ static int handle_NATping(void *object, const IP_Port *source, const uint8_t *so return 1; } -/** Get the most common ip in the ip_portlist. +/** @brief Get the most common ip in the ip_portlist. * Only return ip if it appears in list min_num or more. * len must not be bigger than MAX_FRIEND_CLIENTS. * - * return ip of 0 if failure. + * @return ip of 0 if failure. */ +non_null() static IP nat_commonip(const IP_Port *ip_portlist, uint16_t len, uint16_t min_num) { IP zero; @@ -2165,12 +2415,13 @@ static IP nat_commonip(const IP_Port *ip_portlist, uint16_t len, uint16_t min_nu return zero; } -/** Return all the ports for one ip in a list. +/** @brief Return all the ports for one ip in a list. * portlist must be at least len long, * where len is the length of ip_portlist. * - * return number of ports and puts the list of ports in portlist. + * @return number of ports and puts the list of ports in portlist. */ +non_null() static uint16_t nat_getports(uint16_t *portlist, const IP_Port *ip_portlist, uint16_t len, const IP *ip) { uint16_t num = 0; @@ -2185,6 +2436,7 @@ static uint16_t nat_getports(uint16_t *portlist, const IP_Port *ip_portlist, uin return num; } +non_null() static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint16_t numports, uint16_t friend_num) { if (!dht->hole_punching_enabled) { @@ -2196,24 +2448,25 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1 } const uint16_t first_port = port_list[0]; - uint32_t i; + uint16_t port_candidate; - for (i = 0; i < numports; ++i) { - if (first_port != port_list[i]) { + for (port_candidate = 0; port_candidate < numports; ++port_candidate) { + if (first_port != port_list[port_candidate]) { break; } } - if (i == numports) { /* If all ports are the same, only try that one port. */ + if (port_candidate == numports) { /* If all ports are the same, only try that one port. */ IP_Port pinging; ip_copy(&pinging.ip, ip); pinging.port = net_htons(first_port); ping_send_request(dht->ping, &pinging, dht->friends_list[friend_num].public_key); } else { + uint16_t i; for (i = 0; i < MAX_PUNCHING_PORTS; ++i) { /* TODO(irungentoo): Improve port guessing algorithm. */ const uint32_t it = i + dht->friends_list[friend_num].nat.punching_index; - const int8_t sign = (it % 2) ? -1 : 1; + const int8_t sign = (it % 2 != 0) ? -1 : 1; const uint32_t delta = sign * (it / (2 * numports)); const uint32_t index = (it / 2) % numports; const uint16_t port = port_list[index] + delta; @@ -2227,12 +2480,13 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1 } if (dht->friends_list[friend_num].nat.tries > MAX_NORMAL_PUNCHING_TRIES) { - const uint16_t port = 1024; IP_Port pinging; ip_copy(&pinging.ip, ip); + uint16_t i; for (i = 0; i < MAX_PUNCHING_PORTS; ++i) { uint32_t it = i + dht->friends_list[friend_num].nat.punching_index2; + const uint16_t port = 1024; pinging.port = net_htons(port + it); ping_send_request(dht->ping, &pinging, dht->friends_list[friend_num].public_key); } @@ -2243,6 +2497,7 @@ static void punch_holes(DHT *dht, const IP *ip, const uint16_t *port_list, uint1 ++dht->friends_list[friend_num].nat.tries; } +non_null() static void do_NAT(DHT *dht) { const uint64_t temp_time = mono_time_get(dht->mono_time); @@ -2261,7 +2516,7 @@ static void do_NAT(DHT *dht) dht->friends_list[i].nat.nat_ping_timestamp = temp_time; } - if (dht->friends_list[i].nat.hole_punching == 1 && + if (dht->friends_list[i].nat.hole_punching && dht->friends_list[i].nat.punching_timestamp + PUNCH_INTERVAL < temp_time && dht->friends_list[i].nat.recv_nat_ping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) { @@ -2282,7 +2537,7 @@ static void do_NAT(DHT *dht) punch_holes(dht, &ip, port_list, numports, i); dht->friends_list[i].nat.punching_timestamp = temp_time; - dht->friends_list[i].nat.hole_punching = 0; + dht->friends_list[i].nat.hole_punching = false; } } } @@ -2290,12 +2545,13 @@ static void do_NAT(DHT *dht) /*----------------------------------------------------------------------------------*/ /*-----------------------END OF NAT PUNCHING FUNCTIONS------------------------------*/ -/** Put up to max_num nodes in nodes from the closelist. +/** @brief Put up to max_num nodes in nodes from the closelist. * - * return the number of nodes. + * @return the number of nodes. */ -static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_time, - Node_format *nodes, uint16_t max_num) +non_null() +static uint16_t list_nodes(const Random *rng, const Client_data *list, size_t length, + uint64_t cur_time, Node_format *nodes, uint16_t max_num) { if (max_num == 0) { return 0; @@ -2313,7 +2569,7 @@ static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_ if (!assoc_timeout(cur_time, &list[i - 1].assoc6)) { if (assoc == nullptr) { assoc = &list[i - 1].assoc6; - } else if (random_u08() % 2) { + } else if ((random_u08(rng) % 2) != 0) { assoc = &list[i - 1].assoc6; } } @@ -2332,9 +2588,12 @@ static uint16_t list_nodes(const Client_data *list, size_t length, uint64_t cur_ return count; } -/** Put up to max_num nodes in nodes from the random friends. +/** @brief Put up to max_num nodes in nodes from the random friends. + * + * Important: this function relies on the first two DHT friends *not* being real + * friends to avoid leaking information about real friends into the onion paths. * - * return the number of nodes. + * @return the number of nodes. */ uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num) { @@ -2343,10 +2602,14 @@ uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num) } uint16_t count = 0; - const uint32_t r = random_u32(); + const uint32_t r = random_u32(dht->rng); + + assert(DHT_FAKE_FRIEND_NUMBER <= dht->num_friends); - for (size_t i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) { - count += list_nodes(dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].client_list, MAX_FRIEND_CLIENTS, dht->cur_time, + // Only gather nodes from the initial 2 fake friends. + for (uint32_t i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) { + count += list_nodes(dht->rng, dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].client_list, + MAX_FRIEND_CLIENTS, dht->cur_time, nodes + count, max_num - count); if (count >= max_num) { @@ -2357,13 +2620,13 @@ uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num) return count; } -/** Put up to max_num nodes in nodes from the closelist. +/** @brief Put up to max_num nodes in nodes from the closelist. * - * return the number of nodes. + * @return the number of nodes. */ uint16_t closelist_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num) { - return list_nodes(dht->close_clientlist, LCLIENT_LIST, dht->cur_time, nodes, max_num); + return list_nodes(dht->rng, dht->close_clientlist, LCLIENT_LIST, dht->cur_time, nodes, max_num); } /*----------------------------------------------------------------------------------*/ @@ -2374,6 +2637,7 @@ void cryptopacket_registerhandler(DHT *dht, uint8_t byte, cryptopacket_handler_c dht->cryptopackethandlers[byte].object = object; } +non_null() static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -2387,7 +2651,7 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_ } // Check if request is for us. - if (id_equal(packet + 1, dht->self_public_key)) { + if (pk_equal(packet + 1, dht->self_public_key)) { uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t data[MAX_CRYPTO_REQUEST_SIZE]; uint8_t number; @@ -2398,7 +2662,7 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_ return 1; } - if (!dht->cryptopackethandlers[number].function) { + if (dht->cryptopackethandlers[number].function == nullptr) { return 1; } @@ -2417,9 +2681,37 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_ return 1; } +void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function) +{ + dht->get_nodes_response = function; +} + +non_null(1, 2, 3) nullable(5) +static int handle_LANdiscovery(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + DHT *dht = (DHT *)object; + + if (!dht->lan_discovery_enabled) { + return 1; + } + + if (!ip_is_lan(&source->ip)) { + return 1; + } + + if (length != CRYPTO_PUBLIC_KEY_SIZE + 1) { + return 1; + } + + dht_bootstrap(dht, source, packet + 1); + return 0; +} + /*----------------------------------------------------------------------------------*/ -DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool holepunching_enabled) +DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, Networking_Core *net, + bool hole_punching_enabled, bool lan_discovery_enabled) { if (net == nullptr) { return nullptr; @@ -2431,14 +2723,17 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool return nullptr; } + dht->ns = ns; dht->mono_time = mono_time; dht->cur_time = mono_time_get(mono_time); dht->log = log; dht->net = net; + dht->rng = rng; - dht->hole_punching_enabled = holepunching_enabled; + dht->hole_punching_enabled = hole_punching_enabled; + dht->lan_discovery_enabled = lan_discovery_enabled; - dht->ping = ping_new(mono_time, dht); + dht->ping = ping_new(mono_time, rng, dht); if (dht->ping == nullptr) { kill_dht(dht); @@ -2448,9 +2743,14 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool networking_registerhandler(dht->net, NET_PACKET_GET_NODES, &handle_getnodes, dht); networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, &handle_sendnodes_ipv6, dht); networking_registerhandler(dht->net, NET_PACKET_CRYPTO, &cryptopacket_handle, dht); + networking_registerhandler(dht->net, NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht); cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht); - crypto_new_keypair(dht->self_public_key, dht->self_secret_key); +#ifdef CHECK_ANNOUNCE_NODE + networking_registerhandler(dht->net, NET_PACKET_DATA_SEARCH_RESPONSE, &handle_data_search_response, dht); +#endif + + crypto_new_keypair(rng, dht->self_public_key, dht->self_secret_key); dht->dht_ping_array = ping_array_new(DHT_PING_ARRAY_SIZE, PING_TIMEOUT); @@ -2463,7 +2763,7 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool uint8_t random_public_key_bytes[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t random_secret_key_bytes[CRYPTO_SECRET_KEY_SIZE]; - crypto_new_keypair(random_public_key_bytes, random_secret_key_bytes); + crypto_new_keypair(rng, random_public_key_bytes, random_secret_key_bytes); if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, nullptr) != 0) { kill_dht(dht); @@ -2471,6 +2771,12 @@ DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool } } + if (dht->num_friends != DHT_FAKE_FRIEND_NUMBER) { + LOGGER_ERROR(log, "the RNG provided seems to be broken: it generated the same keypair twice"); + kill_dht(dht); + return nullptr; + } + return dht; } @@ -2485,7 +2791,7 @@ void do_dht(DHT *dht) dht->cur_time = cur_time; // Load friends/clients if first call to do_dht - if (dht->loaded_num_nodes) { + if (dht->loaded_num_nodes > 0) { dht_connect_after_load(dht); } @@ -2497,9 +2803,16 @@ void do_dht(DHT *dht) void kill_dht(DHT *dht) { + if (dht == nullptr) { + return; + } + networking_registerhandler(dht->net, NET_PACKET_GET_NODES, nullptr, nullptr); networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, nullptr, nullptr); + networking_registerhandler(dht->net, NET_PACKET_CRYPTO, nullptr, nullptr); + networking_registerhandler(dht->net, NET_PACKET_LAN_DISCOVERY, nullptr, nullptr); cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, nullptr, nullptr); + ping_array_kill(dht->dht_ping_array); ping_kill(dht->ping); free(dht->friends_list); @@ -2531,26 +2844,26 @@ uint32_t dht_size(const DHT *dht) } for (uint32_t i = 0; i < LCLIENT_LIST; ++i) { - numv4 += (dht->close_clientlist[i].assoc4.timestamp != 0); - numv6 += (dht->close_clientlist[i].assoc6.timestamp != 0); + numv4 += dht->close_clientlist[i].assoc4.timestamp != 0; + numv6 += dht->close_clientlist[i].assoc6.timestamp != 0; } for (uint32_t i = 0; i < DHT_FAKE_FRIEND_NUMBER && i < dht->num_friends; ++i) { const DHT_Friend *const fr = &dht->friends_list[i]; for (uint32_t j = 0; j < MAX_FRIEND_CLIENTS; ++j) { - numv4 += (fr->client_list[j].assoc4.timestamp != 0); - numv6 += (fr->client_list[j].assoc6.timestamp != 0); + numv4 += fr->client_list[j].assoc4.timestamp != 0; + numv6 += fr->client_list[j].assoc6.timestamp != 0; } } const uint32_t size32 = sizeof(uint32_t); const uint32_t sizesubhead = size32 * 2; - return size32 + sizesubhead + packed_node_size(net_family_ipv4) * numv4 + packed_node_size(net_family_ipv6) * numv6; + return size32 + sizesubhead + packed_node_size(net_family_ipv4()) * numv4 + packed_node_size(net_family_ipv6()) * numv6; } -/** Save the DHT in data where data is an array of size dht_size(). */ +/** Save the DHT in data where data is an array of size `dht_size()`. */ void dht_save(const DHT *dht, uint8_t *data) { host_to_lendian_bytes32(data, DHT_STATE_COOKIE_GLOBAL); @@ -2607,19 +2920,19 @@ void dht_save(const DHT *dht, uint8_t *data) } } - state_write_section_header(old_data, DHT_STATE_COOKIE_TYPE, pack_nodes(data, sizeof(Node_format) * num, clients, num), - DHT_STATE_TYPE_NODES); + state_write_section_header(old_data, DHT_STATE_COOKIE_TYPE, pack_nodes(dht->log, data, sizeof(Node_format) * num, + clients, num), DHT_STATE_TYPE_NODES); free(clients); } -/** Bootstrap from this number of nodes every time dht_connect_after_load() is called */ +/** Bootstrap from this number of nodes every time `dht_connect_after_load()` is called */ #define SAVE_BOOTSTAP_FREQUENCY 8 -/** Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. +/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. * - * returns 0 if successful - * returns -1 otherwise + * @retval 0 if successful + * @retval -1 otherwise */ int dht_connect_after_load(DHT *dht) { @@ -2627,7 +2940,7 @@ int dht_connect_after_load(DHT *dht) return -1; } - if (!dht->loaded_nodes_list) { + if (dht->loaded_nodes_list == nullptr) { return -1; } @@ -2648,6 +2961,7 @@ int dht_connect_after_load(DHT *dht) return 0; } +non_null() static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type) { DHT *dht = (DHT *)outer; @@ -2668,7 +2982,7 @@ static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *dat break; } - const int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_DHT_NODES, nullptr, data, length, 0); + const int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_DHT_NODES, nullptr, data, length, false); if (num > 0) { dht->loaded_num_nodes = num; @@ -2689,10 +3003,10 @@ static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *dat return STATE_LOAD_STATUS_CONTINUE; } -/** Load the DHT from data of size size. +/** @brief Load the DHT from data of size size. * - * return -1 if failure. - * return 0 if success. + * @retval -1 if failure. + * @retval 0 if success. */ int dht_load(DHT *dht, const uint8_t *data, uint32_t length) { @@ -2711,8 +3025,9 @@ int dht_load(DHT *dht, const uint8_t *data, uint32_t length) return -1; } -/** return false if we are not connected to the DHT. - * return true if we are. +/** + * @retval false if we are not connected to the DHT. + * @retval true if we are. */ bool dht_isconnected(const DHT *dht) { @@ -2728,8 +3043,9 @@ bool dht_isconnected(const DHT *dht) return false; } -/** return false if we are not connected or only connected to lan peers with the DHT. - * return true if we are. +/** + * @retval false if we are not connected or only connected to lan peers with the DHT. + * @retval true if we are. */ bool dht_non_lan_connected(const DHT *dht) { @@ -2750,13 +3066,15 @@ bool dht_non_lan_connected(const DHT *dht) return false; } -/** Copies our own ip_port structure to `dest`. WAN addresses take priority over LAN addresses. +/** @brief Copies our own ip_port structure to `dest`. + * + * WAN addresses take priority over LAN addresses. * * This function will zero the `dest` buffer before use. * - * Return 0 if our ip port can't be found (this usually means we're not connected to the DHT). - * Return 1 if IP is a WAN address. - * Return 2 if IP is a LAN address. + * @retval 0 if our ip port can't be found (this usually means we're not connected to the DHT). + * @retval 1 if IP is a WAN address. + * @retval 2 if IP is a LAN address. */ unsigned int ipport_self_copy(const DHT *dht, IP_Port *dest) { diff --git a/protocols/Tox/libtox/src/toxcore/DHT.h b/protocols/Tox/libtox/src/toxcore/DHT.h index f72ef924c9..34ecf9dd0b 100644 --- a/protocols/Tox/libtox/src/toxcore/DHT.h +++ b/protocols/Tox/libtox/src/toxcore/DHT.h @@ -3,14 +3,15 @@ * Copyright © 2013 Tox project. */ -/** - * An implementation of the DHT as seen in docs/updates/DHT.md +/** @file + * @brief An implementation of the DHT as seen in docs/updates/DHT.md */ #ifndef C_TOXCORE_TOXCORE_DHT_H #define C_TOXCORE_TOXCORE_DHT_H #include <stdbool.h> +#include "attributes.h" #include "crypto_core.h" #include "logger.h" #include "mono_time.h" @@ -21,6 +22,8 @@ extern "C" { #endif +/* Maximum size of a signature (may be smaller) */ +#define SIGNATURE_SIZE CRYPTO_SIGNATURE_SIZE /** Maximum number of clients stored per friend. */ #define MAX_FRIEND_CLIENTS 8 @@ -49,36 +52,82 @@ extern "C" { #define PING_ROUNDTRIP 2 #define BAD_NODE_TIMEOUT (PING_INTERVAL + PINGS_MISSED_NODE_GOES_BAD * (PING_INTERVAL + PING_ROUNDTRIP)) -/** The number of "fake" friends to add (for optimization purposes and so our paths for the onion part are more random) */ +/** + * The number of "fake" friends to add. + * + * (for optimization purposes and so our paths for the onion part are more random) + */ #define DHT_FAKE_FRIEND_NUMBER 2 +/** Maximum packet size for a DHT request packet. */ #define MAX_CRYPTO_REQUEST_SIZE 1024 #define CRYPTO_PACKET_FRIEND_REQ 32 // Friend request crypto packet ID. #define CRYPTO_PACKET_DHTPK 156 #define CRYPTO_PACKET_NAT_PING 254 // NAT ping crypto packet ID. -/** Create a request to peer. - * send_public_key and send_secret_key are the pub/secret keys of the sender. - * recv_public_key is public key of receiver. - * packet must be an array of MAX_CRYPTO_REQUEST_SIZE big. - * Data represents the data we send with the request with length being the length of the data. - * request_id is the id of the request (32 = friend request, 254 = ping request). +/* Max size of a packed node for IPV4 and IPV6 respectively */ +#define PACKED_NODE_SIZE_IP4 (1 + SIZE_IP4 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE) +#define PACKED_NODE_SIZE_IP6 (1 + SIZE_IP6 + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE) + +/** + * This define can eventually be removed; it is necessary if a significant + * proportion of dht nodes do not implement the dht announcements protocol. + */ +#define CHECK_ANNOUNCE_NODE + +/** + * @brief Create a request to peer. + * + * Packs the data and sender public key and encrypts the packet. + * + * @param[in] send_public_key public key of the sender. + * @param[in] send_secret_key secret key of the sender. + * @param[out] packet an array of @ref MAX_CRYPTO_REQUEST_SIZE big. + * @param[in] recv_public_key public key of the receiver. + * @param[in] data represents the data we send with the request. + * @param[in] data_length the length of the data. + * @param[in] request_id the id of the request (32 = friend request, 254 = ping request). + * + * @attention Constraints: + * @code + * sizeof(packet) >= MAX_CRYPTO_REQUEST_SIZE + * @endcode * - * return -1 on failure. - * return the length of the created packet on success. + * @retval -1 on failure. + * @return the length of the created packet on success. */ -int create_request(const uint8_t *send_public_key, const uint8_t *send_secret_key, uint8_t *packet, - const uint8_t *recv_public_key, const uint8_t *data, uint32_t length, uint8_t request_id); +non_null() +int create_request(const Random *rng, const uint8_t *send_public_key, const uint8_t *send_secret_key, + uint8_t *packet, const uint8_t *recv_public_key, + const uint8_t *data, uint32_t data_length, uint8_t request_id); -/** Puts the senders public key in the request in public_key, the data from the request - * in data if a friend or ping request was sent to us and returns the length of the data. - * packet is the request packet and length is its length. +/** + * @brief Decrypts and unpacks a DHT request packet. + * + * Puts the senders public key in the request in @p public_key, the data from + * the request in @p data. + * + * @param[in] self_public_key public key of the receiver (us). + * @param[in] self_secret_key secret key of the receiver (us). + * @param[out] public_key public key of the sender, copied from the input packet. + * @param[out] data decrypted request data, copied from the input packet, must + * have room for @ref MAX_CRYPTO_REQUEST_SIZE bytes. + * @param[in] packet is the request packet. + * @param[in] packet_length length of the packet. * - * return -1 if not valid request. + * @attention Constraints: + * @code + * sizeof(data) >= MAX_CRYPTO_REQUEST_SIZE + * @endcode + * + * @retval -1 if not valid request. + * @return the length of the unpacked data. */ -int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data, - uint8_t *request_id, const uint8_t *packet, uint16_t length); +non_null() +int handle_request( + const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data, + uint8_t *request_id, const uint8_t *packet, uint16_t packet_length); typedef struct IPPTs { IP_Port ip_port; @@ -101,13 +150,18 @@ typedef struct Client_data { uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; IPPTsPng assoc4; IPPTsPng assoc6; + +#ifdef CHECK_ANNOUNCE_NODE + /* Responded to data search? */ + bool announce_node; +#endif } Client_data; /*----------------------------------------------------------------------------------*/ typedef struct NAT { - /* 1 if currently hole punching, otherwise 0 */ - uint8_t hole_punching; + /* true if currently hole punching */ + bool hole_punching; uint32_t punching_index; uint32_t tries; uint32_t punching_index2; @@ -125,48 +179,66 @@ typedef struct Node_format { IP_Port ip_port; } Node_format; +extern const Node_format empty_node_format; + typedef struct DHT_Friend DHT_Friend; -const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend); -const Client_data *dht_friend_client(const DHT_Friend *dht_friend, size_t index); +non_null() const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend); +non_null() const Client_data *dht_friend_client(const DHT_Friend *dht_friend, size_t index); -/** Return packet size of packed node with ip_family on success. - * Return -1 on failure. +/** @return packet size of packed node with ip_family on success. + * @retval -1 on failure. */ int packed_node_size(Family ip_family); -/** Packs an IP_Port structure into data of max size length. +/** @brief Pack an IP_Port structure into data of max size length. * * Packed_length is the offset of data currently packed. * - * Returns size of packed IP_Port data on success - * Return -1 on failure. + * @return size of packed IP_Port data on success. + * @retval -1 on failure. + */ +non_null() +int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port); + +/** @brief Encrypt plain and write resulting DHT packet into packet with max size length. + * + * @return size of packet on success. + * @retval -1 on failure. */ -int pack_ip_port(uint8_t *data, uint16_t length, const IP_Port *ip_port); +non_null() +int dht_create_packet(const Random *rng, + const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], + const uint8_t *shared_key, const uint8_t type, + const uint8_t *plain, size_t plain_length, + uint8_t *packet, size_t length); -/** Unpack IP_Port structure from data of max size length into ip_port. +/** @brief Unpack IP_Port structure from data of max size length into ip_port. * * len_processed is the offset of data currently unpacked. * - * Return size of unpacked ip_port on success. - * Return -1 on failure. + * @return size of unpacked ip_port on success. + * @retval -1 on failure. */ +non_null() int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled); -/** Pack number of nodes into data of maxlength length. +/** @brief Pack number of nodes into data of maxlength length. * - * return length of packed nodes on success. - * return -1 on failure. + * @return length of packed nodes on success. + * @retval -1 on failure. */ -int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number); +non_null() +int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number); -/** Unpack data of length into nodes of size max_num_nodes. +/** @brief Unpack data of length into nodes of size max_num_nodes. * Put the length of the data processed in processed_data_len. * tcp_enabled sets if TCP nodes are expected (true) or not (false). * - * return number of unpacked nodes on success. - * return -1 on failure. + * @return number of unpacked nodes on success. + * @retval -1 on failure. */ +non_null(1, 4) nullable(3) int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed_data_len, const uint8_t *data, uint16_t length, bool tcp_enabled); @@ -195,224 +267,285 @@ typedef int cryptopacket_handler_cb(void *object, const IP_Port *ip_port, const typedef struct DHT DHT; -const uint8_t *dht_get_self_public_key(const DHT *dht); -const uint8_t *dht_get_self_secret_key(const DHT *dht); -void dht_set_self_public_key(DHT *dht, const uint8_t *key); -void dht_set_self_secret_key(DHT *dht, const uint8_t *key); +non_null() const uint8_t *dht_get_self_public_key(const DHT *dht); +non_null() const uint8_t *dht_get_self_secret_key(const DHT *dht); +non_null() void dht_set_self_public_key(DHT *dht, const uint8_t *key); +non_null() void dht_set_self_secret_key(DHT *dht, const uint8_t *key); -Networking_Core *dht_get_net(const DHT *dht); -struct Ping *dht_get_ping(const DHT *dht); -const Client_data *dht_get_close_clientlist(const DHT *dht); -const Client_data *dht_get_close_client(const DHT *dht, uint32_t client_num); -uint16_t dht_get_num_friends(const DHT *dht); +non_null() Networking_Core *dht_get_net(const DHT *dht); +non_null() struct Ping *dht_get_ping(const DHT *dht); +non_null() const Client_data *dht_get_close_clientlist(const DHT *dht); +non_null() const Client_data *dht_get_close_client(const DHT *dht, uint32_t client_num); +non_null() uint16_t dht_get_num_friends(const DHT *dht); -DHT_Friend *dht_get_friend(DHT *dht, uint32_t friend_num); -const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num); +non_null() DHT_Friend *dht_get_friend(DHT *dht, uint32_t friend_num); +non_null() const uint8_t *dht_get_friend_public_key(const DHT *dht, uint32_t friend_num); /*----------------------------------------------------------------------------------*/ -/** Shared key generations are costly, it is therefore smart to store commonly used +/** + * Shared key generations are costly, it is therefore smart to store commonly used * ones so that they can be re-used later without being computed again. * * If a shared key is already in shared_keys, copy it to shared_key. * Otherwise generate it into shared_key and copy it to shared_keys */ -void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_t *shared_key, - const uint8_t *secret_key, const uint8_t *public_key); +non_null() +void get_shared_key( + const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_t *shared_key, + const uint8_t *secret_key, const uint8_t *public_key); -/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key +/** + * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key * for packets that we receive. */ +non_null() void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *public_key); -/** Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key +/** + * Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key * for packets that we send. */ +non_null() void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key); - -/** Sends a getnodes request to `ip_port` with the public key `public_key` for nodes +/** + * Sends a getnodes request to `ip_port` with the public key `public_key` for nodes * that are close to `client_id`. * - * Return true on success. + * @retval true on success. */ +non_null() bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *client_id); typedef void dht_ip_cb(void *object, int32_t number, const IP_Port *ip_port); -/** Add a new friend to the friends list. +typedef void dht_get_nodes_response_cb(const DHT *dht, const Node_format *node, void *user_data); + +/** Sets the callback to be triggered on a getnodes response. */ +non_null(1) nullable(2) +void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function); + +/** @brief Add a new friend to the friends list. * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. * * ip_callback is the callback of a function that will be called when the ip address * is found along with arguments data and number. * - * lock_count will be set to a non zero number that must be passed to dht_delfriend() + * lock_count will be set to a non zero number that must be passed to `dht_delfriend()` * to properly remove the callback. * - * return 0 if success. - * return -1 if failure (friends list is full). + * @retval 0 if success. + * @retval -1 if failure (friends list is full). */ +non_null(1, 2) nullable(3, 4, 6) int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback, void *data, int32_t number, uint16_t *lock_count); -/** Delete a friend from the friends list. +/** @brief Delete a friend from the friends list. * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. * - * return 0 if success. - * return -1 if failure (public_key not in friends list). + * @retval 0 if success. + * @retval -1 if failure (public_key not in friends list). */ +non_null() int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count); -/** Get ip of friend. - * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. - * ip must be 4 bytes long. - * port must be 2 bytes long. +/** @brief Get ip of friend. + * + * @param public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. * - * return -1, -- if public_key does NOT refer to a friend - * return 0, -- if public_key refers to a friend and we failed to find the friend (yet) - * return 1, ip if public_key refers to a friend and we found him + * @retval -1 if public_key does NOT refer to a friend + * @retval 0 if public_key refers to a friend and we failed to find the friend (yet) + * @retval 1 if public_key refers to a friend and we found him */ +non_null() int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port); -/** Compares pk1 and pk2 with pk. +/** @brief Compares pk1 and pk2 with pk. * - * return 0 if both are same distance. - * return 1 if pk1 is closer. - * return 2 if pk2 is closer. + * @retval 0 if both are same distance. + * @retval 1 if pk1 is closer. + * @retval 2 if pk2 is closer. */ +non_null() int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2); -/*** +/** Return index of first unequal bit number between public keys pk1 and pk2. */ +non_null() +unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2); + +/** * Add node to the node list making sure only the nodes closest to cmp_pk are in the list. * * @return true iff the node was added to the list. */ -bool add_to_list(Node_format *nodes_list, uint32_t length, const uint8_t *pk, const IP_Port *ip_port, - const uint8_t *cmp_pk); +non_null() +bool add_to_list( + Node_format *nodes_list, uint32_t length, const uint8_t *pk, const IP_Port *ip_port, const uint8_t *cmp_pk); -/** Return 1 if node can be added to close list, 0 if it can't. - */ +/** Return 1 if node can be added to close list, 0 if it can't. */ +non_null() bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, const IP_Port *ip_port); -/** Get the (maximum MAX_SENT_NODES) closest nodes to public_key we know +#ifdef CHECK_ANNOUNCE_NODE +/** Set node as announce node. */ +non_null() +void set_announce_node(DHT *dht, const uint8_t *public_key); +#endif + +/** + * Get the (maximum MAX_SENT_NODES) closest nodes to public_key we know * and put them in nodes_list (must be MAX_SENT_NODES big). * * sa_family = family (IPv4 or IPv6) (0 if we don't care)? * is_LAN = return some LAN ips (true or false) - * want_good = do we want tested nodes or not? (TODO(irungentoo)) - * - * return the number of nodes returned. + * want_announce: return only nodes which implement the dht announcements protocol. * + * @return the number of nodes returned. */ +non_null() int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family, - bool is_LAN); + bool is_LAN, bool want_announce); -/** Put up to max_num nodes in nodes from the random friends. +/** @brief Put up to max_num nodes in nodes from the random friends. + * + * Important: this function relies on the first two DHT friends *not* being real + * friends to avoid leaking information about real friends into the onion paths. * - * return the number of nodes. + * @return the number of nodes. */ +non_null() uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num); -/** Put up to max_num nodes in nodes from the closelist. +/** @brief Put up to max_num nodes in nodes from the closelist. * - * return the number of nodes. + * @return the number of nodes. */ +non_null() uint16_t closelist_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num); /** Run this function at least a couple times per second (It's the main loop). */ +non_null() void do_dht(DHT *dht); /* * Use these two functions to bootstrap the client. */ -/** Sends a "get nodes" request to the given node with ip, port and public_key +/** + * @brief Sends a "get nodes" request to the given node with ip, port and public_key * to setup connections */ -void dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key); +non_null() +bool dht_bootstrap(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key); -/** Resolves address into an IP address. If successful, sends a "get nodes" - * request to the given node with ip, port and public_key to setup connections +/** @brief Resolves address into an IP address. * - * address can be a hostname or an IP address (IPv4 or IPv6). - * if ipv6enabled is 0 (zero), the resolving sticks STRICTLY to IPv4 addresses - * if ipv6enabled is not 0 (zero), the resolving looks for IPv6 addresses first, - * then IPv4 addresses. + * If successful, sends a "get nodes" request to the given node with ip, port + * and public_key to setup connections * - * returns 1 if the address could be converted into an IP address - * returns 0 otherwise + * @param address can be a hostname or an IP address (IPv4 or IPv6). + * @param ipv6enabled if false, the resolving sticks STRICTLY to IPv4 addresses. + * Otherwise, the resolving looks for IPv6 addresses first, then IPv4 addresses. + * + * @retval 1 if the address could be converted into an IP address + * @retval 0 otherwise */ -int dht_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, +non_null() +int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled, uint16_t port, const uint8_t *public_key); -/** Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. +/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. * - * returns 0 if successful - * returns -1 otherwise + * @retval 0 if successful + * @retval -1 otherwise */ +non_null() int dht_connect_after_load(DHT *dht); /* ROUTING FUNCTIONS */ -/** Send the given packet to node with public_key. +/** @brief Send the given packet to node with public_key. * - * return -1 if failure. + * @return number of bytes sent. + * @retval -1 if failure. */ +non_null() int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length); /** * Send the following packet to everyone who tells us they are connected to friend_id. * - * return ip for friend. - * return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4). + * @return ip for friend. + * @return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4). */ +non_null() uint32_t route_to_friend(const DHT *dht, const uint8_t *friend_id, const Packet *packet); -/** Function to handle crypto packets. - */ +/** Function to handle crypto packets. */ +non_null(1) nullable(3, 4) void cryptopacket_registerhandler(DHT *dht, uint8_t byte, cryptopacket_handler_cb *cb, void *object); /* SAVE/LOAD functions */ /** Get the size of the DHT (for saving). */ +non_null() uint32_t dht_size(const DHT *dht); -/** Save the DHT in data where data is an array of size dht_size(). */ +/** Save the DHT in data where data is an array of size `dht_size()`. */ +non_null() void dht_save(const DHT *dht, uint8_t *data); -/** Load the DHT from data of size size. +/** @brief Load the DHT from data of size size. * - * return -1 if failure. - * return 0 if success. + * @retval -1 if failure. + * @retval 0 if success. */ +non_null() int dht_load(DHT *dht, const uint8_t *data, uint32_t length); /** Initialize DHT. */ -DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool holepunching_enabled); +non_null() +DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, Networking_Core *net, + bool hole_punching_enabled, bool lan_discovery_enabled); +nullable(1) void kill_dht(DHT *dht); -/** return false if we are not connected to the DHT. - * return true if we are. +/** + * @retval false if we are not connected to the DHT. + * @retval true if we are. */ +non_null() bool dht_isconnected(const DHT *dht); -/** return false if we are not connected or only connected to lan peers with the DHT. - * return true if we are. +/** + * @retval false if we are not connected or only connected to lan peers with the DHT. + * @retval true if we are. */ +non_null() bool dht_non_lan_connected(const DHT *dht); - +/** @brief Attempt to add client with ip_port and public_key to the friends client list + * and close_clientlist. + * + * @return 1+ if the item is used in any list, 0 else + */ +non_null() uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key); -/** Copies our own ip_port structure to `dest`. WAN addresses take priority over LAN addresses. +/** @brief Copies our own ip_port structure to `dest`. + * + * WAN addresses take priority over LAN addresses. * * This function will zero the `dest` buffer before use. * - * Return 0 if our ip port can't be found (this usually means we're not connected to the DHT). - * Return 1 if IP is a WAN address. - * Return 2 if IP is a LAN address. + * @retval 0 if our ip port can't be found (this usually means we're not connected to the DHT). + * @retval 1 if IP is a WAN address. + * @retval 2 if IP is a LAN address. */ +non_null() unsigned int ipport_self_copy(const DHT *dht, IP_Port *dest); #ifdef __cplusplus diff --git a/protocols/Tox/libtox/src/toxcore/LAN_discovery.c b/protocols/Tox/libtox/src/toxcore/LAN_discovery.c index 6f9f77b6bb..82c02b59e9 100644 --- a/protocols/Tox/libtox/src/toxcore/LAN_discovery.c +++ b/protocols/Tox/libtox/src/toxcore/LAN_discovery.c @@ -8,6 +8,7 @@ */ #include "LAN_discovery.h" +#include <stdlib.h> #include <string.h> #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) @@ -38,28 +39,35 @@ #include <net/if.h> #endif +#include "ccompat.h" +#include "crypto_core.h" #include "util.h" #define MAX_INTERFACES 16 -/* TODO: multiple threads might concurrently try to set these, and it isn't clear that this couldn't lead to undesirable - * behaviour. Consider storing the data in per-instance variables instead. */ -//!TOKSTYLE- -// No global mutable state in Tokstyle. -static int broadcast_count = -1; -static IP_Port broadcast_ip_ports[MAX_INTERFACES]; -//!TOKSTYLE+ +struct Broadcast_Info { + uint32_t count; + IP ips[MAX_INTERFACES]; +}; #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -static void fetch_broadcast_info(uint16_t port) +non_null() +static Broadcast_Info *fetch_broadcast_info(const Network *ns) { + Broadcast_Info *broadcast = (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info)); + + if (broadcast == nullptr) { + return nullptr; + } + IP_ADAPTER_INFO *pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO)); unsigned long ulOutBufLen = sizeof(IP_ADAPTER_INFO); if (pAdapterInfo == nullptr) { - return; + free(broadcast); + return nullptr; } if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { @@ -67,39 +75,32 @@ static void fetch_broadcast_info(uint16_t port) pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == nullptr) { - return; + free(broadcast); + return nullptr; } } - /* We copy these to the static variables `broadcast_*` only at the end of `fetch_broadcast_info()`. - * The intention is to ensure that even if multiple threads enter `fetch_broadcast_info()` concurrently, only valid - * interfaces will be set to be broadcast to. - * */ - int count = 0; - IP_Port ip_ports[MAX_INTERFACES]; - const int ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); if (ret == NO_ERROR) { IP_ADAPTER_INFO *pAdapter = pAdapterInfo; - while (pAdapter) { + while (pAdapter != nullptr) { IP gateway = {0}; IP subnet_mask = {0}; if (addr_parse_ip(pAdapter->IpAddressList.IpMask.String, &subnet_mask) && addr_parse_ip(pAdapter->GatewayList.IpAddress.String, &gateway)) { if (net_family_is_ipv4(gateway.family) && net_family_is_ipv4(subnet_mask.family)) { - IP_Port *ip_port = &ip_ports[count]; - ip_port->ip.family = net_family_ipv4; - uint32_t gateway_ip = net_ntohl(gateway.ip.v4.uint32); - uint32_t subnet_ip = net_ntohl(subnet_mask.ip.v4.uint32); - uint32_t broadcast_ip = gateway_ip + ~subnet_ip - 1; - ip_port->ip.ip.v4.uint32 = net_htonl(broadcast_ip); - ip_port->port = port; - ++count; - - if (count >= MAX_INTERFACES) { + IP *ip = &broadcast->ips[broadcast->count]; + ip->family = net_family_ipv4(); + const uint32_t gateway_ip = net_ntohl(gateway.ip.v4.uint32); + const uint32_t subnet_ip = net_ntohl(subnet_mask.ip.v4.uint32); + const uint32_t broadcast_ip = gateway_ip + ~subnet_ip - 1; + ip->ip.v4.uint32 = net_htonl(broadcast_ip); + ++broadcast->count; + + if (broadcast->count >= MAX_INTERFACES) { break; } } @@ -109,30 +110,33 @@ static void fetch_broadcast_info(uint16_t port) } } - if (pAdapterInfo) { + if (pAdapterInfo != nullptr) { free(pAdapterInfo); } - broadcast_count = count; - - for (uint32_t i = 0; i < count; ++i) { - broadcast_ip_ports[i] = ip_ports[i]; - } + return broadcast; } #elif !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && (defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)) -static void fetch_broadcast_info(uint16_t port) +non_null() +static Broadcast_Info *fetch_broadcast_info(const Network *ns) { + Broadcast_Info *broadcast = (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info)); + + if (broadcast == nullptr) { + return nullptr; + } + /* Not sure how many platforms this will run on, * so it's wrapped in `__linux__` for now. * Definitely won't work like this on Windows... */ - broadcast_count = 0; - const Socket sock = net_socket(net_family_ipv4, TOX_SOCK_STREAM, 0); + const Socket sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, 0); if (!sock_valid(sock)) { - return; + free(broadcast); + return nullptr; } /* Configure ifconf for the ioctl call. */ @@ -143,28 +147,22 @@ static void fetch_broadcast_info(uint16_t port) ifc.ifc_buf = (char *)i_faces; ifc.ifc_len = sizeof(i_faces); - if (ioctl(sock.socket, SIOCGIFCONF, &ifc) < 0) { - kill_sock(sock); - return; + if (ioctl(sock.sock, SIOCGIFCONF, &ifc) < 0) { + kill_sock(ns, sock); + free(broadcast); + return nullptr; } - /* We copy these to the static variables `broadcast_*` only at the end of `fetch_broadcast_info()`. - * The intention is to ensure that even if multiple threads enter `fetch_broadcast_info()` concurrently, only valid - * interfaces will be set to be broadcast to. - * */ - int count = 0; - IP_Port ip_ports[MAX_INTERFACES]; - /* `ifc.ifc_len` is set by the `ioctl()` to the actual length used. * On usage of the complete array the call should be repeated with * a larger array, not done (640kB and 16 interfaces shall be * enough, for everybody!) */ - int n = ifc.ifc_len / sizeof(struct ifreq); + const int n = ifc.ifc_len / sizeof(struct ifreq); for (int i = 0; i < n; ++i) { /* there are interfaces with are incapable of broadcast */ - if (ioctl(sock.socket, SIOCGIFBRDADDR, &i_faces[i]) < 0) { + if (ioctl(sock.sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) { continue; } @@ -175,62 +173,57 @@ static void fetch_broadcast_info(uint16_t port) const struct sockaddr_in *sock4 = (const struct sockaddr_in *)(void *)&i_faces[i].ifr_broadaddr; - if (count >= MAX_INTERFACES) { + if (broadcast->count >= MAX_INTERFACES) { break; } - IP_Port *ip_port = &ip_ports[count]; - ip_port->ip.family = net_family_ipv4; - ip_port->ip.ip.v4.uint32 = sock4->sin_addr.s_addr; + IP *ip = &broadcast->ips[broadcast->count]; + ip->family = net_family_ipv4(); + ip->ip.v4.uint32 = sock4->sin_addr.s_addr; - if (ip_port->ip.ip.v4.uint32 == 0) { + if (ip->ip.v4.uint32 == 0) { continue; } - ip_port->port = port; - ++count; + ++broadcast->count; } - kill_sock(sock); - - broadcast_count = count; + kill_sock(ns, sock); - for (uint32_t i = 0; i < count; ++i) { - broadcast_ip_ports[i] = ip_ports[i]; - } + return broadcast; } #else // TODO(irungentoo): Other platforms? -static void fetch_broadcast_info(uint16_t port) +non_null() +static Broadcast_Info *fetch_broadcast_info(const Network *ns) { - broadcast_count = 0; + return (Broadcast_Info *)calloc(1, sizeof(Broadcast_Info)); } #endif -/** Send packet to all IPv4 broadcast addresses +/** @brief Send packet to all IPv4 broadcast addresses * - * return 1 if sent to at least one broadcast target. - * return 0 on failure to find any valid broadcast target. + * @retval true if sent to at least one broadcast target. + * @retval false on failure to find any valid broadcast target. */ -static uint32_t send_broadcasts(const Networking_Core *net, uint16_t port, const uint8_t *data, uint16_t length) +non_null() +static bool send_broadcasts(const Networking_Core *net, const Broadcast_Info *broadcast, uint16_t port, + const uint8_t *data, uint16_t length) { - /* fetch only once? on every packet? every X seconds? - * old: every packet, new: once */ - if (broadcast_count < 0) { - fetch_broadcast_info(port); - } - - if (!broadcast_count) { - return 0; + if (broadcast->count == 0) { + return false; } - for (int i = 0; i < broadcast_count; ++i) { - sendpacket(net, &broadcast_ip_ports[i], data, length); + for (uint32_t i = 0; i < broadcast->count; ++i) { + IP_Port ip_port; + ip_port.ip = broadcast->ips[i]; + ip_port.port = port; + sendpacket(net, &ip_port, data, length); } - return 1; + return true; } /** Return the broadcast ip. */ @@ -241,7 +234,7 @@ static IP broadcast_ip(Family family_socket, Family family_broadcast) if (net_family_is_ipv6(family_socket)) { if (net_family_is_ipv6(family_broadcast)) { - ip.family = net_family_ipv6; + ip.family = net_family_ipv6(); /* `FF02::1` is - according to RFC 4291 - multicast all-nodes link-local */ /* `FE80::*:` MUST be exact, for that we would need to look over all * interfaces and check in which status they are */ @@ -249,17 +242,18 @@ static IP broadcast_ip(Family family_socket, Family family_broadcast) ip.ip.v6.uint8[ 1] = 0x02; ip.ip.v6.uint8[15] = 0x01; } else if (net_family_is_ipv4(family_broadcast)) { - ip.family = net_family_ipv6; + ip.family = net_family_ipv6(); ip.ip.v6 = ip6_broadcast; } } else if (net_family_is_ipv4(family_socket) && net_family_is_ipv4(family_broadcast)) { - ip.family = net_family_ipv4; + ip.family = net_family_ipv4(); ip.ip.v4 = ip4_broadcast; } return ip; } +non_null() static bool ip4_is_local(const IP4 *ip4) { /* Loopback. */ @@ -283,13 +277,10 @@ bool ip_is_local(const IP *ip) } /* localhost in IPv6 (::1) */ - if (ip->ip.v6.uint64[0] == 0 && ip->ip.v6.uint32[2] == 0 && ip->ip.v6.uint32[3] == net_htonl(1)) { - return true; - } - - return false; + return ip->ip.v6.uint64[0] == 0 && ip->ip.v6.uint32[2] == 0 && ip->ip.v6.uint32[3] == net_htonl(1); } +non_null() static bool ip4_is_lan(const IP4 *ip4) { /* 10.0.0.0 to 10.255.255.255 range. */ @@ -315,11 +306,7 @@ static bool ip4_is_lan(const IP4 *ip4) /* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10) * (shared address space to stack another layer of NAT) */ - if ((ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40)) { - return true; - } - - return false; + return (ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40); } bool ip_is_lan(const IP *ip) @@ -351,31 +338,18 @@ bool ip_is_lan(const IP *ip) return false; } -static int handle_LANdiscovery(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, - void *userdata) -{ - DHT *dht = (DHT *)object; - - if (!ip_is_lan(&source->ip)) { - return 1; - } - if (length != CRYPTO_PUBLIC_KEY_SIZE + 1) { - return 1; +bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk, uint16_t port) +{ + if (broadcast == nullptr) { + return false; } - dht_bootstrap(dht, source, packet + 1); - return 0; -} - - -bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t port) -{ uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1]; data[0] = NET_PACKET_LAN_DISCOVERY; - id_copy(data + 1, dht_pk); + pk_copy(data + 1, dht_pk); - send_broadcasts(net, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE); + send_broadcasts(net, broadcast, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE); bool res = false; IP_Port ip_port; @@ -383,34 +357,30 @@ bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t po /* IPv6 multicast */ if (net_family_is_ipv6(net_family(net))) { - ip_port.ip = broadcast_ip(net_family_ipv6, net_family_ipv6); + ip_port.ip = broadcast_ip(net_family_ipv6(), net_family_ipv6()); - if (ip_isset(&ip_port.ip)) { - if (sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { - res = true; - } + if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { + res = true; } } /* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is IPv6 */ - ip_port.ip = broadcast_ip(net_family(net), net_family_ipv4); + ip_port.ip = broadcast_ip(net_family(net), net_family_ipv4()); - if (ip_isset(&ip_port.ip)) { - if (sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE)) { - res = true; - } + if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { + res = true; } return res; } -void lan_discovery_init(DHT *dht) +Broadcast_Info *lan_discovery_init(const Network *ns) { - networking_registerhandler(dht_get_net(dht), NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht); + return fetch_broadcast_info(ns); } -void lan_discovery_kill(DHT *dht) +void lan_discovery_kill(Broadcast_Info *broadcast) { - networking_registerhandler(dht_get_net(dht), NET_PACKET_LAN_DISCOVERY, nullptr, nullptr); + free(broadcast); } diff --git a/protocols/Tox/libtox/src/toxcore/LAN_discovery.h b/protocols/Tox/libtox/src/toxcore/LAN_discovery.h index cce7b5c190..85ea9478f4 100644 --- a/protocols/Tox/libtox/src/toxcore/LAN_discovery.h +++ b/protocols/Tox/libtox/src/toxcore/LAN_discovery.h @@ -9,33 +9,39 @@ #ifndef C_TOXCORE_TOXCORE_LAN_DISCOVERY_H #define C_TOXCORE_TOXCORE_LAN_DISCOVERY_H -#include "DHT.h" +#include "network.h" /** * Interval in seconds between LAN discovery packet sending. */ #define LAN_DISCOVERY_INTERVAL 10 +typedef struct Broadcast_Info Broadcast_Info; + /** * Send a LAN discovery pcaket to the broadcast address with port port. * * @return true on success, false on failure. */ -bool lan_discovery_send(Networking_Core *net, const uint8_t *dht_pk, uint16_t port); +non_null() +bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk, uint16_t port); /** - * Sets up packet handlers. + * Discovers broadcast devices and IP addresses. */ -void lan_discovery_init(DHT *dht); +non_null() +Broadcast_Info *lan_discovery_init(const Network *ns); /** - * Clear packet handlers. + * Free all resources associated with the broadcast info. */ -void lan_discovery_kill(DHT *dht); +nullable(1) +void lan_discovery_kill(Broadcast_Info *broadcast); /** * Is IP a local ip or not. */ +non_null() bool ip_is_local(const IP *ip); /** @@ -43,6 +49,7 @@ bool ip_is_local(const IP *ip); * * @return true if ip is a LAN ip, false if it is not. */ +non_null() bool ip_is_lan(const IP *ip); #endif // C_TOXCORE_TOXCORE_LAN_DISCOVERY_H diff --git a/protocols/Tox/libtox/src/toxcore/Messenger.c b/protocols/Tox/libtox/src/toxcore/Messenger.c index 6413dc928e..6042fd725b 100644 --- a/protocols/Tox/libtox/src/toxcore/Messenger.c +++ b/protocols/Tox/libtox/src/toxcore/Messenger.c @@ -14,6 +14,7 @@ #include <string.h> #include <time.h> +#include "ccompat.h" #include "logger.h" #include "mono_time.h" #include "network.h" @@ -23,24 +24,13 @@ static_assert(MAX_CONCURRENT_FILE_PIPES <= UINT8_MAX + 1, "uint8_t cannot represent all file transfer numbers"); -static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, - uint32_t length, uint8_t congestion_control); -static void m_register_default_plugins(Messenger *m); +static const Friend empty_friend = {{0}}; -/** - * Determines if the friendnumber passed is valid in the Messenger object. - * - * @param friendnumber The index in the friend list. - */ -static bool friend_is_valid(const Messenger *m, int32_t friendnumber) -{ - return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0; -} - -/** Set the size of the friend list to numfriends. +/** @brief Set the size of the friend list to numfriends. * - * return -1 if realloc fails. + * @retval -1 if realloc fails. */ +non_null() static int realloc_friendlist(Messenger *m, uint32_t num) { if (num == 0) { @@ -59,31 +49,30 @@ static int realloc_friendlist(Messenger *m, uint32_t num) return 0; } -/** return the friend number associated to that public key. - * return -1 if no such friend. +/** @return the friend number associated to that public key. + * @retval -1 if no such friend. */ int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk) { for (uint32_t i = 0; i < m->numfriends; ++i) { - if (m->friendlist[i].status > 0) { - if (id_equal(real_pk, m->friendlist[i].real_pk)) { - return i; - } + if (m->friendlist[i].status > 0 && pk_equal(real_pk, m->friendlist[i].real_pk)) { + return i; } } return -1; } -/** Copies the public key associated to that friend id into real_pk buffer. +/** @brief Copies the public key associated to that friend id into real_pk buffer. + * * Make sure that real_pk is of size CRYPTO_PUBLIC_KEY_SIZE. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -91,12 +80,12 @@ int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk) return 0; } -/** return friend connection id on success. - * return -1 if failure. +/** @return friend connection id on success. + * @retval -1 if failure. */ int getfriendcon_id(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -104,57 +93,48 @@ int getfriendcon_id(const Messenger *m, int32_t friendnumber) } /** - * return a uint16_t that represents the checksum of address of length len. - */ -static uint16_t address_checksum(const uint8_t *address, uint32_t len) -{ - uint8_t checksum[2] = {0}; - uint16_t check; - - for (uint32_t i = 0; i < len; ++i) { - checksum[i % 2] ^= address[i]; - } - - memcpy(&check, checksum, sizeof(check)); - return check; -} - -/** Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]` + * Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]` * - * return FRIEND_ADDRESS_SIZE byte address to give to others. + * @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others. */ void getaddress(const Messenger *m, uint8_t *address) { - id_copy(address, nc_get_self_public_key(m->net_crypto)); + pk_copy(address, nc_get_self_public_key(m->net_crypto)); uint32_t nospam = get_nospam(m->fr); memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &nospam, sizeof(nospam)); - uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); + uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(nospam), &checksum, sizeof(checksum)); } -static int send_online_packet(Messenger *m, int32_t friendnumber) +non_null() +static bool send_online_packet(Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { - return 0; + if (!m_friend_exists(m, friendnumber)) { + return false; } uint8_t packet = PACKET_ID_ONLINE; return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), 0) != -1; + m->friendlist[friendnumber].friendcon_id), &packet, sizeof(packet), false) != -1; } -static int send_offline_packet(Messenger *m, int friendcon_id) +non_null() +static bool send_offline_packet(Messenger *m, int friendcon_id) { uint8_t packet = PACKET_ID_OFFLINE; return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, friendcon_id), &packet, - sizeof(packet), 0) != -1; + sizeof(packet), false) != -1; } -static int m_handle_status(void *object, int i, uint8_t status, void *userdata); +non_null(1) nullable(4) +static int m_handle_status(void *object, int i, bool status, void *userdata); +non_null(1, 3) nullable(5) static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t len, void *userdata); +non_null(1, 3) nullable(5) static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length, void *userdata); +non_null() static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t status) { if (m->numfriends == UINT32_MAX) { @@ -168,9 +148,9 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta return FAERR_NOMEM; } - memset(&m->friendlist[m->numfriends], 0, sizeof(Friend)); + m->friendlist[m->numfriends] = empty_friend; - int friendcon_id = new_friend_connection(m->fr_c, real_pk); + const int friendcon_id = new_friend_connection(m->fr_c, real_pk); if (friendcon_id == -1) { return FAERR_NOMEM; @@ -181,10 +161,10 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta m->friendlist[i].status = status; m->friendlist[i].friendcon_id = friendcon_id; m->friendlist[i].friendrequest_lastsent = 0; - id_copy(m->friendlist[i].real_pk, real_pk); + pk_copy(m->friendlist[i].real_pk, real_pk); m->friendlist[i].statusmessage_length = 0; m->friendlist[i].userstatus = USERSTATUS_NONE; - m->friendlist[i].is_typing = 0; + m->friendlist[i].is_typing = false; m->friendlist[i].message_id = 0; friend_connection_callbacks(m->fr_c, friendcon_id, MESSENGER_CALLBACK_INDEX, &m_handle_status, &m_handle_packet, &m_handle_lossy_packet, m, i); @@ -215,15 +195,15 @@ static int32_t init_new_friend(Messenger *m, const uint8_t *real_pk, uint8_t sta * @param data is the data. * @param length is the length. * - * return the friend number if success. - * return FA_TOOLONG if message length is too long. - * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte). - * return FAERR_OWNKEY if user's own key. - * return FAERR_ALREADYSENT if friend request already sent or already a friend. - * return FAERR_BADCHECKSUM if bad checksum in address. - * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different. - * (the nospam for that friend was set to the new one). - * return FAERR_NOMEM if increasing the friend list size fails. + * @return the friend number if success. + * @retval FA_TOOLONG if message length is too long. + * @retval FAERR_NOMESSAGE if no message (message length must be >= 1 byte). + * @retval FAERR_OWNKEY if user's own key. + * @retval FAERR_ALREADYSENT if friend request already sent or already a friend. + * @retval FAERR_BADCHECKSUM if bad checksum in address. + * @retval FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different. + * (the nospam for that friend was set to the new one). + * @retval FAERR_NOMEM if increasing the friend list size fails. */ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, uint16_t length) { @@ -232,14 +212,14 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u } uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - id_copy(real_pk, address); + pk_copy(real_pk, address); if (!public_key_valid(real_pk)) { return FAERR_BADCHECKSUM; } uint16_t check; - uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); + const uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); memcpy(&check, address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), sizeof(check)); if (check != checksum) { @@ -250,11 +230,11 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u return FAERR_NOMESSAGE; } - if (id_equal(real_pk, nc_get_self_public_key(m->net_crypto))) { + if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) { return FAERR_OWNKEY; } - int32_t friend_id = getfriend_id(m, real_pk); + const int32_t friend_id = getfriend_id(m, real_pk); if (friend_id != -1) { if (m->friendlist[friend_id].status >= FRIEND_CONFIRMED) { @@ -272,7 +252,7 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u return FAERR_SETNEWNOSPAM; } - int32_t ret = init_new_friend(m, real_pk, FRIEND_ADDED); + const int32_t ret = init_new_friend(m, real_pk, FRIEND_ADDED); if (ret < 0) { return ret; @@ -296,22 +276,23 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk) return FAERR_BADCHECKSUM; } - if (id_equal(real_pk, nc_get_self_public_key(m->net_crypto))) { + if (pk_equal(real_pk, nc_get_self_public_key(m->net_crypto))) { return FAERR_OWNKEY; } return init_new_friend(m, real_pk, FRIEND_CONFIRMED); } +non_null() static int clear_receipts(Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } struct Receipts *receipts = m->friendlist[friendnumber].receipts_start; - while (receipts) { + while (receipts != nullptr) { struct Receipts *temp_r = receipts->next; free(receipts); receipts = temp_r; @@ -322,22 +303,23 @@ static int clear_receipts(Messenger *m, int32_t friendnumber) return 0; } +non_null() static int add_receipt(Messenger *m, int32_t friendnumber, uint32_t packet_num, uint32_t msg_id) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } struct Receipts *new_receipts = (struct Receipts *)calloc(1, sizeof(struct Receipts)); - if (!new_receipts) { + if (new_receipts == nullptr) { return -1; } new_receipts->packet_num = packet_num; new_receipts->msg_id = msg_id; - if (!m->friendlist[friendnumber].receipts_start) { + if (m->friendlist[friendnumber].receipts_start == nullptr) { m->friendlist[friendnumber].receipts_start = new_receipts; } else { m->friendlist[friendnumber].receipts_end->next = new_receipts; @@ -351,9 +333,10 @@ static int add_receipt(Messenger *m, int32_t friendnumber, uint32_t packet_num, * return -1 on failure. * return 0 if packet was received. */ +non_null() static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint32_t number) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -361,20 +344,21 @@ static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint m->friendlist[friendnumber].friendcon_id), number); } +non_null(1) nullable(3) static int do_receipts(Messenger *m, int32_t friendnumber, void *userdata) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } struct Receipts *receipts = m->friendlist[friendnumber].receipts_start; - while (receipts) { + while (receipts != nullptr) { if (friend_received_packet(m, friendnumber, receipts->packet_num) == -1) { break; } - if (m->read_receipt) { + if (m->read_receipt != nullptr) { m->read_receipt(m, friendnumber, receipts->msg_id, userdata); } @@ -387,25 +371,25 @@ static int do_receipts(Messenger *m, int32_t friendnumber, void *userdata) receipts = r_next; } - if (!m->friendlist[friendnumber].receipts_start) { + if (m->friendlist[friendnumber].receipts_start == nullptr) { m->friendlist[friendnumber].receipts_end = nullptr; } return 0; } -/** Remove a friend. +/** @brief Remove a friend. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ int m_delfriend(Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } - if (m->friend_connectionstatuschange_internal) { + if (m->friend_connectionstatuschange_internal != nullptr) { m->friend_connectionstatuschange_internal(m, friendnumber, 0, m->friend_connectionstatuschange_internal_userdata); } @@ -419,7 +403,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber) } kill_friend_connection(m->fr_c, m->friendlist[friendnumber].friendcon_id); - memset(&m->friendlist[friendnumber], 0, sizeof(Friend)); + m->friendlist[friendnumber] = empty_friend; uint32_t i; @@ -440,7 +424,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber) int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -448,9 +432,9 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) return CONNECTION_NONE; } - bool direct_connected = 0; - unsigned int num_online_relays = 0; - int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id); + bool direct_connected = false; + uint32_t num_online_relays = 0; + const int crypt_conn_id = friend_connection_crypt_connection_id(m->fr_c, m->friendlist[friendnumber].friendcon_id); if (!crypto_connection_status(m->net_crypto, crypt_conn_id, &direct_connected, &num_online_relays)) { return CONNECTION_NONE; @@ -460,7 +444,7 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) return CONNECTION_UDP; } - if (num_online_relays) { + if (num_online_relays != 0) { return CONNECTION_TCP; } @@ -471,99 +455,124 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber) return m->friendlist[friendnumber].last_connection_udp_tcp; } -int m_friend_exists(const Messenger *m, int32_t friendnumber) +/** + * Checks if there exists a friend with given friendnumber. + * + * @param friendnumber The index in the friend list. + * + * @retval true if friend exists. + * @retval false if friend doesn't exist. + */ +bool m_friend_exists(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { - return 0; - } - - return 1; + return (unsigned int)friendnumber < m->numfriends && m->friendlist[friendnumber].status != 0; } -/** Send a message of type to an online friend. +/** @brief Send a message of type to an online friend. * - * return -1 if friend not valid. - * return -2 if too large. - * return -3 if friend not online. - * return -4 if send failed (because queue is full). - * return -5 if bad type. - * return 0 if success. + * @retval -1 if friend not valid. + * @retval -2 if too large. + * @retval -3 if friend not online. + * @retval -4 if send failed (because queue is full). + * @retval -5 if bad type. + * @retval 0 if success. * - * the value in message_id will be passed to your read_receipt callback when the other receives the message. + * The value in message_id will be passed to your read_receipt callback when the other receives the message. */ int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length, uint32_t *message_id) { if (type > MESSAGE_ACTION) { - LOGGER_WARNING(m->log, "Message type %d is invalid", type); + LOGGER_WARNING(m->log, "message type %d is invalid", type); return -5; } - if (!friend_is_valid(m, friendnumber)) { - LOGGER_WARNING(m->log, "Friend number %d is invalid", friendnumber); + if (!m_friend_exists(m, friendnumber)) { + LOGGER_WARNING(m->log, "friend number %d is invalid", friendnumber); return -1; } if (length >= MAX_CRYPTO_DATA_SIZE) { - LOGGER_WARNING(m->log, "Message length %u is too large", length); + LOGGER_WARNING(m->log, "message length %u is too large", length); return -2; } if (m->friendlist[friendnumber].status != FRIEND_ONLINE) { - LOGGER_WARNING(m->log, "Friend %d is not online", friendnumber); + LOGGER_WARNING(m->log, "friend %d is not online", friendnumber); return -3; } VLA(uint8_t, packet, length + 1); packet[0] = PACKET_ID_MESSAGE + type; - if (length != 0) { - memcpy(packet + 1, message, length); - } + assert(message != nullptr); + memcpy(packet + 1, message, length); - int64_t packet_num = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), packet, length + 1, 0); + const int64_t packet_num = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, + m->friendlist[friendnumber].friendcon_id), packet, length + 1, false); if (packet_num == -1) { - LOGGER_WARNING(m->log, "Failed to write crypto packet for message of length %d to friend %d", - length, friendnumber); return -4; } - uint32_t msg_id = ++m->friendlist[friendnumber].message_id; + const uint32_t msg_id = ++m->friendlist[friendnumber].message_id; add_receipt(m, friendnumber, packet_num, msg_id); - if (message_id) { + if (message_id != nullptr) { *message_id = msg_id; } return 0; } -/** Send a name packet to friendnumber. +non_null() +static bool write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, + uint32_t length, bool congestion_control) +{ + if (!m_friend_exists(m, friendnumber)) { + return false; + } + + if (length >= MAX_CRYPTO_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) { + return false; + } + + VLA(uint8_t, packet, length + 1); + packet[0] = packet_id; + + assert(data != nullptr); + memcpy(packet + 1, data, length); + + return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, + m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1; +} + +/** @brief Send a name packet to friendnumber. * length is the length with the NULL terminator. */ -static int m_sendname(const Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length) +non_null() +static bool m_sendname(const Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length) { if (length > MAX_NAME_LENGTH) { - return 0; + return false; } - return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length, 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length, false); } -/** Set the name and name_length of a friend. +/** @brief Set the name and name_length of a friend. + * * name must be a string of maximum MAX_NAME_LENGTH length. * length must be at least 1 byte. * length is the length of name with the NULL terminator. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -576,13 +585,14 @@ int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint1 return 0; } -/** Set our nickname. +/** @brief Set our nickname. + * * name must be a string of maximum MAX_NAME_LENGTH length. * length must be at least 1 byte. * length is the length of name with the NULL terminator. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ int setname(Messenger *m, const uint8_t *name, uint16_t length) { @@ -594,26 +604,27 @@ int setname(Messenger *m, const uint8_t *name, uint16_t length) return 0; } - if (length) { + if (length > 0) { memcpy(m->name, name, length); } m->name_length = length; for (uint32_t i = 0; i < m->numfriends; ++i) { - m->friendlist[i].name_sent = 0; + m->friendlist[i].name_sent = false; } return 0; } /** - * Get your nickname. + * @brief Get your nickname. + * * m - The messenger context to use. * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. * - * return length of the name. - * return 0 on error. + * @return length of the name. + * @retval 0 on error. */ uint16_t getself_name(const Messenger *m, uint8_t *name) { @@ -626,15 +637,16 @@ uint16_t getself_name(const Messenger *m, uint8_t *name) return m->name_length; } -/** Get name of friendnumber and put it in name. +/** @brief Get name of friendnumber and put it in name. + * * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. * - * return length of name if success. - * return -1 if failure. + * @return length of name if success. + * @retval -1 if failure. */ int getname(const Messenger *m, int32_t friendnumber, uint8_t *name) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -644,7 +656,7 @@ int getname(const Messenger *m, int32_t friendnumber, uint8_t *name) int m_get_name_size(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -666,14 +678,14 @@ int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length) return 0; } - if (length) { + if (length > 0) { memcpy(m->statusmessage, status, length); } m->statusmessage_length = length; for (uint32_t i = 0; i < m->numfriends; ++i) { - m->friendlist[i].statusmessage_sent = 0; + m->friendlist[i].statusmessage_sent = false; } return 0; @@ -692,7 +704,7 @@ int m_set_userstatus(Messenger *m, uint8_t status) m->userstatus = (Userstatus)status; for (uint32_t i = 0; i < m->numfriends; ++i) { - m->friendlist[i].userstatus_sent = 0; + m->friendlist[i].userstatus_sent = false; } return 0; @@ -701,41 +713,42 @@ int m_set_userstatus(Messenger *m, uint8_t status) /** * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. * - * returns the length of friendnumber's status message, including null on success. - * returns -1 on failure. + * @return the length of friendnumber's status message, including null on success. + * @retval -1 on failure. */ int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } return m->friendlist[friendnumber].statusmessage_length; } -/** Copy friendnumber's status message into buf, truncating if size is over maxlen. +/** @brief Copy friendnumber's status message into buf, truncating if size is over maxlen. + * * Get the size you need to allocate from m_get_statusmessage_size. * The self variant will copy our own status message. * - * returns the length of the copied data on success - * returns -1 on failure. + * @return the length of the copied data on success + * @retval -1 on failure. */ int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } // TODO(iphydf): This should be uint16_t and min_u16. If maxlen exceeds // uint16_t's range, it won't affect the result. - uint32_t msglen = min_u32(maxlen, m->friendlist[friendnumber].statusmessage_length); + const uint32_t msglen = min_u32(maxlen, m->friendlist[friendnumber].statusmessage_length); memcpy(buf, m->friendlist[friendnumber].statusmessage, msglen); memset(buf + msglen, 0, maxlen - msglen); return msglen; } -/** return the size of friendnumber's user status. +/** @return the size of friendnumber's user status. * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. */ int m_get_self_statusmessage_size(const Messenger *m) @@ -751,7 +764,7 @@ int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf) uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return USERSTATUS_INVALID; } @@ -771,20 +784,16 @@ uint8_t m_get_self_userstatus(const Messenger *m) uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return UINT64_MAX; } return m->friendlist[friendnumber].last_seen_time; } -int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing) +int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing) { - if (is_typing != 0 && is_typing != 1) { - return -1; - } - - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -793,39 +802,43 @@ int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing) } m->friendlist[friendnumber].user_istyping = is_typing; - m->friendlist[friendnumber].user_istyping_sent = 0; + m->friendlist[friendnumber].user_istyping_sent = false; return 0; } int m_get_istyping(const Messenger *m, int32_t friendnumber) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } - return m->friendlist[friendnumber].is_typing; + return m->friendlist[friendnumber].is_typing ? 1 : 0; } -static int send_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length) +non_null() +static bool send_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length) { - return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length, 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length, false); } -static int send_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status) +non_null() +static bool send_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status) { - return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &status, sizeof(status), 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &status, sizeof(status), false); } -static int send_user_istyping(const Messenger *m, int32_t friendnumber, uint8_t is_typing) +non_null() +static bool send_user_istyping(const Messenger *m, int32_t friendnumber, bool is_typing) { - uint8_t typing = is_typing; - return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), 0); + const uint8_t typing = is_typing ? 1 : 0; + return write_cryptpacket_id(m, friendnumber, PACKET_ID_TYPING, &typing, sizeof(typing), false); } +non_null() static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, const uint8_t *status, uint16_t length) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -833,7 +846,7 @@ static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, co return -1; } - if (length) { + if (length > 0) { memcpy(m->friendlist[friendnumber].statusmessage, status, length); } @@ -841,12 +854,14 @@ static int set_friend_statusmessage(const Messenger *m, int32_t friendnumber, co return 0; } +non_null() static void set_friend_userstatus(const Messenger *m, int32_t friendnumber, uint8_t status) { m->friendlist[friendnumber].userstatus = (Userstatus)status; } -static void set_friend_typing(const Messenger *m, int32_t friendnumber, uint8_t is_typing) +non_null() +static void set_friend_typing(const Messenger *m, int32_t friendnumber, bool is_typing) { m->friendlist[friendnumber].is_typing = is_typing; } @@ -854,7 +869,7 @@ static void set_friend_typing(const Messenger *m, int32_t friendnumber, uint8_t /** Set the function that will be executed when a friend request is received. */ void m_callback_friendrequest(Messenger *m, m_friend_request_cb *function) { - callback_friendrequest(m->fr, (fr_friend_request_cb *)function, m); + m->friend_request = function; } /** Set the function that will be executed when a message from a friend is received. */ @@ -905,18 +920,19 @@ void m_callback_connectionstatus_internal_av(Messenger *m, m_friend_connectionst m->friend_connectionstatuschange_internal_userdata = userdata; } +non_null(1) nullable(3) static void check_friend_tcp_udp(Messenger *m, int32_t friendnumber, void *userdata) { - int last_connection_udp_tcp = m->friendlist[friendnumber].last_connection_udp_tcp; + const int last_connection_udp_tcp = m->friendlist[friendnumber].last_connection_udp_tcp; - int ret = m_get_friend_connectionstatus(m, friendnumber); + const int ret = m_get_friend_connectionstatus(m, friendnumber); if (ret == -1) { return; } if (last_connection_udp_tcp != ret) { - if (m->friend_connectionstatuschange) { + if (m->friend_connectionstatuschange != nullptr) { m->friend_connectionstatuschange(m, friendnumber, ret, userdata); } } @@ -924,113 +940,90 @@ static void check_friend_tcp_udp(Messenger *m, int32_t friendnumber, void *userd m->friendlist[friendnumber].last_connection_udp_tcp = (Connection_Status)ret; } +non_null() static void break_files(const Messenger *m, int32_t friendnumber); + +non_null(1) nullable(4) static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status, void *userdata) { if (status == NOFRIEND) { return; } - const uint8_t was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE; - const uint8_t is_online = status == FRIEND_ONLINE; + const bool was_online = m->friendlist[friendnumber].status == FRIEND_ONLINE; + const bool is_online = status == FRIEND_ONLINE; if (is_online != was_online) { if (was_online) { break_files(m, friendnumber); clear_receipts(m, friendnumber); } else { - m->friendlist[friendnumber].name_sent = 0; - m->friendlist[friendnumber].userstatus_sent = 0; - m->friendlist[friendnumber].statusmessage_sent = 0; - m->friendlist[friendnumber].user_istyping_sent = 0; + m->friendlist[friendnumber].name_sent = false; + m->friendlist[friendnumber].userstatus_sent = false; + m->friendlist[friendnumber].statusmessage_sent = false; + m->friendlist[friendnumber].user_istyping_sent = false; } m->friendlist[friendnumber].status = status; check_friend_tcp_udp(m, friendnumber, userdata); - if (m->friend_connectionstatuschange_internal) { + if (m->friend_connectionstatuschange_internal != nullptr) { m->friend_connectionstatuschange_internal(m, friendnumber, is_online, m->friend_connectionstatuschange_internal_userdata); } } } +non_null(1) nullable(4) static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status, void *userdata) { check_friend_connectionstatus(m, friendnumber, status, userdata); m->friendlist[friendnumber].status = status; } -static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, - uint32_t length, uint8_t congestion_control) -{ - if (!friend_is_valid(m, friendnumber)) { - return 0; - } - - if (length >= MAX_CRYPTO_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) { - return 0; - } - - VLA(uint8_t, packet, length + 1); - packet[0] = packet_id; - - if (length != 0) { - memcpy(packet + 1, data, length); - } - - return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), packet, length + 1, congestion_control) != -1; -} - /*** CONFERENCES */ -/** Set the callback for conference invites. - */ +/** @brief Set the callback for conference invites. */ void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function) { m->conference_invite = function; } -/** Send a conference invite packet. +/** @brief Send a conference invite packet. * - * return 1 on success - * return 0 on failure + * return true on success + * return false on failure */ -int send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) +bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) { - return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_INVITE_CONFERENCE, data, length, false); } /*** FILE SENDING */ -/** Set the callback for file send requests. - */ +/** @brief Set the callback for file send requests. */ void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function) { m->file_sendrequest = function; } -/** Set the callback for file control requests. - */ +/** @brief Set the callback for file control requests. */ void callback_file_control(Messenger *m, m_file_recv_control_cb *function) { m->file_filecontrol = function; } -/** Set the callback for file data. - */ +/** @brief Set the callback for file data. */ void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function) { m->file_filedata = function; } -/** Set the callback for file request chunk. - */ +/** @brief Set the callback for file request chunk. */ void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function) { m->file_reqchunk = function; @@ -1038,15 +1031,15 @@ void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function) #define MAX_FILENAME_LENGTH 255 -/** Copy the file transfer file id to file_id +/** @brief Copy the file transfer file id to file_id * - * return 0 on success. - * return -1 if friend not valid. - * return -2 if filenumber not valid + * @retval 0 on success. + * @retval -1 if friend not valid. + * @retval -2 if filenumber not valid */ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1055,14 +1048,14 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u } uint32_t temp_filenum; - uint8_t send_receive; + bool inbound; uint8_t file_number; if (filenumber >= (1 << 16)) { - send_receive = 1; + inbound = true; temp_filenum = (filenumber >> 16) - 1; } else { - send_receive = 0; + inbound = false; temp_filenum = filenumber; } @@ -1072,13 +1065,9 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u file_number = temp_filenum; - struct File_Transfers *ft; - - if (send_receive) { - ft = &m->friendlist[friendnumber].file_receiving[file_number]; - } else { - ft = &m->friendlist[friendnumber].file_sending[file_number]; - } + const struct File_Transfers *const ft = inbound + ? &m->friendlist[friendnumber].file_receiving[file_number] + : &m->friendlist[friendnumber].file_sending[file_number]; if (ft->status == FILESTATUS_NONE) { return -2; @@ -1088,20 +1077,21 @@ int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, u return 0; } -/** Send a file send request. +/** @brief Send a file send request. * Maximum filename length is 255 bytes. - * return 1 on success - * return 0 on failure + * @retval 1 on success + * @retval 0 on failure */ -static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type, - uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length) +non_null() +static bool file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type, + uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length) { - if (!friend_is_valid(m, friendnumber)) { - return 0; + if (!m_friend_exists(m, friendnumber)) { + return false; } if (filename_length > MAX_FILENAME_LENGTH) { - return 0; + return false; } VLA(uint8_t, packet, 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH + filename_length); @@ -1111,26 +1101,27 @@ static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t fi net_pack_u64(packet + 1 + sizeof(file_type), filesize); memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize), file_id, FILE_ID_LENGTH); - if (filename_length) { + if (filename_length > 0) { memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH, filename, filename_length); } - return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, SIZEOF_VLA(packet), 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, SIZEOF_VLA(packet), false); } -/** Send a file send request. +/** @brief Send a file send request. + * * Maximum filename length is 255 bytes. - * return file number on success - * return -1 if friend not found. - * return -2 if filename length invalid. - * return -3 if no more file sending slots left. - * return -4 if could not send packet (friend offline). * + * @return file number on success + * @retval -1 if friend not found. + * @retval -2 if filename length invalid. + * @retval -3 if no more file sending slots left. + * @retval -4 if could not send packet (friend offline). */ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1150,7 +1141,7 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_ return -3; } - if (file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length) == 0) { + if (!file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length)) { return -4; } @@ -1171,41 +1162,44 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_ return i; } -static int send_file_control_packet(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber, - uint8_t control_type, const uint8_t *data, uint16_t data_length) +non_null(1) nullable(6) +static bool send_file_control_packet(const Messenger *m, int32_t friendnumber, bool inbound, uint8_t filenumber, + uint8_t control_type, const uint8_t *data, uint16_t data_length) { + assert(data_length == 0 || data != nullptr); + if ((unsigned int)(1 + 3 + data_length) > MAX_CRYPTO_DATA_SIZE) { - return -1; + return false; } VLA(uint8_t, packet, 3 + data_length); - packet[0] = send_receive; + packet[0] = inbound ? 1 : 0; packet[1] = filenumber; packet[2] = control_type; - if (data_length) { + if (data_length > 0) { memcpy(packet + 3, data, data_length); } - return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, SIZEOF_VLA(packet), 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, SIZEOF_VLA(packet), false); } -/** Send a file control request. +/** @brief Send a file control request. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if file number invalid. - * return -4 if file control is bad. - * return -5 if file already paused. - * return -6 if resume file failed because it was only paused by the other. - * return -7 if resume file failed because it wasn't paused. - * return -8 if packet failed to send. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if file number invalid. + * @retval -4 if file control is bad. + * @retval -5 if file already paused. + * @retval -6 if resume file failed because it was only paused by the other. + * @retval -7 if resume file failed because it wasn't paused. + * @retval -8 if packet failed to send. */ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1214,14 +1208,14 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, } uint32_t temp_filenum; - uint8_t send_receive; + bool inbound; uint8_t file_number; if (filenumber >= (1 << 16)) { - send_receive = 1; + inbound = true; temp_filenum = (filenumber >> 16) - 1; } else { - send_receive = 0; + inbound = false; temp_filenum = filenumber; } @@ -1233,7 +1227,7 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, struct File_Transfers *ft; - if (send_receive) { + if (inbound) { ft = &m->friendlist[friendnumber].file_receiving[file_number]; } else { ft = &m->friendlist[friendnumber].file_sending[file_number]; @@ -1247,14 +1241,14 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, return -4; } - if (control == FILECONTROL_PAUSE && ((ft->paused & FILE_PAUSE_US) || ft->status != FILESTATUS_TRANSFERRING)) { + if (control == FILECONTROL_PAUSE && ((ft->paused & FILE_PAUSE_US) != 0 || ft->status != FILESTATUS_TRANSFERRING)) { return -5; } if (control == FILECONTROL_ACCEPT) { if (ft->status == FILESTATUS_TRANSFERRING) { - if (!(ft->paused & FILE_PAUSE_US)) { - if (ft->paused & FILE_PAUSE_OTHER) { + if ((ft->paused & FILE_PAUSE_US) == 0) { + if ((ft->paused & FILE_PAUSE_OTHER) != 0) { return -6; } @@ -1265,27 +1259,34 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, return -7; } - if (!send_receive) { + if (!inbound) { return -6; } } } - if (send_file_control_packet(m, friendnumber, send_receive, file_number, control, nullptr, 0)) { - if (control == FILECONTROL_KILL) { - if (send_receive == 0 && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) { - // We are actively sending that file, remove from list - --m->friendlist[friendnumber].num_sending_files; - } + if (send_file_control_packet(m, friendnumber, inbound, file_number, control, nullptr, 0)) { + switch (control) { + case FILECONTROL_KILL: { + if (!inbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) { + // We are actively sending that file, remove from list + --m->friendlist[friendnumber].num_sending_files; + } - ft->status = FILESTATUS_NONE; - } else if (control == FILECONTROL_PAUSE) { - ft->paused |= FILE_PAUSE_US; - } else if (control == FILECONTROL_ACCEPT) { - ft->status = FILESTATUS_TRANSFERRING; + ft->status = FILESTATUS_NONE; + break; + } + case FILECONTROL_PAUSE: { + ft->paused |= FILE_PAUSE_US; + break; + } + case FILECONTROL_ACCEPT: { + ft->status = FILESTATUS_TRANSFERRING; - if (ft->paused & FILE_PAUSE_US) { - ft->paused ^= FILE_PAUSE_US; + if ((ft->paused & FILE_PAUSE_US) != 0) { + ft->paused ^= FILE_PAUSE_US; + } + break; } } } else { @@ -1295,20 +1296,20 @@ int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, return 0; } -/** Send a seek file control request. +/** @brief Send a seek file control request. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if file number invalid. - * return -4 if not receiving file. - * return -5 if file status wrong. - * return -6 if position bad. - * return -8 if packet failed to send. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if file number invalid. + * @retval -4 if not receiving file. + * @retval -5 if file status wrong. + * @retval -6 if position bad. + * @retval -8 if packet failed to send. */ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1321,14 +1322,14 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin return -4; } - uint32_t temp_filenum = (filenumber >> 16) - 1; + const uint32_t temp_filenum = (filenumber >> 16) - 1; if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES) { return -3; } assert(temp_filenum <= UINT8_MAX); - uint8_t file_number = temp_filenum; + const uint8_t file_number = temp_filenum; // We're always receiving at this point. struct File_Transfers *ft = &m->friendlist[friendnumber].file_receiving[file_number]; @@ -1348,7 +1349,7 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin uint8_t sending_pos[sizeof(uint64_t)]; net_pack_u64(sending_pos, position); - if (send_file_control_packet(m, friendnumber, 1, file_number, FILECONTROL_SEEK, sending_pos, + if (send_file_control_packet(m, friendnumber, true, file_number, FILECONTROL_SEEK, sending_pos, sizeof(sending_pos))) { ft->transferred = position; } else { @@ -1358,13 +1359,16 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin return 0; } -/** return packet number on success. - * return -1 on failure. +/** @return packet number on success. + * @retval -1 on failure. */ +non_null(1) nullable(4) static int64_t send_file_data_packet(const Messenger *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length) { - if (!friend_is_valid(m, friendnumber)) { + assert(length == 0 || data != nullptr); + + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1372,31 +1376,33 @@ static int64_t send_file_data_packet(const Messenger *m, int32_t friendnumber, u packet[0] = PACKET_ID_FILE_DATA; packet[1] = filenumber; - if (length) { + if (length > 0) { memcpy(packet + 2, data, length); } return write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), packet, SIZEOF_VLA(packet), 1); + m->friendlist[friendnumber].friendcon_id), packet, SIZEOF_VLA(packet), true); } #define MAX_FILE_DATA_SIZE (MAX_CRYPTO_DATA_SIZE - 2) #define MIN_SLOTS_FREE (CRYPTO_MIN_QUEUE_LENGTH / 4) -/** Send file data. +/** @brief Send file data. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if filenumber invalid. - * return -4 if file transfer not transferring. - * return -5 if bad data size. - * return -6 if packet queue full. - * return -7 if wrong position. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if filenumber invalid. + * @retval -4 if file transfer not transferring. + * @retval -5 if bad data size. + * @retval -6 if packet queue full. + * @retval -7 if wrong position. */ -int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, - uint16_t length) +int send_file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, + const uint8_t *data, uint16_t length) { - if (!friend_is_valid(m, friendnumber)) { + assert(length == 0 || data != nullptr); + + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1437,7 +1443,7 @@ int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin return -6; } - int64_t ret = send_file_data_packet(m, friendnumber, filenumber, data, length); + const int64_t ret = send_file_data_packet(m, friendnumber, filenumber, data, length); if (ret != -1) { // TODO(irungentoo): record packet ids to check if other received complete file. @@ -1468,6 +1474,7 @@ int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin * @return true if there's still work to do, false otherwise. * */ +non_null() static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userdata, uint32_t *free_slots) { Friend *const friendcon = &m->friendlist[friendnumber]; @@ -1491,9 +1498,16 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd continue; } + if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id( + m->fr_c, friendcon->friendcon_id))) { + LOGGER_DEBUG(m->log, "maximum connection speed reached"); + // connection doesn't support any more data + return false; + } + // If the file transfer is complete, we request a chunk of size 0. if (ft->status == FILESTATUS_FINISHED && friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) { - if (m->file_reqchunk) { + if (m->file_reqchunk != nullptr) { m->file_reqchunk(m, friendnumber, i, ft->transferred, 0, userdata); } @@ -1503,7 +1517,7 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd } else if (ft->status == FILESTATUS_TRANSFERRING && ft->paused == FILE_PAUSE_NOT) { if (ft->size == 0) { /* Send 0 data to friend if file is 0 length. */ - file_data(m, friendnumber, i, 0, nullptr, 0); + send_file_data(m, friendnumber, i, 0, nullptr, 0); continue; } @@ -1516,26 +1530,19 @@ static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userd const uint64_t position = ft->requested; ft->requested += length; - if (m->file_reqchunk) { + if (m->file_reqchunk != nullptr) { m->file_reqchunk(m, friendnumber, i, position, length, userdata); } // The allocated slot is no longer free. --*free_slots; } - - // Must be last to allow finishing file transfers - if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id( - m->fr_c, friendcon->friendcon_id))) { - LOGGER_TRACE(m->log, "Maximum connection speed reached"); - // connection doesn't support any more data - return false; - } } return true; } +non_null(1) nullable(3) static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdata) { // We're not currently doing any file transfers. @@ -1560,7 +1567,9 @@ static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdat // that the file transfer has finished and may end up in an infinite loop. // // Request up to that number of chunks per file from the client - const uint32_t max_ft_loops = 16; + // + // TODO(Jfreegman): set this cap dynamically + const uint32_t max_ft_loops = 128; for (uint32_t i = 0; i < max_ft_loops; ++i) { if (!do_all_filetransfers(m, friendnumber, userdata, &free_slots)) { @@ -1575,34 +1584,32 @@ static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdat } -/** Run this when the friend disconnects. - * Kill all current file transfers. +/** @brief Run this when the friend disconnects. + * Kill all current file transfers. */ static void break_files(const Messenger *m, int32_t friendnumber) { + Friend *const f = &m->friendlist[friendnumber]; + // TODO(irungentoo): Inform the client which file transfers get killed with a callback? for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) { - if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE) { - m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_NONE; - } - - if (m->friendlist[friendnumber].file_receiving[i].status != FILESTATUS_NONE) { - m->friendlist[friendnumber].file_receiving[i].status = FILESTATUS_NONE; - } + f->file_sending[i].status = FILESTATUS_NONE; + f->file_receiving[i].status = FILESTATUS_NONE; } } -static struct File_Transfers *get_file_transfer(uint8_t receive_send, uint8_t filenumber, +non_null() +static struct File_Transfers *get_file_transfer(bool outbound, uint8_t filenumber, uint32_t *real_filenumber, Friend *sender) { struct File_Transfers *ft; - if (receive_send == 0) { - *real_filenumber = (filenumber + 1) << 16; - ft = &sender->file_receiving[filenumber]; - } else { + if (outbound) { *real_filenumber = filenumber; ft = &sender->file_sending[filenumber]; + } else { + *real_filenumber = (filenumber + 1) << 16; + ft = &sender->file_receiving[filenumber]; } if (ft->status == FILESTATUS_NONE) { @@ -1612,34 +1619,30 @@ static struct File_Transfers *get_file_transfer(uint8_t receive_send, uint8_t fi return ft; } -/** return -1 on failure, 0 on success. +/** @retval -1 on failure + * @retval 0 on success. */ -static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, +non_null(1, 6) nullable(8) +static int handle_filecontrol(Messenger *m, int32_t friendnumber, bool outbound, uint8_t filenumber, uint8_t control_type, const uint8_t *data, uint16_t length, void *userdata) { - if (receive_send > 1) { - LOGGER_DEBUG(m->log, "file control (friend %d, file %d): receive_send value is invalid (should be 0 or 1): %d", - friendnumber, filenumber, receive_send); - return -1; - } - uint32_t real_filenumber; - struct File_Transfers *ft = get_file_transfer(receive_send, filenumber, &real_filenumber, &m->friendlist[friendnumber]); + struct File_Transfers *ft = get_file_transfer(outbound, filenumber, &real_filenumber, &m->friendlist[friendnumber]); if (ft == nullptr) { LOGGER_DEBUG(m->log, "file control (friend %d, file %d): file transfer does not exist; telling the other to kill it", friendnumber, filenumber); - send_file_control_packet(m, friendnumber, !receive_send, filenumber, FILECONTROL_KILL, nullptr, 0); + send_file_control_packet(m, friendnumber, !outbound, filenumber, FILECONTROL_KILL, nullptr, 0); return -1; } switch (control_type) { case FILECONTROL_ACCEPT: { - if (receive_send && ft->status == FILESTATUS_NOT_ACCEPTED) { + if (outbound && ft->status == FILESTATUS_NOT_ACCEPTED) { ft->status = FILESTATUS_TRANSFERRING; ++m->friendlist[friendnumber].num_sending_files; } else { - if (ft->paused & FILE_PAUSE_OTHER) { + if ((ft->paused & FILE_PAUSE_OTHER) != 0) { ft->paused ^= FILE_PAUSE_OTHER; } else { LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to resume file transfer that wasn't paused", @@ -1648,7 +1651,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv } } - if (m->file_filecontrol) { + if (m->file_filecontrol != nullptr) { m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata); } @@ -1656,7 +1659,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv } case FILECONTROL_PAUSE: { - if ((ft->paused & FILE_PAUSE_OTHER) || ft->status != FILESTATUS_TRANSFERRING) { + if ((ft->paused & FILE_PAUSE_OTHER) != 0 || ft->status != FILESTATUS_TRANSFERRING) { LOGGER_DEBUG(m->log, "file control (friend %d, file %d): friend told us to pause file transfer that is already paused", friendnumber, filenumber); return -1; @@ -1664,7 +1667,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv ft->paused |= FILE_PAUSE_OTHER; - if (m->file_filecontrol) { + if (m->file_filecontrol != nullptr) { m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata); } @@ -1672,11 +1675,11 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv } case FILECONTROL_KILL: { - if (m->file_filecontrol) { + if (m->file_filecontrol != nullptr) { m->file_filecontrol(m, friendnumber, real_filenumber, control_type, userdata); } - if (receive_send && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) { + if (outbound && (ft->status == FILESTATUS_TRANSFERRING || ft->status == FILESTATUS_FINISHED)) { --m->friendlist[friendnumber].num_sending_files; } @@ -1695,7 +1698,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv } /* seek can only be sent by the receiver to seek before resuming broken transfers. */ - if (ft->status != FILESTATUS_NOT_ACCEPTED || !receive_send) { + if (ft->status != FILESTATUS_NOT_ACCEPTED || !outbound) { LOGGER_DEBUG(m->log, "file control (friend %d, file %d): seek was either sent by a sender or by the receiver after accepting", friendnumber, filenumber); @@ -1724,22 +1727,21 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv } } -/** Set the callback for msi packets. - */ +/** @brief Set the callback for msi packets. */ void m_callback_msi_packet(Messenger *m, m_msi_packet_cb *function, void *userdata) { m->msi_packet = function; m->msi_packet_userdata = userdata; } -/** Send an msi packet. +/** @brief Send an msi packet. * - * return 1 on success - * return 0 on failure + * @retval true on success + * @retval false on failure */ -int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) +bool m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length) { - return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length, 0); + return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length, false); } static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length, @@ -1747,7 +1749,7 @@ static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *pa { Messenger *m = (Messenger *)object; - if (!friend_is_valid(m, friend_num)) { + if (!m_friend_exists(m, friend_num)) { return 1; } @@ -1755,14 +1757,14 @@ static int m_handle_lossy_packet(void *object, int friend_num, const uint8_t *pa const RTP_Packet_Handler *const ph = &m->friendlist[friend_num].lossy_rtp_packethandlers[packet[0] % PACKET_ID_RANGE_LOSSY_AV_SIZE]; - if (ph->function) { + if (ph->function != nullptr) { return ph->function(m, friend_num, packet, length, ph->object); } return 1; } - if (m->lossy_packethandler) { + if (m->lossy_packethandler != nullptr) { m->lossy_packethandler(m, friend_num, packet[0], packet, length, userdata); } @@ -1777,7 +1779,7 @@ void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lossy_rtp_packet_cb *function, void *object) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1791,7 +1793,7 @@ int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lo } -/** High level function to send custom lossy packets. +/** @brief High level function to send custom lossy packets. * * TODO(oxij): this name is confusing, because this function sends both av and custom lossy packets. * Meanwhile, m_handle_lossy_packet routes custom packets to custom_lossy_packet_registerhandler @@ -1800,16 +1802,16 @@ int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lo * I.e. custom_lossy_packet_registerhandler's "custom lossy packet" and this "custom lossy packet" * are not the same set of packets. * - * return -1 if friend invalid. - * return -2 if length wrong. - * return -3 if first byte invalid. - * return -4 if friend offline. - * return -5 if packet failed to send because of other error. - * return 0 on success. + * @retval -1 if friend invalid. + * @retval -2 if length wrong. + * @retval -3 if first byte invalid. + * @retval -4 if friend offline. + * @retval -5 if packet failed to send because of other error. + * @retval 0 on success. */ int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1834,12 +1836,13 @@ int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const u return 0; } +non_null(1, 3) nullable(5) static int handle_custom_lossless_packet(void *object, int friend_num, const uint8_t *packet, uint16_t length, void *userdata) { Messenger *m = (Messenger *)object; - if (!friend_is_valid(m, friend_num)) { + if (!m_friend_exists(m, friend_num)) { return -1; } @@ -1847,7 +1850,7 @@ static int handle_custom_lossless_packet(void *object, int friend_num, const uin return -1; } - if (m->lossless_packethandler) { + if (m->lossless_packethandler != nullptr) { m->lossless_packethandler(m, friend_num, packet[0], packet, length, userdata); } @@ -1861,7 +1864,7 @@ void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_pack int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length) { - if (!friend_is_valid(m, friendnumber)) { + if (!m_friend_exists(m, friendnumber)) { return -1; } @@ -1879,7 +1882,7 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const } if (write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, - m->friendlist[friendnumber].friendcon_id), data, length, 1) == -1) { + m->friendlist[friendnumber].friendcon_id), data, length, true) == -1) { return -5; } @@ -1887,6 +1890,7 @@ int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const } /** Function to filter out some friend requests*/ +non_null() static int friend_already_added(const uint8_t *real_pk, void *data) { const Messenger *m = (const Messenger *)data; @@ -1898,193 +1902,13 @@ static int friend_already_added(const uint8_t *real_pk, void *data) return -1; } -/** Run this at startup. - * return allocated instance of Messenger on success. - * return 0 if there are problems. +/** @brief Check for and handle a timed-out friend request. * - * if error is not NULL it will be set to one of the values in the enum above. - */ -Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsigned int *error) -{ - if (!options) { - return nullptr; - } - - if (error) { - *error = MESSENGER_ERROR_OTHER; - } - - Messenger *m = (Messenger *)calloc(1, sizeof(Messenger)); - - if (!m) { - return nullptr; - } - - m->mono_time = mono_time; - - m->fr = friendreq_new(); - - if (!m->fr) { - free(m); - return nullptr; - } - - m->log = logger_new(); - - if (m->log == nullptr) { - friendreq_kill(m->fr); - free(m); - return nullptr; - } - - logger_callback_log(m->log, options->log_callback, options->log_context, options->log_user_data); - - unsigned int net_err = 0; - - if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) { - // We don't currently support UDP over proxy. - LOGGER_WARNING(m->log, "UDP enabled and proxy set: disabling UDP"); - options->udp_disabled = true; - } - - if (options->udp_disabled) { - m->net = new_networking_no_udp(m->log); - } else { - IP ip; - ip_init(&ip, options->ipv6enabled); - m->net = new_networking_ex(m->log, &ip, options->port_range[0], options->port_range[1], &net_err); - } - - if (m->net == nullptr) { - friendreq_kill(m->fr); - logger_kill(m->log); - free(m); - - if (error && net_err == 1) { - *error = MESSENGER_ERROR_PORT; - } - - return nullptr; - } - - m->dht = new_dht(m->log, m->mono_time, m->net, options->hole_punching_enabled); - - if (m->dht == nullptr) { - kill_networking(m->net); - friendreq_kill(m->fr); - logger_kill(m->log); - free(m); - return nullptr; - } - - m->net_crypto = new_net_crypto(m->log, m->mono_time, m->dht, &options->proxy_info); - - if (m->net_crypto == nullptr) { - kill_dht(m->dht); - kill_networking(m->net); - friendreq_kill(m->fr); - logger_kill(m->log); - free(m); - return nullptr; - } - - m->onion = new_onion(m->log, m->mono_time, m->dht); - m->onion_a = new_onion_announce(m->log, m->mono_time, m->dht); - m->onion_c = new_onion_client(m->log, m->mono_time, m->net_crypto); - m->fr_c = new_friend_connections(m->log, m->mono_time, m->onion_c, options->local_discovery_enabled); - - if (!(m->onion && m->onion_a && m->onion_c && m->fr_c)) { - kill_friend_connections(m->fr_c); - kill_onion(m->onion); - kill_onion_announce(m->onion_a); - kill_onion_client(m->onion_c); - kill_net_crypto(m->net_crypto); - kill_dht(m->dht); - kill_networking(m->net); - friendreq_kill(m->fr); - logger_kill(m->log); - free(m); - return nullptr; - } - - if (options->tcp_server_port) { - m->tcp_server = new_TCP_server(m->log, options->ipv6enabled, 1, &options->tcp_server_port, - dht_get_self_secret_key(m->dht), m->onion); - - if (m->tcp_server == nullptr) { - kill_friend_connections(m->fr_c); - kill_onion(m->onion); - kill_onion_announce(m->onion_a); - kill_onion_client(m->onion_c); - kill_net_crypto(m->net_crypto); - kill_dht(m->dht); - kill_networking(m->net); - friendreq_kill(m->fr); - logger_kill(m->log); - free(m); - - if (error) { - *error = MESSENGER_ERROR_TCP_SERVER; - } - - return nullptr; - } - } - - m->options = *options; - friendreq_init(m->fr, m->fr_c); - set_nospam(m->fr, random_u32()); - set_filter_function(m->fr, &friend_already_added, m); - - m->lastdump = 0; - - m_register_default_plugins(m); - - if (error) { - *error = MESSENGER_ERROR_NONE; - } - - return m; -} - -/** Run this before closing shop - * Free all datastructures. - */ -void kill_messenger(Messenger *m) -{ - if (!m) { - return; - } - - if (m->tcp_server) { - kill_TCP_server(m->tcp_server); - } - - kill_friend_connections(m->fr_c); - kill_onion(m->onion); - kill_onion_announce(m->onion_a); - kill_onion_client(m->onion_c); - kill_net_crypto(m->net_crypto); - kill_dht(m->dht); - kill_networking(m->net); - - for (uint32_t i = 0; i < m->numfriends; ++i) { - clear_receipts(m, i); - } - - logger_kill(m->log); - free(m->friendlist); - friendreq_kill(m->fr); - - free(m->options.state_plugins); - free(m); -} - -/** Check for and handle a timed-out friend request. If the request has - * timed-out then the friend status is set back to FRIEND_ADDED. - * i: friendlist index of the timed-out friend - * t: time + * If the request has timed-out then the friend status is set back to FRIEND_ADDED. + * @param i friendlist index of the timed-out friend + * @param t time */ +non_null(1) nullable(4) static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t, void *userdata) { Friend *f = &m->friendlist[i]; @@ -2098,7 +1922,7 @@ static void check_friend_request_timed_out(Messenger *m, uint32_t i, uint64_t t, } } -static int m_handle_status(void *object, int i, uint8_t status, void *userdata) +static int m_handle_status(void *object, int i, bool status, void *userdata) { Messenger *m = (Messenger *)object; @@ -2120,9 +1944,9 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le } Messenger *m = (Messenger *)object; - uint8_t packet_id = temp[0]; + const uint8_t packet_id = temp[0]; const uint8_t *data = temp + 1; - uint16_t data_length = len - 1; + const uint16_t data_length = len - 1; if (m->friendlist[i].status != FRIEND_ONLINE) { if (packet_id == PACKET_ID_ONLINE && len == 1) { @@ -2135,7 +1959,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le switch (packet_id) { case PACKET_ID_OFFLINE: { - if (data_length != 0) { + if (data_length > 0) { break; } @@ -2154,7 +1978,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le data_terminated[data_length] = 0; /* inform of namechange before we overwrite the old name */ - if (m->friend_namechange) { + if (m->friend_namechange != nullptr) { m->friend_namechange(m, i, data_terminated, data_length, userdata); } @@ -2174,7 +1998,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le memcpy(data_terminated, data, data_length); data_terminated[data_length] = 0; - if (m->friend_statusmessagechange) { + if (m->friend_statusmessagechange != nullptr) { m->friend_statusmessagechange(m, i, data_terminated, data_length, userdata); } @@ -2187,13 +2011,13 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - Userstatus status = (Userstatus)data[0]; + const Userstatus status = (Userstatus)data[0]; if (status >= USERSTATUS_INVALID) { break; } - if (m->friend_userstatuschange) { + if (m->friend_userstatuschange != nullptr) { m->friend_userstatuschange(m, i, status, userdata); } @@ -2206,11 +2030,11 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - bool typing = !!data[0]; + const bool typing = data[0] != 0; set_friend_typing(m, i, typing); - if (m->friend_typingchange) { + if (m->friend_typingchange != nullptr) { m->friend_typingchange(m, i, typing, userdata); } @@ -2224,15 +2048,15 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le } const uint8_t *message = data; - uint16_t message_length = data_length; + const uint16_t message_length = data_length; /* Make sure the NULL terminator is present. */ VLA(uint8_t, message_terminated, message_length + 1); memcpy(message_terminated, message, message_length); message_terminated[message_length] = 0; - uint8_t type = packet_id - PACKET_ID_MESSAGE; + const uint8_t type = packet_id - PACKET_ID_MESSAGE; - if (m->friend_message) { + if (m->friend_message != nullptr) { m->friend_message(m, i, type, message_terminated, message_length, userdata); } @@ -2244,7 +2068,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - if (m->conference_invite) { + if (m->conference_invite != nullptr) { m->conference_invite(m, i, data, data_length, userdata); } @@ -2258,7 +2082,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - uint8_t filenumber = data[0]; + const uint8_t filenumber = data[0]; #if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES @@ -2270,7 +2094,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le uint64_t filesize; uint32_t file_type; - uint16_t filename_length = data_length - head_length; + const uint16_t filename_length = data_length - head_length; if (filename_length > MAX_FILENAME_LENGTH) { break; @@ -2295,7 +2119,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le VLA(uint8_t, filename_terminated, filename_length + 1); const uint8_t *filename = nullptr; - if (filename_length) { + if (filename_length > 0) { /* Force NULL terminate file name. */ memcpy(filename_terminated, data + head_length, filename_length); filename_terminated[filename_length] = 0; @@ -2306,7 +2130,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le real_filenumber += 1; real_filenumber <<= 16; - if (m->file_sendrequest) { + if (m->file_sendrequest != nullptr) { m->file_sendrequest(m, i, real_filenumber, file_type, filesize, filename, filename_length, userdata); } @@ -2319,9 +2143,12 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - uint8_t send_receive = data[0]; + // On the other side, "outbound" is "inbound", i.e. if they send 1, + // that means "inbound" on their side, but we call it "outbound" + // here. + const bool outbound = data[0] == 1; uint8_t filenumber = data[1]; - uint8_t control_type = data[2]; + const uint8_t control_type = data[2]; #if UINT8_MAX >= MAX_CONCURRENT_FILE_PIPES @@ -2331,7 +2158,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le #endif - if (handle_filecontrol(m, i, send_receive, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) { + if (handle_filecontrol(m, i, outbound, filenumber, control_type, data + 3, data_length - 3, userdata) == -1) { // TODO(iphydf): Do something different here? Right now, this // check is pointless. break; @@ -2379,19 +2206,19 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le file_data_length = ft->size - ft->transferred; } - if (m->file_filedata) { + if (m->file_filedata != nullptr) { m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata); } ft->transferred += file_data_length; - if (file_data_length && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) { + if (file_data_length > 0 && (ft->transferred >= ft->size || file_data_length != MAX_FILE_DATA_SIZE)) { file_data_length = 0; file_data = nullptr; position = ft->transferred; /* Full file received. */ - if (m->file_filedata) { + if (m->file_filedata != nullptr) { m->file_filedata(m, i, real_filenumber, position, file_data, file_data_length, userdata); } } @@ -2409,7 +2236,7 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le break; } - if (m->msi_packet) { + if (m->msi_packet != nullptr) { m->msi_packet(m, i, data, data_length, m->msi_packet_userdata); } @@ -2425,13 +2252,14 @@ static int m_handle_packet(void *object, int i, const uint8_t *temp, uint16_t le return 0; } +non_null(1) nullable(2) static void do_friends(Messenger *m, void *userdata) { - uint64_t temp_time = mono_time_get(m->mono_time); + const uint64_t temp_time = mono_time_get(m->mono_time); for (uint32_t i = 0; i < m->numfriends; ++i) { if (m->friendlist[i].status == FRIEND_ADDED) { - int fr = send_friend_request_packet(m->fr_c, m->friendlist[i].friendcon_id, m->friendlist[i].friendrequest_nospam, + const int fr = send_friend_request_packet(m->fr_c, m->friendlist[i].friendcon_id, m->friendlist[i].friendrequest_nospam, m->friendlist[i].info, m->friendlist[i].info_size); @@ -2452,27 +2280,27 @@ static void do_friends(Messenger *m, void *userdata) } if (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online. */ - if (m->friendlist[i].name_sent == 0) { + if (!m->friendlist[i].name_sent) { if (m_sendname(m, i, m->name, m->name_length)) { - m->friendlist[i].name_sent = 1; + m->friendlist[i].name_sent = true; } } - if (m->friendlist[i].statusmessage_sent == 0) { + if (!m->friendlist[i].statusmessage_sent) { if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) { - m->friendlist[i].statusmessage_sent = 1; + m->friendlist[i].statusmessage_sent = true; } } - if (m->friendlist[i].userstatus_sent == 0) { + if (!m->friendlist[i].userstatus_sent) { if (send_userstatus(m, i, m->userstatus)) { - m->friendlist[i].userstatus_sent = 1; + m->friendlist[i].userstatus_sent = true; } } - if (m->friendlist[i].user_istyping_sent == 0) { + if (!m->friendlist[i].user_istyping_sent) { if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) { - m->friendlist[i].user_istyping_sent = 1; + m->friendlist[i].user_istyping_sent = true; } } @@ -2485,12 +2313,13 @@ static void do_friends(Messenger *m, void *userdata) } } -static void connection_status_callback(Messenger *m, void *userdata) +non_null(1) nullable(2) +static void m_connection_status_callback(Messenger *m, void *userdata) { - unsigned int conn_status = onion_connection_status(m->onion_c); + const Onion_Connection_Status conn_status = onion_connection_status(m->onion_c); if (conn_status != m->last_connection_status) { - if (m->core_connection_change) { + if (m->core_connection_change != nullptr) { m->core_connection_change(m, conn_status, userdata); } @@ -2503,6 +2332,7 @@ static void connection_status_callback(Messenger *m, void *userdata) #define IDSTRING_LEN (CRYPTO_PUBLIC_KEY_SIZE * 2 + 1) /** id_str should be of length at least IDSTRING_LEN */ +non_null() static char *id_to_string(const uint8_t *pk, char *id_str, size_t length) { if (length < IDSTRING_LEN) { @@ -2514,22 +2344,24 @@ static char *id_to_string(const uint8_t *pk, char *id_str, size_t length) snprintf(&id_str[i * 2], length - i * 2, "%02X", pk[i]); } - id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = 0; + id_str[CRYPTO_PUBLIC_KEY_SIZE * 2] = '\0'; return id_str; } -/** Minimum messenger run interval in ms - * TODO(mannol): A/V */ +/** @brief Minimum messenger run interval in ms + * TODO(mannol): A/V + */ #define MIN_RUN_INTERVAL 50 -/** Return the time in milliseconds before do_messenger() should be called again - * for optimal performance. +/** + * @brief Return the time in milliseconds before `do_messenger()` should be called again + * for optimal performance. * - * returns time (in ms) before the next do_messenger() needs to be run on success. + * @return time (in ms) before the next `do_messenger()` needs to be run on success. */ uint32_t messenger_run_interval(const Messenger *m) { - uint32_t crypto_interval = crypto_run_interval(m->net_crypto); + const uint32_t crypto_interval = crypto_run_interval(m->net_crypto); if (crypto_interval > MIN_RUN_INTERVAL) { return MIN_RUN_INTERVAL; @@ -2538,7 +2370,7 @@ uint32_t messenger_run_interval(const Messenger *m) return crypto_interval; } -/** The main loop that needs to be run at least 20 times per second. */ +/** @brief The main loop that needs to be run at least 20 times per second. */ void do_messenger(Messenger *m, void *userdata) { // Add the TCP relays, but only if this is the first time calling do_messenger @@ -2551,11 +2383,11 @@ void do_messenger(Messenger *m, void *userdata) m->num_loaded_relays = 0; - if (m->tcp_server) { + if (m->tcp_server != nullptr) { /* Add self tcp server. */ IP_Port local_ip_port; local_ip_port.port = m->options.tcp_server_port; - local_ip_port.ip.family = net_family_ipv4; + local_ip_port.ip.family = net_family_ipv4(); local_ip_port.ip.ip.v4 = get_ip4_loopback(); add_tcp_relay(m->net_crypto, &local_ip_port, tcp_server_public_key(m->tcp_server)); } @@ -2566,7 +2398,7 @@ void do_messenger(Messenger *m, void *userdata) do_dht(m->dht); } - if (m->tcp_server) { + if (m->tcp_server != nullptr) { do_TCP_server(m->tcp_server, m->mono_time); } @@ -2574,7 +2406,7 @@ void do_messenger(Messenger *m, void *userdata) do_onion_client(m->onion_c); do_friend_connections(m->fr_c, userdata); do_friends(m, userdata); - connection_status_callback(m, userdata); + m_connection_status_callback(m, userdata); if (mono_time_get(m->mono_time) > m->lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { m->lastdump = mono_time_get(m->mono_time); @@ -2584,7 +2416,7 @@ void do_messenger(Messenger *m, void *userdata) const Client_data *cptr = dht_get_close_client(m->dht, client); const IPPTsPng *const assocs[] = { &cptr->assoc4, &cptr->assoc6, nullptr }; - for (const IPPTsPng * const *it = assocs; *it; ++it) { + for (const IPPTsPng * const *it = assocs; *it != nullptr; ++it) { const IPPTsPng *const assoc = *it; if (ip_isset(&assoc->ip_port.ip)) { @@ -2594,10 +2426,10 @@ void do_messenger(Messenger *m, void *userdata) last_pinged = 999; } - char ip_str[IP_NTOA_LEN]; + Ip_Ntoa ip_str; char id_str[IDSTRING_LEN]; LOGGER_TRACE(m->log, "C[%2u] %s:%u [%3u] %s", - client, ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)), + client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str), net_ntohs(assoc->ip_port.port), last_pinged, id_to_string(cptr->public_key, id_str, sizeof(id_str))); } @@ -2606,7 +2438,7 @@ void do_messenger(Messenger *m, void *userdata) /* dht contains additional "friends" (requests) */ - uint32_t num_dhtfriends = dht_get_num_friends(m->dht); + const uint32_t num_dhtfriends = dht_get_num_friends(m->dht); VLA(int32_t, m2dht, num_dhtfriends); VLA(int32_t, dht2m, num_dhtfriends); @@ -2619,8 +2451,9 @@ void do_messenger(Messenger *m, void *userdata) } for (uint32_t dhtfriend = 0; dhtfriend < dht_get_num_friends(m->dht); ++dhtfriend) { - if (id_equal(m->friendlist[friend_idx].real_pk, dht_get_friend_public_key(m->dht, dhtfriend))) { - m2dht[friend_idx] = dhtfriend; + if (pk_equal(m->friendlist[friend_idx].real_pk, dht_get_friend_public_key(m->dht, dhtfriend))) { + assert(dhtfriend < INT32_MAX); + m2dht[friend_idx] = (int32_t)dhtfriend; break; } } @@ -2628,7 +2461,8 @@ void do_messenger(Messenger *m, void *userdata) for (uint32_t friend_idx = 0; friend_idx < num_dhtfriends; ++friend_idx) { if (m2dht[friend_idx] >= 0) { - dht2m[m2dht[friend_idx]] = friend_idx; + assert(friend_idx < INT32_MAX); + dht2m[m2dht[friend_idx]] = (int32_t)friend_idx; } } @@ -2640,7 +2474,7 @@ void do_messenger(Messenger *m, void *userdata) const Friend *const msgfptr = dht2m[friend_idx] >= 0 ? &m->friendlist[dht2m[friend_idx]] : nullptr; const DHT_Friend *const dhtfptr = dht_get_friend(m->dht, friend_idx); - if (msgfptr) { + if (msgfptr != nullptr) { char id_str[IDSTRING_LEN]; LOGGER_TRACE(m->log, "F[%2u:%2u] <%s> %s", dht2m[friend_idx], friend_idx, msgfptr->name, @@ -2665,10 +2499,10 @@ void do_messenger(Messenger *m, void *userdata) last_pinged = 999; } - char ip_str[IP_NTOA_LEN]; + Ip_Ntoa ip_str; char id_str[IDSTRING_LEN]; LOGGER_TRACE(m->log, "F[%2u] => C[%2u] %s:%u [%3u] %s", - friend_idx, client, ip_ntoa(&assoc->ip_port.ip, ip_str, sizeof(ip_str)), + friend_idx, client, net_ip_ntoa(&assoc->ip_port.ip, &ip_str), net_ntohs(assoc->ip_port.port), last_pinged, id_to_string(cptr->public_key, id_str, sizeof(id_str))); } @@ -2702,24 +2536,30 @@ static uint32_t friend_size(void) uint32_t data = 0; const struct Saved_Friend *const temp = nullptr; -#define VALUE_MEMBER(name) do { data += sizeof(temp->name); } while (0) -#define ARRAY_MEMBER(name) do { data += sizeof(temp->name); } while (0) +#define VALUE_MEMBER(data, name) \ + do { \ + data += sizeof(name); \ + } while (0) +#define ARRAY_MEMBER(data, name) \ + do { \ + data += sizeof(name); \ + } while (0) // Exactly the same in friend_load, friend_save, and friend_size - VALUE_MEMBER(status); - ARRAY_MEMBER(real_pk); - ARRAY_MEMBER(info); + VALUE_MEMBER(data, temp->status); + ARRAY_MEMBER(data, temp->real_pk); + ARRAY_MEMBER(data, temp->info); ++data; // padding - VALUE_MEMBER(info_size); - ARRAY_MEMBER(name); - VALUE_MEMBER(name_length); - ARRAY_MEMBER(statusmessage); + VALUE_MEMBER(data, temp->info_size); + ARRAY_MEMBER(data, temp->name); + VALUE_MEMBER(data, temp->name_length); + ARRAY_MEMBER(data, temp->statusmessage); ++data; // padding - VALUE_MEMBER(statusmessage_length); - VALUE_MEMBER(userstatus); + VALUE_MEMBER(data, temp->statusmessage_length); + VALUE_MEMBER(data, temp->userstatus); data += 3; // padding - VALUE_MEMBER(friendrequest_nospam); - ARRAY_MEMBER(last_seen_time); + VALUE_MEMBER(data, temp->friendrequest_nospam); + ARRAY_MEMBER(data, temp->last_seen_time); #undef VALUE_MEMBER #undef ARRAY_MEMBER @@ -2727,33 +2567,36 @@ static uint32_t friend_size(void) return data; } +non_null() static uint8_t *friend_save(const struct Saved_Friend *temp, uint8_t *data) { -#define VALUE_MEMBER(name) do { \ - memcpy(data, &temp->name, sizeof(temp->name)); \ - data += sizeof(temp->name); \ -} while (0) +#define VALUE_MEMBER(data, name) \ + do { \ + memcpy(data, &name, sizeof(name)); \ + data += sizeof(name); \ + } while (0) -#define ARRAY_MEMBER(name) do { \ - memcpy(data, temp->name, sizeof(temp->name)); \ - data += sizeof(temp->name); \ -} while (0) +#define ARRAY_MEMBER(data, name) \ + do { \ + memcpy(data, name, sizeof(name)); \ + data += sizeof(name); \ + } while (0) // Exactly the same in friend_load, friend_save, and friend_size - VALUE_MEMBER(status); - ARRAY_MEMBER(real_pk); - ARRAY_MEMBER(info); + VALUE_MEMBER(data, temp->status); + ARRAY_MEMBER(data, temp->real_pk); + ARRAY_MEMBER(data, temp->info); ++data; // padding - VALUE_MEMBER(info_size); - ARRAY_MEMBER(name); - VALUE_MEMBER(name_length); - ARRAY_MEMBER(statusmessage); + VALUE_MEMBER(data, temp->info_size); + ARRAY_MEMBER(data, temp->name); + VALUE_MEMBER(data, temp->name_length); + ARRAY_MEMBER(data, temp->statusmessage); ++data; // padding - VALUE_MEMBER(statusmessage_length); - VALUE_MEMBER(userstatus); + VALUE_MEMBER(data, temp->statusmessage_length); + VALUE_MEMBER(data, temp->userstatus); data += 3; // padding - VALUE_MEMBER(friendrequest_nospam); - ARRAY_MEMBER(last_seen_time); + VALUE_MEMBER(data, temp->friendrequest_nospam); + ARRAY_MEMBER(data, temp->last_seen_time); #undef VALUE_MEMBER #undef ARRAY_MEMBER @@ -2762,33 +2605,36 @@ static uint8_t *friend_save(const struct Saved_Friend *temp, uint8_t *data) } +non_null() static const uint8_t *friend_load(struct Saved_Friend *temp, const uint8_t *data) { -#define VALUE_MEMBER(name) do { \ - memcpy(&temp->name, data, sizeof(temp->name)); \ - data += sizeof(temp->name); \ -} while (0) +#define VALUE_MEMBER(data, name) \ + do { \ + memcpy(&name, data, sizeof(name)); \ + data += sizeof(name); \ + } while (0) -#define ARRAY_MEMBER(name) do { \ - memcpy(temp->name, data, sizeof(temp->name)); \ - data += sizeof(temp->name); \ -} while (0) +#define ARRAY_MEMBER(data, name) \ + do { \ + memcpy(name, data, sizeof(name)); \ + data += sizeof(name); \ + } while (0) // Exactly the same in friend_load, friend_save, and friend_size - VALUE_MEMBER(status); - ARRAY_MEMBER(real_pk); - ARRAY_MEMBER(info); + VALUE_MEMBER(data, temp->status); + ARRAY_MEMBER(data, temp->real_pk); + ARRAY_MEMBER(data, temp->info); ++data; // padding - VALUE_MEMBER(info_size); - ARRAY_MEMBER(name); - VALUE_MEMBER(name_length); - ARRAY_MEMBER(statusmessage); + VALUE_MEMBER(data, temp->info_size); + ARRAY_MEMBER(data, temp->name); + VALUE_MEMBER(data, temp->name_length); + ARRAY_MEMBER(data, temp->statusmessage); ++data; // padding - VALUE_MEMBER(statusmessage_length); - VALUE_MEMBER(userstatus); + VALUE_MEMBER(data, temp->statusmessage_length); + VALUE_MEMBER(data, temp->userstatus); data += 3; // padding - VALUE_MEMBER(friendrequest_nospam); - ARRAY_MEMBER(last_seen_time); + VALUE_MEMBER(data, temp->friendrequest_nospam); + ARRAY_MEMBER(data, temp->last_seen_time); #undef VALUE_MEMBER #undef ARRAY_MEMBER @@ -2797,6 +2643,7 @@ static const uint8_t *friend_load(struct Saved_Friend *temp, const uint8_t *data } +non_null() static uint32_t m_state_plugins_size(const Messenger *m) { const uint32_t size32 = sizeof(uint32_t); @@ -2813,19 +2660,19 @@ static uint32_t m_state_plugins_size(const Messenger *m) return size; } -/** Registers a state plugin for saving, loadding, and getting the size of a section of the save +/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save. * - * returns true on success - * returns false on error + * @retval true on success + * @retval false on error */ -bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size_callback, - m_state_load_cb load_callback, - m_state_save_cb save_callback) +bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb *size_callback, + m_state_load_cb *load_callback, + m_state_save_cb *save_callback) { Messenger_State_Plugin *temp = (Messenger_State_Plugin *)realloc(m->options.state_plugins, sizeof(Messenger_State_Plugin) * (m->options.state_plugins_length + 1)); - if (!temp) { + if (temp == nullptr) { return false; } @@ -2841,6 +2688,7 @@ bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size return true; } +non_null() static uint32_t m_plugin_size(const Messenger *m, State_Type type) { for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) { @@ -2862,7 +2710,7 @@ uint32_t messenger_size(const Messenger *m) return m_state_plugins_size(m); } -/** Save the messenger in data (must be allocated memory of size at least Messenger_size()) */ +/** Save the messenger in data (must be allocated memory of size at least `Messenger_size()`) */ uint8_t *messenger_save(const Messenger *m, uint8_t *data) { for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) { @@ -2874,11 +2722,13 @@ uint8_t *messenger_save(const Messenger *m, uint8_t *data) } // nospam state plugin +non_null() static uint32_t nospam_keys_size(const Messenger *m) { return sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE; } +non_null() static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uint32_t length) { if (length != m_plugin_size(m, STATE_TYPE_NOSPAMKEYS)) { @@ -2890,19 +2740,20 @@ static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uin set_nospam(m->fr, nospam); load_secret_key(m->net_crypto, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE); - if (public_key_cmp(data + sizeof(uint32_t), nc_get_self_public_key(m->net_crypto)) != 0) { + if (!pk_equal(data + sizeof(uint32_t), nc_get_self_public_key(m->net_crypto))) { return STATE_LOAD_STATUS_ERROR; } return STATE_LOAD_STATUS_CONTINUE; } +non_null() static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_NOSPAMKEYS); - assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t)); + static_assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t), "nospam doesn't fit in a 32 bit int"); data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NOSPAMKEYS); - uint32_t nospam = get_nospam(m->fr); + const uint32_t nospam = get_nospam(m->fr); host_to_lendian_bytes32(data, nospam); save_keys(m->net_crypto, data + sizeof(uint32_t)); data += len; @@ -2910,11 +2761,13 @@ static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data) } // DHT state plugin +non_null() static uint32_t m_dht_size(const Messenger *m) { return dht_size(m->dht); } +non_null() static uint8_t *save_dht(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_DHT); @@ -2924,6 +2777,7 @@ static uint8_t *save_dht(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status m_dht_load(Messenger *m, const uint8_t *data, uint32_t length) { dht_load(m->dht, data, length); // TODO(endoffile78): Should we throw an error if dht_load fails? @@ -2931,11 +2785,13 @@ static State_Load_Status m_dht_load(Messenger *m, const uint8_t *data, uint32_t } // friendlist state plugin +non_null() static uint32_t saved_friendslist_size(const Messenger *m) { return count_friendlist(m) * friend_size(); } +non_null() static uint8_t *friends_list_save(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_FRIENDS); @@ -2986,6 +2842,7 @@ static uint8_t *friends_list_save(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, uint32_t length) { const uint32_t l_friend_size = friend_size(); @@ -2994,7 +2851,7 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui return STATE_LOAD_STATUS_ERROR; // TODO(endoffile78): error or continue? } - uint32_t num = length / l_friend_size; + const uint32_t num = length / l_friend_size; const uint8_t *cur_data = data; for (uint32_t i = 0; i < num; ++i) { @@ -3005,7 +2862,7 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui cur_data = next_data; if (temp.status >= 3) { - int fnum = m_addfriend_norequest(m, temp.real_pk); + const int fnum = m_addfriend_norequest(m, temp.real_pk); if (fnum < 0) { continue; @@ -3018,9 +2875,9 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui } else if (temp.status != 0) { /* TODO(irungentoo): This is not a good way to do this. */ uint8_t address[FRIEND_ADDRESS_SIZE]; - id_copy(address, temp.real_pk); + pk_copy(address, temp.real_pk); memcpy(address + CRYPTO_PUBLIC_KEY_SIZE, &temp.friendrequest_nospam, sizeof(uint32_t)); - uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); + uint16_t checksum = data_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); memcpy(address + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), &checksum, sizeof(checksum)); m_addfriend(m, address, temp.info, net_ntohs(temp.info_size)); } @@ -3030,11 +2887,13 @@ static State_Load_Status friends_list_load(Messenger *m, const uint8_t *data, ui } // name state plugin +non_null() static uint32_t name_size(const Messenger *m) { return m->name_length; } +non_null() static uint8_t *save_name(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_NAME); @@ -3044,6 +2903,7 @@ static uint8_t *save_name(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status load_name(Messenger *m, const uint8_t *data, uint32_t length) { if (length > 0 && length <= MAX_NAME_LENGTH) { @@ -3054,11 +2914,13 @@ static State_Load_Status load_name(Messenger *m, const uint8_t *data, uint32_t l } // status message state plugin +non_null() static uint32_t status_message_size(const Messenger *m) { return m->statusmessage_length; } +non_null() static uint8_t *save_status_message(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUSMESSAGE); @@ -3068,6 +2930,7 @@ static uint8_t *save_status_message(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status load_status_message(Messenger *m, const uint8_t *data, uint32_t length) { if (length > 0 && length <= MAX_STATUSMESSAGE_LENGTH) { @@ -3078,11 +2941,13 @@ static State_Load_Status load_status_message(Messenger *m, const uint8_t *data, } // status state plugin +non_null() static uint32_t status_size(const Messenger *m) { return 1; } +non_null() static uint8_t *save_status(const Messenger *m, uint8_t *data) { const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUS); @@ -3092,6 +2957,7 @@ static uint8_t *save_status(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status load_status(Messenger *m, const uint8_t *data, uint32_t length) { if (length == 1) { @@ -3102,14 +2968,16 @@ static State_Load_Status load_status(Messenger *m, const uint8_t *data, uint32_t } // TCP Relay state plugin +non_null() static uint32_t tcp_relay_size(const Messenger *m) { - return NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6); + return NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6()); } +non_null() static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data) { - Node_format relays[NUM_SAVED_TCP_RELAYS] = {0}; + Node_format relays[NUM_SAVED_TCP_RELAYS] = {{{0}}}; uint8_t *temp_data = data; data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, 0, STATE_TYPE_TCP_RELAY); @@ -3120,7 +2988,7 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data) uint32_t num = m->num_loaded_relays; num += copy_connected_tcp_relays(m->net_crypto, relays + num, NUM_SAVED_TCP_RELAYS - num); - int l = pack_nodes(data, NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6), relays, num); + const int l = pack_nodes(m->log, data, NUM_SAVED_TCP_RELAYS * packed_node_size(net_family_tcp_ipv6()), relays, num); if (l > 0) { const uint32_t len = l; @@ -3131,10 +2999,11 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status load_tcp_relays(Messenger *m, const uint8_t *data, uint32_t length) { - if (length != 0) { - const int num = unpack_nodes(m->loaded_relays, NUM_SAVED_TCP_RELAYS, nullptr, data, length, 1); + if (length > 0) { + const int num = unpack_nodes(m->loaded_relays, NUM_SAVED_TCP_RELAYS, nullptr, data, length, true); if (num == -1) { m->num_loaded_relays = 0; @@ -3149,11 +3018,13 @@ static State_Load_Status load_tcp_relays(Messenger *m, const uint8_t *data, uint } // path node state plugin +non_null() static uint32_t path_node_size(const Messenger *m) { - return NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6); + return NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6()); } +non_null() static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data) { Node_format nodes[NUM_SAVED_PATH_NODES]; @@ -3161,7 +3032,7 @@ static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data) data = state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_PATH_NODE); memset(nodes, 0, sizeof(nodes)); const unsigned int num = onion_backup_nodes(m->onion_c, nodes, NUM_SAVED_PATH_NODES); - const int l = pack_nodes(data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6), nodes, num); + const int l = pack_nodes(m->log, data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6()), nodes, num); if (l > 0) { const uint32_t len = l; @@ -3172,12 +3043,12 @@ static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data) return data; } +non_null() static State_Load_Status load_path_nodes(Messenger *m, const uint8_t *data, uint32_t length) { - Node_format nodes[NUM_SAVED_PATH_NODES]; - - if (length != 0) { - const int num = unpack_nodes(nodes, NUM_SAVED_PATH_NODES, nullptr, data, length, 0); + if (length > 0) { + Node_format nodes[NUM_SAVED_PATH_NODES]; + const int num = unpack_nodes(nodes, NUM_SAVED_PATH_NODES, nullptr, data, length, false); if (num == -1) { return STATE_LOAD_STATUS_CONTINUE; @@ -3191,6 +3062,7 @@ static State_Load_Status load_path_nodes(Messenger *m, const uint8_t *data, uint return STATE_LOAD_STATUS_CONTINUE; } +non_null() static void m_register_default_plugins(Messenger *m) { m_register_state_plugin(m, STATE_TYPE_NOSPAMKEYS, nospam_keys_size, load_nospam_keys, save_nospam_keys); @@ -3219,9 +3091,11 @@ bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t le return false; } -/** Return the number of friends in the instance m. +/** @brief Return the number of friends in the instance m. + * * You should use this to determine how much memory to allocate - * for copy_friendlist. */ + * for copy_friendlist. + */ uint32_t count_friendlist(const Messenger *m) { uint32_t ret = 0; @@ -3235,14 +3109,15 @@ uint32_t count_friendlist(const Messenger *m) return ret; } -/** Copy a list of valid friend IDs into the array out_list. +/** @brief Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. * If the array was too small, the contents - * of out_list will be truncated to list_size. */ + * of out_list will be truncated to list_size. + */ uint32_t copy_friendlist(Messenger const *m, uint32_t *out_list, uint32_t list_size) { - if (!out_list) { + if (out_list == nullptr) { return 0; } @@ -3265,3 +3140,240 @@ uint32_t copy_friendlist(Messenger const *m, uint32_t *out_list, uint32_t list_s return ret; } + +static fr_friend_request_cb m_handle_friend_request; +non_null(1, 2, 3) nullable(5) +static void m_handle_friend_request( + void *object, const uint8_t *public_key, const uint8_t *message, size_t length, void *user_data) +{ + Messenger *m = (Messenger *)object; + assert(m != nullptr); + m->friend_request(m, public_key, message, length, user_data); +} + +/** @brief Run this at startup. + * + * @return allocated instance of Messenger on success. + * @retval 0 if there are problems. + * + * if error is not NULL it will be set to one of the values in the enum above. + */ +Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network *ns, Messenger_Options *options, Messenger_Error *error) +{ + if (options == nullptr) { + return nullptr; + } + + if (error != nullptr) { + *error = MESSENGER_ERROR_OTHER; + } + + Messenger *m = (Messenger *)calloc(1, sizeof(Messenger)); + + if (m == nullptr) { + return nullptr; + } + + m->mono_time = mono_time; + m->rng = rng; + m->ns = ns; + + m->fr = friendreq_new(); + + if (m->fr == nullptr) { + free(m); + return nullptr; + } + + m->log = logger_new(); + + if (m->log == nullptr) { + friendreq_kill(m->fr); + free(m); + return nullptr; + } + + logger_callback_log(m->log, options->log_callback, options->log_context, options->log_user_data); + + unsigned int net_err = 0; + + if (!options->udp_disabled && options->proxy_info.proxy_type != TCP_PROXY_NONE) { + // We don't currently support UDP over proxy. + LOGGER_INFO(m->log, "UDP enabled and proxy set: disabling UDP"); + options->udp_disabled = true; + } + + if (options->udp_disabled) { + m->net = new_networking_no_udp(m->log, m->ns); + } else { + IP ip; + ip_init(&ip, options->ipv6enabled); + m->net = new_networking_ex(m->log, m->ns, &ip, options->port_range[0], options->port_range[1], &net_err); + } + + if (m->net == nullptr) { + friendreq_kill(m->fr); + logger_kill(m->log); + free(m); + + if (error != nullptr && net_err == 1) { + *error = MESSENGER_ERROR_PORT; + } + + return nullptr; + } + + m->dht = new_dht(m->log, m->rng, m->ns, m->mono_time, m->net, options->hole_punching_enabled, options->local_discovery_enabled); + + if (m->dht == nullptr) { + kill_networking(m->net); + friendreq_kill(m->fr); + logger_kill(m->log); + free(m); + return nullptr; + } + + m->net_crypto = new_net_crypto(m->log, m->rng, m->ns, m->mono_time, m->dht, &options->proxy_info); + + if (m->net_crypto == nullptr) { + kill_dht(m->dht); + kill_networking(m->net); + friendreq_kill(m->fr); + logger_kill(m->log); + free(m); + return nullptr; + } + + if (options->dht_announcements_enabled) { + m->forwarding = new_forwarding(m->log, m->rng, m->mono_time, m->dht); + m->announce = new_announcements(m->log, m->rng, m->mono_time, m->forwarding); + } else { + m->forwarding = nullptr; + m->announce = nullptr; + } + + m->onion = new_onion(m->log, m->mono_time, m->rng, m->dht); + m->onion_a = new_onion_announce(m->log, m->rng, m->mono_time, m->dht); + m->onion_c = new_onion_client(m->log, m->rng, m->mono_time, m->net_crypto); + m->fr_c = new_friend_connections(m->log, m->mono_time, m->ns, m->onion_c, options->local_discovery_enabled); + + if ((options->dht_announcements_enabled && (m->forwarding == nullptr || m->announce == nullptr)) || + m->onion == nullptr || m->onion_a == nullptr || m->onion_c == nullptr || m->fr_c == nullptr) { + kill_friend_connections(m->fr_c); + kill_onion(m->onion); + kill_onion_announce(m->onion_a); + kill_onion_client(m->onion_c); + kill_announcements(m->announce); + kill_forwarding(m->forwarding); + kill_net_crypto(m->net_crypto); + kill_dht(m->dht); + kill_networking(m->net); + friendreq_kill(m->fr); + logger_kill(m->log); + free(m); + return nullptr; + } + + if (options->tcp_server_port != 0) { + m->tcp_server = new_TCP_server(m->log, m->rng, m->ns, options->ipv6enabled, 1, &options->tcp_server_port, + dht_get_self_secret_key(m->dht), m->onion, m->forwarding); + + if (m->tcp_server == nullptr) { + kill_friend_connections(m->fr_c); + kill_onion(m->onion); + kill_onion_announce(m->onion_a); + kill_onion_client(m->onion_c); + kill_announcements(m->announce); + kill_forwarding(m->forwarding); + kill_net_crypto(m->net_crypto); + kill_dht(m->dht); + kill_networking(m->net); + friendreq_kill(m->fr); + logger_kill(m->log); + free(m); + + if (error != nullptr) { + *error = MESSENGER_ERROR_TCP_SERVER; + } + + return nullptr; + } + } + + m->options = *options; + friendreq_init(m->fr, m->fr_c); + set_nospam(m->fr, random_u32(m->rng)); + set_filter_function(m->fr, &friend_already_added, m); + + m->lastdump = 0; + m->is_receiving_file = 0; + + m_register_default_plugins(m); + callback_friendrequest(m->fr, m_handle_friend_request, m); + + if (error != nullptr) { + *error = MESSENGER_ERROR_NONE; + } + + return m; +} + +/** @brief Run this before closing shop. + * + * Free all datastructures. + */ +void kill_messenger(Messenger *m) +{ + if (m == nullptr) { + return; + } + + if (m->tcp_server != nullptr) { + kill_TCP_server(m->tcp_server); + } + + kill_friend_connections(m->fr_c); + kill_onion(m->onion); + kill_onion_announce(m->onion_a); + kill_onion_client(m->onion_c); + kill_announcements(m->announce); + kill_forwarding(m->forwarding); + kill_net_crypto(m->net_crypto); + kill_dht(m->dht); + kill_networking(m->net); + + for (uint32_t i = 0; i < m->numfriends; ++i) { + clear_receipts(m, i); + } + + logger_kill(m->log); + free(m->friendlist); + friendreq_kill(m->fr); + + free(m->options.state_plugins); + free(m); +} + +bool m_is_receiving_file(Messenger *m) +{ + // Only run the expensive loop below once every 64 tox_iterate calls. + const uint8_t skip_count = 64; + + if (m->is_receiving_file != 0) { + --m->is_receiving_file; + return true; + } + + // TODO(iphydf): This is a very expensive loop. Consider keeping track of + // the number of live file transfers. + for (size_t friend_number = 0; friend_number < m->numfriends; ++friend_number) { + for (size_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) { + if (m->friendlist[friend_number].file_receiving[i].status == FILESTATUS_TRANSFERRING) { + m->is_receiving_file = skip_count; + return true; + } + } + } + + return false; +} diff --git a/protocols/Tox/libtox/src/toxcore/Messenger.h b/protocols/Tox/libtox/src/toxcore/Messenger.h index 477b997391..4abd7905d0 100644 --- a/protocols/Tox/libtox/src/toxcore/Messenger.h +++ b/protocols/Tox/libtox/src/toxcore/Messenger.h @@ -11,6 +11,8 @@ #define C_TOXCORE_TOXCORE_MESSENGER_H #include "TCP_server.h" +#include "announce.h" +#include "forwarding.h" #include "friend_connection.h" #include "friend_requests.h" #include "logger.h" @@ -60,6 +62,7 @@ typedef struct Messenger_Options { bool hole_punching_enabled; bool local_discovery_enabled; + bool dht_announcements_enabled; logger_cb *log_callback; void *log_context; @@ -85,7 +88,8 @@ typedef enum Friend_Status { FRIEND_ONLINE, } Friend_Status; -/** Errors for m_addfriend +/** @brief Errors for m_addfriend + * * FAERR - Friend Add Error */ typedef enum Friend_Add_Error { @@ -108,10 +112,9 @@ typedef enum Connection_Status { CONNECTION_UDP, } Connection_Status; -/** USERSTATUS - +/** * Represents userstatuses someone can have. */ - typedef enum Userstatus { USERSTATUS_NONE, USERSTATUS_AWAY, @@ -158,7 +161,7 @@ typedef enum Filekind { } Filekind; -typedef void m_self_connection_status_cb(Messenger *m, unsigned int connection_status, void *user_data); +typedef void m_self_connection_status_cb(Messenger *m, Onion_Connection_Status connection_status, void *user_data); typedef void m_friend_status_cb(Messenger *m, uint32_t friend_number, unsigned int status, void *user_data); typedef void m_friend_connection_status_cb(Messenger *m, uint32_t friend_number, unsigned int connection_status, void *user_data); @@ -207,15 +210,15 @@ typedef struct Friend { uint8_t info[MAX_FRIEND_REQUEST_DATA_SIZE]; // the data that is sent during the friend requests we do. uint8_t name[MAX_NAME_LENGTH]; uint16_t name_length; - uint8_t name_sent; // 0 if we didn't send our name to this friend 1 if we have. + bool name_sent; // false if we didn't send our name to this friend, true if we have. uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH]; uint16_t statusmessage_length; - uint8_t statusmessage_sent; + bool statusmessage_sent; Userstatus userstatus; - uint8_t userstatus_sent; - uint8_t user_istyping; - uint8_t user_istyping_sent; - uint8_t is_typing; + bool userstatus_sent; + bool user_istyping; + bool user_istyping_sent; + bool is_typing; uint16_t info_size; // Length of the info. uint32_t message_id; // a semi-unique id used in read receipts. uint32_t friendrequest_nospam; // The nospam number used in the friend request. @@ -234,11 +237,16 @@ typedef struct Friend { struct Messenger { Logger *log; Mono_Time *mono_time; + const Random *rng; + const Network *ns; Networking_Core *net; Net_Crypto *net_crypto; DHT *dht; + Forwarding *forwarding; + Announcements *announce; + Onion *onion; Onion_Announce *onion_a; Onion_Client *onion_c; @@ -258,13 +266,15 @@ struct Messenger { Friend *friendlist; uint32_t numfriends; - time_t lastdump; + uint64_t lastdump; + uint8_t is_receiving_file; bool has_added_relays; // If the first connection has occurred in do_messenger uint16_t num_loaded_relays; Node_format loaded_relays[NUM_SAVED_TCP_RELAYS]; // Relays loaded from config + m_friend_request_cb *friend_request; m_friend_message_cb *friend_message; m_friend_name_cb *friend_namechange; m_friend_status_message_cb *friend_statusmessagechange; @@ -290,15 +300,17 @@ struct Messenger { m_friend_lossless_packet_cb *lossless_packethandler; m_self_connection_status_cb *core_connection_change; - unsigned int last_connection_status; + Onion_Connection_Status last_connection_status; Messenger_Options options; }; -/** Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]` +/** + * Format: `[real_pk (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]` * - * return FRIEND_ADDRESS_SIZE byte address to give to others. + * @param[out] address FRIEND_ADDRESS_SIZE byte address to give to others. */ +non_null() void getaddress(const Messenger *m, uint8_t *address); /** @@ -312,365 +324,391 @@ void getaddress(const Messenger *m, uint8_t *address); * @param data is the data. * @param length is the length. * - * return the friend number if success. - * return FA_TOOLONG if message length is too long. - * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte). - * return FAERR_OWNKEY if user's own key. - * return FAERR_ALREADYSENT if friend request already sent or already a friend. - * return FAERR_BADCHECKSUM if bad checksum in address. - * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different. - * (the nospam for that friend was set to the new one). - * return FAERR_NOMEM if increasing the friend list size fails. - */ + * @return the friend number if success. + * @retval FA_TOOLONG if message length is too long. + * @retval FAERR_NOMESSAGE if no message (message length must be >= 1 byte). + * @retval FAERR_OWNKEY if user's own key. + * @retval FAERR_ALREADYSENT if friend request already sent or already a friend. + * @retval FAERR_BADCHECKSUM if bad checksum in address. + * @retval FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different. + * (the nospam for that friend was set to the new one). + * @retval FAERR_NOMEM if increasing the friend list size fails. + */ +non_null() int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, uint16_t length); -/** Add a friend without sending a friendrequest. - * return the friend number if success. - * return -3 if user's own key. - * return -4 if friend request already sent or already a friend. - * return -6 if bad checksum in address. - * return -8 if increasing the friend list size fails. +/** @brief Add a friend without sending a friendrequest. + * @return the friend number if success. + * @retval -3 if user's own key. + * @retval -4 if friend request already sent or already a friend. + * @retval -6 if bad checksum in address. + * @retval -8 if increasing the friend list size fails. */ +non_null() int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk); -/** return the friend number associated to that public key. - * return -1 if no such friend. +/** @return the friend number associated to that public key. + * @retval -1 if no such friend. */ +non_null() int32_t getfriend_id(const Messenger *m, const uint8_t *real_pk); -/** Copies the public key associated to that friend id into real_pk buffer. +/** @brief Copies the public key associated to that friend id into real_pk buffer. + * * Make sure that real_pk is of size CRYPTO_PUBLIC_KEY_SIZE. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ +non_null() int get_real_pk(const Messenger *m, int32_t friendnumber, uint8_t *real_pk); -/** return friend connection id on success. - * return -1 if failure. +/** @return friend connection id on success. + * @retval -1 if failure. */ +non_null() int getfriendcon_id(const Messenger *m, int32_t friendnumber); -/** Remove a friend. +/** @brief Remove a friend. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ +non_null() int m_delfriend(Messenger *m, int32_t friendnumber); -/** Checks friend's connection status. +/** @brief Checks friend's connection status. * - * return CONNECTION_UDP (2) if friend is directly connected to us (Online UDP). - * return CONNECTION_TCP (1) if friend is connected to us (Online TCP). - * return CONNECTION_NONE (0) if friend is not connected to us (Offline). - * return -1 on failure. + * @retval CONNECTION_UDP (2) if friend is directly connected to us (Online UDP). + * @retval CONNECTION_TCP (1) if friend is connected to us (Online TCP). + * @retval CONNECTION_NONE (0) if friend is not connected to us (Offline). + * @retval -1 on failure. */ +non_null() int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber); -/** Checks if there exists a friend with given friendnumber. +/** + * Checks if there exists a friend with given friendnumber. + * + * @param friendnumber The index in the friend list. * - * return 1 if friend exists. - * return 0 if friend doesn't exist. + * @retval true if friend exists. + * @retval false if friend doesn't exist. */ -int m_friend_exists(const Messenger *m, int32_t friendnumber); +non_null() +bool m_friend_exists(const Messenger *m, int32_t friendnumber); -/** Send a message of type to an online friend. +/** @brief Send a message of type to an online friend. * - * return -1 if friend not valid. - * return -2 if too large. - * return -3 if friend not online. - * return -4 if send failed (because queue is full). - * return -5 if bad type. - * return 0 if success. + * @retval -1 if friend not valid. + * @retval -2 if too large. + * @retval -3 if friend not online. + * @retval -4 if send failed (because queue is full). + * @retval -5 if bad type. + * @retval 0 if success. * - * the value in message_id will be passed to your read_receipt callback when the other receives the message. + * The value in message_id will be passed to your read_receipt callback when the other receives the message. */ +non_null(1, 4) nullable(6) int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length, uint32_t *message_id); -/** Set the name and name_length of a friend. +/** @brief Set the name and name_length of a friend. + * * name must be a string of maximum MAX_NAME_LENGTH length. * length must be at least 1 byte. * length is the length of name with the NULL terminator. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ +non_null() int setfriendname(Messenger *m, int32_t friendnumber, const uint8_t *name, uint16_t length); -/** Set our nickname. +/** @brief Set our nickname. + * * name must be a string of maximum MAX_NAME_LENGTH length. * length must be at least 1 byte. * length is the length of name with the NULL terminator. * - * return 0 if success. - * return -1 if failure. + * @retval 0 if success. + * @retval -1 if failure. */ +non_null() int setname(Messenger *m, const uint8_t *name, uint16_t length); /** - * Get your nickname. + * @brief Get your nickname. + * * m - The messenger context to use. * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. * - * return length of the name. - * return 0 on error. + * @return length of the name. + * @retval 0 on error. */ +non_null() uint16_t getself_name(const Messenger *m, uint8_t *name); -/** Get name of friendnumber and put it in name. +/** @brief Get name of friendnumber and put it in name. + * * name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. * - * return length of name if success. - * return -1 if failure. + * @return length of name if success. + * @retval -1 if failure. */ +non_null() int getname(const Messenger *m, int32_t friendnumber, uint8_t *name); -/** return the length of name, including null on success. - * return -1 on failure. +/** @return the length of name, including null on success. + * @retval -1 on failure. */ -int m_get_name_size(const Messenger *m, int32_t friendnumber); -int m_get_self_name_size(const Messenger *m); +non_null() int m_get_name_size(const Messenger *m, int32_t friendnumber); +non_null() int m_get_self_name_size(const Messenger *m); -/** Set our user status. +/** @brief Set our user status. * You are responsible for freeing status after. * - * returns 0 on success. - * returns -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ -int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length); -int m_set_userstatus(Messenger *m, uint8_t status); +non_null() int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length); +non_null() int m_set_userstatus(Messenger *m, uint8_t status); /** * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. * - * returns the length of friendnumber's status message, including null on success. - * returns -1 on failure. + * @return the length of friendnumber's status message, including null on success. + * @retval -1 on failure. */ -int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber); -int m_get_self_statusmessage_size(const Messenger *m); +non_null() int m_get_statusmessage_size(const Messenger *m, int32_t friendnumber); +non_null() int m_get_self_statusmessage_size(const Messenger *m); -/** Copy friendnumber's status message into buf, truncating if size is over maxlen. +/** @brief Copy friendnumber's status message into buf, truncating if size is over maxlen. + * * Get the size you need to allocate from m_get_statusmessage_size. * The self variant will copy our own status message. * - * returns the length of the copied data on success - * returns -1 on failure. + * @return the length of the copied data on success + * @retval -1 on failure. */ -int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen); -int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf); +non_null() int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen); +non_null() int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf); -/** return one of Userstatus values. - * Values unknown to your application should be represented as USERSTATUS_NONE. - * As above, the self variant will return our own Userstatus. - * If friendnumber is invalid, this shall return USERSTATUS_INVALID. +/** @brief return one of Userstatus values. + * + * Values unknown to your application should be represented as USERSTATUS_NONE. + * As above, the self variant will return our own Userstatus. + * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */ -uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber); -uint8_t m_get_self_userstatus(const Messenger *m); +non_null() uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber); +non_null() uint8_t m_get_self_userstatus(const Messenger *m); -/** returns timestamp of last time friendnumber was seen online or 0 if never seen. +/** @brief returns timestamp of last time friendnumber was seen online or 0 if never seen. * if friendnumber is invalid this function will return UINT64_MAX. */ -uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber); +non_null() uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber); -/** Set our typing status for a friend. +/** @brief Set our typing status for a friend. * You are responsible for turning it on or off. * - * returns 0 on success. - * returns -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ -int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing); +non_null() +int m_set_usertyping(Messenger *m, int32_t friendnumber, bool is_typing); -/** Get the typing status of a friend. +/** @brief Get the typing status of a friend. * - * returns 0 if friend is not typing. - * returns 1 if friend is typing. + * @retval -1 if friend number is invalid. + * @retval 0 if friend is not typing. + * @retval 1 if friend is typing. */ +non_null() int m_get_istyping(const Messenger *m, int32_t friendnumber); /** Set the function that will be executed when a friend request is received. */ +non_null(1) nullable(2) void m_callback_friendrequest(Messenger *m, m_friend_request_cb *function); /** Set the function that will be executed when a message from a friend is received. */ -void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function); +non_null() void m_callback_friendmessage(Messenger *m, m_friend_message_cb *function); -/** Set the callback for name changes. - * You are not responsible for freeing newname. +/** @brief Set the callback for name changes. + * You are not responsible for freeing newname. */ -void m_callback_namechange(Messenger *m, m_friend_name_cb *function); +non_null() void m_callback_namechange(Messenger *m, m_friend_name_cb *function); -/** Set the callback for status message changes. +/** @brief Set the callback for status message changes. * - * You are not responsible for freeing newstatus + * You are not responsible for freeing newstatus */ -void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function); +non_null() void m_callback_statusmessage(Messenger *m, m_friend_status_message_cb *function); -/** Set the callback for status type changes. - */ -void m_callback_userstatus(Messenger *m, m_friend_status_cb *function); +/** @brief Set the callback for status type changes. */ +non_null() void m_callback_userstatus(Messenger *m, m_friend_status_cb *function); -/** Set the callback for typing changes. - */ -void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function); +/** @brief Set the callback for typing changes. */ +non_null() void m_callback_typingchange(Messenger *m, m_friend_typing_cb *function); -/** Set the callback for read receipts. +/** @brief Set the callback for read receipts. * - * If you are keeping a record of returns from m_sendmessage, - * receipt might be one of those values, meaning the message - * has been received on the other side. - * Since core doesn't track ids for you, receipt may not correspond to any message. - * In that case, you should discard it. + * If you are keeping a record of returns from m_sendmessage, + * receipt might be one of those values, meaning the message + * has been received on the other side. + * Since core doesn't track ids for you, receipt may not correspond to any message. + * In that case, you should discard it. */ -void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function); +non_null() void m_callback_read_receipt(Messenger *m, m_friend_read_receipt_cb *function); -/** Set the callback for connection status changes. +/** @brief Set the callback for connection status changes. * - * Status: - * 0 -- friend went offline after being previously online. - * 1 -- friend went online. + * Status: + * - 0: friend went offline after being previously online. + * - 1: friend went online. * - * Note that this callback is not called when adding friends, thus the - * "after being previously online" part. - * It's assumed that when adding friends, their connection status is offline. + * Note that this callback is not called when adding friends, thus the + * "after being previously online" part. + * It's assumed that when adding friends, their connection status is offline. */ -void m_callback_connectionstatus(Messenger *m, m_friend_connection_status_cb *function); +non_null() void m_callback_connectionstatus(Messenger *m, m_friend_connection_status_cb *function); /** Same as previous but for internal A/V core usage only */ -void m_callback_connectionstatus_internal_av(Messenger *m, m_friend_connectionstatuschange_internal_cb *function, - void *userdata); +non_null() void m_callback_connectionstatus_internal_av( + Messenger *m, m_friend_connectionstatuschange_internal_cb *function, void *userdata); -/** Set the callback for typing changes. - */ -void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function); +/** @brief Set the callback for typing changes. */ +non_null() void m_callback_core_connection(Messenger *m, m_self_connection_status_cb *function); /*** CONFERENCES */ -/** Set the callback for conference invites. - */ +/** @brief Set the callback for conference invites. */ +non_null(1) nullable(2) void m_callback_conference_invite(Messenger *m, m_conference_invite_cb *function); -/** Send a conference invite packet. +/** @brief Send a conference invite packet. * - * return 1 on success - * return 0 on failure + * return true on success + * return false on failure */ -int send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length); +non_null() +bool send_conference_invite_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length); /*** FILE SENDING */ -/** Set the callback for file send requests. - */ -void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function); +/** @brief Set the callback for file send requests. */ +non_null() void callback_file_sendrequest(Messenger *m, m_file_recv_cb *function); -/** Set the callback for file control requests. - */ -void callback_file_control(Messenger *m, m_file_recv_control_cb *function); +/** @brief Set the callback for file control requests. */ +non_null() void callback_file_control(Messenger *m, m_file_recv_control_cb *function); -/** Set the callback for file data. - */ -void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function); +/** @brief Set the callback for file data. */ +non_null() void callback_file_data(Messenger *m, m_file_recv_chunk_cb *function); -/** Set the callback for file request chunk. - */ -void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function); +/** @brief Set the callback for file request chunk. */ +non_null() void callback_file_reqchunk(Messenger *m, m_file_chunk_request_cb *function); -/** Copy the file transfer file id to file_id +/** @brief Copy the file transfer file id to file_id * - * return 0 on success. - * return -1 if friend not valid. - * return -2 if filenumber not valid + * @retval 0 on success. + * @retval -1 if friend not valid. + * @retval -2 if filenumber not valid */ +non_null() int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id); -/** Send a file send request. +/** @brief Send a file send request. + * * Maximum filename length is 255 bytes. - * return file number on success - * return -1 if friend not found. - * return -2 if filename length invalid. - * return -3 if no more file sending slots left. - * return -4 if could not send packet (friend offline). * + * @return file number on success + * @retval -1 if friend not found. + * @retval -2 if filename length invalid. + * @retval -3 if no more file sending slots left. + * @retval -4 if could not send packet (friend offline). */ +non_null() long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length); -/** Send a file control request. +/** @brief Send a file control request. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if file number invalid. - * return -4 if file control is bad. - * return -5 if file already paused. - * return -6 if resume file failed because it was only paused by the other. - * return -7 if resume file failed because it wasn't paused. - * return -8 if packet failed to send. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if file number invalid. + * @retval -4 if file control is bad. + * @retval -5 if file already paused. + * @retval -6 if resume file failed because it was only paused by the other. + * @retval -7 if resume file failed because it wasn't paused. + * @retval -8 if packet failed to send. */ +non_null() int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control); -/** Send a seek file control request. +/** @brief Send a seek file control request. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if file number invalid. - * return -4 if not receiving file. - * return -5 if file status wrong. - * return -6 if position bad. - * return -8 if packet failed to send. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if file number invalid. + * @retval -4 if not receiving file. + * @retval -5 if file status wrong. + * @retval -6 if position bad. + * @retval -8 if packet failed to send. */ +non_null() int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position); -/** Send file data. +/** @brief Send file data. * - * return 0 on success - * return -1 if friend not valid. - * return -2 if friend not online. - * return -3 if filenumber invalid. - * return -4 if file transfer not transferring. - * return -5 if bad data size. - * return -6 if packet queue full. - * return -7 if wrong position. + * @retval 0 on success + * @retval -1 if friend not valid. + * @retval -2 if friend not online. + * @retval -3 if filenumber invalid. + * @retval -4 if file transfer not transferring. + * @retval -5 if bad data size. + * @retval -6 if packet queue full. + * @retval -7 if wrong position. */ -int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, - uint16_t length); +non_null(1) nullable(5) +int send_file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, uint16_t length); /*** A/V related */ -/** Set the callback for msi packets. - */ +/** @brief Set the callback for msi packets. */ +non_null(1) nullable(2, 3) void m_callback_msi_packet(Messenger *m, m_msi_packet_cb *function, void *userdata); -/** Send an msi packet. +/** @brief Send an msi packet. * - * return 1 on success - * return 0 on failure + * @retval true on success + * @retval false on failure */ -int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length); +non_null() +bool m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length); -/** Set handlers for lossy rtp packets. +/** @brief Set handlers for lossy rtp packets. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null(1) nullable(4, 5) int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, m_lossy_rtp_packet_cb *function, void *object); /*** CUSTOM PACKETS */ -/** Set handlers for custom lossy packets. - * - */ -void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler); +/** @brief Set handlers for custom lossy packets. */ +non_null() void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb *lossy_packethandler); -/** High level function to send custom lossy packets. +/** @brief High level function to send custom lossy packets. * * TODO(oxij): this name is confusing, because this function sends both av and custom lossy packets. * Meanwhile, m_handle_lossy_packet routes custom packets to custom_lossy_packet_registerhandler @@ -679,30 +717,31 @@ void custom_lossy_packet_registerhandler(Messenger *m, m_friend_lossy_packet_cb * I.e. custom_lossy_packet_registerhandler's "custom lossy packet" and this "custom lossy packet" * are not the same set of packets. * - * return -1 if friend invalid. - * return -2 if length wrong. - * return -3 if first byte invalid. - * return -4 if friend offline. - * return -5 if packet failed to send because of other error. - * return 0 on success. + * @retval -1 if friend invalid. + * @retval -2 if length wrong. + * @retval -3 if first byte invalid. + * @retval -4 if friend offline. + * @retval -5 if packet failed to send because of other error. + * @retval 0 on success. */ +non_null() int m_send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length); -/** Set handlers for custom lossless packets. - * - */ +/** @brief Set handlers for custom lossless packets. */ +non_null() void custom_lossless_packet_registerhandler(Messenger *m, m_friend_lossless_packet_cb *lossless_packethandler); -/** High level function to send custom lossless packets. +/** @brief High level function to send custom lossless packets. * - * return -1 if friend invalid. - * return -2 if length wrong. - * return -3 if first byte invalid. - * return -4 if friend offline. - * return -5 if packet failed to send because of other error. - * return 0 on success. + * @retval -1 if friend invalid. + * @retval -2 if length wrong. + * @retval -3 if first byte invalid. + * @retval -4 if friend offline. + * @retval -5 if packet failed to send because of other error. + * @retval 0 on success. */ +non_null() int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length); /*** Messenger constructor/destructor/operations. */ @@ -714,46 +753,58 @@ typedef enum Messenger_Error { MESSENGER_ERROR_OTHER, } Messenger_Error; -/** Run this at startup. - * return allocated instance of Messenger on success. - * return 0 if there are problems. +/** @brief Run this at startup. + * + * @return allocated instance of Messenger on success. + * @retval 0 if there are problems. * - * if error is not NULL it will be set to one of the values in the enum above. + * if error is not NULL it will be set to one of the values in the enum above. */ -Messenger *new_messenger(Mono_Time *mono_time, Messenger_Options *options, unsigned int *error); +non_null() +Messenger *new_messenger(Mono_Time *mono_time, const Random *rng, const Network *ns, Messenger_Options *options, Messenger_Error *error); -/** Run this before closing shop +/** @brief Run this before closing shop. + * * Free all datastructures. */ +nullable(1) void kill_messenger(Messenger *m); -/** The main loop that needs to be run at least 20 times per second. */ +/** @brief The main loop that needs to be run at least 20 times per second. */ +non_null(1) nullable(2) void do_messenger(Messenger *m, void *userdata); -/** Return the time in milliseconds before do_messenger() should be called again - * for optimal performance. +/** + * @brief Return the time in milliseconds before `do_messenger()` should be called again + * for optimal performance. * - * returns time (in ms) before the next do_messenger() needs to be run on success. + * @return time (in ms) before the next `do_messenger()` needs to be run on success. */ +non_null() uint32_t messenger_run_interval(const Messenger *m); /* SAVING AND LOADING FUNCTIONS: */ -/** Registers a state plugin for saving, loadding, and getting the size of a section of the save +/** @brief Registers a state plugin for saving, loading, and getting the size of a section of the save. * - * returns true on success - * returns false on error + * @retval true on success + * @retval false on error */ -bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size_callback, - m_state_load_cb load_callback, m_state_save_cb save_callback); +non_null() +bool m_register_state_plugin(Messenger *m, State_Type type, + m_state_size_cb *size_callback, + m_state_load_cb *load_callback, + m_state_save_cb *save_callback); /** return size of the messenger data (for saving). */ +non_null() uint32_t messenger_size(const Messenger *m); -/** Save the messenger in data (must be allocated memory of size at least Messenger_size()) */ +/** Save the messenger in data (must be allocated memory of size at least `Messenger_size()`) */ +non_null() uint8_t *messenger_save(const Messenger *m, uint8_t *data); -/** Load a state section. +/** @brief Load a state section. * * @param data Data to load. * @param length Length of data. @@ -761,19 +812,28 @@ uint8_t *messenger_save(const Messenger *m, uint8_t *data); * @param status Result of loading section is stored here if the section is handled. * @return true iff section handled. */ +non_null() bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t length, uint16_t type, State_Load_Status *status); -/** Return the number of friends in the instance m. +/** @brief Return the number of friends in the instance m. + * * You should use this to determine how much memory to allocate - * for copy_friendlist. */ + * for copy_friendlist. + */ +non_null() uint32_t count_friendlist(const Messenger *m); -/** Copy a list of valid friend IDs into the array out_list. +/** @brief Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. * If the array was too small, the contents - * of out_list will be truncated to list_size. */ + * of out_list will be truncated to list_size. + */ +non_null() uint32_t copy_friendlist(const Messenger *m, uint32_t *out_list, uint32_t list_size); +non_null() +bool m_is_receiving_file(Messenger *m); + #endif diff --git a/protocols/Tox/libtox/src/toxcore/TCP_client.c b/protocols/Tox/libtox/src/toxcore/TCP_client.c index 30aa268f1c..1bc0964004 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_client.c +++ b/protocols/Tox/libtox/src/toxcore/TCP_client.c @@ -13,6 +13,7 @@ #include <string.h> #include "TCP_common.h" +#include "ccompat.h" #include "mono_time.h" #include "util.h" @@ -56,6 +57,9 @@ struct TCP_Client_Connection { tcp_onion_response_cb *onion_callback; void *onion_callback_object; + forwarded_response_cb *forwarded_response_callback; + void *forwarded_response_callback_object; + /* Can be used by user. */ void *custom_object; uint32_t custom_uint; @@ -92,26 +96,25 @@ void tcp_con_set_custom_uint(TCP_Client_Connection *con, uint32_t value) con->custom_uint = value; } -/** return 1 on success - * return 0 on failure +/** + * @retval true on success + * @retval false on failure */ -static int connect_sock_to(const Logger *logger, Socket sock, const IP_Port *ip_port, const TCP_Proxy_Info *proxy_info) +non_null() +static bool connect_sock_to(const Logger *logger, Socket sock, const IP_Port *ip_port, const TCP_Proxy_Info *proxy_info) { - IP_Port ipp_copy = *ip_port; - if (proxy_info->proxy_type != TCP_PROXY_NONE) { - ipp_copy = proxy_info->ip_port; + return net_connect(logger, sock, &proxy_info->ip_port); + } else { + return net_connect(logger, sock, ip_port); } - - /* nonblocking socket, connect will never return success */ - net_connect(logger, sock, &ipp_copy); - - return 1; } -/** return 1 on success. - * return 0 on failure. +/** + * @retval 1 on success. + * @retval 0 on failure. */ +non_null() static int proxy_http_generate_connection_request(TCP_Client_Connection *tcp_conn) { char one[] = "CONNECT "; @@ -134,20 +137,22 @@ static int proxy_http_generate_connection_request(TCP_Client_Connection *tcp_con tcp_conn->con.last_packet_length = written; tcp_conn->con.last_packet_sent = 0; - return 1; } -/** return 1 on success. - * return 0 if no data received. - * return -1 on failure (connection refused). +/** + * @retval 1 on success. + * @retval 0 if no data received. + * @retval -1 on failure (connection refused). */ +non_null() static int proxy_http_read_connection_response(const Logger *logger, const TCP_Client_Connection *tcp_conn) { char success[] = "200"; uint8_t data[16]; // draining works the best if the length is a power of 2 - int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data) - 1, &tcp_conn->con.ip_port); + const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data) - 1, + &tcp_conn->con.ip_port); if (ret == -1) { return 0; @@ -155,13 +160,21 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C data[sizeof(data) - 1] = 0; - if (strstr((const char *)data, success)) { + if (strstr((const char *)data, success) != nullptr) { // drain all data - const uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.sock); + uint16_t data_left = net_socket_data_recv_buffer(tcp_conn->con.ns, tcp_conn->con.sock); - if (data_left) { - VLA(uint8_t, temp_data, data_left); - read_TCP_packet(logger, tcp_conn->con.sock, temp_data, data_left, &tcp_conn->con.ip_port); + while (data_left > 0) { + uint8_t temp_data[16]; + const uint16_t temp_data_size = min_u16(data_left, sizeof(temp_data)); + + if (read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, temp_data, temp_data_size, + &tcp_conn->con.ip_port) == -1) { + LOGGER_ERROR(logger, "failed to drain TCP data (but ignoring failure)"); + return 1; + } + + data_left -= temp_data_size; } return 1; @@ -179,6 +192,7 @@ static int proxy_http_read_connection_response(const Logger *logger, const TCP_C #define TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV4 0x01 #define TCP_SOCKS5_PROXY_HS_ADDR_TYPE_IPV6 0x04 +non_null() static void proxy_socks5_generate_greetings(TCP_Client_Connection *tcp_conn) { tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5; @@ -189,14 +203,16 @@ static void proxy_socks5_generate_greetings(TCP_Client_Connection *tcp_conn) tcp_conn->con.last_packet_sent = 0; } -/** return 1 on success. - * return 0 if no data received. - * return -1 on failure (connection refused). +/** + * @retval 1 on success. + * @retval 0 if no data received. + * @retval -1 on failure (connection refused). */ +non_null() static int socks5_read_handshake_response(const Logger *logger, const TCP_Client_Connection *tcp_conn) { uint8_t data[2]; - int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); + const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); if (ret == -1) { return 0; @@ -209,6 +225,7 @@ static int socks5_read_handshake_response(const Logger *logger, const TCP_Client return -1; } +non_null() static void proxy_socks5_generate_connection_request(TCP_Client_Connection *tcp_conn) { tcp_conn->con.last_packet[0] = TCP_SOCKS5_PROXY_HS_VERSION_SOCKS5; @@ -235,15 +252,17 @@ static void proxy_socks5_generate_connection_request(TCP_Client_Connection *tcp_ tcp_conn->con.last_packet_sent = 0; } -/** return 1 on success. - * return 0 if no data received. - * return -1 on failure (connection refused). +/** + * @retval 1 on success. + * @retval 0 if no data received. + * @retval -1 on failure (connection refused). */ +non_null() static int proxy_socks5_read_connection_response(const Logger *logger, const TCP_Client_Connection *tcp_conn) { if (net_family_is_ipv4(tcp_conn->ip_port.ip.family)) { uint8_t data[4 + sizeof(IP4) + sizeof(uint16_t)]; - int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); + const int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); if (ret == -1) { return 0; @@ -254,7 +273,7 @@ static int proxy_socks5_read_connection_response(const Logger *logger, const TCP } } else { uint8_t data[4 + sizeof(IP6) + sizeof(uint16_t)]; - int ret = read_TCP_packet(logger, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); + int ret = read_TCP_packet(logger, tcp_conn->con.ns, tcp_conn->con.sock, data, sizeof(data), &tcp_conn->con.ip_port); if (ret == -1) { return 0; @@ -268,18 +287,20 @@ static int proxy_socks5_read_connection_response(const Logger *logger, const TCP return -1; } -/** return 0 on success. - * return -1 on failure. +/** + * @retval 0 on success. + * @retval -1 on failure. */ +non_null() static int generate_handshake(TCP_Client_Connection *tcp_conn) { uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE]; - crypto_new_keypair(plain, tcp_conn->temp_secret_key); - random_nonce(tcp_conn->con.sent_nonce); + crypto_new_keypair(tcp_conn->con.rng, plain, tcp_conn->temp_secret_key); + random_nonce(tcp_conn->con.rng, tcp_conn->con.sent_nonce); memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, tcp_conn->con.sent_nonce, CRYPTO_NONCE_SIZE); memcpy(tcp_conn->con.last_packet, tcp_conn->self_public_key, CRYPTO_PUBLIC_KEY_SIZE); - random_nonce(tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE); - int len = encrypt_data_symmetric(tcp_conn->con.shared_key, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE, plain, + random_nonce(tcp_conn->con.rng, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE); + const int len = encrypt_data_symmetric(tcp_conn->con.shared_key, tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE, plain, sizeof(plain), tcp_conn->con.last_packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE); if (len != sizeof(plain) + CRYPTO_MAC_SIZE) { @@ -291,15 +312,17 @@ static int generate_handshake(TCP_Client_Connection *tcp_conn) return 0; } -/** data must be of length TCP_SERVER_HANDSHAKE_SIZE +/** + * @param data must be of length TCP_SERVER_HANDSHAKE_SIZE * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null() static int handle_handshake(TCP_Client_Connection *tcp_conn, const uint8_t *data) { uint8_t plain[CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE]; - int len = decrypt_data_symmetric(tcp_conn->con.shared_key, data, data + CRYPTO_NONCE_SIZE, + const int len = decrypt_data_symmetric(tcp_conn->con.shared_key, data, data + CRYPTO_NONCE_SIZE, TCP_SERVER_HANDSHAKE_SIZE - CRYPTO_NONCE_SIZE, plain); if (len != sizeof(plain)) { @@ -312,16 +335,17 @@ static int handle_handshake(TCP_Client_Connection *tcp_conn, const uint8_t *data return 0; } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ int send_routing_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key) { uint8_t packet[1 + CRYPTO_PUBLIC_KEY_SIZE]; packet[0] = TCP_PACKET_ROUTING_REQUEST; memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE); - return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1); + return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true); } void routing_response_handler(TCP_Client_Connection *con, tcp_routing_response_cb *response_callback, void *object) @@ -336,12 +360,13 @@ void routing_status_handler(TCP_Client_Connection *con, tcp_routing_status_cb *s con->status_callback_object = object; } -static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con); -static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con); +non_null() static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con); +non_null() static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure. +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure. */ int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id, const uint8_t *data, uint16_t length) { @@ -360,12 +385,13 @@ int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id, VLA(uint8_t, packet, 1 + length); packet[0] = con_id + NUM_RESERVED_PORTS; memcpy(packet + 1, data, length); - return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0); + return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure. +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure. */ int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key, const uint8_t *data, uint16_t length) @@ -378,11 +404,11 @@ int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint packet[0] = TCP_PACKET_OOB_SEND; memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length); - return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0); + return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false); } -/** Set the number that will be used as an argument in the callbacks related to con_id. +/** @brief Set the number that will be used as an argument in the callbacks related to con_id. * * When not set by this function, the number is -1. * @@ -415,32 +441,35 @@ void oob_data_handler(TCP_Client_Connection *con, tcp_oob_data_cb *oob_data_call con->oob_data_callback_object = object; } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() static int client_send_disconnect_notification(const Logger *logger, TCP_Client_Connection *con, uint8_t id) { uint8_t packet[1 + 1]; packet[0] = TCP_PACKET_DISCONNECT_NOTIFICATION; packet[1] = id; - return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1); + return write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *con) { - if (!con->ping_request_id) { + if (con->ping_request_id == 0) { return 1; } uint8_t packet[1 + sizeof(uint64_t)]; packet[0] = TCP_PACKET_PING; memcpy(packet + 1, &con->ping_request_id, sizeof(uint64_t)); - const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1); + const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true); if (ret == 1) { con->ping_request_id = 0; @@ -449,20 +478,21 @@ static int tcp_send_ping_request(const Logger *logger, TCP_Client_Connection *co return ret; } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *con) { - if (!con->ping_response_id) { + if (con->ping_response_id == 0) { return 1; } uint8_t packet[1 + sizeof(uint64_t)]; packet[0] = TCP_PACKET_PONG; memcpy(packet + 1, &con->ping_response_id, sizeof(uint64_t)); - const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), 1); + const int ret = write_packet_TCP_secure_connection(logger, &con->con, packet, sizeof(packet), true); if (ret == 1) { con->ping_response_id = 0; @@ -471,9 +501,10 @@ static int tcp_send_ping_response(const Logger *logger, TCP_Client_Connection *c return ret; } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id) { @@ -486,16 +517,17 @@ int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, ui return client_send_disconnect_notification(logger, con, con_id + NUM_RESERVED_PORTS); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ int send_onion_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *data, uint16_t length) { VLA(uint8_t, packet, 1 + length); packet[0] = TCP_PACKET_ONION_REQUEST; memcpy(packet + 1, data, length); - return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), 0); + return write_packet_TCP_secure_connection(logger, &con->con, packet, SIZEOF_VLA(packet), false); } void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *onion_callback, void *object) @@ -504,21 +536,45 @@ void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *o con->onion_callback_object = object; } -/** Create new TCP connection to ip_port/public_key +/** @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ -TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time *mono_time, const IP_Port *ip_port, - const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key, - const TCP_Proxy_Info *proxy_info) +int send_forward_request_tcp(const Logger *logger, TCP_Client_Connection *con, const IP_Port *dest, const uint8_t *data, uint16_t length) { - if (networking_at_startup() != 0) { - return nullptr; + if (length > MAX_FORWARD_DATA_SIZE) { + return -1; + } + + VLA(uint8_t, packet, 1 + MAX_PACKED_IPPORT_SIZE + length); + packet[0] = TCP_PACKET_FORWARD_REQUEST; + const int ipport_length = pack_ip_port(logger, packet + 1, MAX_PACKED_IPPORT_SIZE, dest); + + if (ipport_length == -1) { + return 0; } + memcpy(packet + 1 + ipport_length, data, length); + return write_packet_TCP_secure_connection(logger, &con->con, packet, 1 + ipport_length + length, false); +} + +void forwarding_handler(TCP_Client_Connection *con, forwarded_response_cb *forwarded_response_callback, void *object) +{ + con->forwarded_response_callback = forwarded_response_callback; + con->forwarded_response_callback_object = object; +} + +/** Create new TCP connection to ip_port/public_key */ +TCP_Client_Connection *new_TCP_connection( + const Logger *logger, const Mono_Time *mono_time, const Random *rng, const Network *ns, const IP_Port *ip_port, + const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key, + const TCP_Proxy_Info *proxy_info) +{ if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { return nullptr; } - const TCP_Proxy_Info default_proxyinfo = {0}; + const TCP_Proxy_Info default_proxyinfo = {{{{0}}}}; if (proxy_info == nullptr) { proxy_info = &default_proxyinfo; @@ -530,29 +586,31 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time family = proxy_info->ip_port.ip.family; } - Socket sock = net_socket(family, TOX_SOCK_STREAM, TOX_PROTO_TCP); + const Socket sock = net_socket(ns, family, TOX_SOCK_STREAM, TOX_PROTO_TCP); if (!sock_valid(sock)) { return nullptr; } - if (!set_socket_nosigpipe(sock)) { - kill_sock(sock); + if (!set_socket_nosigpipe(ns, sock)) { + kill_sock(ns, sock); return nullptr; } - if (!(set_socket_nonblock(sock) && connect_sock_to(logger, sock, ip_port, proxy_info))) { - kill_sock(sock); + if (!(set_socket_nonblock(ns, sock) && connect_sock_to(logger, sock, ip_port, proxy_info))) { + kill_sock(ns, sock); return nullptr; } TCP_Client_Connection *temp = (TCP_Client_Connection *)calloc(1, sizeof(TCP_Client_Connection)); if (temp == nullptr) { - kill_sock(sock); + kill_sock(ns, sock); return nullptr; } + temp->con.ns = ns; + temp->con.rng = rng; temp->con.sock = sock; temp->con.ip_port = *ip_port; memcpy(temp->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); @@ -578,7 +636,7 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time temp->status = TCP_CLIENT_CONNECTING; if (generate_handshake(temp) == -1) { - kill_sock(sock); + kill_sock(ns, sock); free(temp); return nullptr; } @@ -592,143 +650,184 @@ TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time return temp; } -/** return 0 on success - * return -1 on failure - */ -static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data, - uint16_t length, void *userdata) +non_null() +static int handle_TCP_client_routing_response(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length) { - if (length <= 1) { + if (length != 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE) { return -1; } - switch (data[0]) { - case TCP_PACKET_ROUTING_RESPONSE: { - if (length != 1 + 1 + CRYPTO_PUBLIC_KEY_SIZE) { - return -1; - } + if (data[1] < NUM_RESERVED_PORTS) { + return 0; + } - if (data[1] < NUM_RESERVED_PORTS) { - return 0; - } + const uint8_t con_id = data[1] - NUM_RESERVED_PORTS; - uint8_t con_id = data[1] - NUM_RESERVED_PORTS; + if (conn->connections[con_id].status != 0) { + return 0; + } - if (conn->connections[con_id].status != 0) { - return 0; - } + conn->connections[con_id].status = 1; + conn->connections[con_id].number = -1; + memcpy(conn->connections[con_id].public_key, data + 2, CRYPTO_PUBLIC_KEY_SIZE); - conn->connections[con_id].status = 1; - conn->connections[con_id].number = -1; - memcpy(conn->connections[con_id].public_key, data + 2, CRYPTO_PUBLIC_KEY_SIZE); + if (conn->response_callback != nullptr) { + conn->response_callback(conn->response_callback_object, con_id, conn->connections[con_id].public_key); + } - if (conn->response_callback) { - conn->response_callback(conn->response_callback_object, con_id, conn->connections[con_id].public_key); - } + return 0; +} - return 0; - } +non_null() +static int handle_TCP_client_connection_notification(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length) +{ + if (length != 1 + 1) { + return -1; + } - case TCP_PACKET_CONNECTION_NOTIFICATION: { - if (length != 1 + 1) { - return -1; - } + if (data[1] < NUM_RESERVED_PORTS) { + return -1; + } - if (data[1] < NUM_RESERVED_PORTS) { - return -1; - } + const uint8_t con_id = data[1] - NUM_RESERVED_PORTS; - uint8_t con_id = data[1] - NUM_RESERVED_PORTS; + if (conn->connections[con_id].status != 1) { + return 0; + } - if (conn->connections[con_id].status != 1) { - return 0; - } + conn->connections[con_id].status = 2; - conn->connections[con_id].status = 2; + if (conn->status_callback != nullptr) { + conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id, + conn->connections[con_id].status); + } - if (conn->status_callback) { - conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id, - conn->connections[con_id].status); - } + return 0; +} - return 0; - } +non_null() +static int handle_TCP_client_disconnect_notification(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length) +{ + if (length != 1 + 1) { + return -1; + } - case TCP_PACKET_DISCONNECT_NOTIFICATION: { - if (length != 1 + 1) { - return -1; - } + if (data[1] < NUM_RESERVED_PORTS) { + return -1; + } - if (data[1] < NUM_RESERVED_PORTS) { - return -1; - } + const uint8_t con_id = data[1] - NUM_RESERVED_PORTS; - uint8_t con_id = data[1] - NUM_RESERVED_PORTS; + if (conn->connections[con_id].status == 0) { + return 0; + } - if (conn->connections[con_id].status == 0) { - return 0; - } + if (conn->connections[con_id].status != 2) { + return 0; + } - if (conn->connections[con_id].status != 2) { - return 0; - } + conn->connections[con_id].status = 1; - conn->connections[con_id].status = 1; + if (conn->status_callback != nullptr) { + conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id, + conn->connections[con_id].status); + } - if (conn->status_callback) { - conn->status_callback(conn->status_callback_object, conn->connections[con_id].number, con_id, - conn->connections[con_id].status); - } + return 0; +} - return 0; - } +non_null() +static int handle_TCP_client_ping(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data, uint16_t length) +{ + if (length != 1 + sizeof(uint64_t)) { + return -1; + } - case TCP_PACKET_PING: { - if (length != 1 + sizeof(uint64_t)) { - return -1; - } + uint64_t ping_id; + memcpy(&ping_id, data + 1, sizeof(uint64_t)); + conn->ping_response_id = ping_id; + tcp_send_ping_response(logger, conn); + return 0; +} - uint64_t ping_id; - memcpy(&ping_id, data + 1, sizeof(uint64_t)); - conn->ping_response_id = ping_id; - tcp_send_ping_response(logger, conn); - return 0; +non_null() +static int handle_TCP_client_pong(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length) +{ + if (length != 1 + sizeof(uint64_t)) { + return -1; + } + + uint64_t ping_id; + memcpy(&ping_id, data + 1, sizeof(uint64_t)); + + if (ping_id != 0) { + if (ping_id == conn->ping_id) { + conn->ping_id = 0; } - case TCP_PACKET_PONG: { - if (length != 1 + sizeof(uint64_t)) { - return -1; - } + return 0; + } - uint64_t ping_id; - memcpy(&ping_id, data + 1, sizeof(uint64_t)); + return -1; +} - if (ping_id) { - if (ping_id == conn->ping_id) { - conn->ping_id = 0; - } +non_null(1, 2) nullable(4) +static int handle_TCP_client_oob_recv(TCP_Client_Connection *conn, const uint8_t *data, uint16_t length, void *userdata) +{ + if (length <= 1 + CRYPTO_PUBLIC_KEY_SIZE) { + return -1; + } - return 0; - } + if (conn->oob_data_callback != nullptr) { + conn->oob_data_callback(conn->oob_data_callback_object, data + 1, data + 1 + CRYPTO_PUBLIC_KEY_SIZE, + length - (1 + CRYPTO_PUBLIC_KEY_SIZE), userdata); + } - return -1; - } + return 0; +} - case TCP_PACKET_OOB_RECV: { - if (length <= 1 + CRYPTO_PUBLIC_KEY_SIZE) { - return -1; - } +/** + * @retval 0 on success + * @retval -1 on failure + */ +non_null(1, 2, 3) nullable(5) +static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection *conn, const uint8_t *data, + uint16_t length, void *userdata) +{ + if (length <= 1) { + return -1; + } - if (conn->oob_data_callback) { - conn->oob_data_callback(conn->oob_data_callback_object, data + 1, data + 1 + CRYPTO_PUBLIC_KEY_SIZE, - length - (1 + CRYPTO_PUBLIC_KEY_SIZE), userdata); - } + switch (data[0]) { + case TCP_PACKET_ROUTING_RESPONSE: + return handle_TCP_client_routing_response(conn, data, length); + + case TCP_PACKET_CONNECTION_NOTIFICATION: + return handle_TCP_client_connection_notification(conn, data, length); + + case TCP_PACKET_DISCONNECT_NOTIFICATION: + return handle_TCP_client_disconnect_notification(conn, data, length); + case TCP_PACKET_PING: + return handle_TCP_client_ping(logger, conn, data, length); + + case TCP_PACKET_PONG: + return handle_TCP_client_pong(conn, data, length); + + case TCP_PACKET_OOB_RECV: + return handle_TCP_client_oob_recv(conn, data, length, userdata); + + case TCP_PACKET_ONION_RESPONSE: { + if (conn->onion_callback != nullptr) { + conn->onion_callback(conn->onion_callback_object, data + 1, length - 1, userdata); + } return 0; } - case TCP_PACKET_ONION_RESPONSE: { - conn->onion_callback(conn->onion_callback_object, data + 1, length - 1, userdata); + case TCP_PACKET_FORWARDING: { + if (conn->forwarded_response_callback != nullptr) { + conn->forwarded_response_callback(conn->forwarded_response_callback_object, data + 1, length - 1, userdata); + } return 0; } @@ -737,9 +836,9 @@ static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection return -1; } - uint8_t con_id = data[0] - NUM_RESERVED_PORTS; + const uint8_t con_id = data[0] - NUM_RESERVED_PORTS; - if (conn->data_callback) { + if (conn->data_callback != nullptr) { conn->data_callback(conn->data_callback_object, conn->connections[con_id].number, con_id, data + 1, length - 1, userdata); } @@ -749,11 +848,11 @@ static int handle_TCP_client_packet(const Logger *logger, TCP_Client_Connection return 0; } +non_null(1, 2) nullable(3) static bool tcp_process_packet(const Logger *logger, TCP_Client_Connection *conn, void *userdata) { uint8_t packet[MAX_PACKET_SIZE]; - const int len = read_packet_TCP_secure_connection(logger, conn->con.sock, &conn->next_packet_length, - conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->ip_port); + const int len = read_packet_TCP_secure_connection(logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->ip_port); if (len == 0) { return false; @@ -772,6 +871,7 @@ static bool tcp_process_packet(const Logger *logger, TCP_Client_Connection *conn return true; } +non_null(1, 2, 3) nullable(4) static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, const Mono_Time *mono_time, void *userdata) { @@ -780,9 +880,9 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c tcp_send_ping_request(logger, conn); if (mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_FREQUENCY)) { - uint64_t ping_id = random_u64(); + uint64_t ping_id = random_u64(conn->con.rng); - if (!ping_id) { + if (ping_id == 0) { ++ping_id; } @@ -792,7 +892,7 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c conn->last_pinged = mono_time_get(mono_time); } - if (conn->ping_id && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) { + if (conn->ping_id != 0 && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) { conn->status = TCP_CLIENT_DISCONNECTED; return 0; } @@ -805,8 +905,7 @@ static int do_confirmed_TCP(const Logger *logger, TCP_Client_Connection *conn, c return 0; } -/** Run the TCP connection - */ +/** Run the TCP connection */ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time, TCP_Client_Connection *tcp_connection, void *userdata) { @@ -816,7 +915,7 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time, if (tcp_connection->status == TCP_CLIENT_PROXY_HTTP_CONNECTING) { if (send_pending_data(logger, &tcp_connection->con) == 0) { - int ret = proxy_http_read_connection_response(logger, tcp_connection); + const int ret = proxy_http_read_connection_response(logger, tcp_connection); if (ret == -1) { tcp_connection->kill_at = 0; @@ -870,11 +969,11 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time, if (tcp_connection->status == TCP_CLIENT_UNCONFIRMED) { uint8_t data[TCP_SERVER_HANDSHAKE_SIZE]; - int len = read_TCP_packet(logger, tcp_connection->con.sock, data, sizeof(data), &tcp_connection->con.ip_port); + const int len = read_TCP_packet(logger, tcp_connection->con.ns, tcp_connection->con.sock, data, sizeof(data), &tcp_connection->con.ip_port); if (sizeof(data) == len) { if (handle_handshake(tcp_connection, data) == 0) { - tcp_connection->kill_at = -1; + tcp_connection->kill_at = UINT64_MAX; tcp_connection->status = TCP_CLIENT_CONFIRMED; } else { tcp_connection->kill_at = 0; @@ -892,8 +991,7 @@ void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time, } } -/** Kill the TCP connection - */ +/** Kill the TCP connection */ void kill_TCP_connection(TCP_Client_Connection *tcp_connection) { if (tcp_connection == nullptr) { @@ -901,7 +999,7 @@ void kill_TCP_connection(TCP_Client_Connection *tcp_connection) } wipe_priority_list(tcp_connection->con.priority_queue_start); - kill_sock(tcp_connection->con.sock); + kill_sock(tcp_connection->con.ns, tcp_connection->con.sock); crypto_memzero(tcp_connection, sizeof(TCP_Client_Connection)); free(tcp_connection); } diff --git a/protocols/Tox/libtox/src/toxcore/TCP_client.h b/protocols/Tox/libtox/src/toxcore/TCP_client.h index dd170d7849..fdc91de70d 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_client.h +++ b/protocols/Tox/libtox/src/toxcore/TCP_client.h @@ -9,6 +9,8 @@ #ifndef C_TOXCORE_TOXCORE_TCP_CLIENT_H #define C_TOXCORE_TOXCORE_TCP_CLIENT_H +#include "crypto_core.h" +#include "forwarding.h" #include "mono_time.h" #include "network.h" @@ -38,84 +40,114 @@ typedef enum TCP_Client_Status { typedef struct TCP_Client_Connection TCP_Client_Connection; +non_null() const uint8_t *tcp_con_public_key(const TCP_Client_Connection *con); +non_null() IP_Port tcp_con_ip_port(const TCP_Client_Connection *con); +non_null() TCP_Client_Status tcp_con_status(const TCP_Client_Connection *con); +non_null() void *tcp_con_custom_object(const TCP_Client_Connection *con); +non_null() uint32_t tcp_con_custom_uint(const TCP_Client_Connection *con); +non_null() void tcp_con_set_custom_object(TCP_Client_Connection *con, void *object); +non_null() void tcp_con_set_custom_uint(TCP_Client_Connection *con, uint32_t value); -/** Create new TCP connection to ip_port/public_key - */ -TCP_Client_Connection *new_TCP_connection(const Logger *logger, const Mono_Time *mono_time, const IP_Port *ip_port, +/** Create new TCP connection to ip_port/public_key */ +non_null(1, 2, 3, 4, 5, 6, 7, 8) nullable(9) +TCP_Client_Connection *new_TCP_connection( + const Logger *logger, const Mono_Time *mono_time, const Random *rng, const Network *ns, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key, const TCP_Proxy_Info *proxy_info); -/** Run the TCP connection - */ +/** Run the TCP connection */ +non_null(1, 2, 3) nullable(4) void do_TCP_connection(const Logger *logger, const Mono_Time *mono_time, TCP_Client_Connection *tcp_connection, void *userdata); -/** Kill the TCP connection - */ +/** Kill the TCP connection */ +nullable(1) void kill_TCP_connection(TCP_Client_Connection *tcp_connection); typedef int tcp_onion_response_cb(void *object, const uint8_t *data, uint16_t length, void *userdata); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() int send_onion_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *data, uint16_t length); +non_null() void onion_response_handler(TCP_Client_Connection *con, tcp_onion_response_cb *onion_callback, void *object); +non_null() +int send_forward_request_tcp(const Logger *logger, TCP_Client_Connection *con, const IP_Port *dest, const uint8_t *data, + uint16_t length); +non_null() +void forwarding_handler(TCP_Client_Connection *con, forwarded_response_cb *forwarded_response_callback, void *object); + typedef int tcp_routing_response_cb(void *object, uint8_t connection_id, const uint8_t *public_key); typedef int tcp_routing_status_cb(void *object, uint32_t number, uint8_t connection_id, uint8_t status); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() int send_routing_request(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key); +non_null() void routing_response_handler(TCP_Client_Connection *con, tcp_routing_response_cb *response_callback, void *object); +non_null() void routing_status_handler(TCP_Client_Connection *con, tcp_routing_status_cb *status_callback, void *object); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() int send_disconnect_request(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id); -/** Set the number that will be used as an argument in the callbacks related to con_id. +/** @brief Set the number that will be used as an argument in the callbacks related to con_id. * * When not set by this function, the number is -1. * * return 0 on success. * return -1 on failure. */ +non_null() int set_tcp_connection_number(TCP_Client_Connection *con, uint8_t con_id, uint32_t number); typedef int tcp_routing_data_cb(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data, uint16_t length, void *userdata); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure. +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure. */ +non_null() int send_data(const Logger *logger, TCP_Client_Connection *con, uint8_t con_id, const uint8_t *data, uint16_t length); +non_null() void routing_data_handler(TCP_Client_Connection *con, tcp_routing_data_cb *data_callback, void *object); typedef int tcp_oob_data_cb(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure. +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure. */ +non_null() int send_oob_packet(const Logger *logger, TCP_Client_Connection *con, const uint8_t *public_key, const uint8_t *data, uint16_t length); +non_null() void oob_data_handler(TCP_Client_Connection *con, tcp_oob_data_cb *oob_data_callback, void *object); diff --git a/protocols/Tox/libtox/src/toxcore/TCP_common.c b/protocols/Tox/libtox/src/toxcore/TCP_common.c index 237df20f89..349e35fda7 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_common.c +++ b/protocols/Tox/libtox/src/toxcore/TCP_common.c @@ -8,9 +8,11 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" + void wipe_priority_list(TCP_Priority_List *p) { - while (p) { + while (p != nullptr) { TCP_Priority_List *pp = p; p = p->next; free(pp->data); @@ -18,8 +20,9 @@ void wipe_priority_list(TCP_Priority_List *p) } } -/** return 0 if pending data was sent completely - * return -1 if it wasn't +/** + * @retval 0 if pending data was sent completely + * @retval -1 if it wasn't */ int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con) { @@ -28,7 +31,7 @@ int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con) } const uint16_t left = con->last_packet_length - con->last_packet_sent; - const int len = net_send(logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port); + const int len = net_send(con->ns, logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port); if (len <= 0) { return -1; @@ -44,8 +47,9 @@ int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con) return -1; } -/** return 0 if pending data was sent completely - * return -1 if it wasn't +/** + * @retval 0 if pending data was sent completely + * @retval -1 if it wasn't */ int send_pending_data(const Logger *logger, TCP_Connection *con) { @@ -56,9 +60,9 @@ int send_pending_data(const Logger *logger, TCP_Connection *con) TCP_Priority_List *p = con->priority_queue_start; - while (p) { + while (p != nullptr) { const uint16_t left = p->size - p->sent; - const int len = net_send(logger, con->sock, p->data + p->sent, left, &con->ip_port); + const int len = net_send(con->ns, logger, con->sock, p->data + p->sent, left, &con->ip_port); if (len != left) { if (len > 0) { @@ -76,7 +80,7 @@ int send_pending_data(const Logger *logger, TCP_Connection *con) con->priority_queue_start = p; - if (!p) { + if (p == nullptr) { con->priority_queue_end = nullptr; return 0; } @@ -84,10 +88,12 @@ int send_pending_data(const Logger *logger, TCP_Connection *con) return -1; } -/** return 0 on failure (only if calloc fails) - * return 1 on success +/** + * @retval false on failure (only if calloc fails) + * @retval true on success */ -bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent) +non_null() +static bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent) { TCP_Priority_List *p = con->priority_queue_end; TCP_Priority_List *new_list = (TCP_Priority_List *)calloc(1, sizeof(TCP_Priority_List)); @@ -108,7 +114,7 @@ bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uin memcpy(new_list->data, packet, size); - if (p) { + if (p != nullptr) { p->next = new_list; } else { con->priority_queue_start = new_list; @@ -118,9 +124,10 @@ bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uin return true; } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length, bool priority) @@ -129,11 +136,11 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con return -1; } - bool sendpriority = 1; + bool sendpriority = true; if (send_pending_data(logger, con) == -1) { if (priority) { - sendpriority = 0; + sendpriority = false; } else { return 0; } @@ -150,7 +157,7 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con } if (priority) { - len = sendpriority ? net_send(logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port) : 0; + len = sendpriority ? net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port) : 0; if (len <= 0) { len = 0; @@ -162,10 +169,10 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con return 1; } - return add_priority(con, packet, SIZEOF_VLA(packet), len); + return add_priority(con, packet, SIZEOF_VLA(packet), len) ? 1 : 0; } - len = net_send(logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port); + len = net_send(con->ns, logger, con->sock, packet, SIZEOF_VLA(packet), &con->ip_port); if (len <= 0) { return 0; @@ -183,21 +190,21 @@ int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con return 1; } -/** Read length bytes from socket. +/** @brief Read length bytes from socket. * * return length on success * return -1 on failure/no data in buffer. */ -int read_TCP_packet(const Logger *logger, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port) +int read_TCP_packet(const Logger *logger, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port) { - const uint16_t count = net_socket_data_recv_buffer(sock); + const uint16_t count = net_socket_data_recv_buffer(ns, sock); if (count < length) { LOGGER_TRACE(logger, "recv buffer has %d bytes, but requested %d bytes", count, length); return -1; } - const int len = net_recv(logger, sock, data, length, ip_port); + const int len = net_recv(ns, logger, sock, data, length, ip_port); if (len != length) { LOGGER_ERROR(logger, "FAIL recv packet"); @@ -207,20 +214,21 @@ int read_TCP_packet(const Logger *logger, Socket sock, uint8_t *data, uint16_t l return len; } -/** Read the next two bytes in TCP stream then convert them to +/** @brief Read the next two bytes in TCP stream then convert them to * length (host byte order). * * return length on success * return 0 if nothing has been read from socket. * return -1 on failure. */ -static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port *ip_port) +non_null() +static uint16_t read_TCP_length(const Logger *logger, const Network *ns, Socket sock, const IP_Port *ip_port) { - const uint16_t count = net_socket_data_recv_buffer(sock); + const uint16_t count = net_socket_data_recv_buffer(ns, sock); if (count >= sizeof(uint16_t)) { uint8_t length_buf[sizeof(uint16_t)]; - const int len = net_recv(logger, sock, length_buf, sizeof(length_buf), ip_port); + const int len = net_recv(ns, logger, sock, length_buf, sizeof(length_buf), ip_port); if (len != sizeof(uint16_t)) { LOGGER_ERROR(logger, "FAIL recv packet"); @@ -231,7 +239,7 @@ static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port net_unpack_u16(length_buf, &length); if (length > MAX_PACKET_SIZE) { - LOGGER_WARNING(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE); + LOGGER_ERROR(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE); return -1; } @@ -241,16 +249,18 @@ static uint16_t read_TCP_length(const Logger *logger, Socket sock, const IP_Port return 0; } -/** return length of received packet on success. - * return 0 if could not read any packet. - * return -1 on failure (connection must be killed). +/** + * @return length of received packet on success. + * @retval 0 if could not read any packet. + * @retval -1 on failure (connection must be killed). */ -int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_t *next_packet_length, - const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data, - uint16_t max_len, const IP_Port *ip_port) +int read_packet_TCP_secure_connection( + const Logger *logger, const Network *ns, Socket sock, uint16_t *next_packet_length, + const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data, + uint16_t max_len, const IP_Port *ip_port) { if (*next_packet_length == 0) { - const uint16_t len = read_TCP_length(logger, sock, ip_port); + const uint16_t len = read_TCP_length(logger, ns, sock, ip_port); if (len == (uint16_t) -1) { return -1; @@ -269,10 +279,14 @@ int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_ } VLA(uint8_t, data_encrypted, *next_packet_length); - const int len_packet = read_TCP_packet(logger, sock, data_encrypted, *next_packet_length, ip_port); + const int len_packet = read_TCP_packet(logger, ns, sock, data_encrypted, *next_packet_length, ip_port); + + if (len_packet == -1) { + return 0; + } if (len_packet != *next_packet_length) { - LOGGER_WARNING(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length); + LOGGER_ERROR(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length); return 0; } @@ -281,7 +295,7 @@ int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_ const int len = decrypt_data_symmetric(shared_key, recv_nonce, data_encrypted, len_packet, data); if (len + CRYPTO_MAC_SIZE != len_packet) { - LOGGER_WARNING(logger, "decrypted length %d does not match expected length %d", len + CRYPTO_MAC_SIZE, len_packet); + LOGGER_ERROR(logger, "decrypted length %d does not match expected length %d", len + CRYPTO_MAC_SIZE, len_packet); return -1; } diff --git a/protocols/Tox/libtox/src/toxcore/TCP_common.h b/protocols/Tox/libtox/src/toxcore/TCP_common.h index 92c0a9a3d9..88c0cb665a 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_common.h +++ b/protocols/Tox/libtox/src/toxcore/TCP_common.h @@ -17,6 +17,7 @@ struct TCP_Priority_List { uint8_t *data; }; +nullable(1) void wipe_priority_list(TCP_Priority_List *p); #define NUM_RESERVED_PORTS 16 @@ -32,6 +33,8 @@ void wipe_priority_list(TCP_Priority_List *p); #define TCP_PACKET_OOB_RECV 7 #define TCP_PACKET_ONION_REQUEST 8 #define TCP_PACKET_ONION_RESPONSE 9 +#define TCP_PACKET_FORWARD_REQUEST 10 +#define TCP_PACKET_FORWARDING 11 #define TCP_HANDSHAKE_PLAIN_SIZE (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE) #define TCP_SERVER_HANDSHAKE_SIZE (CRYPTO_NONCE_SIZE + TCP_HANDSHAKE_PLAIN_SIZE + CRYPTO_MAC_SIZE) @@ -45,6 +48,8 @@ void wipe_priority_list(TCP_Priority_List *p); #define MAX_PACKET_SIZE 2048 typedef struct TCP_Connection { + const Random *rng; + const Network *ns; Socket sock; IP_Port ip_port; // for debugging. uint8_t sent_nonce[CRYPTO_NONCE_SIZE]; /* Nonce of sent packets. */ @@ -57,41 +62,48 @@ typedef struct TCP_Connection { TCP_Priority_List *priority_queue_end; } TCP_Connection; -/** return 0 if pending data was sent completely - * return -1 if it wasn't +/** + * @retval 0 if pending data was sent completely + * @retval -1 if it wasn't */ +non_null() int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con); -/** return 0 if pending data was sent completely - * return -1 if it wasn't +/** + * @retval 0 if pending data was sent completely + * @retval -1 if it wasn't */ +non_null() int send_pending_data(const Logger *logger, TCP_Connection *con); -/** return 0 on failure (only if calloc fails) - * return 1 on success +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ -bool add_priority(TCP_Connection *con, const uint8_t *packet, uint16_t size, uint16_t sent); +non_null() +int write_packet_TCP_secure_connection( + const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length, + bool priority); -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). - */ -int write_packet_TCP_secure_connection(const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length, - bool priority); - -/** Read length bytes from socket. +/** @brief Read length bytes from socket. * * return length on success * return -1 on failure/no data in buffer. */ -int read_TCP_packet(const Logger *logger, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port); - -/** return length of received packet on success. - * return 0 if could not read any packet. - * return -1 on failure (connection must be killed). +non_null() +int read_TCP_packet( + const Logger *logger, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port); + +/** + * @return length of received packet on success. + * @retval 0 if could not read any packet. + * @retval -1 on failure (connection must be killed). */ -int read_packet_TCP_secure_connection(const Logger *logger, Socket sock, uint16_t *next_packet_length, - const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data, - uint16_t max_len, const IP_Port *ip_port); +non_null() +int read_packet_TCP_secure_connection( + const Logger *logger, const Network *ns, Socket sock, uint16_t *next_packet_length, + const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data, + uint16_t max_len, const IP_Port *ip_port); #endif diff --git a/protocols/Tox/libtox/src/toxcore/TCP_connection.c b/protocols/Tox/libtox/src/toxcore/TCP_connection.c index 4b406097c2..c67df1b2eb 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_connection.c +++ b/protocols/Tox/libtox/src/toxcore/TCP_connection.c @@ -13,12 +13,15 @@ #include <string.h> #include "TCP_client.h" +#include "ccompat.h" #include "mono_time.h" #include "util.h" struct TCP_Connections { const Logger *logger; + const Random *rng; Mono_Time *mono_time; + const Network *ns; DHT *dht; uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; @@ -39,6 +42,9 @@ struct TCP_Connections { tcp_onion_cb *tcp_onion_callback; void *tcp_onion_callback_object; + forwarded_response_cb *tcp_forwarded_response_callback; + void *tcp_forwarded_response_callback_object; + TCP_Proxy_Info proxy_info; bool onion_status; @@ -46,6 +52,10 @@ struct TCP_Connections { }; +static const TCP_Connection_to empty_tcp_connection_to = {0}; +static const TCP_con empty_tcp_con = {0}; + + const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c) { return tcp_c->self_public_key; @@ -57,14 +67,15 @@ uint32_t tcp_connections_count(const TCP_Connections *tcp_c) return tcp_c->tcp_connections_length; } -/** Set the size of the array to num. +/** @brief Set the size of the array to num. * - * return -1 if realloc fails. - * return 0 if it succeeds. + * @retval -1 if realloc fails. + * @retval 0 if it succeeds. */ +non_null() static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num) { - if (!num) { + if (num == 0) { free(*array); *array = nullptr; return 0; @@ -73,7 +84,7 @@ static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num) TCP_Connection_to *temp_pointer = (TCP_Connection_to *)realloc(*array, num * sizeof(TCP_Connection_to)); - if (!temp_pointer) { + if (temp_pointer == nullptr) { return -1; } @@ -82,9 +93,10 @@ static int realloc_TCP_Connection_to(TCP_Connection_to **array, size_t num) return 0; } +non_null() static int realloc_TCP_con(TCP_con **array, size_t num) { - if (!num) { + if (num == 0) { free(*array); *array = nullptr; return 0; @@ -92,7 +104,7 @@ static int realloc_TCP_con(TCP_con **array, size_t num) TCP_con *temp_pointer = (TCP_con *)realloc(*array, num * sizeof(TCP_con)); - if (!temp_pointer) { + if (temp_pointer == nullptr) { return -1; } @@ -105,6 +117,7 @@ static int realloc_TCP_con(TCP_con **array, size_t num) /** * Return true if the connections_number is valid. */ +non_null() static bool connections_number_is_valid(const TCP_Connections *tcp_c, int connections_number) { if ((unsigned int)connections_number >= tcp_c->connections_length) { @@ -115,16 +128,13 @@ static bool connections_number_is_valid(const TCP_Connections *tcp_c, int connec return false; } - if (tcp_c->connections[connections_number].status == TCP_CONN_NONE) { - return false; - } - - return true; + return tcp_c->connections[connections_number].status != TCP_CONN_NONE; } /** * Return true if the tcp_connections_number is valid. */ +non_null() static bool tcp_connections_number_is_valid(const TCP_Connections *tcp_c, int tcp_connections_number) { if ((uint32_t)tcp_connections_number >= tcp_c->tcp_connections_length) { @@ -135,18 +145,15 @@ static bool tcp_connections_number_is_valid(const TCP_Connections *tcp_c, int tc return false; } - if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_NONE) { - return false; - } - - return true; + return tcp_c->tcp_connections[tcp_connections_number].status != TCP_CONN_NONE; } -/** Create a new empty connection. +/** @brief Create a new empty connection. * * return -1 on failure. * return connections_number on success. */ +non_null() static int create_connection(TCP_Connections *tcp_c) { for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { @@ -160,17 +167,18 @@ static int create_connection(TCP_Connections *tcp_c) if (realloc_TCP_Connection_to(&tcp_c->connections, tcp_c->connections_length + 1) == 0) { id = tcp_c->connections_length; ++tcp_c->connections_length; - memset(&tcp_c->connections[id], 0, sizeof(TCP_Connection_to)); + tcp_c->connections[id] = empty_tcp_connection_to; } return id; } -/** Create a new empty tcp connection. +/** @brief Create a new empty tcp connection. * * return -1 on failure. * return tcp_connections_number on success. */ +non_null() static int create_tcp_connection(TCP_Connections *tcp_c) { for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { @@ -184,17 +192,18 @@ static int create_tcp_connection(TCP_Connections *tcp_c) if (realloc_TCP_con(&tcp_c->tcp_connections, tcp_c->tcp_connections_length + 1) == 0) { id = tcp_c->tcp_connections_length; ++tcp_c->tcp_connections_length; - memset(&tcp_c->tcp_connections[id], 0, sizeof(TCP_con)); + tcp_c->tcp_connections[id] = empty_tcp_con; } return id; } -/** Wipe a connection. +/** @brief Wipe a connection. * * return -1 on failure. * return 0 on success. */ +non_null() static int wipe_connection(TCP_Connections *tcp_c, int connections_number) { if (!connections_number_is_valid(tcp_c, connections_number)) { @@ -202,7 +211,7 @@ static int wipe_connection(TCP_Connections *tcp_c, int connections_number) } uint32_t i; - memset(&tcp_c->connections[connections_number], 0, sizeof(TCP_Connection_to)); + tcp_c->connections[connections_number] = empty_tcp_connection_to; for (i = tcp_c->connections_length; i != 0; --i) { if (tcp_c->connections[i - 1].status != TCP_CONN_NONE) { @@ -218,18 +227,19 @@ static int wipe_connection(TCP_Connections *tcp_c, int connections_number) return 0; } -/** Wipe a connection. +/** @brief Wipe a connection. * * return -1 on failure. * return 0 on success. */ +non_null() static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_number) { if (!tcp_connections_number_is_valid(tcp_c, tcp_connections_number)) { return -1; } - memset(&tcp_c->tcp_connections[tcp_connections_number], 0, sizeof(TCP_con)); + tcp_c->tcp_connections[tcp_connections_number] = empty_tcp_con; uint32_t i; @@ -247,6 +257,7 @@ static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_numbe return 0; } +non_null() static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int connections_number) { if (!connections_number_is_valid(tcp_c, connections_number)) { @@ -256,6 +267,7 @@ static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int conne return &tcp_c->connections[connections_number]; } +non_null() static TCP_con *get_tcp_connection(const TCP_Connections *tcp_c, int tcp_connections_number) { if (!tcp_connections_number_is_valid(tcp_c, tcp_connections_number)) { @@ -285,7 +297,7 @@ uint32_t tcp_connected_relays_count(const TCP_Connections *tcp_c) return count; } -/** Send a packet to the TCP connection. +/** @brief Send a packet to the TCP connection. * * return -1 on failure. * return 0 on success. @@ -295,7 +307,7 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num { const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return -1; } @@ -303,25 +315,25 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num // TODO(irungentoo): thread safety? int ret = -1; - bool limit_reached = 0; + bool limit_reached = false; - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { uint32_t tcp_con_num = con_to->connections[i].tcp_connection; - uint8_t status = con_to->connections[i].status; - uint8_t connection_id = con_to->connections[i].connection_id; + const uint8_t status = con_to->connections[i].status; + const uint8_t connection_id = con_to->connections[i].connection_id; - if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_ONLINE) { + if (tcp_con_num > 0 && status == TCP_CONNECTIONS_STATUS_ONLINE) { tcp_con_num -= 1; TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num); - if (!tcp_con) { + if (tcp_con == nullptr) { continue; } ret = send_data(tcp_c->logger, tcp_con->connection, connection_id, packet, length); if (ret == 0) { - limit_reached = 1; + limit_reached = true; } if (ret == 1) { @@ -334,39 +346,35 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num return 0; } - if (!limit_reached) { - ret = 0; + if (limit_reached) { + return -1; + } - /* Send oob packets to all relays tied to the connection. */ - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { - uint32_t tcp_con_num = con_to->connections[i].tcp_connection; - uint8_t status = con_to->connections[i].status; + bool sent_any = false; - if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_REGISTERED) { - tcp_con_num -= 1; - TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num); + /* Send oob packets to all relays tied to the connection. */ + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + uint32_t tcp_con_num = con_to->connections[i].tcp_connection; + const uint8_t status = con_to->connections[i].status; - if (!tcp_con) { - continue; - } + if (tcp_con_num > 0 && status == TCP_CONNECTIONS_STATUS_REGISTERED) { + tcp_con_num -= 1; + TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num); - if (send_oob_packet(tcp_c->logger, tcp_con->connection, con_to->public_key, packet, length) == 1) { - ret += 1; - } + if (tcp_con == nullptr) { + continue; } - } - if (ret >= 1) { - return 0; + if (send_oob_packet(tcp_c->logger, tcp_con->connection, con_to->public_key, packet, length) == 1) { + sent_any = true; + } } - - return -1; } - return -1; + return sent_any ? 0 : -1; } -/** Return a random TCP connection number for use in send_tcp_onion_request. +/** @brief Return a TCP connection number for use in send_tcp_onion_request. * * TODO(irungentoo): This number is just the index of an array that the elements * can change without warning. @@ -376,10 +384,10 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num */ int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c) { - const uint32_t r = random_u32(); + const uint32_t r = random_u32(tcp_c->rng); for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { - uint32_t index = (i + r) % tcp_c->tcp_connections_length; + const uint32_t index = (i + r) % tcp_c->tcp_connections_length; if (tcp_c->tcp_connections[index].onion && tcp_c->tcp_connections[index].status == TCP_CONN_CONNECTED) { return index; @@ -389,7 +397,44 @@ int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c) return -1; } -/** Send an onion packet via the TCP relay corresponding to tcp_connections_number. +/** @brief Return TCP connection number of active TCP connection with ip_port. + * + * return TCP connection number on success. + * return -1 on failure. + */ +non_null() +static int get_conn_number_by_ip_port(TCP_Connections *tcp_c, const IP_Port *ip_port) +{ + for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { + const IP_Port conn_ip_port = tcp_con_ip_port(tcp_c->tcp_connections[i].connection); + + if (ipport_equal(ip_port, &conn_ip_port) && + tcp_c->tcp_connections[i].status == TCP_CONN_CONNECTED) { + return i; + } + } + + return -1; +} + +/** @brief Put IP_Port of a random onion TCP connection in ip_port. + * + * return true on success. + * return false on failure. + */ +bool tcp_get_random_conn_ip_port(const TCP_Connections *tcp_c, IP_Port *ip_port) +{ + const int index = get_random_tcp_onion_conn_number(tcp_c); + + if (index == -1) { + return false; + } + + *ip_port = tcp_con_ip_port(tcp_c->tcp_connections[index].connection); + return true; +} + +/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. * return -1 on failure. @@ -413,7 +458,37 @@ int tcp_send_onion_request(TCP_Connections *tcp_c, uint32_t tcp_connections_numb return -1; } -/** Send an oob packet via the TCP relay corresponding to tcp_connections_number. +/* Send a forward request to the TCP relay with IP_Port tcp_forwarder, + * requesting to forward data via a chain of dht nodes starting with dht_node. + * A chain_length of 0 means that dht_node is the final destination of data. + * + * return 0 on success. + * return -1 on failure. + */ +int tcp_send_forward_request(const Logger *logger, TCP_Connections *tcp_c, const IP_Port *tcp_forwarder, + const IP_Port *dht_node, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length) +{ + const int index = get_conn_number_by_ip_port(tcp_c, tcp_forwarder); + + if (index == -1) { + return -1; + } + + if (chain_length == 0) { + return send_forward_request_tcp(logger, tcp_c->tcp_connections[index].connection, dht_node, data, + data_length) == 1 ? 0 : -1; + } + + const uint16_t len = forward_chain_packet_size(chain_length, data_length); + VLA(uint8_t, packet, len); + + return create_forward_chain_packet(chain_keys, chain_length, data, data_length, packet) + && send_forward_request_tcp(logger, tcp_c->tcp_connections[index].connection, dht_node, packet, len) == 1 ? 0 : -1; +} + +/** @brief Send an oob packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. * return -1 on failure. @@ -423,7 +498,7 @@ int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connectio { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -440,9 +515,10 @@ int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connectio return -1; } +non_null() static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t *relay_pk); -/** Send an oob packet via the TCP relay corresponding to relay_pk. +/** @brief Send an oob packet via the TCP relay corresponding to relay_pk. * * return 0 on success. * return -1 on failure. @@ -450,7 +526,7 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t int tcp_send_oob_packet_using_relay(const TCP_Connections *tcp_c, const uint8_t *relay_pk, const uint8_t *public_key, const uint8_t *packet, uint16_t length) { - int tcp_con_number = find_tcp_connection_relay(tcp_c, relay_pk); + const int tcp_con_number = find_tcp_connection_relay(tcp_c, relay_pk); if (tcp_con_number < 0) { return -1; @@ -459,42 +535,72 @@ int tcp_send_oob_packet_using_relay(const TCP_Connections *tcp_c, const uint8_t return tcp_send_oob_packet(tcp_c, tcp_con_number, public_key, packet, length); } -/** Set the callback for TCP data packets. - */ +/** @brief Set the callback for TCP data packets. */ void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp_data_callback, void *object) { tcp_c->tcp_data_callback = tcp_data_callback; tcp_c->tcp_data_callback_object = object; } -/** Set the callback for TCP oob data packets. - */ +/** @brief Set the callback for TCP oob data packets. */ void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object) { tcp_c->tcp_oob_callback = tcp_oob_callback; tcp_c->tcp_oob_callback_object = object; } -/** Set the callback for TCP onion packets. - */ +/** @brief Set the callback for TCP onion packets. */ void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object) { tcp_c->tcp_onion_callback = tcp_onion_callback; tcp_c->tcp_onion_callback_object = object; } -/** Find the TCP connection with public_key. +/** @brief Set the callback for TCP forwarding packets. */ +void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c, + forwarded_response_cb *tcp_forwarded_response_callback, + void *object) +{ + tcp_c->tcp_forwarded_response_callback = tcp_forwarded_response_callback; + tcp_c->tcp_forwarded_response_callback_object = object; +} + +/** @brief Encode tcp_connections_number as a custom ip_port. + * + * return ip_port. + */ +IP_Port tcp_connections_number_to_ip_port(unsigned int tcp_connections_number) +{ + IP_Port ip_port = {{{0}}}; + ip_port.ip.family = net_family_tcp_server(); + ip_port.ip.ip.v6.uint32[0] = tcp_connections_number; + return ip_port; +} + +/** @brief Decode ip_port created by tcp_connections_number_to_ip_port to tcp_connections_number. + * + * return true on success. + * return false if ip_port is invalid. + */ +bool ip_port_to_tcp_connections_number(const IP_Port *ip_port, unsigned int *tcp_connections_number) +{ + *tcp_connections_number = ip_port->ip.ip.v6.uint32[0]; + return net_family_is_tcp_server(ip_port->ip.family); +} + +/** @brief Find the TCP connection with public_key. * * return connections_number on success. * return -1 on failure. */ +non_null() static int find_tcp_connection_to(const TCP_Connections *tcp_c, const uint8_t *public_key) { - for (unsigned int i = 0; i < tcp_c->connections_length; ++i) { + for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { const TCP_Connection_to *con_to = get_connection(tcp_c, i); - if (con_to) { - if (public_key_cmp(con_to->public_key, public_key) == 0) { + if (con_to != nullptr) { + if (pk_equal(con_to->public_key, public_key)) { return i; } } @@ -503,7 +609,7 @@ static int find_tcp_connection_to(const TCP_Connections *tcp_c, const uint8_t *p return -1; } -/** Find the TCP connection to a relay with relay_pk. +/** @brief Find the TCP connection to a relay with relay_pk. * * return connections_number on success. * return -1 on failure. @@ -513,13 +619,13 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { const TCP_con *tcp_con = get_tcp_connection(tcp_c, i); - if (tcp_con) { + if (tcp_con != nullptr) { if (tcp_con->status == TCP_CONN_SLEEPING) { - if (public_key_cmp(tcp_con->relay_pk, relay_pk) == 0) { + if (pk_equal(tcp_con->relay_pk, relay_pk)) { return i; } } else { - if (public_key_cmp(tcp_con_public_key(tcp_con->connection), relay_pk) == 0) { + if (pk_equal(tcp_con_public_key(tcp_con->connection), relay_pk)) { return i; } } @@ -529,9 +635,9 @@ static int find_tcp_connection_relay(const TCP_Connections *tcp_c, const uint8_t return -1; } -/** Create a new TCP connection to public_key. +/** @brief Create a new TCP connection to public_key. * - * public_key must be the counterpart to the secret key that the other peer used with new_tcp_connections(). + * public_key must be the counterpart to the secret key that the other peer used with `new_tcp_connections()`. * * id is the id in the callbacks for that connection. * @@ -544,7 +650,7 @@ int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int return -1; } - int connections_number = create_connection(tcp_c); + const int connections_number = create_connection(tcp_c); if (connections_number == -1) { return -1; @@ -559,23 +665,24 @@ int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int return connections_number; } -/** return 0 on success. - * return -1 on failure. +/** + * @retval 0 on success. + * @retval -1 on failure. */ int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number) { const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return -1; } - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { - if (con_to->connections[i].tcp_connection) { - unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1; + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + if (con_to->connections[i].tcp_connection > 0) { + const unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1; TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { continue; } @@ -596,7 +703,7 @@ int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number) return wipe_connection(tcp_c, connections_number); } -/** Set connection status. +/** @brief Set connection status. * * status of 1 means we are using the connection. * status of 0 means we are not using it. @@ -610,7 +717,7 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n { TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return -1; } @@ -620,12 +727,12 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n return -1; } - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { - if (con_to->connections[i].tcp_connection) { - unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1; + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + if (con_to->connections[i].tcp_connection > 0) { + const unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1; TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { continue; } @@ -644,12 +751,12 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n return -1; } - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { - if (con_to->connections[i].tcp_connection) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + if (con_to->connections[i].tcp_connection > 0) { unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1; TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { continue; } @@ -663,27 +770,30 @@ int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_n return 0; } +non_null() static bool tcp_connection_in_conn(const TCP_Connection_to *con_to, unsigned int tcp_connections_number) { - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) { - return 1; + return true; } } - return 0; + return false; } -/** return index on success. - * return -1 on failure. +/** + * @return index on success. + * @retval -1 on failure. */ +non_null() static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number) { if (tcp_connection_in_conn(con_to, tcp_connections_number)) { return -1; } - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { if (con_to->connections[i].tcp_connection == 0) { con_to->connections[i].tcp_connection = tcp_connections_number + 1; con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE; @@ -695,12 +805,14 @@ static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, unsigned int tc return -1; } -/** return index on success. - * return -1 on failure. +/** + * @return index on success. + * @retval -1 on failure. */ +non_null() static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, unsigned int tcp_connections_number) { - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) { con_to->connections[i].tcp_connection = 0; con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE; @@ -712,15 +824,17 @@ static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, unsigned int t return -1; } -/** return number of online connections on success. - * return -1 on failure. +/** + * @return number of online connections on success. + * @retval -1 on failure. */ -static unsigned int online_tcp_connection_from_conn(const TCP_Connection_to *con_to) +non_null() +static uint32_t online_tcp_connection_from_conn(const TCP_Connection_to *con_to) { - unsigned int count = 0; + uint32_t count = 0; - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { - if (con_to->connections[i].tcp_connection) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + if (con_to->connections[i].tcp_connection > 0) { if (con_to->connections[i].status == TCP_CONNECTIONS_STATUS_ONLINE) { ++count; } @@ -730,14 +844,16 @@ static unsigned int online_tcp_connection_from_conn(const TCP_Connection_to *con return count; } -/** return index on success. - * return -1 on failure. +/** + * @return index on success. + * @retval -1 on failure. */ +non_null() static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp_connections_number, uint8_t status, uint8_t connection_id) { - for (unsigned int i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { + for (uint32_t i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) { if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) { if (con_to->connections[i].status == status) { @@ -753,7 +869,7 @@ static int set_tcp_connection_status(TCP_Connection_to *con_to, unsigned int tcp return -1; } -/** Kill a TCP relay connection. +/** @brief Kill a TCP relay connection. * * return 0 on success. * return -1 on failure. @@ -762,14 +878,14 @@ int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } - for (unsigned int i = 0; i < tcp_c->connections_length; ++i) { + for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { TCP_Connection_to *con_to = get_connection(tcp_c, i); - if (con_to) { + if (con_to != nullptr) { rm_tcp_connection_from_conn(con_to, tcp_connections_number); } } @@ -783,11 +899,12 @@ int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number return wipe_tcp_connection(tcp_c, tcp_connections_number); } +non_null() static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -799,18 +916,17 @@ static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connec uint8_t relay_pk[CRYPTO_PUBLIC_KEY_SIZE]; memcpy(relay_pk, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE); kill_TCP_connection(tcp_con->connection); - tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &ip_port, relay_pk, tcp_c->self_public_key, - tcp_c->self_secret_key, &tcp_c->proxy_info); + tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info); - if (!tcp_con->connection) { + if (tcp_con->connection == nullptr) { kill_tcp_relay_connection(tcp_c, tcp_connections_number); return -1; } - for (unsigned int i = 0; i < tcp_c->connections_length; ++i) { + for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { TCP_Connection_to *con_to = get_connection(tcp_c, i); - if (con_to) { + if (con_to != nullptr) { set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); } } @@ -829,11 +945,12 @@ static int reconnect_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connec return 0; } +non_null() static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -851,10 +968,10 @@ static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connection kill_TCP_connection(tcp_con->connection); tcp_con->connection = nullptr; - for (unsigned int i = 0; i < tcp_c->connections_length; ++i) { + for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { TCP_Connection_to *con_to = get_connection(tcp_c, i); - if (con_to) { + if (con_to != nullptr) { set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_NONE, 0); } } @@ -873,11 +990,12 @@ static int sleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connection return 0; } +non_null() static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -885,10 +1003,11 @@ static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connecti return -1; } - tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &tcp_con->ip_port, tcp_con->relay_pk, - tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info); + tcp_con->connection = new_TCP_connection( + tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &tcp_con->ip_port, + tcp_con->relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info); - if (!tcp_con->connection) { + if (tcp_con->connection == nullptr) { kill_tcp_relay_connection(tcp_c, tcp_connections_number); return -1; } @@ -898,20 +1017,22 @@ static int unsleep_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connecti tcp_con->connected_time = 0; tcp_con->status = TCP_CONN_VALID; tcp_con->unsleep = 0; + return 0; } -/** Send a TCP routing request. +/** @brief Send a TCP routing request. * * return 0 on success. * return -1 on failure. */ +non_null() static int send_tcp_relay_routing_request(const TCP_Connections *tcp_c, int tcp_connections_number, const uint8_t *public_key) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -926,19 +1047,20 @@ static int send_tcp_relay_routing_request(const TCP_Connections *tcp_c, int tcp_ return 0; } +non_null() static int tcp_response_callback(void *object, uint8_t connection_id, const uint8_t *public_key) { TCP_Client_Connection *tcp_client_con = (TCP_Client_Connection *)object; const TCP_Connections *tcp_c = (const TCP_Connections *)tcp_con_custom_object(tcp_client_con); - unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); + const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } - int connections_number = find_tcp_connection_to(tcp_c, public_key); + const int connections_number = find_tcp_connection_to(tcp_c, public_key); if (connections_number == -1) { return -1; @@ -959,16 +1081,17 @@ static int tcp_response_callback(void *object, uint8_t connection_id, const uint return 0; } +non_null() static int tcp_status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status) { const TCP_Client_Connection *tcp_client_con = (const TCP_Client_Connection *)object; const TCP_Connections *tcp_c = (const TCP_Connections *)tcp_con_custom_object(tcp_client_con); - unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); + const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); TCP_Connection_to *con_to = get_connection(tcp_c, number); - if (!con_to || !tcp_con) { + if (con_to == nullptr || tcp_con == nullptr) { return -1; } @@ -997,6 +1120,7 @@ static int tcp_status_callback(void *object, uint32_t number, uint8_t connection return 0; } +non_null(1, 4) nullable(6) static int tcp_conn_data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data, uint16_t length, void *userdata) { @@ -1007,26 +1131,27 @@ static int tcp_conn_data_callback(void *object, uint32_t number, uint8_t connect const TCP_Client_Connection *tcp_client_con = (TCP_Client_Connection *)object; TCP_Connections *tcp_c = (TCP_Connections *)tcp_con_custom_object(tcp_client_con); - unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); + const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } const TCP_Connection_to *con_to = get_connection(tcp_c, number); - if (!con_to) { + if (con_to == nullptr) { return -1; } - if (tcp_c->tcp_data_callback) { + if (tcp_c->tcp_data_callback != nullptr) { tcp_c->tcp_data_callback(tcp_c->tcp_data_callback_object, con_to->id, data, length, userdata); } return 0; } +non_null() static int tcp_conn_oob_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata) { @@ -1037,50 +1162,62 @@ static int tcp_conn_oob_callback(void *object, const uint8_t *public_key, const const TCP_Client_Connection *tcp_client_con = (const TCP_Client_Connection *)object; TCP_Connections *tcp_c = (TCP_Connections *)tcp_con_custom_object(tcp_client_con); - unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); + const unsigned int tcp_connections_number = tcp_con_custom_uint(tcp_client_con); const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } /* TODO(irungentoo): optimize */ - int connections_number = find_tcp_connection_to(tcp_c, public_key); + const int connections_number = find_tcp_connection_to(tcp_c, public_key); const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (con_to && tcp_connection_in_conn(con_to, tcp_connections_number)) { + if (con_to != nullptr && tcp_connection_in_conn(con_to, tcp_connections_number)) { return tcp_conn_data_callback(object, connections_number, 0, data, length, userdata); } - if (tcp_c->tcp_oob_callback) { + if (tcp_c->tcp_oob_callback != nullptr) { tcp_c->tcp_oob_callback(tcp_c->tcp_oob_callback_object, public_key, tcp_connections_number, data, length, userdata); } return 0; } +non_null() static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length, void *userdata) { TCP_Connections *tcp_c = (TCP_Connections *)object; - if (tcp_c->tcp_onion_callback) { + if (tcp_c->tcp_onion_callback != nullptr) { tcp_c->tcp_onion_callback(tcp_c->tcp_onion_callback_object, data, length, userdata); } return 0; } -/** Set callbacks for the TCP relay connection. +non_null() +static void tcp_forwarding_callback(void *object, const uint8_t *data, uint16_t length, void *userdata) +{ + TCP_Connections *tcp_c = (TCP_Connections *)object; + + if (tcp_c->tcp_forwarded_response_callback != nullptr) { + tcp_c->tcp_forwarded_response_callback(tcp_c->tcp_forwarded_response_callback_object, data, length, userdata); + } +} + +/** @brief Set callbacks for the TCP relay connection. * * return 0 on success. * return -1 on failure. */ +non_null() static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_number) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -1089,6 +1226,7 @@ static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_n tcp_con_set_custom_object(con, tcp_c); tcp_con_set_custom_uint(con, tcp_connections_number); onion_response_handler(con, &tcp_onion_callback, tcp_c); + forwarding_handler(con, &tcp_forwarding_callback, tcp_c); routing_response_handler(con, &tcp_response_callback, con); routing_status_handler(con, &tcp_status_callback, con); routing_data_handler(con, &tcp_conn_data_callback, con); @@ -1097,23 +1235,24 @@ static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_n return 0; } +non_null() static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_number) { TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } - unsigned int sent = 0; + bool sent_any = false; - for (unsigned int i = 0; i < tcp_c->connections_length; ++i) { + for (uint32_t i = 0; i < tcp_c->connections_length; ++i) { TCP_Connection_to *con_to = get_connection(tcp_c, i); - if (con_to) { + if (con_to != nullptr) { if (tcp_connection_in_conn(con_to, tcp_connections_number)) { if (send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key) == 0) { - ++sent; + sent_any = true; } } } @@ -1123,7 +1262,7 @@ static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_numbe tcp_con->status = TCP_CONN_CONNECTED; /* If this connection isn't used by any connection, we don't need to wait for them to come online. */ - if (sent) { + if (sent_any) { tcp_con->connected_time = mono_time_get(tcp_c->mono_time); } else { tcp_con->connected_time = 0; @@ -1137,21 +1276,22 @@ static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_numbe return 0; } +non_null() static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk) { IP_Port ipp_copy = *ip_port; if (net_family_is_tcp_ipv4(ipp_copy.ip.family)) { - ipp_copy.ip.family = net_family_ipv4; + ipp_copy.ip.family = net_family_ipv4(); } else if (net_family_is_tcp_ipv6(ipp_copy.ip.family)) { - ipp_copy.ip.family = net_family_ipv6; + ipp_copy.ip.family = net_family_ipv6(); } if (!net_family_is_ipv4(ipp_copy.ip.family) && !net_family_is_ipv6(ipp_copy.ip.family)) { return -1; } - int tcp_connections_number = create_tcp_connection(tcp_c); + const int tcp_connections_number = create_tcp_connection(tcp_c); if (tcp_connections_number == -1) { return -1; @@ -1159,10 +1299,11 @@ static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number]; - tcp_con->connection = new_TCP_connection(tcp_c->logger, tcp_c->mono_time, &ipp_copy, relay_pk, tcp_c->self_public_key, - tcp_c->self_secret_key, &tcp_c->proxy_info); + tcp_con->connection = new_TCP_connection( + tcp_c->logger, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ipp_copy, + relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info); - if (!tcp_con->connection) { + if (tcp_con->connection == nullptr) { return -1; } @@ -1171,14 +1312,14 @@ static int add_tcp_relay_instance(TCP_Connections *tcp_c, const IP_Port *ip_port return tcp_connections_number; } -/** Add a TCP relay to the TCP_Connections instance. +/** @brief Add a TCP relay to the TCP_Connections instance. * * return 0 on success. * return -1 on failure. */ int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk) { - int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk); + const int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk); if (tcp_connections_number != -1) { return -1; @@ -1191,7 +1332,7 @@ int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const u return 0; } -/** Add a TCP relay tied to a connection. +/** @brief Add a TCP relay tied to a connection. * * NOTE: This can only be used during the tcp_oob_callback. * @@ -1203,13 +1344,13 @@ int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connection { TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return -1; } TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -1230,7 +1371,7 @@ int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connection return 0; } -/** Add a TCP relay tied to a connection. +/** @brief Add a TCP relay tied to a connection. * * This should be called with the same relay by two peers who want to create a TCP connection with each other. * @@ -1242,7 +1383,7 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con { TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return -1; } @@ -1260,7 +1401,7 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con const TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number); - if (!tcp_con) { + if (tcp_con == nullptr) { return -1; } @@ -1271,21 +1412,55 @@ int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, con return 0; } -/** return number of online tcp relays tied to the connection on success. - * return 0 on failure. +/** + * @return number of online tcp relays tied to the connection on success. + * @retval 0 on failure. */ -unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number) +uint32_t tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number) { const TCP_Connection_to *con_to = get_connection(tcp_c, connections_number); - if (!con_to) { + if (con_to == nullptr) { return 0; } return online_tcp_connection_from_conn(con_to); } -/** Copy a maximum of max_num TCP relays we are connected to to tcp_relays. +/** @brief Copies the tcp relay from tcp connections designated by `idx` to `tcp_relay`. + * + * Returns true if the relay was successfully copied. + * Returns false if the connection index is invalid, or if the relay is not connected. + */ +non_null() +static bool copy_tcp_relay_conn(const TCP_Connections *tcp_c, Node_format *tcp_relay, uint16_t idx) +{ + const TCP_con *tcp_con = get_tcp_connection(tcp_c, idx); + + if (tcp_con == nullptr) { + return false; + } + + if (tcp_con->status != TCP_CONN_CONNECTED) { + return false; + } + + memcpy(tcp_relay->public_key, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE); + tcp_relay->ip_port = tcp_con_ip_port(tcp_con->connection); + + Family *const family = &tcp_relay->ip_port.ip.family; + + if (net_family_is_ipv4(*family)) { + *family = net_family_tcp_ipv4(); + } else if (net_family_is_ipv6(*family)) { + *family = net_family_tcp_ipv6(); + } + + return true; +} + +/** @brief Copy a maximum of max_num TCP relays we are connected to to tcp_relays. + * * NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6. * * return number of relays copied to tcp_relays on success. @@ -1293,28 +1468,34 @@ unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, i */ uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num) { - const uint32_t r = random_u32(); + const uint32_t r = random_u32(tcp_c->rng); uint32_t copied = 0; for (uint32_t i = 0; (i < tcp_c->tcp_connections_length) && (copied < max_num); ++i) { - const TCP_con *tcp_con = get_tcp_connection(tcp_c, (i + r) % tcp_c->tcp_connections_length); + const uint16_t idx = (i + r) % tcp_c->tcp_connections_length; - if (!tcp_con) { - continue; + if (copy_tcp_relay_conn(tcp_c, &tcp_relays[copied], idx)) { + ++copied; } + } - if (tcp_con->status == TCP_CONN_CONNECTED) { - memcpy(tcp_relays[copied].public_key, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE); - tcp_relays[copied].ip_port = tcp_con_ip_port(tcp_con->connection); + return copied; +} - Family *const family = &tcp_relays[copied].ip_port.ip.family; +uint32_t tcp_copy_connected_relays_index(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num, + uint32_t idx) +{ + if (tcp_c->tcp_connections_length == 0) { + return 0; + } - if (net_family_is_ipv4(*family)) { - *family = net_family_tcp_ipv4; - } else if (net_family_is_ipv6(*family)) { - *family = net_family_tcp_ipv6; - } + uint32_t copied = 0; + const uint16_t num_to_copy = min_u16(max_num, tcp_c->tcp_connections_length); + const uint16_t start = idx % tcp_c->tcp_connections_length; + const uint16_t end = (start + num_to_copy) % tcp_c->tcp_connections_length; + for (uint16_t i = start; i != end; i = (i + 1) % tcp_c->tcp_connections_length) { + if (copy_tcp_relay_conn(tcp_c, &tcp_relays[copied], i)) { ++copied; } } @@ -1322,7 +1503,7 @@ uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tc return copied; } -/** Set if we want TCP_connection to allocate some connection for onion use. +/** @brief Set if we want TCP_connection to allocate some connection for onion use. * * If status is 1, allocate some connections. if status is 0, don't. * @@ -1339,7 +1520,7 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status) for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { TCP_con *tcp_con = get_tcp_connection(tcp_c, i); - if (tcp_con) { + if (tcp_con != nullptr) { if (tcp_con->status == TCP_CONN_CONNECTED && !tcp_con->onion) { ++tcp_c->onion_num_conns; tcp_con->onion = 1; @@ -1352,18 +1533,18 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status) } if (tcp_c->onion_num_conns < NUM_ONION_TCP_CONNECTIONS) { - unsigned int wakeup = NUM_ONION_TCP_CONNECTIONS - tcp_c->onion_num_conns; + const unsigned int wakeup = NUM_ONION_TCP_CONNECTIONS - tcp_c->onion_num_conns; for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { TCP_con *tcp_con = get_tcp_connection(tcp_c, i); - if (tcp_con) { + if (tcp_con != nullptr) { if (tcp_con->status == TCP_CONN_SLEEPING) { tcp_con->unsleep = 1; } } - if (!wakeup) { + if (wakeup == 0) { break; } } @@ -1374,7 +1555,7 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status) for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { TCP_con *tcp_con = get_tcp_connection(tcp_c, i); - if (tcp_con) { + if (tcp_con != nullptr) { if (tcp_con->onion) { --tcp_c->onion_num_conns; tcp_con->onion = 0; @@ -1388,15 +1569,16 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status) return 0; } -/** Returns a new TCP_Connections object associated with the secret_key. +/** @brief Returns a new TCP_Connections object associated with the secret_key. * - * In order for others to connect to this instance new_tcp_connection_to() must be called with the + * In order for others to connect to this instance `new_tcp_connection_to()` must be called with the * public_key associated with secret_key. * * Returns NULL on failure. */ -TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, const uint8_t *secret_key, - const TCP_Proxy_Info *proxy_info) +TCP_Connections *new_tcp_connections( + const Logger *logger, const Random *rng, const Network *ns, Mono_Time *mono_time, const uint8_t *secret_key, + const TCP_Proxy_Info *proxy_info) { if (secret_key == nullptr) { return nullptr; @@ -1409,7 +1591,9 @@ TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, } temp->logger = logger; + temp->rng = rng; temp->mono_time = mono_time; + temp->ns = ns; memcpy(temp->self_secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE); crypto_derive_public_key(temp->self_public_key, temp->self_secret_key); @@ -1418,6 +1602,7 @@ TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, return temp; } +non_null(1, 2) nullable(3) static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *userdata) { for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { @@ -1451,7 +1636,7 @@ static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *use } if (tcp_con->status == TCP_CONN_CONNECTED - && !tcp_con->onion && tcp_con->lock_count + && !tcp_con->onion && tcp_con->lock_count > 0 && tcp_con->lock_count == tcp_con->sleep_count && mono_time_is_timeout(tcp_c->mono_time, tcp_con->connected_time, TCP_CONNECTION_ANNOUNCE_TIMEOUT)) { sleep_tcp_relay_connection(tcp_c, i); @@ -1464,6 +1649,7 @@ static void do_tcp_conns(const Logger *logger, TCP_Connections *tcp_c, void *use } } +non_null() static void kill_nonused_tcp(TCP_Connections *tcp_c) { if (tcp_c->tcp_connections_length <= RECOMMENDED_FRIEND_TCP_CONNECTIONS) { @@ -1487,7 +1673,7 @@ static void kill_nonused_tcp(TCP_Connections *tcp_c) } if (tcp_con->status == TCP_CONN_CONNECTED) { - if (tcp_con->onion || tcp_con->lock_count) { // connection is in use so we skip it + if (tcp_con->onion || tcp_con->lock_count > 0) { // connection is in use so we skip it continue; } @@ -1507,6 +1693,10 @@ void do_tcp_connections(const Logger *logger, TCP_Connections *tcp_c, void *user void kill_tcp_connections(TCP_Connections *tcp_c) { + if (tcp_c == nullptr) { + return; + } + for (uint32_t i = 0; i < tcp_c->tcp_connections_length; ++i) { kill_TCP_connection(tcp_c->tcp_connections[i].connection); } diff --git a/protocols/Tox/libtox/src/toxcore/TCP_connection.h b/protocols/Tox/libtox/src/toxcore/TCP_connection.h index 84890b98d7..65ead4d87f 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_connection.h +++ b/protocols/Tox/libtox/src/toxcore/TCP_connection.h @@ -9,12 +9,12 @@ #ifndef C_TOXCORE_TOXCORE_TCP_CONNECTION_H #define C_TOXCORE_TOXCORE_TCP_CONNECTION_H +#include <stdbool.h> + #include "DHT.h" // for Node_format #include "TCP_client.h" #include "TCP_common.h" -#include <stdbool.h> - #define TCP_CONN_NONE 0 #define TCP_CONN_VALID 1 @@ -33,8 +33,9 @@ /** Time until connection to friend gets killed (if it doesn't get locked within that time) */ #define TCP_CONNECTION_ANNOUNCE_TIMEOUT TCP_CONNECTION_TIMEOUT -/** The amount of recommended connections for each friend - * NOTE: Must be at most (MAX_FRIEND_TCP_CONNECTIONS / 2) */ +/** @brief The amount of recommended connections for each friend + * NOTE: Must be at most (MAX_FRIEND_TCP_CONNECTIONS / 2) + */ #define RECOMMENDED_FRIEND_TCP_CONNECTIONS (MAX_FRIEND_TCP_CONNECTIONS / 2) /** Number of TCP connections used for onion purposes. */ @@ -71,22 +72,26 @@ typedef struct TCP_con { typedef struct TCP_Connections TCP_Connections; +non_null() const uint8_t *tcp_connections_public_key(const TCP_Connections *tcp_c); +non_null() uint32_t tcp_connections_count(const TCP_Connections *tcp_c); /** Returns the number of connected TCP relays */ +non_null() uint32_t tcp_connected_relays_count(const TCP_Connections *tcp_c); -/** Send a packet to the TCP connection. +/** @brief Send a packet to the TCP connection. * * return -1 on failure. * return 0 on success. */ +non_null() int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length); -/** Return a random TCP connection number for use in send_tcp_onion_request. +/** @brief Return a TCP connection number for use in send_tcp_onion_request. * * TODO(irungentoo): This number is just the index of an array that the elements * can change without warning. @@ -94,72 +99,123 @@ int send_packet_tcp_connection(const TCP_Connections *tcp_c, int connections_num * return TCP connection number on success. * return -1 on failure. */ +non_null() int get_random_tcp_onion_conn_number(const TCP_Connections *tcp_c); -/** Send an onion packet via the TCP relay corresponding to tcp_connections_number. +/** @brief Put IP_Port of a random onion TCP connection in ip_port. + * + * return true on success. + * return false on failure. + */ +non_null() +bool tcp_get_random_conn_ip_port(const TCP_Connections *tcp_c, IP_Port *ip_port); + +/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. * return -1 on failure. */ +non_null() int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length); -/** Set if we want TCP_connection to allocate some connection for onion use. +/** @brief Set if we want TCP_connection to allocate some connection for onion use. * * If status is 1, allocate some connections. if status is 0, don't. * * return 0 on success. * return -1 on failure. */ +non_null() int set_tcp_onion_status(TCP_Connections *tcp_c, bool status); -/** Send an oob packet via the TCP relay corresponding to tcp_connections_number. +/** + * Send a forward request to the TCP relay with IP_Port tcp_forwarder, + * requesting to forward data via a chain of dht nodes starting with dht_node. + * A chain_length of 0 means that dht_node is the final destination of data. + * + * return 0 on success. + * return -1 on failure. + */ +non_null() +int tcp_send_forward_request(const Logger *logger, TCP_Connections *tcp_c, const IP_Port *tcp_forwarder, + const IP_Port *dht_node, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length); + +/** @brief Send an oob packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. * return -1 on failure. */ +non_null() int tcp_send_oob_packet(const TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key, const uint8_t *packet, uint16_t length); typedef int tcp_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); +non_null() int tcp_send_oob_packet_using_relay(const TCP_Connections *tcp_c, const uint8_t *relay_pk, const uint8_t *public_key, const uint8_t *packet, uint16_t length); -/** Set the callback for TCP data packets. - */ +/** @brief Set the callback for TCP data packets. */ +non_null() void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_data_cb *tcp_data_callback, void *object); typedef int tcp_onion_cb(void *object, const uint8_t *data, uint16_t length, void *userdata); -/** Set the callback for TCP onion packets. - */ +/** @brief Set the callback for TCP onion packets. */ +non_null(1) nullable(2, 3) void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_onion_cb *tcp_onion_callback, void *object); +/** @brief Set the callback for TCP forwarding packets. */ +non_null(1) nullable(2, 3) +void set_forwarding_packet_tcp_connection_callback(TCP_Connections *tcp_c, + forwarded_response_cb *tcp_forwarded_response_callback, + void *object); + + typedef int tcp_oob_cb(void *object, const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length, void *userdata); -/** Set the callback for TCP oob data packets. - */ +/** @brief Set the callback for TCP oob data packets. */ +non_null() void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, tcp_oob_cb *tcp_oob_callback, void *object); -/** Create a new TCP connection to public_key. +/** @brief Encode tcp_connections_number as a custom ip_port. * - * public_key must be the counterpart to the secret key that the other peer used with new_tcp_connections(). + * return ip_port. + */ +IP_Port tcp_connections_number_to_ip_port(unsigned int tcp_connections_number); + +/** @brief Decode ip_port created by tcp_connections_number_to_ip_port to tcp_connections_number. + * + * return true on success. + * return false if ip_port is invalid. + */ +non_null() +bool ip_port_to_tcp_connections_number(const IP_Port *ip_port, unsigned int *tcp_connections_number); + +/** @brief Create a new TCP connection to public_key. + * + * public_key must be the counterpart to the secret key that the other peer used with `new_tcp_connections()`. * * id is the id in the callbacks for that connection. * * return connections_number on success. * return -1 on failure. */ +non_null() int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id); -/** return 0 on success. - * return -1 on failure. +/** + * @retval 0 on success. + * @retval -1 on failure. */ +non_null() int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number); -/** Set connection status. +/** @brief Set connection status. * * status of 1 means we are using the connection. * status of 0 means we are not using it. @@ -169,61 +225,86 @@ int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number); * return 0 on success. * return -1 on failure. */ +non_null() int set_tcp_connection_to_status(const TCP_Connections *tcp_c, int connections_number, bool status); -/** return number of online tcp relays tied to the connection on success. - * return 0 on failure. +/** + * @return number of online tcp relays tied to the connection on success. + * @retval 0 on failure. */ -unsigned int tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number); +non_null() +uint32_t tcp_connection_to_online_tcp_relays(const TCP_Connections *tcp_c, int connections_number); -/** Add a TCP relay tied to a connection. +/** @brief Add a TCP relay tied to a connection. * * NOTE: This can only be used during the tcp_oob_callback. * * return 0 on success. * return -1 on failure. */ +non_null() int add_tcp_number_relay_connection(const TCP_Connections *tcp_c, int connections_number, unsigned int tcp_connections_number); -/** Add a TCP relay tied to a connection. +/** @brief Add a TCP relay tied to a connection. * * This should be called with the same relay by two peers who want to create a TCP connection with each other. * * return 0 on success. * return -1 on failure. */ +non_null() int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, const IP_Port *ip_port, const uint8_t *relay_pk); -/** Add a TCP relay to the TCP_Connections instance. +/** @brief Add a TCP relay to the TCP_Connections instance. * * return 0 on success. * return -1 on failure. */ +non_null() int add_tcp_relay_global(TCP_Connections *tcp_c, const IP_Port *ip_port, const uint8_t *relay_pk); -/** Copy a maximum of max_num TCP relays we are connected to to tcp_relays. +/** @brief Copy a maximum of max_num TCP relays we are connected to to tcp_relays. + * * NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6. * * return number of relays copied to tcp_relays on success. * return 0 on failure. */ +non_null() uint32_t tcp_copy_connected_relays(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num); -/** Returns a new TCP_Connections object associated with the secret_key. +/** @brief Copy a maximum of `max_num` TCP relays we are connected to starting at idx. + * + * @param idx is the index in the TCP relay array for `tcp_c` designated. + * If idx is greater than the array length a modulo operation is performed. + * + * Returns the number of relays successfully copied. + */ +non_null() +uint32_t tcp_copy_connected_relays_index(const TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num, + uint32_t idx); + +/** @brief Returns a new TCP_Connections object associated with the secret_key. * - * In order for others to connect to this instance new_tcp_connection_to() must be called with the + * In order for others to connect to this instance `new_tcp_connection_to()` must be called with the * public_key associated with secret_key. * * Returns NULL on failure. */ -TCP_Connections *new_tcp_connections(const Logger *logger, Mono_Time *mono_time, const uint8_t *secret_key, - const TCP_Proxy_Info *proxy_info); +non_null() +TCP_Connections *new_tcp_connections( + const Logger *logger, const Random *rng, const Network *ns, Mono_Time *mono_time, + const uint8_t *secret_key, const TCP_Proxy_Info *proxy_info); +non_null() int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number); +non_null(1, 2) nullable(3) void do_tcp_connections(const Logger *logger, TCP_Connections *tcp_c, void *userdata); + +nullable(1) void kill_tcp_connections(TCP_Connections *tcp_c); #endif diff --git a/protocols/Tox/libtox/src/toxcore/TCP_server.c b/protocols/Tox/libtox/src/toxcore/TCP_server.c index 4f7fe12121..f571a312d2 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_server.c +++ b/protocols/Tox/libtox/src/toxcore/TCP_server.c @@ -20,6 +20,7 @@ #endif #include "TCP_common.h" +#include "ccompat.h" #include "list.h" #include "mono_time.h" #include "util.h" @@ -57,7 +58,10 @@ typedef struct TCP_Secure_Connection { struct TCP_Server { const Logger *logger; + const Random *rng; + const Network *ns; Onion *onion; + Forwarding *forwarding; #ifdef TCP_SERVER_USE_EPOLL int efd; @@ -92,19 +96,19 @@ size_t tcp_server_listen_count(const TCP_Server *tcp_server) return tcp_server->num_listening_socks; } -/** This is needed to compile on Android below API 21 - */ +/** This is needed to compile on Android below API 21 */ #ifdef TCP_SERVER_USE_EPOLL #ifndef EPOLLRDHUP #define EPOLLRDHUP 0x2000 #endif #endif -/** Increase the size of the connection list +/** @brief Increase the size of the connection list * - * return -1 on failure - * return 0 on success. + * @retval -1 on failure + * @retval 0 on success. */ +non_null() static int alloc_new_connections(TCP_Server *tcp_server, uint32_t num) { const uint32_t new_size = tcp_server->size_accepted_connections + num; @@ -130,20 +134,23 @@ static int alloc_new_connections(TCP_Server *tcp_server, uint32_t num) return 0; } +non_null() static void wipe_secure_connection(TCP_Secure_Connection *con) { - if (con->status) { + if (con->status != 0) { wipe_priority_list(con->con.priority_queue_start); crypto_memzero(con, sizeof(TCP_Secure_Connection)); } } +non_null() static void move_secure_connection(TCP_Secure_Connection *con_new, TCP_Secure_Connection *con_old) { *con_new = *con_old; crypto_memzero(con_old, sizeof(TCP_Secure_Connection)); } +non_null() static void free_accepted_connection_array(TCP_Server *tcp_server) { if (tcp_server->accepted_connection_array == nullptr) { @@ -159,22 +166,26 @@ static void free_accepted_connection_array(TCP_Server *tcp_server) tcp_server->size_accepted_connections = 0; } -/** return index corresponding to connection with peer on success - * return -1 on failure. +/** + * @return index corresponding to connection with peer on success + * @retval -1 on failure. */ +non_null() static int get_TCP_connection_index(const TCP_Server *tcp_server, const uint8_t *public_key) { return bs_list_find(&tcp_server->accepted_key_list, public_key); } +non_null() static int kill_accepted(TCP_Server *tcp_server, int index); -/** Add accepted TCP connection to the list. +/** @brief Add accepted TCP connection to the list. * - * return index on success - * return -1 on failure + * @return index on success + * @retval -1 on failure */ +non_null() static int add_accepted(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_Secure_Connection *con) { int index = get_TCP_connection_index(tcp_server, con->public_key); @@ -219,11 +230,12 @@ static int add_accepted(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_ return index; } -/** Delete accepted connection from list. +/** @brief Delete accepted connection from list. * - * return 0 on success - * return -1 on failure + * @retval 0 on success + * @retval -1 on failure */ +non_null() static int del_accepted(TCP_Server *tcp_server, int index) { if ((uint32_t)index >= tcp_server->size_accepted_connections) { @@ -248,17 +260,18 @@ static int del_accepted(TCP_Server *tcp_server, int index) return 0; } -/** Kill a TCP_Secure_Connection - */ +/** Kill a TCP_Secure_Connection */ +non_null() static void kill_TCP_secure_connection(TCP_Secure_Connection *con) { - kill_sock(con->con.sock); + kill_sock(con->con.ns, con->con.sock); wipe_secure_connection(con); } +non_null() static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *con, uint8_t con_number); -/** Kill an accepted TCP_Secure_Connection +/** @brief Kill an accepted TCP_Secure_Connection * * return -1 on failure. * return 0 on success. @@ -273,29 +286,31 @@ static int kill_accepted(TCP_Server *tcp_server, int index) rm_connection_index(tcp_server, &tcp_server->accepted_connection_array[index], i); } - Socket sock = tcp_server->accepted_connection_array[index].con.sock; + const Socket sock = tcp_server->accepted_connection_array[index].con.sock; if (del_accepted(tcp_server, index) != 0) { return -1; } - kill_sock(sock); + kill_sock(tcp_server->ns, sock); return 0; } -/** return 1 if everything went well. - * return -1 if the connection must be killed. +/** + * @retval 1 if everything went well. + * @retval -1 if the connection must be killed. */ +non_null() static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con, const uint8_t *data, uint16_t length, const uint8_t *self_secret_key) { if (length != TCP_CLIENT_HANDSHAKE_SIZE) { - LOGGER_WARNING(logger, "invalid handshake length: %d != %d", length, TCP_CLIENT_HANDSHAKE_SIZE); + LOGGER_ERROR(logger, "invalid handshake length: %d != %d", length, TCP_CLIENT_HANDSHAKE_SIZE); return -1; } if (con->status != TCP_STATUS_CONNECTED) { - LOGGER_WARNING(logger, "TCP connection %u not connected", (unsigned int)con->identifier); + LOGGER_ERROR(logger, "TCP connection %u not connected", (unsigned int)con->identifier); return -1; } @@ -306,7 +321,7 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con data + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, TCP_HANDSHAKE_PLAIN_SIZE + CRYPTO_MAC_SIZE, plain); if (len != TCP_HANDSHAKE_PLAIN_SIZE) { - LOGGER_WARNING(logger, "invalid TCP handshake decrypted length: %d != %d", len, TCP_HANDSHAKE_PLAIN_SIZE); + LOGGER_ERROR(logger, "invalid TCP handshake decrypted length: %d != %d", len, TCP_HANDSHAKE_PLAIN_SIZE); crypto_memzero(shared_key, sizeof(shared_key)); return -1; } @@ -314,13 +329,13 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con memcpy(con->public_key, data, CRYPTO_PUBLIC_KEY_SIZE); uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; - crypto_new_keypair(resp_plain, temp_secret_key); - random_nonce(con->con.sent_nonce); + crypto_new_keypair(con->con.rng, resp_plain, temp_secret_key); + random_nonce(con->con.rng, con->con.sent_nonce); memcpy(resp_plain + CRYPTO_PUBLIC_KEY_SIZE, con->con.sent_nonce, CRYPTO_NONCE_SIZE); memcpy(con->recv_nonce, plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_NONCE_SIZE); uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; - random_nonce(response); + random_nonce(con->con.rng, response); len = encrypt_data_symmetric(shared_key, response, resp_plain, TCP_HANDSHAKE_PLAIN_SIZE, response + CRYPTO_NONCE_SIZE); @@ -330,9 +345,9 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con return -1; } - IP_Port ipp = {0}; + IP_Port ipp = {{{0}}}; - if (TCP_SERVER_HANDSHAKE_SIZE != net_send(logger, con->con.sock, response, TCP_SERVER_HANDSHAKE_SIZE, &ipp)) { + if (TCP_SERVER_HANDSHAKE_SIZE != net_send(con->con.ns, logger, con->con.sock, response, TCP_SERVER_HANDSHAKE_SIZE, &ipp)) { crypto_memzero(shared_key, sizeof(shared_key)); return -1; } @@ -345,14 +360,16 @@ static int handle_TCP_handshake(const Logger *logger, TCP_Secure_Connection *con return 1; } -/** return 1 if connection handshake was handled correctly. - * return 0 if we didn't get it yet. - * return -1 if the connection must be killed. +/** + * @retval 1 if connection handshake was handled correctly. + * @retval 0 if we didn't get it yet. + * @retval -1 if the connection must be killed. */ +non_null() static int read_connection_handshake(const Logger *logger, TCP_Secure_Connection *con, const uint8_t *self_secret_key) { uint8_t data[TCP_CLIENT_HANDSHAKE_SIZE]; - const int len = read_TCP_packet(logger, con->con.sock, data, TCP_CLIENT_HANDSHAKE_SIZE, &con->con.ip_port); + const int len = read_TCP_packet(logger, con->con.ns, con->con.sock, data, TCP_CLIENT_HANDSHAKE_SIZE, &con->con.ip_port); if (len == -1) { LOGGER_TRACE(logger, "connection handshake is not ready yet"); @@ -362,10 +379,12 @@ static int read_connection_handshake(const Logger *logger, TCP_Secure_Connection return handle_TCP_handshake(logger, con, data, len, self_secret_key); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() static int send_routing_response(const Logger *logger, TCP_Secure_Connection *con, uint8_t rpid, const uint8_t *public_key) { @@ -374,39 +393,45 @@ static int send_routing_response(const Logger *logger, TCP_Secure_Connection *co data[1] = rpid; memcpy(data + 2, public_key, CRYPTO_PUBLIC_KEY_SIZE); - return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1); + return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() static int send_connect_notification(const Logger *logger, TCP_Secure_Connection *con, uint8_t id) { uint8_t data[2] = {TCP_PACKET_CONNECTION_NOTIFICATION, (uint8_t)(id + NUM_RESERVED_PORTS)}; - return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1); + return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true); } -/** return 1 on success. - * return 0 if could not send packet. - * return -1 on failure (connection must be killed). +/** + * @retval 1 on success. + * @retval 0 if could not send packet. + * @retval -1 on failure (connection must be killed). */ +non_null() static int send_disconnect_notification(const Logger *logger, TCP_Secure_Connection *con, uint8_t id) { uint8_t data[2] = {TCP_PACKET_DISCONNECT_NOTIFICATION, (uint8_t)(id + NUM_RESERVED_PORTS)}; - return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), 1); + return write_packet_TCP_secure_connection(logger, &con->con, data, sizeof(data), true); } -/** return 0 on success. - * return -1 on failure (connection must be killed). +/** + * @retval 0 on success. + * @retval -1 on failure (connection must be killed). */ +non_null() static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *public_key) { uint32_t index = -1; TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id]; /* If person tries to cennect to himself we deny the request*/ - if (public_key_cmp(con->public_key, public_key) == 0) { + if (pk_equal(con->public_key, public_key)) { if (send_routing_response(tcp_server->logger, con, 0, public_key) == -1) { return -1; } @@ -416,7 +441,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const for (uint32_t i = 0; i < NUM_CLIENT_CONNECTIONS; ++i) { if (con->connections[i].status != 0) { - if (public_key_cmp(public_key, con->connections[i].public_key) == 0) { + if (pk_equal(public_key, con->connections[i].public_key)) { if (send_routing_response(tcp_server->logger, con, i + NUM_RESERVED_PORTS, public_key) == -1) { return -1; } @@ -436,7 +461,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const return 0; } - int ret = send_routing_response(tcp_server->logger, con, index + NUM_RESERVED_PORTS, public_key); + const int ret = send_routing_response(tcp_server->logger, con, index + NUM_RESERVED_PORTS, public_key); if (ret == 0) { return 0; @@ -448,7 +473,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const con->connections[index].status = 1; memcpy(con->connections[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - int other_index = get_TCP_connection_index(tcp_server, public_key); + const int other_index = get_TCP_connection_index(tcp_server, public_key); if (other_index != -1) { uint32_t other_id = -1; @@ -456,7 +481,7 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const for (uint32_t i = 0; i < NUM_CLIENT_CONNECTIONS; ++i) { if (other_conn->connections[i].status == 1 - && public_key_cmp(other_conn->connections[i].public_key, con->public_key) == 0) { + && pk_equal(other_conn->connections[i].public_key, con->public_key)) { other_id = i; break; } @@ -478,9 +503,11 @@ static int handle_TCP_routing_req(TCP_Server *tcp_server, uint32_t con_id, const return 0; } -/** return 0 on success. - * return -1 on failure (connection must be killed). +/** + * @retval 0 on success. + * @retval -1 on failure (connection must be killed). */ +non_null() static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *public_key, const uint8_t *data, uint16_t length) { @@ -490,7 +517,7 @@ static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const ui const TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id]; - int other_index = get_TCP_connection_index(tcp_server, public_key); + const int other_index = get_TCP_connection_index(tcp_server, public_key); if (other_index != -1) { VLA(uint8_t, resp_packet, 1 + CRYPTO_PUBLIC_KEY_SIZE + length); @@ -498,13 +525,13 @@ static int handle_TCP_oob_send(TCP_Server *tcp_server, uint32_t con_id, const ui memcpy(resp_packet + 1, con->public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(resp_packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length); write_packet_TCP_secure_connection(tcp_server->logger, &tcp_server->accepted_connection_array[other_index].con, - resp_packet, SIZEOF_VLA(resp_packet), 0); + resp_packet, SIZEOF_VLA(resp_packet), false); } return 0; } -/** Remove connection with con_number from the connections array of con. +/** @brief Remove connection with con_number from the connections array of con. * * return -1 on failure. * return 0 on success. @@ -515,7 +542,7 @@ static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *co return -1; } - if (con->connections[con_number].status) { + if (con->connections[con_number].status != 0) { if (con->connections[con_number].status == 2) { const uint32_t index = con->connections[con_number].index; const uint8_t other_id = con->connections[con_number].other_id; @@ -540,35 +567,99 @@ static int rm_connection_index(TCP_Server *tcp_server, TCP_Secure_Connection *co return -1; } +/** @brief Encode con_id and identifier as a custom IP_Port. + * + * @return ip_port. + */ +static IP_Port con_id_to_ip_port(uint32_t con_id, uint64_t identifier) +{ + IP_Port ip_port = {{{0}}}; + ip_port.ip.family = net_family_tcp_client(); + ip_port.ip.ip.v6.uint32[0] = con_id; + ip_port.ip.ip.v6.uint64[1] = identifier; + return ip_port; + +} + +/** @brief Decode ip_port created by con_id_to_ip_port to con_id. + * + * @retval true on success. + * @retval false if ip_port is invalid. + */ +non_null() +static bool ip_port_to_con_id(const TCP_Server *tcp_server, const IP_Port *ip_port, uint32_t *con_id) +{ + *con_id = ip_port->ip.ip.v6.uint32[0]; + + return net_family_is_tcp_client(ip_port->ip.family) && + *con_id < tcp_server->size_accepted_connections && + tcp_server->accepted_connection_array[*con_id].identifier == ip_port->ip.ip.v6.uint64[1]; +} + +non_null() static int handle_onion_recv_1(void *object, const IP_Port *dest, const uint8_t *data, uint16_t length) { TCP_Server *tcp_server = (TCP_Server *)object; - uint32_t index = dest->ip.ip.v6.uint32[0]; + uint32_t index; - if (index >= tcp_server->size_accepted_connections) { + if (!ip_port_to_con_id(tcp_server, dest, &index)) { return 1; } TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[index]; - if (con->identifier != dest->ip.ip.v6.uint64[1]) { - return 1; - } - VLA(uint8_t, packet, 1 + length); memcpy(packet + 1, data, length); packet[0] = TCP_PACKET_ONION_RESPONSE; - if (write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), 0) != 1) { + if (write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), false) != 1) { return 1; } return 0; } -/** return 0 on success - * return -1 on failure +non_null() +static bool handle_forward_reply_tcp(void *object, const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *data, uint16_t length) +{ + TCP_Server *tcp_server = (TCP_Server *)object; + + if (sendback_data_len != 1 + sizeof(uint32_t) + sizeof(uint64_t)) { + return false; + } + + if (*sendback_data != SENDBACK_TCP) { + return false; + } + + uint32_t con_id; + uint64_t identifier; + net_unpack_u32(sendback_data + 1, &con_id); + net_unpack_u64(sendback_data + 1 + sizeof(uint32_t), &identifier); + + if (con_id >= tcp_server->size_accepted_connections) { + return false; + } + + TCP_Secure_Connection *con = &tcp_server->accepted_connection_array[con_id]; + + if (con->identifier != identifier) { + return false; + } + + VLA(uint8_t, packet, 1 + length); + memcpy(packet + 1, data, length); + packet[0] = TCP_PACKET_FORWARDING; + + return write_packet_TCP_secure_connection(tcp_server->logger, &con->con, packet, SIZEOF_VLA(packet), false) == 1; +} + +/** + * @retval 0 on success + * @retval -1 on failure */ +non_null() static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint8_t *data, uint16_t length) { if (length == 0) { @@ -615,7 +706,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint uint8_t response[1 + sizeof(uint64_t)]; response[0] = TCP_PACKET_PONG; memcpy(response + 1, data + 1, sizeof(uint64_t)); - write_packet_TCP_secure_connection(tcp_server->logger, &con->con, response, sizeof(response), 1); + write_packet_TCP_secure_connection(tcp_server->logger, &con->con, response, sizeof(response), true); return 0; } @@ -629,7 +720,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint uint64_t ping_id; memcpy(&ping_id, data + 1, sizeof(uint64_t)); - if (ping_id) { + if (ping_id != 0) { if (ping_id == con->ping_id) { con->ping_id = 0; } @@ -654,17 +745,12 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint case TCP_PACKET_ONION_REQUEST: { LOGGER_TRACE(tcp_server->logger, "handling onion request for %d", con_id); - if (tcp_server->onion) { + if (tcp_server->onion != nullptr) { if (length <= 1 + CRYPTO_NONCE_SIZE + ONION_SEND_BASE * 2) { return -1; } - IP_Port source; - source.port = 0; // dummy initialise - source.ip.family = net_family_tcp_onion; - source.ip.ip.v6.uint32[0] = con_id; - source.ip.ip.v6.uint32[1] = 0; - source.ip.ip.v6.uint64[1] = con->identifier; + IP_Port source = con_id_to_ip_port(con_id, con->identifier); onion_send_1(tcp_server->onion, data + 1 + CRYPTO_NONCE_SIZE, length - (1 + CRYPTO_NONCE_SIZE), &source, data + 1); } @@ -677,6 +763,39 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint return -1; } + case TCP_PACKET_FORWARD_REQUEST: { + if (tcp_server->forwarding == nullptr) { + return -1; + } + + const uint16_t sendback_data_len = 1 + sizeof(uint32_t) + sizeof(uint64_t); + uint8_t sendback_data[1 + sizeof(uint32_t) + sizeof(uint64_t)]; + sendback_data[0] = SENDBACK_TCP; + net_pack_u32(sendback_data + 1, con_id); + net_pack_u64(sendback_data + 1 + sizeof(uint32_t), con->identifier); + + IP_Port dest; + const int ipport_length = unpack_ip_port(&dest, data + 1, length - 1, false); + + if (ipport_length == -1) { + return -1; + } + + const uint8_t *const forward_data = data + (1 + ipport_length); + const uint16_t forward_data_len = length - (1 + ipport_length); + + if (forward_data_len > MAX_FORWARD_DATA_SIZE) { + return -1; + } + + send_forwarding(tcp_server->forwarding, &dest, sendback_data, sendback_data_len, forward_data, forward_data_len); + return 0; + } + + case TCP_PACKET_FORWARDING: { + return -1; + } + default: { if (data[0] < NUM_RESERVED_PORTS) { return -1; @@ -703,7 +822,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint memcpy(new_data, data, length); new_data[0] = other_c_id; const int ret = write_packet_TCP_secure_connection(tcp_server->logger, - &tcp_server->accepted_connection_array[index].con, new_data, length, 0); + &tcp_server->accepted_connection_array[index].con, new_data, length, false); if (ret == -1) { return -1; @@ -717,6 +836,7 @@ static int handle_TCP_packet(TCP_Server *tcp_server, uint32_t con_id, const uint } +non_null() static int confirm_TCP_connection(TCP_Server *tcp_server, const Mono_Time *mono_time, TCP_Secure_Connection *con, const uint8_t *data, uint16_t length) { @@ -740,22 +860,24 @@ static int confirm_TCP_connection(TCP_Server *tcp_server, const Mono_Time *mono_ return index; } -/** return index on success - * return -1 on failure +/** + * @return index on success + * @retval -1 on failure */ +non_null() static int accept_connection(TCP_Server *tcp_server, Socket sock) { if (!sock_valid(sock)) { return -1; } - if (!set_socket_nonblock(sock)) { - kill_sock(sock); + if (!set_socket_nonblock(tcp_server->ns, sock)) { + kill_sock(tcp_server->ns, sock); return -1; } - if (!set_socket_nosigpipe(sock)) { - kill_sock(sock); + if (!set_socket_nosigpipe(tcp_server->ns, sock)) { + kill_sock(tcp_server->ns, sock); return -1; } @@ -769,6 +891,8 @@ static int accept_connection(TCP_Server *tcp_server, Socket sock) } conn->status = TCP_STATUS_CONNECTED; + conn->con.ns = tcp_server->ns; + conn->con.rng = tcp_server->rng; conn->con.sock = sock; conn->next_packet_length = 0; @@ -776,56 +900,70 @@ static int accept_connection(TCP_Server *tcp_server, Socket sock) return index; } -static Socket new_listening_TCP_socket(Family family, uint16_t port) +non_null() +static Socket new_listening_TCP_socket(const Logger *logger, const Network *ns, Family family, uint16_t port) { - Socket sock = net_socket(family, TOX_SOCK_STREAM, TOX_PROTO_TCP); + const Socket sock = net_socket(ns, family, TOX_SOCK_STREAM, TOX_PROTO_TCP); if (!sock_valid(sock)) { + LOGGER_ERROR(logger, "TCP socket creation failed (family = %d)", family.value); return net_invalid_socket; } - int ok = set_socket_nonblock(sock); + bool ok = set_socket_nonblock(ns, sock); if (ok && net_family_is_ipv6(family)) { - ok = set_socket_dualstack(sock); + ok = set_socket_dualstack(ns, sock); } if (ok) { - ok = set_socket_reuseaddr(sock); + ok = set_socket_reuseaddr(ns, sock); } - ok = ok && bind_to_port(sock, family, port) && (net_listen(sock, TCP_MAX_BACKLOG) == 0); + ok = ok && bind_to_port(ns, sock, family, port) && (net_listen(ns, sock, TCP_MAX_BACKLOG) == 0); if (!ok) { - kill_sock(sock); + char *const error = net_new_strerror(net_error()); + LOGGER_WARNING(logger, "could not bind to TCP port %d (family = %d): %s", + port, family.value, error != nullptr ? error : "(null)"); + net_kill_strerror(error); + kill_sock(ns, sock); return net_invalid_socket; } + LOGGER_DEBUG(logger, "successfully bound to TCP port %d", port); return sock; } -TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, - const uint8_t *secret_key, Onion *onion) +TCP_Server *new_TCP_server(const Logger *logger, const Random *rng, const Network *ns, + bool ipv6_enabled, uint16_t num_sockets, + const uint16_t *ports, const uint8_t *secret_key, Onion *onion, Forwarding *forwarding) { if (num_sockets == 0 || ports == nullptr) { + LOGGER_ERROR(logger, "no sockets"); return nullptr; } - if (networking_at_startup() != 0) { + if (ns == nullptr) { + LOGGER_ERROR(logger, "NULL network"); return nullptr; } TCP_Server *temp = (TCP_Server *)calloc(1, sizeof(TCP_Server)); if (temp == nullptr) { + LOGGER_ERROR(logger, "TCP server allocation failed"); return nullptr; } temp->logger = logger; + temp->ns = ns; + temp->rng = rng; temp->socks_listening = (Socket *)calloc(num_sockets, sizeof(Socket)); if (temp->socks_listening == nullptr) { + LOGGER_ERROR(logger, "socket allocation failed"); free(temp); return nullptr; } @@ -834,6 +972,7 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t temp->efd = epoll_create(8); if (temp->efd == -1) { + LOGGER_ERROR(logger, "epoll initialisation failed"); free(temp->socks_listening); free(temp); return nullptr; @@ -841,27 +980,29 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t #endif - const Family family = ipv6_enabled ? net_family_ipv6 : net_family_ipv4; + const Family family = ipv6_enabled ? net_family_ipv6() : net_family_ipv4(); for (uint32_t i = 0; i < num_sockets; ++i) { - Socket sock = new_listening_TCP_socket(family, ports[i]); + const Socket sock = new_listening_TCP_socket(logger, ns, family, ports[i]); + + if (!sock_valid(sock)) { + continue; + } - if (sock_valid(sock)) { #ifdef TCP_SERVER_USE_EPOLL - struct epoll_event ev; + struct epoll_event ev; - ev.events = EPOLLIN | EPOLLET; - ev.data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_LISTENING << 32); + ev.events = EPOLLIN | EPOLLET; + ev.data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_LISTENING << 32); - if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, sock.socket, &ev) == -1) { - continue; - } + if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, sock.sock, &ev) == -1) { + continue; + } #endif - temp->socks_listening[temp->num_listening_socks] = sock; - ++temp->num_listening_socks; - } + temp->socks_listening[temp->num_listening_socks] = sock; + ++temp->num_listening_socks; } if (temp->num_listening_socks == 0) { @@ -870,11 +1011,16 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t return nullptr; } - if (onion) { + if (onion != nullptr) { temp->onion = onion; set_callback_handle_recv_1(onion, &handle_onion_recv_1, temp); } + if (forwarding != nullptr) { + temp->forwarding = forwarding; + set_callback_forward_reply(forwarding, &handle_forward_reply_tcp, temp); + } + memcpy(temp->secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE); crypto_derive_public_key(temp->public_key, temp->secret_key); @@ -884,18 +1030,20 @@ TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t } #ifndef TCP_SERVER_USE_EPOLL +non_null() static void do_TCP_accept_new(TCP_Server *tcp_server) { for (uint32_t i = 0; i < tcp_server->num_listening_socks; ++i) { Socket sock; do { - sock = net_accept(tcp_server->socks_listening[i]); + sock = net_accept(tcp_server->ns, tcp_server->socks_listening[i]); } while (accept_connection(tcp_server, sock) != -1); } } #endif +non_null() static int do_incoming(TCP_Server *tcp_server, uint32_t i) { TCP_Secure_Connection *const conn = &tcp_server->incoming_connection_queue[i]; @@ -923,7 +1071,7 @@ static int do_incoming(TCP_Server *tcp_server, uint32_t i) TCP_Secure_Connection *conn_new = &tcp_server->unconfirmed_connection_queue[index_new]; if (conn_new->status != TCP_STATUS_NO_STATUS) { - LOGGER_WARNING(tcp_server->logger, "incoming connection %d would overwrite existing", i); + LOGGER_ERROR(tcp_server->logger, "incoming connection %d would overwrite existing", i); kill_TCP_secure_connection(conn_new); } @@ -933,6 +1081,7 @@ static int do_incoming(TCP_Server *tcp_server, uint32_t i) return index_new; } +non_null() static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, uint32_t i) { TCP_Secure_Connection *const conn = &tcp_server->unconfirmed_connection_queue[i]; @@ -944,8 +1093,7 @@ static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, ui LOGGER_TRACE(tcp_server->logger, "handling unconfirmed TCP connection %d", i); uint8_t packet[MAX_PACKET_SIZE]; - const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.sock, &conn->next_packet_length, - conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port); + const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port); if (len == 0) { return -1; @@ -959,13 +1107,13 @@ static int do_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time, ui return confirm_TCP_connection(tcp_server, mono_time, conn, packet, len); } +non_null() static bool tcp_process_secure_packet(TCP_Server *tcp_server, uint32_t i) { TCP_Secure_Connection *const conn = &tcp_server->accepted_connection_array[i]; uint8_t packet[MAX_PACKET_SIZE]; - const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.sock, &conn->next_packet_length, - conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port); + const int len = read_packet_TCP_secure_connection(tcp_server->logger, conn->con.ns, conn->con.sock, &conn->next_packet_length, conn->con.shared_key, conn->recv_nonce, packet, sizeof(packet), &conn->con.ip_port); LOGGER_TRACE(tcp_server->logger, "processing packet for %d: %d", i, len); if (len == 0) { @@ -986,6 +1134,7 @@ static bool tcp_process_secure_packet(TCP_Server *tcp_server, uint32_t i) return true; } +non_null() static void do_confirmed_recv(TCP_Server *tcp_server, uint32_t i) { while (tcp_process_secure_packet(tcp_server, i)) { @@ -995,6 +1144,7 @@ static void do_confirmed_recv(TCP_Server *tcp_server, uint32_t i) } #ifndef TCP_SERVER_USE_EPOLL +non_null() static void do_TCP_incoming(TCP_Server *tcp_server) { for (uint32_t i = 0; i < MAX_INCOMING_CONNECTIONS; ++i) { @@ -1002,6 +1152,7 @@ static void do_TCP_incoming(TCP_Server *tcp_server) } } +non_null() static void do_TCP_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_time) { for (uint32_t i = 0; i < MAX_INCOMING_CONNECTIONS; ++i) { @@ -1010,6 +1161,7 @@ static void do_TCP_unconfirmed(TCP_Server *tcp_server, const Mono_Time *mono_tim } #endif +non_null() static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time) { #ifdef TCP_SERVER_USE_EPOLL @@ -1031,14 +1183,14 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time) if (mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_FREQUENCY)) { uint8_t ping[1 + sizeof(uint64_t)]; ping[0] = TCP_PACKET_PING; - uint64_t ping_id = random_u64(); + uint64_t ping_id = random_u64(conn->con.rng); - if (!ping_id) { + if (ping_id == 0) { ++ping_id; } memcpy(ping + 1, &ping_id, sizeof(uint64_t)); - int ret = write_packet_TCP_secure_connection(tcp_server->logger, &conn->con, ping, sizeof(ping), 1); + const int ret = write_packet_TCP_secure_connection(tcp_server->logger, &conn->con, ping, sizeof(ping), true); if (ret == 1) { conn->last_pinged = mono_time_get(mono_time); @@ -1051,7 +1203,7 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time) } } - if (conn->ping_id && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) { + if (conn->ping_id != 0 && mono_time_is_timeout(mono_time, conn->last_pinged, TCP_PING_TIMEOUT)) { kill_accepted(tcp_server, i); continue; } @@ -1067,6 +1219,7 @@ static void do_TCP_confirmed(TCP_Server *tcp_server, const Mono_Time *mono_time) } #ifdef TCP_SERVER_USE_EPOLL +non_null() static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time) { #define MAX_EVENTS 16 @@ -1079,7 +1232,7 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time const int status = (events[n].data.u64 >> 32) & 0xFF; const int index = events[n].data.u64 >> 40; - if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (events[n].events & EPOLLRDHUP)) { + if ((events[n].events & EPOLLERR) != 0 || (events[n].events & EPOLLHUP) != 0 || (events[n].events & EPOLLRDHUP) != 0) { switch (status) { case TCP_SOCKET_LISTENING: { // should never happen @@ -1110,15 +1263,15 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time } - if (!(events[n].events & EPOLLIN)) { + if ((events[n].events & EPOLLIN) == 0) { continue; } switch (status) { case TCP_SOCKET_LISTENING: { // socket is from socks_listening, accept connection - while (1) { - Socket sock_new = net_accept(sock); + while (true) { + const Socket sock_new = net_accept(tcp_server->ns, sock); if (!sock_valid(sock_new)) { break; @@ -1134,9 +1287,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP; - ev.data.u64 = sock_new.socket | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40); + ev.data.u64 = sock_new.sock | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40); - if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, sock_new.socket, &ev) == -1) { + if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, sock_new.sock, &ev) == -1) { LOGGER_DEBUG(tcp_server->logger, "new connection %d was dropped due to epoll error %d", index, net_error()); kill_TCP_secure_connection(&tcp_server->incoming_connection_queue[index_new]); continue; @@ -1152,9 +1305,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time if (index_new != -1) { LOGGER_TRACE(tcp_server->logger, "incoming connection %d was accepted as %d", index, index_new); events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP; - events[n].data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40); + events[n].data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40); - if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.socket, &events[n]) == -1) { + if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.sock, &events[n]) == -1) { LOGGER_DEBUG(tcp_server->logger, "incoming connection %d was dropped due to epoll error %d", index, net_error()); kill_TCP_secure_connection(&tcp_server->unconfirmed_connection_queue[index_new]); break; @@ -1170,9 +1323,9 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time if (index_new != -1) { LOGGER_TRACE(tcp_server->logger, "unconfirmed connection %d was confirmed as %d", index, index_new); events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP; - events[n].data.u64 = sock.socket | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40); + events[n].data.u64 = sock.sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40); - if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.socket, &events[n]) == -1) { + if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, sock.sock, &events[n]) == -1) { // remove from confirmed connections LOGGER_DEBUG(tcp_server->logger, "unconfirmed connection %d was dropped due to epoll error %d", index, net_error()); kill_accepted(tcp_server, index_new); @@ -1193,6 +1346,7 @@ static bool tcp_epoll_process(TCP_Server *tcp_server, const Mono_Time *mono_time return nfds > 0; } +non_null() static void do_TCP_epoll(TCP_Server *tcp_server, const Mono_Time *mono_time) { while (tcp_epoll_process(tcp_server, mono_time)) { @@ -1218,14 +1372,22 @@ void do_TCP_server(TCP_Server *tcp_server, const Mono_Time *mono_time) void kill_TCP_server(TCP_Server *tcp_server) { + if (tcp_server == nullptr) { + return; + } + for (uint32_t i = 0; i < tcp_server->num_listening_socks; ++i) { - kill_sock(tcp_server->socks_listening[i]); + kill_sock(tcp_server->ns, tcp_server->socks_listening[i]); } - if (tcp_server->onion) { + if (tcp_server->onion != nullptr) { set_callback_handle_recv_1(tcp_server->onion, nullptr, nullptr); } + if (tcp_server->forwarding != nullptr) { + set_callback_forward_reply(tcp_server->forwarding, nullptr, nullptr); + } + bs_list_free(&tcp_server->accepted_key_list); #ifdef TCP_SERVER_USE_EPOLL diff --git a/protocols/Tox/libtox/src/toxcore/TCP_server.h b/protocols/Tox/libtox/src/toxcore/TCP_server.h index 3269ed3b0a..2224938a09 100644 --- a/protocols/Tox/libtox/src/toxcore/TCP_server.h +++ b/protocols/Tox/libtox/src/toxcore/TCP_server.h @@ -10,6 +10,7 @@ #define C_TOXCORE_TOXCORE_TCP_SERVER_H #include "crypto_core.h" +#include "forwarding.h" #include "onion.h" #define MAX_INCOMING_CONNECTIONS 256 @@ -27,20 +28,23 @@ typedef enum TCP_Status { typedef struct TCP_Server TCP_Server; +non_null() const uint8_t *tcp_server_public_key(const TCP_Server *tcp_server); +non_null() size_t tcp_server_listen_count(const TCP_Server *tcp_server); -/** Create new TCP server instance. - */ -TCP_Server *new_TCP_server(const Logger *logger, uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, - const uint8_t *secret_key, Onion *onion); +/** Create new TCP server instance. */ +non_null(1, 2, 3, 6, 7) nullable(8, 9) +TCP_Server *new_TCP_server(const Logger *logger, const Random *rng, const Network *ns, + bool ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, + const uint8_t *secret_key, Onion *onion, Forwarding *forwarding); -/** Run the TCP_server - */ +/** Run the TCP_server */ +non_null() void do_TCP_server(TCP_Server *tcp_server, const Mono_Time *mono_time); -/** Kill the TCP server - */ +/** Kill the TCP server */ +nullable(1) void kill_TCP_server(TCP_Server *tcp_server); diff --git a/protocols/Tox/libtox/src/toxcore/announce.c b/protocols/Tox/libtox/src/toxcore/announce.c new file mode 100644 index 0000000000..7914d19615 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/announce.c @@ -0,0 +1,689 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2020-2021 The TokTok team. + */ + +/** + * "Server side" of the DHT announcements protocol. + */ + +#include "announce.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "LAN_discovery.h" +#include "ccompat.h" +#include "timed_auth.h" +#include "util.h" + +uint8_t announce_response_of_request_type(uint8_t request_type) +{ + switch (request_type) { + case NET_PACKET_DATA_SEARCH_REQUEST: + return NET_PACKET_DATA_SEARCH_RESPONSE; + + case NET_PACKET_DATA_RETRIEVE_REQUEST: + return NET_PACKET_DATA_RETRIEVE_RESPONSE; + + case NET_PACKET_STORE_ANNOUNCE_REQUEST: + return NET_PACKET_STORE_ANNOUNCE_RESPONSE; + + default: { + assert(false); + return NET_PACKET_MAX; + } + } +} + +typedef struct Announce_Entry { + uint64_t store_until; + uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t *data; + uint32_t length; +} Announce_Entry; + +struct Announcements { + const Logger *log; + const Random *rng; + Forwarding *forwarding; + const Mono_Time *mono_time; + DHT *dht; + Networking_Core *net; + const uint8_t *public_key; + const uint8_t *secret_key; + + Shared_Keys shared_keys; + uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; + + int32_t synch_offset; + + uint64_t start_time; + + Announce_Entry entries[ANNOUNCE_BUCKETS * ANNOUNCE_BUCKET_SIZE]; +}; + +void announce_set_synch_offset(Announcements *announce, int32_t synch_offset) +{ + announce->synch_offset = synch_offset; +} + +/** + * An entry is considered to be "deleted" for the purposes of the protocol + * once it has timed out. + */ +non_null() +static bool entry_is_empty(const Announcements *announce, const Announce_Entry *entry) +{ + return mono_time_get(announce->mono_time) >= entry->store_until; +} + +non_null() +static void delete_entry(Announce_Entry *entry) +{ + entry->store_until = 0; +} + +/** Return bits (at most 8) from pk starting at index as uint8_t */ +non_null() +static uint8_t truncate_pk_at_index(const uint8_t *pk, uint16_t index, uint16_t bits) +{ + assert(bits < 8); + const uint8_t i = index / 8; + const uint8_t j = index % 8; + return ((uint8_t)((i < CRYPTO_PUBLIC_KEY_SIZE ? pk[i] : 0) << j) >> (8 - bits)) | + ((i + 1 < CRYPTO_PUBLIC_KEY_SIZE ? pk[i + 1] : 0) >> (16 - bits - j)); +} + +uint16_t announce_get_bucketnum(const uint8_t *base, const uint8_t *pk) +{ + const uint16_t index = bit_by_bit_cmp(base, pk); + + return truncate_pk_at_index(base, index + 1, ANNOUNCE_BUCKET_PREFIX_LENGTH) ^ + truncate_pk_at_index(pk, index + 1, ANNOUNCE_BUCKET_PREFIX_LENGTH); +} + +non_null() +static Announce_Entry *bucket_of_key(Announcements *announce, const uint8_t *pk) +{ + return &announce->entries[announce_get_bucketnum(announce->public_key, pk) * ANNOUNCE_BUCKET_SIZE]; +} + +non_null() +static Announce_Entry *get_stored(Announcements *announce, const uint8_t *data_public_key) +{ + Announce_Entry *const bucket = bucket_of_key(announce, data_public_key); + + for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) { + if (pk_equal(bucket[i].data_public_key, data_public_key)) { + if (entry_is_empty(announce, &bucket[i])) { + break; + } + + return &bucket[i]; + } + } + + return nullptr; +} + +non_null() +static const Announce_Entry *bucket_of_key_const(const Announcements *announce, const uint8_t *pk) +{ + return &announce->entries[announce_get_bucketnum(announce->public_key, pk) * ANNOUNCE_BUCKET_SIZE]; +} + +non_null() +static const Announce_Entry *get_stored_const(const Announcements *announce, const uint8_t *data_public_key) +{ + const Announce_Entry *const bucket = bucket_of_key_const(announce, data_public_key); + + for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) { + if (pk_equal(bucket[i].data_public_key, data_public_key)) { + if (entry_is_empty(announce, &bucket[i])) { + break; + } + + return &bucket[i]; + } + } + + return nullptr; +} + + +bool announce_on_stored(const Announcements *announce, const uint8_t *data_public_key, + announce_on_retrieve_cb *on_retrieve_callback, void *object) +{ + const Announce_Entry *const entry = get_stored_const(announce, data_public_key); + + if (entry == nullptr || entry->data == nullptr) { + return false; + } + + if (on_retrieve_callback != nullptr) { + on_retrieve_callback(object, entry->data, entry->length); + } + + return true; +} + +/** + * Return existing entry for this key if it exists, else an empty + * slot in the key's bucket if one exists, else an entry in the key's bucket + * of greatest 2-adic distance greater than that of the key bucket if one + * exists, else nullptr. + */ +non_null() +static Announce_Entry *find_entry_slot(Announcements *announce, const uint8_t *data_public_key) +{ + Announce_Entry *const bucket = bucket_of_key(announce, data_public_key); + + Announce_Entry *slot = nullptr; + uint16_t min_index = bit_by_bit_cmp(announce->public_key, data_public_key); + + for (uint32_t i = 0; i < ANNOUNCE_BUCKET_SIZE; ++i) { + if (pk_equal(bucket[i].data_public_key, data_public_key)) { + return &bucket[i]; + } + + if (entry_is_empty(announce, &bucket[i])) { + slot = &bucket[i]; + min_index = 0; + continue; + } + + const uint16_t index = bit_by_bit_cmp(announce->public_key, bucket[i].data_public_key); + + if (index < min_index) { + slot = &bucket[i]; + min_index = index; + } + } + + return slot; +} + +non_null() +static bool would_accept_store_request(Announcements *announce, const uint8_t *data_public_key) +{ + return find_entry_slot(announce, data_public_key) != nullptr; +} + +bool announce_store_data(Announcements *announce, const uint8_t *data_public_key, + const uint8_t *data, uint32_t length, uint32_t timeout) +{ + if (length > MAX_ANNOUNCEMENT_SIZE) { + return false; + } + + Announce_Entry *entry = find_entry_slot(announce, data_public_key); + + if (entry == nullptr) { + return false; + } + + if (length > 0) { + assert(data != nullptr); + + if (entry->data != nullptr) { + free(entry->data); + } + + entry->data = (uint8_t *)malloc(length); + + if (entry->data == nullptr) { + return false; + } + + memcpy(entry->data, data, length); + } + + entry->length = length; + memcpy(entry->data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + entry->store_until = mono_time_get(announce->mono_time) + timeout; + + return true; +} + +non_null() +static uint32_t calculate_timeout(const Announcements *announce, uint32_t requested_timeout) +{ + const uint64_t uptime = mono_time_get(announce->mono_time) - announce->start_time; + const uint32_t max_announcement_timeout = max_u32( + (uint32_t)min_u64( + MAX_MAX_ANNOUNCEMENT_TIMEOUT, + uptime / MAX_ANNOUNCEMENT_TIMEOUT_UPTIME_RATIO), + MIN_MAX_ANNOUNCEMENT_TIMEOUT); + + return min_u32(max_announcement_timeout, requested_timeout); +} + +#define DATA_SEARCH_TO_AUTH_MAX_SIZE (CRYPTO_PUBLIC_KEY_SIZE * 2 + MAX_PACKED_IPPORT_SIZE + MAX_SENDBACK_SIZE) + +non_null(1, 2, 3, 4, 7) nullable(5) +static int create_data_search_to_auth(const Logger *logger, const uint8_t *data_public_key, + const uint8_t *requester_key, + const IP_Port *source, const uint8_t *sendback, uint16_t sendback_length, + uint8_t *dest, uint16_t max_length) +{ + if (max_length < DATA_SEARCH_TO_AUTH_MAX_SIZE + || sendback_length > MAX_SENDBACK_SIZE) { + return -1; + } + + memcpy(dest, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + memcpy(dest + CRYPTO_PUBLIC_KEY_SIZE, requester_key, CRYPTO_PUBLIC_KEY_SIZE); + + const int ipport_length = pack_ip_port(logger, dest + CRYPTO_PUBLIC_KEY_SIZE * 2, MAX_PACKED_IPPORT_SIZE, source); + + if (ipport_length == -1) { + return -1; + } + + if (sendback_length > 0) { + assert(sendback != nullptr); + memcpy(dest + CRYPTO_PUBLIC_KEY_SIZE * 2 + ipport_length, sendback, sendback_length); + } + + return CRYPTO_PUBLIC_KEY_SIZE * 2 + ipport_length + sendback_length; +} + +#define DATA_SEARCH_TIMEOUT 60 + +non_null() +static int create_reply_plain_data_search_request(Announcements *announce, + const IP_Port *source, + const uint8_t *data, uint16_t length, + uint8_t *reply, uint16_t reply_max_length, + uint8_t *to_auth, uint16_t to_auth_length) +{ + if (length != CRYPTO_PUBLIC_KEY_SIZE && + length != CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA256_SIZE) { + return -1; + } + + const uint8_t *const data_public_key = data; + + const uint8_t *previous_hash = nullptr; + + if (length == CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA256_SIZE) { + previous_hash = data + CRYPTO_PUBLIC_KEY_SIZE; + } + + const int nodes_max_length = (int)reply_max_length - + (CRYPTO_PUBLIC_KEY_SIZE + 1 + CRYPTO_SHA256_SIZE + TIMED_AUTH_SIZE + 1 + 1); + + if (nodes_max_length < 0) { + return -1; + } + + uint8_t *p = reply; + + memcpy(p, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + p += CRYPTO_PUBLIC_KEY_SIZE; + + const Announce_Entry *const stored = get_stored_const(announce, data_public_key); + + if (stored == nullptr) { + *p = 0; + ++p; + } else { + *p = 1; + ++p; + crypto_sha256(p, stored->data, stored->length); + p += CRYPTO_SHA256_SIZE; + } + + generate_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key, + to_auth, to_auth_length, p); + p += TIMED_AUTH_SIZE; + + *p = would_accept_store_request(announce, data_public_key); + ++p; + + Node_format nodes_list[MAX_SENT_NODES]; + const int num_nodes = get_close_nodes(announce->dht, data_public_key, nodes_list, + net_family_unspec(), ip_is_lan(&source->ip), true); + + if (num_nodes < 0 || num_nodes > MAX_SENT_NODES) { + return -1; + } + + *p = num_nodes; + ++p; + + p += pack_nodes(announce->log, p, nodes_max_length, nodes_list, num_nodes); + + const uint32_t reply_len = p - reply; + + if (previous_hash != nullptr) { + uint8_t hash[CRYPTO_SHA256_SIZE]; + + crypto_sha256(hash, reply, reply_len); + + if (crypto_sha256_eq(hash, previous_hash)) { + return CRYPTO_PUBLIC_KEY_SIZE; + } + } + + return reply_len; +} + +non_null() +static int create_reply_plain_data_retrieve_request(Announcements *announce, + const IP_Port *source, + const uint8_t *data, uint16_t length, + uint8_t *reply, uint16_t reply_max_length, + uint8_t *to_auth, uint16_t to_auth_length) +{ + if (length != CRYPTO_PUBLIC_KEY_SIZE + 1 + TIMED_AUTH_SIZE) { + return -1; + } + + if (data[CRYPTO_PUBLIC_KEY_SIZE] != 0) { + return -1; + } + + const uint8_t *const data_public_key = data; + const uint8_t *const auth = data + CRYPTO_PUBLIC_KEY_SIZE + 1; + + if (!check_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key, + to_auth, to_auth_length, auth)) { + return -1; + } + + const Announce_Entry *const entry = get_stored_const(announce, data_public_key); + + if (entry == nullptr) { + return -1; + } + + const uint16_t reply_len = CRYPTO_PUBLIC_KEY_SIZE + 1 + entry->length; + + if (reply_max_length < reply_len) { + return -1; + } + + memcpy(reply, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + reply[CRYPTO_PUBLIC_KEY_SIZE] = 1; + memcpy(reply + CRYPTO_PUBLIC_KEY_SIZE + 1, entry->data, entry->length); + + return reply_len; +} + +non_null() +static int create_reply_plain_store_announce_request(Announcements *announce, + const IP_Port *source, + const uint8_t *data, uint16_t length, + uint8_t *reply, uint16_t reply_max_length, + uint8_t *to_auth, uint16_t to_auth_length) +{ + const int plain_len = (int)length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE); + const int announcement_len = (int)plain_len - (TIMED_AUTH_SIZE + sizeof(uint32_t) + 1); + + const uint8_t *const data_public_key = data; + + if (announcement_len < 0) { + return -1; + } + + VLA(uint8_t, plain, plain_len); + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + + get_shared_key(announce->mono_time, &announce->shared_keys, shared_key, + announce->secret_key, data_public_key); + + if (decrypt_data_symmetric(shared_key, + data + CRYPTO_PUBLIC_KEY_SIZE, + data + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, + plain_len + CRYPTO_MAC_SIZE, + plain) != plain_len) { + return -1; + } + + const uint8_t *const auth = plain; + uint32_t requested_timeout; + net_unpack_u32(plain + TIMED_AUTH_SIZE, &requested_timeout); + const uint32_t timeout = calculate_timeout(announce, requested_timeout); + const uint8_t announcement_type = plain[TIMED_AUTH_SIZE + sizeof(uint32_t)]; + const uint8_t *announcement = plain + TIMED_AUTH_SIZE + sizeof(uint32_t) + 1; + + if (!check_timed_auth(announce->mono_time, DATA_SEARCH_TIMEOUT, announce->hmac_key, + to_auth, to_auth_length, auth)) { + return -1; + } + + if (announcement_type > 1) { + return -1; + } + + if (announcement_type == 1) { + if (announcement_len != CRYPTO_SHA256_SIZE) { + return -1; + } + + Announce_Entry *stored = get_stored(announce, data_public_key); + + if (stored == nullptr) { + return -1; + } + + uint8_t stored_hash[CRYPTO_SHA256_SIZE]; + crypto_sha256(stored_hash, stored->data, stored->length); + + if (!crypto_sha256_eq(announcement, stored_hash)) { + delete_entry(stored); + return -1; + } else { + stored->store_until = mono_time_get(announce->mono_time) + timeout; + } + } else { + if (!announce_store_data(announce, data_public_key, announcement, announcement_len, timeout)) { + return -1; + } + } + + const uint16_t reply_len = CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t) + sizeof(uint64_t); + + if (reply_max_length < reply_len) { + return -1; + } + + memcpy(reply, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); + net_pack_u32(reply + CRYPTO_PUBLIC_KEY_SIZE, timeout); + net_pack_u64(reply + CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint32_t), + mono_time_get(announce->mono_time) + announce->synch_offset); + return reply_len; +} + +non_null(1, 2, 3, 7, 9) nullable(5) +static int create_reply_plain(Announcements *announce, + const uint8_t *requester_key, const IP_Port *source, uint8_t type, + const uint8_t *sendback, uint16_t sendback_length, + const uint8_t *data, uint16_t length, + uint8_t *reply, uint16_t reply_max_length) +{ + if (length < CRYPTO_PUBLIC_KEY_SIZE) { + return -1; + } + + const uint8_t *const data_public_key = data; + + uint8_t to_auth[DATA_SEARCH_TO_AUTH_MAX_SIZE]; + const int to_auth_length = create_data_search_to_auth(announce->log, data_public_key, requester_key, source, + sendback, sendback_length, to_auth, DATA_SEARCH_TO_AUTH_MAX_SIZE); + + if (to_auth_length == -1) { + return -1; + } + + switch (type) { + case NET_PACKET_DATA_SEARCH_REQUEST: + return create_reply_plain_data_search_request(announce, source, data, length, reply, reply_max_length, to_auth, + (uint16_t)to_auth_length); + + case NET_PACKET_DATA_RETRIEVE_REQUEST: + return create_reply_plain_data_retrieve_request(announce, source, data, length, reply, reply_max_length, to_auth, + (uint16_t)to_auth_length); + + case NET_PACKET_STORE_ANNOUNCE_REQUEST: + return create_reply_plain_store_announce_request(announce, source, data, length, reply, reply_max_length, to_auth, + (uint16_t)to_auth_length); + + default: + return -1; + } +} + +non_null(1, 2, 5, 7) nullable(3) +static int create_reply(Announcements *announce, const IP_Port *source, + const uint8_t *sendback, uint16_t sendback_length, + const uint8_t *data, uint16_t length, + uint8_t *reply, uint16_t reply_max_length) +{ + const int plain_len = (int)length - (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE); + + if (plain_len < (int)sizeof(uint64_t)) { + return -1; + } + + VLA(uint8_t, plain, plain_len); + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + + dht_get_shared_key_recv(announce->dht, shared_key, data + 1); + + if (decrypt_data_symmetric(shared_key, + data + 1 + CRYPTO_PUBLIC_KEY_SIZE, + data + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, + plain_len + CRYPTO_MAC_SIZE, + plain) != plain_len) { + return -1; + } + + const int plain_reply_max_len = (int)reply_max_length - + (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE); + + if (plain_reply_max_len < sizeof(uint64_t)) { + return -1; + } + + VLA(uint8_t, plain_reply, plain_reply_max_len); + + const int plain_reply_noping_len = create_reply_plain(announce, + data + 1, source, data[0], + sendback, sendback_length, + plain, plain_len - sizeof(uint64_t), + plain_reply, plain_reply_max_len - sizeof(uint64_t)); + + if (plain_reply_noping_len == -1) { + return -1; + } + + memcpy(plain_reply + plain_reply_noping_len, + plain + (plain_len - sizeof(uint64_t)), sizeof(uint64_t)); + + const uint16_t plain_reply_len = plain_reply_noping_len + sizeof(uint64_t); + + const uint8_t response_type = announce_response_of_request_type(data[0]); + + return dht_create_packet(announce->rng, announce->public_key, shared_key, response_type, + plain_reply, plain_reply_len, reply, reply_max_length); +} + +non_null(1, 2, 3, 5) nullable(7) +static void forwarded_request_callback(void *object, const IP_Port *forwarder, + const uint8_t *sendback, uint16_t sendback_length, + const uint8_t *data, uint16_t length, void *userdata) +{ + Announcements *announce = (Announcements *) object; + + uint8_t reply[MAX_FORWARD_DATA_SIZE]; + + const int len = create_reply(announce, forwarder, + sendback, sendback_length, + data, length, reply, sizeof(reply)); + + if (len == -1) { + return; + } + + forward_reply(announce->net, forwarder, sendback, sendback_length, reply, len); +} + +non_null(1, 2, 3) nullable(5) +static int handle_dht_announce_request(void *object, const IP_Port *source, + const uint8_t *data, uint16_t length, void *userdata) +{ + Announcements *announce = (Announcements *) object; + + uint8_t reply[MAX_FORWARD_DATA_SIZE]; + + const int len = create_reply(announce, source, + nullptr, 0, + data, length, reply, sizeof(reply)); + + if (len == -1) { + return -1; + } + + return sendpacket(announce->net, source, reply, len) == len ? 0 : -1; +} + +Announcements *new_announcements(const Logger *log, const Random *rng, const Mono_Time *mono_time, + Forwarding *forwarding) +{ + if (log == nullptr || mono_time == nullptr || forwarding == nullptr) { + return nullptr; + } + + Announcements *announce = (Announcements *)calloc(1, sizeof(Announcements)); + + if (announce == nullptr) { + return nullptr; + } + + announce->log = log; + announce->rng = rng; + announce->forwarding = forwarding; + announce->mono_time = mono_time; + announce->dht = forwarding_get_dht(forwarding); + announce->net = dht_get_net(announce->dht); + announce->public_key = dht_get_self_public_key(announce->dht); + announce->secret_key = dht_get_self_secret_key(announce->dht); + new_hmac_key(announce->rng, announce->hmac_key); + + announce->start_time = mono_time_get(announce->mono_time); + + set_callback_forwarded_request(forwarding, forwarded_request_callback, announce); + + networking_registerhandler(announce->net, NET_PACKET_DATA_SEARCH_REQUEST, handle_dht_announce_request, announce); + networking_registerhandler(announce->net, NET_PACKET_DATA_RETRIEVE_REQUEST, handle_dht_announce_request, announce); + networking_registerhandler(announce->net, NET_PACKET_STORE_ANNOUNCE_REQUEST, handle_dht_announce_request, announce); + + return announce; +} + +void kill_announcements(Announcements *announce) +{ + if (announce == nullptr) { + return; + } + + set_callback_forwarded_request(announce->forwarding, nullptr, nullptr); + + networking_registerhandler(announce->net, NET_PACKET_DATA_SEARCH_REQUEST, nullptr, nullptr); + networking_registerhandler(announce->net, NET_PACKET_DATA_RETRIEVE_REQUEST, nullptr, nullptr); + networking_registerhandler(announce->net, NET_PACKET_STORE_ANNOUNCE_REQUEST, nullptr, nullptr); + + crypto_memzero(announce->hmac_key, CRYPTO_HMAC_KEY_SIZE); + crypto_memzero(&announce->shared_keys, sizeof(Shared_Keys)); + + for (uint32_t i = 0; i < ANNOUNCE_BUCKETS * ANNOUNCE_BUCKET_SIZE; ++i) { + if (announce->entries[i].data != nullptr) { + free(announce->entries[i].data); + } + } + + free(announce); +} diff --git a/protocols/Tox/libtox/src/toxcore/announce.h b/protocols/Tox/libtox/src/toxcore/announce.h new file mode 100644 index 0000000000..e680da68b6 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/announce.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2020-2021 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_ANNOUNCE_H +#define C_TOXCORE_TOXCORE_ANNOUNCE_H + +#include "forwarding.h" + +#define MAX_ANNOUNCEMENT_SIZE 512 + +typedef void announce_on_retrieve_cb(void *object, const uint8_t *data, uint16_t length); + +uint8_t announce_response_of_request_type(uint8_t request_type); + +typedef struct Announcements Announcements; + +non_null() +Announcements *new_announcements(const Logger *log, const Random *rng, const Mono_Time *mono_time, Forwarding *forwarding); + +/** + * @brief If data is stored, run `on_retrieve_callback` on it. + * + * @return true if data is stored, false otherwise. + */ +non_null(1, 2) nullable(3, 4) +bool announce_on_stored(const Announcements *announce, const uint8_t *data_public_key, + announce_on_retrieve_cb *on_retrieve_callback, void *object); + +non_null() +void announce_set_synch_offset(Announcements *announce, int32_t synch_offset); + +nullable(1) +void kill_announcements(Announcements *announce); + + +/* The declarations below are not public, they are exposed only for tests. */ + +/** @private + * Return xor of first ANNOUNCE_BUCKET_PREFIX_LENGTH bits from one bit after + * base and pk first differ + */ +non_null() +uint16_t announce_get_bucketnum(const uint8_t *base, const uint8_t *pk); + +/** @private */ +non_null(1, 2) nullable(3) +bool announce_store_data(Announcements *announce, const uint8_t *data_public_key, + const uint8_t *data, uint32_t length, uint32_t timeout); + +/** @private */ +#define MAX_MAX_ANNOUNCEMENT_TIMEOUT 900 +#define MIN_MAX_ANNOUNCEMENT_TIMEOUT 10 +#define MAX_ANNOUNCEMENT_TIMEOUT_UPTIME_RATIO 4 + +/** @private + * For efficient lookup and updating, entries are stored as a hash table keyed + * to the first ANNOUNCE_BUCKET_PREFIX_LENGTH bits starting from one bit after + * the first bit in which data public key first differs from the dht key, with + * (2-adically) closest keys preferentially stored within a given bucket. A + * given key appears at most once (even if timed out). + */ +#define ANNOUNCE_BUCKET_SIZE 8 +#define ANNOUNCE_BUCKET_PREFIX_LENGTH 5 +#define ANNOUNCE_BUCKETS 32 // ANNOUNCE_BUCKETS = 2 ** ANNOUNCE_BUCKET_PREFIX_LENGTH + +#endif diff --git a/protocols/Tox/libtox/src/toxcore/attributes.h b/protocols/Tox/libtox/src/toxcore/attributes.h new file mode 100644 index 0000000000..3da768f2a3 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/attributes.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +/** + * printf and nonnull attributes for GCC/Clang and Cimple. + */ +#ifndef C_TOXCORE_TOXCORE_ATTRIBUTES_H +#define C_TOXCORE_TOXCORE_ATTRIBUTES_H + +/* No declarations here. */ + +//!TOKSTYLE- + +#ifdef __GNUC__ +#define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a))) +#else +#define GNU_PRINTF(f, a) +#endif + +#if defined(__GNUC__) && defined(_DEBUG) && !defined(__OPTIMIZE__) +#define non_null(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +#define non_null(...) +#endif + +#define nullable(...) + +//!TOKSTYLE+ + +#endif // C_TOXCORE_TOXCORE_ATTRIBUTES_H diff --git a/protocols/Tox/libtox/src/toxcore/bin_pack.c b/protocols/Tox/libtox/src/toxcore/bin_pack.c new file mode 100644 index 0000000000..ca8940ee44 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/bin_pack.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "bin_pack.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../third_party/cmp/cmp.h" +#include "ccompat.h" + +struct Bin_Pack { + uint8_t *bytes; + uint32_t bytes_size; + uint32_t bytes_pos; + cmp_ctx_t ctx; +}; + +non_null() +static bool null_reader(cmp_ctx_t *ctx, void *data, size_t limit) +{ + assert(limit == 0); + return false; +} + +non_null() +static bool null_skipper(cmp_ctx_t *ctx, size_t limit) +{ + assert(limit == 0); + return false; +} + +non_null() +static size_t buf_writer(cmp_ctx_t *ctx, const void *data, size_t count) +{ + Bin_Pack *bp = (Bin_Pack *)ctx->buf; + assert(bp != nullptr); + const uint32_t new_pos = bp->bytes_pos + count; + if (new_pos < bp->bytes_pos) { + // 32 bit overflow. + return 0; + } + if (bp->bytes != nullptr) { + if (new_pos > bp->bytes_size) { + // Buffer too small. + return 0; + } + memcpy(bp->bytes + bp->bytes_pos, data, count); + } + bp->bytes_pos += count; + return count; +} + +non_null(1) nullable(2) +static void bin_pack_init(Bin_Pack *bp, uint8_t *buf, uint32_t buf_size) +{ + bp->bytes = buf; + bp->bytes_size = buf_size; + bp->bytes_pos = 0; + cmp_init(&bp->ctx, bp, null_reader, null_skipper, buf_writer); +} + +bool bin_pack_obj(bin_pack_cb *callback, const void *obj, uint8_t *buf, uint32_t buf_size) +{ + Bin_Pack bp; + bin_pack_init(&bp, buf, buf_size); + return callback(&bp, obj); +} + +uint32_t bin_pack_obj_size(bin_pack_cb *callback, const void *obj) +{ + Bin_Pack bp; + bin_pack_init(&bp, nullptr, 0); + callback(&bp, obj); + return bp.bytes_pos; +} + +Bin_Pack *bin_pack_new(uint8_t *buf, uint32_t buf_size) +{ + Bin_Pack *bp = (Bin_Pack *)calloc(1, sizeof(Bin_Pack)); + if (bp == nullptr) { + return nullptr; + } + bin_pack_init(bp, buf, buf_size); + return bp; +} + +void bin_pack_free(Bin_Pack *bp) +{ + free(bp); +} + +bool bin_pack_array(Bin_Pack *bp, uint32_t size) +{ + return cmp_write_array(&bp->ctx, size); +} + +bool bin_pack_bool(Bin_Pack *bp, bool val) +{ + return cmp_write_bool(&bp->ctx, val); +} + +bool bin_pack_u08(Bin_Pack *bp, uint8_t val) +{ + return cmp_write_uinteger(&bp->ctx, val); +} + +bool bin_pack_u16(Bin_Pack *bp, uint16_t val) +{ + return cmp_write_uinteger(&bp->ctx, val); +} + +bool bin_pack_u32(Bin_Pack *bp, uint32_t val) +{ + return cmp_write_uinteger(&bp->ctx, val); +} + +bool bin_pack_u64(Bin_Pack *bp, uint64_t val) +{ + return cmp_write_uinteger(&bp->ctx, val); +} + +bool bin_pack_bin(Bin_Pack *bp, const uint8_t *data, uint32_t length) +{ + return cmp_write_bin(&bp->ctx, data, length); +} + +bool bin_pack_bin_marker(Bin_Pack *bp, uint32_t size) +{ + return cmp_write_bin_marker(&bp->ctx, size); +} + +bool bin_pack_u08_b(Bin_Pack *bp, uint8_t val) +{ + return bp->ctx.write(&bp->ctx, &val, 1) == 1; +} + +bool bin_pack_u16_b(Bin_Pack *bp, uint16_t val) +{ + return bin_pack_u08_b(bp, (val >> 8) & 0xff) + && bin_pack_u08_b(bp, val & 0xff); +} + +bool bin_pack_u32_b(Bin_Pack *bp, uint32_t val) +{ + return bin_pack_u16_b(bp, (val >> 16) & 0xffff) + && bin_pack_u16_b(bp, val & 0xffff); +} + +bool bin_pack_u64_b(Bin_Pack *bp, uint64_t val) +{ + return bin_pack_u32_b(bp, (val >> 32) & 0xffffffff) + && bin_pack_u32_b(bp, val & 0xffffffff); +} + +bool bin_pack_bin_b(Bin_Pack *bp, const uint8_t *data, uint32_t length) +{ + return bp->ctx.write(&bp->ctx, data, length) == length; +} diff --git a/protocols/Tox/libtox/src/toxcore/bin_pack.h b/protocols/Tox/libtox/src/toxcore/bin_pack.h new file mode 100644 index 0000000000..542f533b88 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/bin_pack.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ +#ifndef C_TOXCORE_TOXCORE_BIN_PACK_H +#define C_TOXCORE_TOXCORE_BIN_PACK_H + +#include <stdbool.h> +#include <stdint.h> + +#include "attributes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Binary serialisation object. + */ +typedef struct Bin_Pack Bin_Pack; + +/** @brief Function used to pack an object. + * + * This function would typically cast the `void *` to the actual object pointer type and then call + * more appropriately typed packing functions. + */ +typedef bool bin_pack_cb(Bin_Pack *bp, const void *obj); + +/** @brief Determine the serialised size of an object. + * + * @param callback The function called on the created packer and packed object. + * @param obj The object to be packed, passed as `obj` to the callback. + * + * @return The packed size of the passed object according to the callback. UINT32_MAX in case of + * errors such as buffer overflow. + */ +non_null(1) nullable(2) +uint32_t bin_pack_obj_size(bin_pack_cb *callback, const void *obj); + +/** @brief Pack an object into a buffer of a given size. + * + * This function creates and initialises a `Bin_Pack` packer object, calls the callback with the + * packer object and the to-be-packed object, and then cleans up the packer object. + * + * You can use `bin_pack_obj_size` to determine the minimum required size of `buf`. If packing + * overflows `uint32_t`, this function returns `false`. + * + * @param callback The function called on the created packer and packed object. + * @param obj The object to be packed, passed as `obj` to the callback. + * @param buf A byte array large enough to hold the serialised representation of `obj`. + * @param buf_size The size of the byte array. Can be `UINT32_MAX` to disable bounds checking. + * + * @retval false if an error occurred (e.g. buffer overflow). + */ +non_null(1, 3) nullable(2) +bool bin_pack_obj(bin_pack_cb *callback, const void *obj, uint8_t *buf, uint32_t buf_size); + +/** @brief Allocate a new packer object. + * + * This is the only function that allocates memory in this module. + * + * @param buf A byte array large enough to hold the serialised representation of `obj`. + * @param buf_size The size of the byte array. Can be `UINT32_MAX` to disable bounds checking. + * + * @retval nullptr on allocation failure. + */ +non_null() +Bin_Pack *bin_pack_new(uint8_t *buf, uint32_t buf_size); + +/** @brief Deallocates a packer object. + * + * Does not deallocate the buffer inside. + */ +nullable(1) +void bin_pack_free(Bin_Pack *bp); + +/** @brief Start packing a MessagePack array. + * + * A call to this function must be followed by exactly `size` calls to other functions below. + */ +non_null() +bool bin_pack_array(Bin_Pack *bp, uint32_t size); + +/** @brief Pack a MessagePack bool. */ +non_null() bool bin_pack_bool(Bin_Pack *bp, bool val); +/** @brief Pack a `uint8_t` as MessagePack positive integer. */ +non_null() bool bin_pack_u08(Bin_Pack *bp, uint8_t val); +/** @brief Pack a `uint16_t` as MessagePack positive integer. */ +non_null() bool bin_pack_u16(Bin_Pack *bp, uint16_t val); +/** @brief Pack a `uint32_t` as MessagePack positive integer. */ +non_null() bool bin_pack_u32(Bin_Pack *bp, uint32_t val); +/** @brief Pack a `uint64_t` as MessagePack positive integer. */ +non_null() bool bin_pack_u64(Bin_Pack *bp, uint64_t val); +/** @brief Pack a byte array as MessagePack bin. */ +non_null() bool bin_pack_bin(Bin_Pack *bp, const uint8_t *data, uint32_t length); + +/** @brief Start packing a custom binary representation. + * + * A call to this function must be followed by exactly `size` bytes packed by functions below. + */ +non_null() bool bin_pack_bin_marker(Bin_Pack *bp, uint32_t size); + +/** @brief Write a `uint8_t` directly to the packer in 1 byte. */ +non_null() bool bin_pack_u08_b(Bin_Pack *bp, uint8_t val); +/** @brief Write a `uint16_t` as big endian 16 bit int in 2 bytes. */ +non_null() bool bin_pack_u16_b(Bin_Pack *bp, uint16_t val); +/** @brief Write a `uint32_t` as big endian 32 bit int in 4 bytes. */ +non_null() bool bin_pack_u32_b(Bin_Pack *bp, uint32_t val); +/** @brief Write a `uint64_t` as big endian 64 bit int in 8 bytes. */ +non_null() bool bin_pack_u64_b(Bin_Pack *bp, uint64_t val); + +/** @brief Write a byte array directly to the packer in `length` bytes. + * + * Note that unless you prepend the array length manually, there is no record of it in the resulting + * serialised representation. + */ +non_null() bool bin_pack_bin_b(Bin_Pack *bp, const uint8_t *data, uint32_t length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // C_TOXCORE_TOXCORE_BIN_PACK_H diff --git a/protocols/Tox/libtox/src/toxcore/bin_unpack.c b/protocols/Tox/libtox/src/toxcore/bin_unpack.c new file mode 100644 index 0000000000..e4daec3a22 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/bin_unpack.c @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "bin_unpack.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../third_party/cmp/cmp.h" +#include "ccompat.h" + +struct Bin_Unpack { + const uint8_t *bytes; + uint32_t bytes_size; + cmp_ctx_t ctx; +}; + +non_null() +static bool buf_reader(cmp_ctx_t *ctx, void *data, size_t limit) +{ + Bin_Unpack *reader = (Bin_Unpack *)ctx->buf; + assert(reader != nullptr && reader->bytes != nullptr); + if (limit > reader->bytes_size) { + return false; + } + memcpy(data, reader->bytes, limit); + reader->bytes += limit; + reader->bytes_size -= limit; + return true; +} + +non_null() +static bool buf_skipper(cmp_ctx_t *ctx, size_t limit) +{ + Bin_Unpack *reader = (Bin_Unpack *)ctx->buf; + assert(reader != nullptr && reader->bytes != nullptr); + if (limit > reader->bytes_size) { + return false; + } + reader->bytes += limit; + reader->bytes_size -= limit; + return true; +} + +non_null() +static size_t null_writer(cmp_ctx_t *ctx, const void *data, size_t count) +{ + assert(count == 0); + return 0; +} + +Bin_Unpack *bin_unpack_new(const uint8_t *buf, uint32_t buf_size) +{ + Bin_Unpack *bu = (Bin_Unpack *)calloc(1, sizeof(Bin_Unpack)); + if (bu == nullptr) { + return nullptr; + } + bu->bytes = buf; + bu->bytes_size = buf_size; + cmp_init(&bu->ctx, bu, buf_reader, buf_skipper, null_writer); + return bu; +} + +void bin_unpack_free(Bin_Unpack *bu) +{ + free(bu); +} + +bool bin_unpack_array(Bin_Unpack *bu, uint32_t *size) +{ + return cmp_read_array(&bu->ctx, size) && *size <= bu->bytes_size; +} + +bool bin_unpack_array_fixed(Bin_Unpack *bu, uint32_t required_size) +{ + uint32_t size; + return cmp_read_array(&bu->ctx, &size) && size == required_size; +} + +bool bin_unpack_bool(Bin_Unpack *bu, bool *val) +{ + return cmp_read_bool(&bu->ctx, val); +} + +bool bin_unpack_u08(Bin_Unpack *bu, uint8_t *val) +{ + return cmp_read_uchar(&bu->ctx, val); +} + +bool bin_unpack_u16(Bin_Unpack *bu, uint16_t *val) +{ + return cmp_read_ushort(&bu->ctx, val); +} + +bool bin_unpack_u32(Bin_Unpack *bu, uint32_t *val) +{ + return cmp_read_uint(&bu->ctx, val); +} + +bool bin_unpack_u64(Bin_Unpack *bu, uint64_t *val) +{ + return cmp_read_ulong(&bu->ctx, val); +} + +bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_ptr) +{ + uint32_t bin_size; + if (!bin_unpack_bin_size(bu, &bin_size) || bin_size > bu->bytes_size) { + // There aren't as many bytes as this bin claims to want to allocate. + return false; + } + uint8_t *const data = (uint8_t *)malloc(bin_size); + + if (!bin_unpack_bin_b(bu, data, bin_size)) { + free(data); + return false; + } + + *data_ptr = data; + *data_length_ptr = bin_size; + return true; +} + +bool bin_unpack_bin_fixed(Bin_Unpack *bu, uint8_t *data, uint32_t data_length) +{ + uint32_t bin_size; + if (!bin_unpack_bin_size(bu, &bin_size) || bin_size != data_length) { + return false; + } + + return bin_unpack_bin_b(bu, data, bin_size); +} + +bool bin_unpack_bin_size(Bin_Unpack *bu, uint32_t *size) +{ + return cmp_read_bin_size(&bu->ctx, size); +} + +bool bin_unpack_u08_b(Bin_Unpack *bu, uint8_t *val) +{ + return bin_unpack_bin_b(bu, val, 1); +} + +bool bin_unpack_u16_b(Bin_Unpack *bu, uint16_t *val) +{ + uint8_t hi = 0; + uint8_t lo = 0; + if (!(bin_unpack_u08_b(bu, &hi) + && bin_unpack_u08_b(bu, &lo))) { + return false; + } + *val = ((uint16_t)hi << 8) | lo; + return true; +} + +bool bin_unpack_u32_b(Bin_Unpack *bu, uint32_t *val) +{ + uint16_t hi = 0; + uint16_t lo = 0; + if (!(bin_unpack_u16_b(bu, &hi) + && bin_unpack_u16_b(bu, &lo))) { + return false; + } + *val = ((uint32_t)hi << 16) | lo; + return true; +} + +bool bin_unpack_u64_b(Bin_Unpack *bu, uint64_t *val) +{ + uint32_t hi = 0; + uint32_t lo = 0; + if (!(bin_unpack_u32_b(bu, &hi) + && bin_unpack_u32_b(bu, &lo))) { + return false; + } + *val = ((uint64_t)hi << 32) | lo; + return true; +} + +bool bin_unpack_bin_b(Bin_Unpack *bu, uint8_t *data, uint32_t length) +{ + return bu->ctx.read(&bu->ctx, data, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/bin_unpack.h b/protocols/Tox/libtox/src/toxcore/bin_unpack.h new file mode 100644 index 0000000000..c6ead96e1b --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/bin_unpack.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_BIN_UNPACK_H +#define C_TOXCORE_TOXCORE_BIN_UNPACK_H + +#include <stdbool.h> +#include <stdint.h> + +#include "attributes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Binary deserialisation object. + */ +typedef struct Bin_Unpack Bin_Unpack; + +/** @brief Allocate a new unpacker object. + * + * @param buf The byte array to unpack values from. + * @param buf_size The size of the byte array. + * + * @retval nullptr on allocation failure. + */ +non_null() +Bin_Unpack *bin_unpack_new(const uint8_t *buf, uint32_t buf_size); + +/** @brief Deallocates an unpacker object. + * + * Does not deallocate the buffer inside. + */ +nullable(1) +void bin_unpack_free(Bin_Unpack *bu); + +/** @brief Start unpacking a MessagePack array. + * + * A call to this function must be followed by exactly `size` calls to other functions below. + * + * @param size Will contain the number of array elements following the array marker. + */ +non_null() bool bin_unpack_array(Bin_Unpack *bu, uint32_t *size); + +/** @brief Start unpacking a fixed size MessagePack array. + * + * @retval false if the packed array size is not exactly the required size. + */ +non_null() bool bin_unpack_array_fixed(Bin_Unpack *bu, uint32_t required_size); + +/** @brief Unpack a MessagePack bool. */ +non_null() bool bin_unpack_bool(Bin_Unpack *bu, bool *val); +/** @brief Unpack a MessagePack positive int into a `uint8_t`. */ +non_null() bool bin_unpack_u08(Bin_Unpack *bu, uint8_t *val); +/** @brief Unpack a MessagePack positive int into a `uint16_t`. */ +non_null() bool bin_unpack_u16(Bin_Unpack *bu, uint16_t *val); +/** @brief Unpack a MessagePack positive int into a `uint32_t`. */ +non_null() bool bin_unpack_u32(Bin_Unpack *bu, uint32_t *val); +/** @brief Unpack a MessagePack positive int into a `uint64_t`. */ +non_null() bool bin_unpack_u64(Bin_Unpack *bu, uint64_t *val); +/** @brief Unpack a MessagePack bin into a newly allocated byte array. + * + * Allocates a new byte array and stores it into `data_ptr` with its length stored in + * `data_length_ptr`. This function requires that the unpacking buffer has at least as many bytes + * remaining to be unpacked as the bin claims to need, so it's not possible to cause an arbitrarily + * large allocation unless the input array was already that large. + */ +non_null() bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_ptr); +/** @brief Unpack a MessagePack bin of a fixed length into a pre-allocated byte array. + * + * Unlike the function above, this function does not allocate any memory, but requires the size to + * be known up front. + */ +non_null() bool bin_unpack_bin_fixed(Bin_Unpack *bu, uint8_t *data, uint32_t data_length); + +/** @brief Start unpacking a custom binary representation. + * + * A call to this function must be followed by exactly `size` bytes packed by functions below. + */ +non_null() bool bin_unpack_bin_size(Bin_Unpack *bu, uint32_t *size); + +/** @brief Read a `uint8_t` directly from the unpacker, consuming 1 byte. */ +non_null() bool bin_unpack_u08_b(Bin_Unpack *bu, uint8_t *val); +/** @brief Read a `uint16_t` as big endian 16 bit int, consuming 2 bytes. */ +non_null() bool bin_unpack_u16_b(Bin_Unpack *bu, uint16_t *val); +/** @brief Read a `uint32_t` as big endian 32 bit int, consuming 4 bytes. */ +non_null() bool bin_unpack_u32_b(Bin_Unpack *bu, uint32_t *val); +/** @brief Read a `uint64_t` as big endian 64 bit int, consuming 8 bytes. */ +non_null() bool bin_unpack_u64_b(Bin_Unpack *bu, uint64_t *val); + +/** @brief Read a byte array directly from the packer, consuming `length` bytes. */ +non_null() bool bin_unpack_bin_b(Bin_Unpack *bu, uint8_t *data, uint32_t length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // C_TOXCORE_TOXCORE_BIN_UNPACK_H diff --git a/protocols/Tox/libtox/src/toxcore/ccompat.c b/protocols/Tox/libtox/src/toxcore/ccompat.c new file mode 100644 index 0000000000..30e689d06d --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/ccompat.c @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ +#include "ccompat.h" diff --git a/protocols/Tox/libtox/src/toxcore/ccompat.h b/protocols/Tox/libtox/src/toxcore/ccompat.h index 295cd10d9b..9ea6739a7d 100644 --- a/protocols/Tox/libtox/src/toxcore/ccompat.h +++ b/protocols/Tox/libtox/src/toxcore/ccompat.h @@ -8,9 +8,9 @@ #ifndef C_TOXCORE_TOXCORE_CCOMPAT_H #define C_TOXCORE_TOXCORE_CCOMPAT_H -#include <stdbool.h> +#include <stddef.h> // NULL, size_t -bool unused_for_tokstyle(void); +#include "attributes.h" //!TOKSTYLE- @@ -25,7 +25,7 @@ bool unused_for_tokstyle(void); // you may run out of stack space. #if !defined(DISABLE_VLA) && !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 VLAs. -#define VLA(type, name, size) type name[size] +#define ALLOC_VLA(type, name, size) type name[size] #define SIZEOF_VLA sizeof #else @@ -46,19 +46,35 @@ bool unused_for_tokstyle(void); #endif #endif -#define VLA(type, name, size) \ - const size_t name##_size = (size) * sizeof(type); \ - type *const name = (type *)alloca(name##_size) -#define SIZEOF_VLA(name) name##_size +#define ALLOC_VLA(type, name, size) \ + const size_t name##_vla_size = (size) * sizeof(type); \ + type *const name = (type *)alloca(name##_vla_size) +#define SIZEOF_VLA(name) name##_vla_size #endif +#ifdef MAX_VLA_SIZE +#include <assert.h> +#define VLA(type, name, size) \ + ALLOC_VLA(type, name, size); \ + assert((size_t)(size) * sizeof(type) <= MAX_VLA_SIZE) +#else +#define VLA ALLOC_VLA +#endif + #if !defined(__cplusplus) || __cplusplus < 201103L #define nullptr NULL #ifndef static_assert -#define static_assert(cond, msg) extern const int unused_for_static_assert -#endif -#endif +#ifdef __GNUC__ +// We'll just assume gcc and clang support C11 _Static_assert. +#define static_assert _Static_assert +#else // !__GNUC__ +#define STATIC_ASSERT_(cond, msg, line) typedef int static_assert_##line[(cond) ? 1 : -1] +#define STATIC_ASSERT(cond, msg, line) STATIC_ASSERT_(cond, msg, line) +#define static_assert(cond, msg) STATIC_ASSERT(cond, msg, __LINE__) +#endif // !__GNUC__ +#endif // !static_assert +#endif // !__cplusplus #ifdef __GNUC__ #define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a))) diff --git a/protocols/Tox/libtox/src/toxcore/crypto_core.c b/protocols/Tox/libtox/src/toxcore/crypto_core.c index 3402395b63..1ec2cee5fd 100644 --- a/protocols/Tox/libtox/src/toxcore/crypto_core.c +++ b/protocols/Tox/libtox/src/toxcore/crypto_core.c @@ -10,15 +10,15 @@ */ #include "crypto_core.h" +#include <assert.h> #include <stdlib.h> #include <string.h> -#include "ccompat.h" - #ifndef VANILLA_NACL // We use libsodium by default. #include <sodium.h> #else +#include <crypto_auth.h> #include <crypto_box.h> #include <crypto_hash_sha256.h> #include <crypto_hash_sha512.h> @@ -28,15 +28,17 @@ #include <randombytes.h> #endif +#include "ccompat.h" + #ifndef crypto_box_MACBYTES #define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES) #endif -//!TOKSTYLE- -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#include "../testing/fuzzing/fuzz_adapter.h" +#ifndef VANILLA_NACL +// Need dht because of ENC_SECRET_KEY_SIZE and ENC_PUBLIC_KEY_SIZE +#define ENC_PUBLIC_KEY_SIZE CRYPTO_PUBLIC_KEY_SIZE +#define ENC_SECRET_KEY_SIZE CRYPTO_SECRET_KEY_SIZE #endif -//!TOKSTYLE+ static_assert(CRYPTO_PUBLIC_KEY_SIZE == crypto_box_PUBLICKEYBYTES, "CRYPTO_PUBLIC_KEY_SIZE should be equal to crypto_box_PUBLICKEYBYTES"); @@ -50,12 +52,68 @@ static_assert(CRYPTO_MAC_SIZE == crypto_box_MACBYTES, "CRYPTO_MAC_SIZE should be equal to crypto_box_MACBYTES"); static_assert(CRYPTO_NONCE_SIZE == crypto_box_NONCEBYTES, "CRYPTO_NONCE_SIZE should be equal to crypto_box_NONCEBYTES"); +static_assert(CRYPTO_HMAC_SIZE == crypto_auth_BYTES, + "CRYPTO_HMAC_SIZE should be equal to crypto_auth_BYTES"); +static_assert(CRYPTO_HMAC_KEY_SIZE == crypto_auth_KEYBYTES, + "CRYPTO_HMAC_KEY_SIZE should be equal to crypto_auth_KEYBYTES"); static_assert(CRYPTO_SHA256_SIZE == crypto_hash_sha256_BYTES, "CRYPTO_SHA256_SIZE should be equal to crypto_hash_sha256_BYTES"); static_assert(CRYPTO_SHA512_SIZE == crypto_hash_sha512_BYTES, "CRYPTO_SHA512_SIZE should be equal to crypto_hash_sha512_BYTES"); static_assert(CRYPTO_PUBLIC_KEY_SIZE == 32, - "CRYPTO_PUBLIC_KEY_SIZE is required to be 32 bytes for public_key_cmp to work"); + "CRYPTO_PUBLIC_KEY_SIZE is required to be 32 bytes for pk_equal to work"); + +#ifndef VANILLA_NACL +static_assert(CRYPTO_SIGNATURE_SIZE == crypto_sign_BYTES, + "CRYPTO_SIGNATURE_SIZE should be equal to crypto_sign_BYTES"); +static_assert(CRYPTO_SIGN_PUBLIC_KEY_SIZE == crypto_sign_PUBLICKEYBYTES, + "CRYPTO_SIGN_PUBLIC_KEY_SIZE should be equal to crypto_sign_PUBLICKEYBYTES"); +static_assert(CRYPTO_SIGN_SECRET_KEY_SIZE == crypto_sign_SECRETKEYBYTES, + "CRYPTO_SIGN_SECRET_KEY_SIZE should be equal to crypto_sign_SECRETKEYBYTES"); +#endif /* VANILLA_NACL */ + +bool create_extended_keypair(uint8_t *pk, uint8_t *sk) +{ +#ifdef VANILLA_NACL + return false; +#else + /* create signature key pair */ + crypto_sign_keypair(pk + ENC_PUBLIC_KEY_SIZE, sk + ENC_SECRET_KEY_SIZE); + + /* convert public signature key to public encryption key */ + const int res1 = crypto_sign_ed25519_pk_to_curve25519(pk, pk + ENC_PUBLIC_KEY_SIZE); + + /* convert secret signature key to secret encryption key */ + const int res2 = crypto_sign_ed25519_sk_to_curve25519(sk, sk + ENC_SECRET_KEY_SIZE); + + return res1 == 0 && res2 == 0; +#endif +} + +const uint8_t *get_enc_key(const uint8_t *key) +{ + return key; +} + +const uint8_t *get_sig_pk(const uint8_t *key) +{ + return key + ENC_PUBLIC_KEY_SIZE; +} + +void set_sig_pk(uint8_t *key, const uint8_t *sig_pk) +{ + memcpy(key + ENC_PUBLIC_KEY_SIZE, sig_pk, SIG_PUBLIC_KEY_SIZE); +} + +const uint8_t *get_sig_sk(const uint8_t *key) +{ + return key + ENC_SECRET_KEY_SIZE; +} + +const uint8_t *get_chat_id(const uint8_t *key) +{ + return key + ENC_PUBLIC_KEY_SIZE; +} #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) static uint8_t *crypto_malloc(size_t bytes) @@ -69,6 +127,7 @@ static uint8_t *crypto_malloc(size_t bytes) return ptr; } +nullable(1) static void crypto_free(uint8_t *ptr, size_t bytes) { if (ptr != nullptr) { @@ -82,116 +141,154 @@ static void crypto_free(uint8_t *ptr, size_t bytes) void crypto_memzero(void *data, size_t length) { -#ifndef VANILLA_NACL - sodium_memzero(data, length); -#else +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL) memset(data, 0, length); +#else + sodium_memzero(data, length); #endif } bool crypto_memlock(void *data, size_t length) { -#ifndef VANILLA_NACL +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL) + return false; +#else if (sodium_mlock(data, length) != 0) { return false; } return true; -#else - return false; #endif } bool crypto_memunlock(void *data, size_t length) { -#ifndef VANILLA_NACL +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(VANILLA_NACL) + return false; +#else if (sodium_munlock(data, length) != 0) { return false; } return true; -#else - return false; #endif } -int32_t public_key_cmp(const uint8_t *pk1, const uint8_t *pk2) +bool pk_equal(const uint8_t *pk1, const uint8_t *pk2) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Hope that this is better for the fuzzer - return memcmp(pk1, pk2, CRYPTO_PUBLIC_KEY_SIZE) == 0 ? 0 : -1; + return memcmp(pk1, pk2, CRYPTO_PUBLIC_KEY_SIZE) == 0; #else - return crypto_verify_32(pk1, pk2); + return crypto_verify_32(pk1, pk2) == 0; #endif } -int32_t crypto_sha512_cmp(const uint8_t *cksum1, const uint8_t *cksum2) +void pk_copy(uint8_t *dest, const uint8_t *src) { -#ifndef VANILLA_NACL - return crypto_verify_64(cksum1, cksum2); + memcpy(dest, src, CRYPTO_PUBLIC_KEY_SIZE); +} + +bool crypto_sha512_eq(const uint8_t *cksum1, const uint8_t *cksum2) +{ +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) + // Hope that this is better for the fuzzer + return memcmp(cksum1, cksum2, CRYPTO_SHA512_SIZE) == 0; +#elif defined(VANILLA_NACL) + const int lo = crypto_verify_32(cksum1, cksum2) == 0 ? 1 : 0; + const int hi = crypto_verify_32(cksum1 + 8, cksum2 + 8) == 0 ? 1 : 0; + return (lo & hi) == 1; +#else + return crypto_verify_64(cksum1, cksum2) == 0; +#endif +} + +bool crypto_sha256_eq(const uint8_t *cksum1, const uint8_t *cksum2) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // Hope that this is better for the fuzzer + return memcmp(cksum1, cksum2, CRYPTO_SHA256_SIZE) == 0; #else - return crypto_verify_32(cksum1, cksum2) && crypto_verify_32(cksum1 + 8, cksum2 + 8); + return crypto_verify_32(cksum1, cksum2) == 0; #endif } -uint8_t random_u08(void) +uint8_t random_u08(const Random *rng) { uint8_t randnum; - random_bytes(&randnum, 1); + random_bytes(rng, &randnum, 1); return randnum; } -uint16_t random_u16(void) +uint16_t random_u16(const Random *rng) { uint16_t randnum; - random_bytes((uint8_t *)&randnum, sizeof(randnum)); + random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum)); return randnum; } -uint32_t random_u32(void) +uint32_t random_u32(const Random *rng) { uint32_t randnum; - random_bytes((uint8_t *)&randnum, sizeof(randnum)); + random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum)); return randnum; } -uint64_t random_u64(void) +uint64_t random_u64(const Random *rng) { uint64_t randnum; - random_bytes((uint8_t *)&randnum, sizeof(randnum)); + random_bytes(rng, (uint8_t *)&randnum, sizeof(randnum)); return randnum; } -uint32_t random_range_u32(uint32_t upper_bound) +uint32_t random_range_u32(const Random *rng, uint32_t upper_bound) +{ + return rng->funcs->random_uniform(rng->obj, upper_bound); +} + +bool crypto_signature_create(uint8_t *signature, const uint8_t *message, uint64_t message_length, + const uint8_t *secret_key) { #ifdef VANILLA_NACL - return random_u32() % upper_bound; + return false; #else - return randombytes_uniform(upper_bound); -#endif // VANILLA_NACL + return crypto_sign_detached(signature, nullptr, message, message_length, secret_key) == 0; +#endif // VANILLA_NACL } -bool public_key_valid(const uint8_t *public_key) +bool crypto_signature_verify(const uint8_t *signature, const uint8_t *message, uint64_t message_length, + const uint8_t *public_key) { - if (public_key[31] >= 128) { /* Last bit of key is always zero. */ - return 0; - } +#ifdef VANILLA_NACL + return false; +#else + return crypto_sign_verify_detached(signature, message, message_length, public_key) == 0; +#endif +} - return 1; +bool public_key_valid(const uint8_t *public_key) +{ + /* Last bit of key is always zero. */ + return public_key[31] < 128; } int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key, uint8_t *shared_key) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + memcpy(shared_key, public_key, CRYPTO_SHARED_KEY_SIZE); + return 0; +#else return crypto_box_beforenm(shared_key, public_key, secret_key); +#endif } -int32_t encrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, +int32_t encrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *plain, size_t length, uint8_t *encrypted) { - if (length == 0 || !secret_key || !nonce || !plain || !encrypted) { + if (length == 0 || shared_key == nullptr || nonce == nullptr || plain == nullptr || encrypted == nullptr) { return -1; } @@ -224,7 +321,7 @@ int32_t encrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length); if (crypto_box_afternm(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce, - secret_key) != 0) { + shared_key) != 0) { crypto_free(temp_plain, size_temp_plain); crypto_free(temp_encrypted, size_temp_encrypted); return -1; @@ -236,13 +333,15 @@ int32_t encrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, crypto_free(temp_plain, size_temp_plain); crypto_free(temp_encrypted, size_temp_encrypted); #endif - return length + crypto_box_MACBYTES; + assert(length < INT32_MAX - crypto_box_MACBYTES); + return (int32_t)(length + crypto_box_MACBYTES); } -int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, +int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *encrypted, size_t length, uint8_t *plain) { - if (length <= crypto_box_BOXZEROBYTES || !secret_key || !nonce || !encrypted || !plain) { + if (length <= crypto_box_BOXZEROBYTES || shared_key == nullptr || nonce == nullptr || encrypted == nullptr + || plain == nullptr) { return -1; } @@ -273,7 +372,7 @@ int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length); if (crypto_box_open_afternm(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES, nonce, - secret_key) != 0) { + shared_key) != 0) { crypto_free(temp_plain, size_temp_plain); crypto_free(temp_encrypted, size_temp_encrypted); return -1; @@ -284,19 +383,21 @@ int32_t decrypt_data_symmetric(const uint8_t *secret_key, const uint8_t *nonce, crypto_free(temp_plain, size_temp_plain); crypto_free(temp_encrypted, size_temp_encrypted); #endif - return length - crypto_box_MACBYTES; + assert(length > crypto_box_MACBYTES); + assert(length < INT32_MAX); + return (int32_t)(length - crypto_box_MACBYTES); } int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce, const uint8_t *plain, size_t length, uint8_t *encrypted) { - if (!public_key || !secret_key) { + if (public_key == nullptr || secret_key == nullptr) { return -1; } uint8_t k[crypto_box_BEFORENMBYTES]; encrypt_precompute(public_key, secret_key, k); - int ret = encrypt_data_symmetric(k, nonce, plain, length, encrypted); + const int ret = encrypt_data_symmetric(k, nonce, plain, length, encrypted); crypto_memzero(k, sizeof(k)); return ret; } @@ -304,13 +405,13 @@ int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce, const uint8_t *encrypted, size_t length, uint8_t *plain) { - if (!public_key || !secret_key) { + if (public_key == nullptr || secret_key == nullptr) { return -1; } uint8_t k[crypto_box_BEFORENMBYTES]; encrypt_precompute(public_key, secret_key, k); - int ret = decrypt_data_symmetric(k, nonce, encrypted, length, plain); + const int ret = decrypt_data_symmetric(k, nonce, encrypted, length, plain); crypto_memzero(k, sizeof(k)); return ret; } @@ -356,20 +457,20 @@ void increment_nonce_number(uint8_t *nonce, uint32_t increment) } } -void random_nonce(uint8_t *nonce) +void random_nonce(const Random *rng, uint8_t *nonce) { - random_bytes(nonce, crypto_box_NONCEBYTES); + random_bytes(rng, nonce, crypto_box_NONCEBYTES); } -void new_symmetric_key(uint8_t *key) +void new_symmetric_key(const Random *rng, uint8_t *key) { - random_bytes(key, CRYPTO_SYMMETRIC_KEY_SIZE); + random_bytes(rng, key, CRYPTO_SYMMETRIC_KEY_SIZE); } -int32_t crypto_new_keypair(uint8_t *public_key, uint8_t *secret_key) +int32_t crypto_new_keypair(const Random *rng, uint8_t *public_key, uint8_t *secret_key) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - random_bytes(secret_key, CRYPTO_SECRET_KEY_SIZE); + random_bytes(rng, secret_key, CRYPTO_SECRET_KEY_SIZE); memset(public_key, 0, CRYPTO_PUBLIC_KEY_SIZE); // Make MSAN happy crypto_scalarmult_curve25519_base(public_key, secret_key); return 0; @@ -383,21 +484,90 @@ void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key) crypto_scalarmult_curve25519_base(public_key, secret_key); } +void new_hmac_key(const Random *rng, uint8_t *key) +{ + random_bytes(rng, key, CRYPTO_HMAC_KEY_SIZE); +} + +void crypto_hmac(uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], const uint8_t *data, + size_t length) +{ + crypto_auth(auth, data, length, key); +} + +bool crypto_hmac_verify(const uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], + const uint8_t *data, size_t length) +{ + return crypto_auth_verify(auth, data, length, key) == 0; +} + void crypto_sha256(uint8_t *hash, const uint8_t *data, size_t length) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + memset(hash, 0, CRYPTO_SHA256_SIZE); + memcpy(hash, data, length < CRYPTO_SHA256_SIZE ? length : CRYPTO_SHA256_SIZE); +#else crypto_hash_sha256(hash, data, length); +#endif } void crypto_sha512(uint8_t *hash, const uint8_t *data, size_t length) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + memset(hash, 0, CRYPTO_SHA512_SIZE); + memcpy(hash, data, length < CRYPTO_SHA512_SIZE ? length : CRYPTO_SHA512_SIZE); +#else crypto_hash_sha512(hash, data, length); +#endif } -void random_bytes(uint8_t *data, size_t length) +non_null() +static void sys_random_bytes(void *obj, uint8_t *bytes, size_t length) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - fuzz_random_bytes(data, length); + randombytes(bytes, length); +} + +non_null() +static uint32_t sys_random_uniform(void *obj, uint32_t upper_bound) +{ +#ifdef VANILLA_NACL + if (upper_bound == 0) { + return 0; + } + + uint32_t randnum; + sys_random_bytes(obj, (uint8_t *)&randnum, sizeof(randnum)); + return randnum % upper_bound; #else - randombytes(data, length); + return randombytes_uniform(upper_bound); #endif } + +static const Random_Funcs system_random_funcs = { + sys_random_bytes, + sys_random_uniform, +}; + +static const Random system_random_obj = {&system_random_funcs}; + +const Random *system_random(void) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if ((true)) { + return nullptr; + } +#endif +#ifndef VANILLA_NACL + // It is safe to call this function more than once and from different + // threads -- subsequent calls won't have any effects. + if (sodium_init() == -1) { + return nullptr; + } +#endif + return &system_random_obj; +} + +void random_bytes(const Random *rng, uint8_t *bytes, size_t length) +{ + rng->funcs->random_bytes(rng->obj, bytes, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/crypto_core.h b/protocols/Tox/libtox/src/toxcore/crypto_core.h index f3709abaac..0aaadeacf1 100644 --- a/protocols/Tox/libtox/src/toxcore/crypto_core.h +++ b/protocols/Tox/libtox/src/toxcore/crypto_core.h @@ -13,11 +13,28 @@ #include <stddef.h> #include <stdint.h> +#include "attributes.h" + #ifdef __cplusplus extern "C" { #endif /** + * The number of bytes in a signature. + */ +#define CRYPTO_SIGNATURE_SIZE 64 + +/** + * The number of bytes in a Tox public key used for signatures. + */ +#define CRYPTO_SIGN_PUBLIC_KEY_SIZE 32 + +/** + * The number of bytes in a Tox secret key used for signatures. + */ +#define CRYPTO_SIGN_SECRET_KEY_SIZE 64 + +/** * @brief The number of bytes in a Tox public key used for encryption. */ #define CRYPTO_PUBLIC_KEY_SIZE 32 @@ -39,7 +56,7 @@ extern "C" { /** * @brief The number of bytes needed for the MAC (message authentication code) in an - * encrypted message. + * encrypted message. */ #define CRYPTO_MAC_SIZE 16 @@ -58,6 +75,66 @@ extern "C" { */ #define CRYPTO_SHA512_SIZE 64 +typedef void crypto_random_bytes_cb(void *obj, uint8_t *bytes, size_t length); +typedef uint32_t crypto_random_uniform_cb(void *obj, uint32_t upper_bound); + +typedef struct Random_Funcs { + crypto_random_bytes_cb *random_bytes; + crypto_random_uniform_cb *random_uniform; +} Random_Funcs; + +typedef struct Random { + const Random_Funcs *funcs; + void *obj; +} Random; + +const Random *system_random(void); + +/** + * @brief The number of bytes in an encryption public key used by DHT group chats. + */ +#define ENC_PUBLIC_KEY_SIZE CRYPTO_PUBLIC_KEY_SIZE + +/** + * @brief The number of bytes in an encryption secret key used by DHT group chats. + */ +#define ENC_SECRET_KEY_SIZE CRYPTO_SECRET_KEY_SIZE + +/** + * @brief The number of bytes in a signature public key. + */ +#define SIG_PUBLIC_KEY_SIZE CRYPTO_SIGN_PUBLIC_KEY_SIZE + +/** + * @brief The number of bytes in a signature secret key. + */ +#define SIG_SECRET_KEY_SIZE CRYPTO_SIGN_SECRET_KEY_SIZE + +/** + * @brief The number of bytes in a DHT group chat public key identifier. + */ +#define CHAT_ID_SIZE SIG_PUBLIC_KEY_SIZE + +/** + * @brief The number of bytes in an extended public key used by DHT group chats. + */ +#define EXT_PUBLIC_KEY_SIZE (ENC_PUBLIC_KEY_SIZE + SIG_PUBLIC_KEY_SIZE) + +/** + * @brief The number of bytes in an extended secret key used by DHT group chats. + */ +#define EXT_SECRET_KEY_SIZE (ENC_SECRET_KEY_SIZE + SIG_SECRET_KEY_SIZE) + +/** + * @brief The number of bytes in an HMAC authenticator. + */ +#define CRYPTO_HMAC_SIZE 32 + +/** + * @brief The number of bytes in an HMAC secret key. + */ +#define CRYPTO_HMAC_KEY_SIZE 32 + /** * @brief A `bzero`-like function which won't be optimised away by the compiler. * @@ -65,54 +142,95 @@ extern "C" { * will be no reads to the written data. Use this function if you want to be * sure the memory is indeed zeroed. */ +non_null() void crypto_memzero(void *data, size_t length); /** * @brief Compute a SHA256 hash (32 bytes). */ +non_null() void crypto_sha256(uint8_t *hash, const uint8_t *data, size_t length); /** * @brief Compute a SHA512 hash (64 bytes). */ +non_null() void crypto_sha512(uint8_t *hash, const uint8_t *data, size_t length); /** + * @brief Compute an HMAC authenticator (32 bytes). + * + * @param auth Resulting authenticator. + * @param key Secret key, as generated by `new_hmac_key()`. + */ +non_null() +void crypto_hmac(uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], const uint8_t *data, + size_t length); + +/** + * @brief Verify an HMAC authenticator. + */ +non_null() +bool crypto_hmac_verify(const uint8_t auth[CRYPTO_HMAC_SIZE], const uint8_t key[CRYPTO_HMAC_KEY_SIZE], + const uint8_t *data, size_t length); + +/** * @brief Compare 2 public keys of length @ref CRYPTO_PUBLIC_KEY_SIZE, not vulnerable to * timing attacks. * - * @retval 0 if both mem locations of length are equal - * @retval -1 if they are not + * @retval true if both mem locations of length are equal + * @retval false if they are not */ -int32_t public_key_cmp(const uint8_t *pk1, const uint8_t *pk2); +non_null() +bool pk_equal(const uint8_t pk1[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t pk2[CRYPTO_PUBLIC_KEY_SIZE]); + +/** + * @brief Copy a public key from `src` to `dest`. + */ +non_null() +void pk_copy(uint8_t dest[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t src[CRYPTO_PUBLIC_KEY_SIZE]); /** * @brief Compare 2 SHA512 checksums of length CRYPTO_SHA512_SIZE, not vulnerable to * timing attacks. * - * @return 0 if both mem locations of length are equal, -1 if they are not. + * @return true if both mem locations of length are equal, false if they are not. */ -int32_t crypto_sha512_cmp(const uint8_t *cksum1, const uint8_t *cksum2); +non_null() +bool crypto_sha512_eq(const uint8_t *cksum1, const uint8_t *cksum2); + +/** + * @brief Compare 2 SHA256 checksums of length CRYPTO_SHA256_SIZE, not vulnerable to + * timing attacks. + * + * @return true if both mem locations of length are equal, false if they are not. + */ +non_null() +bool crypto_sha256_eq(const uint8_t *cksum1, const uint8_t *cksum2); /** * @brief Return a random 8 bit integer. */ -uint8_t random_u08(void); +non_null() +uint8_t random_u08(const Random *rng); /** * @brief Return a random 16 bit integer. */ -uint16_t random_u16(void); +non_null() +uint16_t random_u16(const Random *rng); /** * @brief Return a random 32 bit integer. */ -uint32_t random_u32(void); +non_null() +uint32_t random_u32(const Random *rng); /** * @brief Return a random 64 bit integer. */ -uint64_t random_u64(void); +non_null() +uint64_t random_u64(const Random *rng); /** * @brief Return a random 32 bit integer between 0 and upper_bound (excluded). @@ -120,17 +238,50 @@ uint64_t random_u64(void); * On libsodium builds this function guarantees a uniform distribution of possible outputs. * On vanilla NACL builds this function is equivalent to `random() % upper_bound`. */ -uint32_t random_range_u32(uint32_t upper_bound); +non_null() +uint32_t random_range_u32(const Random *rng, uint32_t upper_bound); + +/** @brief Cryptographically signs a message using the supplied secret key and puts the resulting signature + * in the supplied buffer. + * + * @param signature The buffer for the resulting signature, which must have room for at + * least CRYPTO_SIGNATURE_SIZE bytes. + * @param message The message being signed. + * @param message_length The length in bytes of the message being signed. + * @param secret_key The secret key used to create the signature. The key should be + * produced by either `create_extended_keypair` or the libsodium function `crypto_sign_keypair`. + * + * @retval true on success. + */ +non_null() +bool crypto_signature_create(uint8_t *signature, const uint8_t *message, uint64_t message_length, + const uint8_t *secret_key); + +/** @brief Verifies that the given signature was produced by a given message and public key. + * + * @param signature The signature we wish to verify. + * @param message The message we wish to verify. + * @param message_length The length of the message. + * @param public_key The public key counterpart of the secret key that was used to + * create the signature. + * + * @retval true on success. + */ +non_null() +bool crypto_signature_verify(const uint8_t *signature, const uint8_t *message, uint64_t message_length, + const uint8_t *public_key); /** * @brief Fill the given nonce with random bytes. */ -void random_nonce(uint8_t *nonce); +non_null() +void random_nonce(const Random *rng, uint8_t *nonce); /** * @brief Fill an array of bytes with random values. */ -void random_bytes(uint8_t *bytes, size_t length); +non_null() +void random_bytes(const Random *rng, uint8_t *bytes, size_t length); /** * @brief Check if a Tox public key CRYPTO_PUBLIC_KEY_SIZE is valid or not. @@ -139,18 +290,39 @@ void random_bytes(uint8_t *bytes, size_t length); * * @return false if it isn't, true if it is. */ +non_null() bool public_key_valid(const uint8_t *public_key); +/** @brief Creates an extended keypair: curve25519 and ed25519 for encryption and signing + * respectively. The Encryption keys are derived from the signature keys. + * + * @param pk The buffer where the public key will be stored. Must have room for EXT_PUBLIC_KEY_SIZE bytes. + * @param sk The buffer where the secret key will be stored. Must have room for EXT_SECRET_KEY_SIZE bytes. + * + * @retval true on success. + */ +non_null() +bool create_extended_keypair(uint8_t *pk, uint8_t *sk); + +/** Functions for groupchat extended keys */ +non_null() const uint8_t *get_enc_key(const uint8_t *key); +non_null() const uint8_t *get_sig_pk(const uint8_t *key); +non_null() void set_sig_pk(uint8_t *key, const uint8_t *sig_pk); +non_null() const uint8_t *get_sig_sk(const uint8_t *key); +non_null() const uint8_t *get_chat_id(const uint8_t *key); + /** * @brief Generate a new random keypair. * * Every call to this function is likely to generate a different keypair. */ -int32_t crypto_new_keypair(uint8_t *public_key, uint8_t *secret_key); +non_null() +int32_t crypto_new_keypair(const Random *rng, uint8_t *public_key, uint8_t *secret_key); /** * @brief Derive the public key from a given secret key. */ +non_null() void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key); /** @@ -162,8 +334,9 @@ void crypto_derive_public_key(uint8_t *public_key, const uint8_t *secret_key); * @ref CRYPTO_NONCE_SIZE byte nonce. * * @retval -1 if there was a problem. - * @retval >=0 length of encrypted data if everything was fine. + * @return length of encrypted data if everything was fine. */ +non_null() int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce, const uint8_t *plain, size_t length, uint8_t *encrypted); @@ -176,8 +349,9 @@ int32_t encrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const * @ref CRYPTO_NONCE_SIZE byte nonce. * * @retval -1 if there was a problem (decryption failed). - * @retval >=0 length of plain text data if everything was fine. + * @return length of plain text data if everything was fine. */ +non_null() int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *nonce, const uint8_t *encrypted, size_t length, uint8_t *plain); @@ -188,6 +362,7 @@ int32_t decrypt_data(const uint8_t *public_key, const uint8_t *secret_key, const * shared-key generation once so it does not have to be performed on every * encrypt/decrypt. */ +non_null() int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key, uint8_t *shared_key); /** @@ -197,22 +372,24 @@ int32_t encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key, * using a shared key @ref CRYPTO_SYMMETRIC_KEY_SIZE big and a @ref CRYPTO_NONCE_SIZE * byte nonce. * - * @return -1 if there was a problem, length of encrypted data if everything - * was fine. + * @retval -1 if there was a problem. + * @return length of encrypted data if everything was fine. */ +non_null() int32_t encrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *plain, size_t length, uint8_t *encrypted); /** * @brief Decrypt message with precomputed shared key. * - * Decrypts encrypted of length length to plain of length length - - * @ref CRYPTO_MAC_SIZE using a shared key @ref CRYPTO_SHARED_KEY_SIZE big and a - * @ref CRYPTO_NONCE_SIZE byte nonce. + * Decrypts encrypted of length length to plain of length + * `length - CRYPTO_MAC_SIZE` using a shared key @ref CRYPTO_SHARED_KEY_SIZE + * big and a @ref CRYPTO_NONCE_SIZE byte nonce. * - * @return -1 if there was a problem (decryption failed), length of plain data - * if everything was fine. + * @retval -1 if there was a problem (decryption failed). + * @return length of plain data if everything was fine. */ +non_null() int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, const uint8_t *encrypted, size_t length, uint8_t *plain); @@ -220,6 +397,7 @@ int32_t decrypt_data_symmetric(const uint8_t *shared_key, const uint8_t *nonce, * @brief Increment the given nonce by 1 in big endian (rightmost byte incremented * first). */ +non_null() void increment_nonce(uint8_t *nonce); /** @@ -227,12 +405,14 @@ void increment_nonce(uint8_t *nonce); * * The number should be in host byte order. */ +non_null() void increment_nonce_number(uint8_t *nonce, uint32_t increment); /** * @brief Fill a key @ref CRYPTO_SYMMETRIC_KEY_SIZE big with random bytes. */ -void new_symmetric_key(uint8_t *key); +non_null() +void new_symmetric_key(const Random *rng, uint8_t *key); /** * @brief Locks `length` bytes of memory pointed to by `data`. @@ -242,6 +422,7 @@ void new_symmetric_key(uint8_t *key); * * @return true on success. */ +non_null() bool crypto_memlock(void *data, size_t length); /** @@ -255,8 +436,15 @@ bool crypto_memlock(void *data, size_t length); * * @return true on success. */ +non_null() bool crypto_memunlock(void *data, size_t length); +/** + * @brief Generate a random secret HMAC key. + */ +non_null() +void new_hmac_key(const Random *rng, uint8_t key[CRYPTO_HMAC_KEY_SIZE]); + #ifdef __cplusplus } // extern "C" #endif diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_connected.c b/protocols/Tox/libtox/src/toxcore/events/conference_connected.c new file mode 100644 index 0000000000..ce278755e1 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_connected.c @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Connected { + uint32_t conference_number; +}; + +non_null() +static void tox_event_conference_connected_construct(Tox_Event_Conference_Connected *conference_connected) +{ + *conference_connected = (Tox_Event_Conference_Connected) { + 0 + }; +} +non_null() +static void tox_event_conference_connected_destruct(Tox_Event_Conference_Connected *conference_connected) +{ + return; +} + +non_null() +static void tox_event_conference_connected_set_conference_number( + Tox_Event_Conference_Connected *conference_connected, uint32_t conference_number) +{ + assert(conference_connected != nullptr); + conference_connected->conference_number = conference_number; +} +uint32_t tox_event_conference_connected_get_conference_number( + const Tox_Event_Conference_Connected *conference_connected) +{ + assert(conference_connected != nullptr); + return conference_connected->conference_number; +} + +non_null() +static bool tox_event_conference_connected_pack( + const Tox_Event_Conference_Connected *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_CONNECTED) + && bin_pack_u32(bp, event->conference_number); +} + +non_null() +static bool tox_event_conference_connected_unpack( + Tox_Event_Conference_Connected *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + return bin_unpack_u32(bu, &event->conference_number); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Connected *tox_events_add_conference_connected(Tox_Events *events) +{ + if (events->conference_connected_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_connected_size == events->conference_connected_capacity) { + const uint32_t new_conference_connected_capacity = events->conference_connected_capacity * 2 + 1; + Tox_Event_Conference_Connected *new_conference_connected = (Tox_Event_Conference_Connected *)realloc( + events->conference_connected, new_conference_connected_capacity * sizeof(Tox_Event_Conference_Connected)); + + if (new_conference_connected == nullptr) { + return nullptr; + } + + events->conference_connected = new_conference_connected; + events->conference_connected_capacity = new_conference_connected_capacity; + } + + Tox_Event_Conference_Connected *const conference_connected = + &events->conference_connected[events->conference_connected_size]; + tox_event_conference_connected_construct(conference_connected); + ++events->conference_connected_size; + return conference_connected; +} + +void tox_events_clear_conference_connected(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_connected_size; ++i) { + tox_event_conference_connected_destruct(&events->conference_connected[i]); + } + + free(events->conference_connected); + events->conference_connected = nullptr; + events->conference_connected_size = 0; + events->conference_connected_capacity = 0; +} + +uint32_t tox_events_get_conference_connected_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_connected_size; +} + +const Tox_Event_Conference_Connected *tox_events_get_conference_connected(const Tox_Events *events, uint32_t index) +{ + assert(index < events->conference_connected_size); + assert(events->conference_connected != nullptr); + return &events->conference_connected[index]; +} + +bool tox_events_pack_conference_connected(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_connected_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_connected_pack(tox_events_get_conference_connected(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_connected(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Connected *event = tox_events_add_conference_connected(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_connected_unpack(event, bu); +} + + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_connected(Tox *tox, uint32_t conference_number, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Connected *conference_connected = tox_events_add_conference_connected(state->events); + + if (conference_connected == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_connected_set_conference_number(conference_connected, conference_number); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_invite.c b/protocols/Tox/libtox/src/toxcore/events/conference_invite.c new file mode 100644 index 0000000000..1d88f41d76 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_invite.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Invite { + uint32_t friend_number; + Tox_Conference_Type type; + uint8_t *cookie; + uint32_t cookie_length; +}; + +non_null() +static void tox_event_conference_invite_construct(Tox_Event_Conference_Invite *conference_invite) +{ + *conference_invite = (Tox_Event_Conference_Invite) { + 0 + }; +} +non_null() +static void tox_event_conference_invite_destruct(Tox_Event_Conference_Invite *conference_invite) +{ + free(conference_invite->cookie); +} + +non_null() +static void tox_event_conference_invite_set_friend_number(Tox_Event_Conference_Invite *conference_invite, + uint32_t friend_number) +{ + assert(conference_invite != nullptr); + conference_invite->friend_number = friend_number; +} +uint32_t tox_event_conference_invite_get_friend_number(const Tox_Event_Conference_Invite *conference_invite) +{ + assert(conference_invite != nullptr); + return conference_invite->friend_number; +} + +non_null() +static void tox_event_conference_invite_set_type(Tox_Event_Conference_Invite *conference_invite, + Tox_Conference_Type type) +{ + assert(conference_invite != nullptr); + conference_invite->type = type; +} +Tox_Conference_Type tox_event_conference_invite_get_type(const Tox_Event_Conference_Invite *conference_invite) +{ + assert(conference_invite != nullptr); + return conference_invite->type; +} + +non_null() +static bool tox_event_conference_invite_set_cookie(Tox_Event_Conference_Invite *conference_invite, + const uint8_t *cookie, uint32_t cookie_length) +{ + assert(conference_invite != nullptr); + + if (conference_invite->cookie != nullptr) { + free(conference_invite->cookie); + conference_invite->cookie = nullptr; + conference_invite->cookie_length = 0; + } + + conference_invite->cookie = (uint8_t *)malloc(cookie_length); + + if (conference_invite->cookie == nullptr) { + return false; + } + + memcpy(conference_invite->cookie, cookie, cookie_length); + conference_invite->cookie_length = cookie_length; + return true; +} +uint32_t tox_event_conference_invite_get_cookie_length(const Tox_Event_Conference_Invite *conference_invite) +{ + assert(conference_invite != nullptr); + return conference_invite->cookie_length; +} +const uint8_t *tox_event_conference_invite_get_cookie(const Tox_Event_Conference_Invite *conference_invite) +{ + assert(conference_invite != nullptr); + return conference_invite->cookie; +} + +non_null() +static bool tox_event_conference_invite_pack( + const Tox_Event_Conference_Invite *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_INVITE) + && bin_pack_array(bp, 3) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->type) + && bin_pack_bin(bp, event->cookie, event->cookie_length); +} + +non_null() +static bool tox_event_conference_invite_unpack( + Tox_Event_Conference_Invite *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 3)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && tox_unpack_conference_type(bu, &event->type) + && bin_unpack_bin(bu, &event->cookie, &event->cookie_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Invite *tox_events_add_conference_invite(Tox_Events *events) +{ + if (events->conference_invite_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_invite_size == events->conference_invite_capacity) { + const uint32_t new_conference_invite_capacity = events->conference_invite_capacity * 2 + 1; + Tox_Event_Conference_Invite *new_conference_invite = (Tox_Event_Conference_Invite *)realloc( + events->conference_invite, new_conference_invite_capacity * sizeof(Tox_Event_Conference_Invite)); + + if (new_conference_invite == nullptr) { + return nullptr; + } + + events->conference_invite = new_conference_invite; + events->conference_invite_capacity = new_conference_invite_capacity; + } + + Tox_Event_Conference_Invite *const conference_invite = &events->conference_invite[events->conference_invite_size]; + tox_event_conference_invite_construct(conference_invite); + ++events->conference_invite_size; + return conference_invite; +} + +void tox_events_clear_conference_invite(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_invite_size; ++i) { + tox_event_conference_invite_destruct(&events->conference_invite[i]); + } + + free(events->conference_invite); + events->conference_invite = nullptr; + events->conference_invite_size = 0; + events->conference_invite_capacity = 0; +} + +uint32_t tox_events_get_conference_invite_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_invite_size; +} + +const Tox_Event_Conference_Invite *tox_events_get_conference_invite(const Tox_Events *events, uint32_t index) +{ + assert(index < events->conference_invite_size); + assert(events->conference_invite != nullptr); + return &events->conference_invite[index]; +} + +bool tox_events_pack_conference_invite(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_invite_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_invite_pack(tox_events_get_conference_invite(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_invite(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Invite *event = tox_events_add_conference_invite(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_invite_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_invite(Tox *tox, uint32_t friend_number, Tox_Conference_Type type, + const uint8_t *cookie, size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Invite *conference_invite = tox_events_add_conference_invite(state->events); + + if (conference_invite == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_invite_set_friend_number(conference_invite, friend_number); + tox_event_conference_invite_set_type(conference_invite, type); + tox_event_conference_invite_set_cookie(conference_invite, cookie, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_message.c b/protocols/Tox/libtox/src/toxcore/events/conference_message.c new file mode 100644 index 0000000000..bc9a19b738 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_message.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Message { + uint32_t conference_number; + uint32_t peer_number; + Tox_Message_Type type; + uint8_t *message; + uint32_t message_length; +}; + +non_null() +static void tox_event_conference_message_construct(Tox_Event_Conference_Message *conference_message) +{ + *conference_message = (Tox_Event_Conference_Message) { + 0 + }; +} +non_null() +static void tox_event_conference_message_destruct(Tox_Event_Conference_Message *conference_message) +{ + free(conference_message->message); +} + +non_null() +static void tox_event_conference_message_set_conference_number(Tox_Event_Conference_Message *conference_message, + uint32_t conference_number) +{ + assert(conference_message != nullptr); + conference_message->conference_number = conference_number; +} +uint32_t tox_event_conference_message_get_conference_number(const Tox_Event_Conference_Message *conference_message) +{ + assert(conference_message != nullptr); + return conference_message->conference_number; +} + +non_null() +static void tox_event_conference_message_set_peer_number(Tox_Event_Conference_Message *conference_message, + uint32_t peer_number) +{ + assert(conference_message != nullptr); + conference_message->peer_number = peer_number; +} +uint32_t tox_event_conference_message_get_peer_number(const Tox_Event_Conference_Message *conference_message) +{ + assert(conference_message != nullptr); + return conference_message->peer_number; +} + +non_null() +static void tox_event_conference_message_set_type(Tox_Event_Conference_Message *conference_message, + Tox_Message_Type type) +{ + assert(conference_message != nullptr); + conference_message->type = type; +} +Tox_Message_Type tox_event_conference_message_get_type(const Tox_Event_Conference_Message *conference_message) +{ + assert(conference_message != nullptr); + return conference_message->type; +} + +non_null() +static bool tox_event_conference_message_set_message(Tox_Event_Conference_Message *conference_message, + const uint8_t *message, uint32_t message_length) +{ + assert(conference_message != nullptr); + + if (conference_message->message != nullptr) { + free(conference_message->message); + conference_message->message = nullptr; + conference_message->message_length = 0; + } + + conference_message->message = (uint8_t *)malloc(message_length); + + if (conference_message->message == nullptr) { + return false; + } + + memcpy(conference_message->message, message, message_length); + conference_message->message_length = message_length; + return true; +} +uint32_t tox_event_conference_message_get_message_length(const Tox_Event_Conference_Message *conference_message) +{ + assert(conference_message != nullptr); + return conference_message->message_length; +} +const uint8_t *tox_event_conference_message_get_message(const Tox_Event_Conference_Message *conference_message) +{ + assert(conference_message != nullptr); + return conference_message->message; +} + +non_null() +static bool tox_event_conference_message_pack( + const Tox_Event_Conference_Message *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_MESSAGE) + && bin_pack_array(bp, 4) + && bin_pack_u32(bp, event->conference_number) + && bin_pack_u32(bp, event->peer_number) + && bin_pack_u32(bp, event->type) + && bin_pack_bin(bp, event->message, event->message_length); +} + +non_null() +static bool tox_event_conference_message_unpack( + Tox_Event_Conference_Message *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 4)) { + return false; + } + + return bin_unpack_u32(bu, &event->conference_number) + && bin_unpack_u32(bu, &event->peer_number) + && tox_unpack_message_type(bu, &event->type) + && bin_unpack_bin(bu, &event->message, &event->message_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Message *tox_events_add_conference_message(Tox_Events *events) +{ + if (events->conference_message_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_message_size == events->conference_message_capacity) { + const uint32_t new_conference_message_capacity = events->conference_message_capacity * 2 + 1; + Tox_Event_Conference_Message *new_conference_message = (Tox_Event_Conference_Message *)realloc( + events->conference_message, new_conference_message_capacity * sizeof(Tox_Event_Conference_Message)); + + if (new_conference_message == nullptr) { + return nullptr; + } + + events->conference_message = new_conference_message; + events->conference_message_capacity = new_conference_message_capacity; + } + + Tox_Event_Conference_Message *const conference_message = &events->conference_message[events->conference_message_size]; + tox_event_conference_message_construct(conference_message); + ++events->conference_message_size; + return conference_message; +} + +void tox_events_clear_conference_message(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_message_size; ++i) { + tox_event_conference_message_destruct(&events->conference_message[i]); + } + + free(events->conference_message); + events->conference_message = nullptr; + events->conference_message_size = 0; + events->conference_message_capacity = 0; +} + +uint32_t tox_events_get_conference_message_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_message_size; +} + +const Tox_Event_Conference_Message *tox_events_get_conference_message(const Tox_Events *events, uint32_t index) +{ + assert(index < events->conference_message_size); + assert(events->conference_message != nullptr); + return &events->conference_message[index]; +} + +bool tox_events_pack_conference_message(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_message_pack(tox_events_get_conference_message(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_message(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Message *event = tox_events_add_conference_message(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_message_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_message(Tox *tox, uint32_t conference_number, uint32_t peer_number, + Tox_Message_Type type, const uint8_t *message, size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Message *conference_message = tox_events_add_conference_message(state->events); + + if (conference_message == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_message_set_conference_number(conference_message, conference_number); + tox_event_conference_message_set_peer_number(conference_message, peer_number); + tox_event_conference_message_set_type(conference_message, type); + tox_event_conference_message_set_message(conference_message, message, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c b/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c new file mode 100644 index 0000000000..b6aaa3bf7a --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_peer_list_changed.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Peer_List_Changed { + uint32_t conference_number; +}; + +non_null() +static void tox_event_conference_peer_list_changed_construct(Tox_Event_Conference_Peer_List_Changed + *conference_peer_list_changed) +{ + *conference_peer_list_changed = (Tox_Event_Conference_Peer_List_Changed) { + 0 + }; +} +non_null() +static void tox_event_conference_peer_list_changed_destruct(Tox_Event_Conference_Peer_List_Changed + *conference_peer_list_changed) +{ + return; +} + +non_null() +static void tox_event_conference_peer_list_changed_set_conference_number(Tox_Event_Conference_Peer_List_Changed + *conference_peer_list_changed, uint32_t conference_number) +{ + assert(conference_peer_list_changed != nullptr); + conference_peer_list_changed->conference_number = conference_number; +} +uint32_t tox_event_conference_peer_list_changed_get_conference_number(const Tox_Event_Conference_Peer_List_Changed + *conference_peer_list_changed) +{ + assert(conference_peer_list_changed != nullptr); + return conference_peer_list_changed->conference_number; +} + +non_null() +static bool tox_event_conference_peer_list_changed_pack( + const Tox_Event_Conference_Peer_List_Changed *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED) + && bin_pack_u32(bp, event->conference_number); +} + +non_null() +static bool tox_event_conference_peer_list_changed_unpack( + Tox_Event_Conference_Peer_List_Changed *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + return bin_unpack_u32(bu, &event->conference_number); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Peer_List_Changed *tox_events_add_conference_peer_list_changed(Tox_Events *events) +{ + if (events->conference_peer_list_changed_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_peer_list_changed_size == events->conference_peer_list_changed_capacity) { + const uint32_t new_conference_peer_list_changed_capacity = events->conference_peer_list_changed_capacity * 2 + 1; + Tox_Event_Conference_Peer_List_Changed *new_conference_peer_list_changed = (Tox_Event_Conference_Peer_List_Changed *) + realloc( + events->conference_peer_list_changed, + new_conference_peer_list_changed_capacity * sizeof(Tox_Event_Conference_Peer_List_Changed)); + + if (new_conference_peer_list_changed == nullptr) { + return nullptr; + } + + events->conference_peer_list_changed = new_conference_peer_list_changed; + events->conference_peer_list_changed_capacity = new_conference_peer_list_changed_capacity; + } + + Tox_Event_Conference_Peer_List_Changed *const conference_peer_list_changed = + &events->conference_peer_list_changed[events->conference_peer_list_changed_size]; + tox_event_conference_peer_list_changed_construct(conference_peer_list_changed); + ++events->conference_peer_list_changed_size; + return conference_peer_list_changed; +} + +void tox_events_clear_conference_peer_list_changed(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_peer_list_changed_size; ++i) { + tox_event_conference_peer_list_changed_destruct(&events->conference_peer_list_changed[i]); + } + + free(events->conference_peer_list_changed); + events->conference_peer_list_changed = nullptr; + events->conference_peer_list_changed_size = 0; + events->conference_peer_list_changed_capacity = 0; +} + +uint32_t tox_events_get_conference_peer_list_changed_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_peer_list_changed_size; +} + +const Tox_Event_Conference_Peer_List_Changed *tox_events_get_conference_peer_list_changed(const Tox_Events *events, + uint32_t index) +{ + assert(index < events->conference_peer_list_changed_size); + assert(events->conference_peer_list_changed != nullptr); + return &events->conference_peer_list_changed[index]; +} + +bool tox_events_pack_conference_peer_list_changed(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_peer_list_changed_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_peer_list_changed_pack(tox_events_get_conference_peer_list_changed(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_peer_list_changed(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Peer_List_Changed *event = tox_events_add_conference_peer_list_changed(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_peer_list_changed_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_peer_list_changed(Tox *tox, uint32_t conference_number, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed = tox_events_add_conference_peer_list_changed( + state->events); + + if (conference_peer_list_changed == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_peer_list_changed_set_conference_number(conference_peer_list_changed, conference_number); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c b/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c new file mode 100644 index 0000000000..4d3f285eee --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_peer_name.c @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Peer_Name { + uint32_t conference_number; + uint32_t peer_number; + uint8_t *name; + uint32_t name_length; +}; + +non_null() +static void tox_event_conference_peer_name_construct(Tox_Event_Conference_Peer_Name *conference_peer_name) +{ + *conference_peer_name = (Tox_Event_Conference_Peer_Name) { + 0 + }; +} +non_null() +static void tox_event_conference_peer_name_destruct(Tox_Event_Conference_Peer_Name *conference_peer_name) +{ + free(conference_peer_name->name); +} + +non_null() +static void tox_event_conference_peer_name_set_conference_number(Tox_Event_Conference_Peer_Name *conference_peer_name, + uint32_t conference_number) +{ + assert(conference_peer_name != nullptr); + conference_peer_name->conference_number = conference_number; +} +uint32_t tox_event_conference_peer_name_get_conference_number(const Tox_Event_Conference_Peer_Name + *conference_peer_name) +{ + assert(conference_peer_name != nullptr); + return conference_peer_name->conference_number; +} + +non_null() +static void tox_event_conference_peer_name_set_peer_number(Tox_Event_Conference_Peer_Name *conference_peer_name, + uint32_t peer_number) +{ + assert(conference_peer_name != nullptr); + conference_peer_name->peer_number = peer_number; +} +uint32_t tox_event_conference_peer_name_get_peer_number(const Tox_Event_Conference_Peer_Name *conference_peer_name) +{ + assert(conference_peer_name != nullptr); + return conference_peer_name->peer_number; +} + +non_null() +static bool tox_event_conference_peer_name_set_name(Tox_Event_Conference_Peer_Name *conference_peer_name, + const uint8_t *name, uint32_t name_length) +{ + assert(conference_peer_name != nullptr); + + if (conference_peer_name->name != nullptr) { + free(conference_peer_name->name); + conference_peer_name->name = nullptr; + conference_peer_name->name_length = 0; + } + + conference_peer_name->name = (uint8_t *)malloc(name_length); + + if (conference_peer_name->name == nullptr) { + return false; + } + + memcpy(conference_peer_name->name, name, name_length); + conference_peer_name->name_length = name_length; + return true; +} +uint32_t tox_event_conference_peer_name_get_name_length(const Tox_Event_Conference_Peer_Name *conference_peer_name) +{ + assert(conference_peer_name != nullptr); + return conference_peer_name->name_length; +} +const uint8_t *tox_event_conference_peer_name_get_name(const Tox_Event_Conference_Peer_Name *conference_peer_name) +{ + assert(conference_peer_name != nullptr); + return conference_peer_name->name; +} + +non_null() +static bool tox_event_conference_peer_name_pack( + const Tox_Event_Conference_Peer_Name *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_PEER_NAME) + && bin_pack_array(bp, 3) + && bin_pack_u32(bp, event->conference_number) + && bin_pack_u32(bp, event->peer_number) + && bin_pack_bin(bp, event->name, event->name_length); +} + +non_null() +static bool tox_event_conference_peer_name_unpack( + Tox_Event_Conference_Peer_Name *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 3)) { + return false; + } + + return bin_unpack_u32(bu, &event->conference_number) + && bin_unpack_u32(bu, &event->peer_number) + && bin_unpack_bin(bu, &event->name, &event->name_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Peer_Name *tox_events_add_conference_peer_name(Tox_Events *events) +{ + if (events->conference_peer_name_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_peer_name_size == events->conference_peer_name_capacity) { + const uint32_t new_conference_peer_name_capacity = events->conference_peer_name_capacity * 2 + 1; + Tox_Event_Conference_Peer_Name *new_conference_peer_name = (Tox_Event_Conference_Peer_Name *)realloc( + events->conference_peer_name, new_conference_peer_name_capacity * sizeof(Tox_Event_Conference_Peer_Name)); + + if (new_conference_peer_name == nullptr) { + return nullptr; + } + + events->conference_peer_name = new_conference_peer_name; + events->conference_peer_name_capacity = new_conference_peer_name_capacity; + } + + Tox_Event_Conference_Peer_Name *const conference_peer_name = + &events->conference_peer_name[events->conference_peer_name_size]; + tox_event_conference_peer_name_construct(conference_peer_name); + ++events->conference_peer_name_size; + return conference_peer_name; +} + +void tox_events_clear_conference_peer_name(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_peer_name_size; ++i) { + tox_event_conference_peer_name_destruct(&events->conference_peer_name[i]); + } + + free(events->conference_peer_name); + events->conference_peer_name = nullptr; + events->conference_peer_name_size = 0; + events->conference_peer_name_capacity = 0; +} + +uint32_t tox_events_get_conference_peer_name_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_peer_name_size; +} + +const Tox_Event_Conference_Peer_Name *tox_events_get_conference_peer_name(const Tox_Events *events, uint32_t index) +{ + assert(index < events->conference_peer_name_size); + assert(events->conference_peer_name != nullptr); + return &events->conference_peer_name[index]; +} + +bool tox_events_pack_conference_peer_name(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_peer_name_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_peer_name_pack(tox_events_get_conference_peer_name(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_peer_name(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Peer_Name *event = tox_events_add_conference_peer_name(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_peer_name_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_peer_name(Tox *tox, uint32_t conference_number, uint32_t peer_number, + const uint8_t *name, size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Peer_Name *conference_peer_name = tox_events_add_conference_peer_name(state->events); + + if (conference_peer_name == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_peer_name_set_conference_number(conference_peer_name, conference_number); + tox_event_conference_peer_name_set_peer_number(conference_peer_name, peer_number); + tox_event_conference_peer_name_set_name(conference_peer_name, name, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/conference_title.c b/protocols/Tox/libtox/src/toxcore/events/conference_title.c new file mode 100644 index 0000000000..1866966e9f --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/conference_title.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Conference_Title { + uint32_t conference_number; + uint32_t peer_number; + uint8_t *title; + uint32_t title_length; +}; + +non_null() +static void tox_event_conference_title_construct(Tox_Event_Conference_Title *conference_title) +{ + *conference_title = (Tox_Event_Conference_Title) { + 0 + }; +} +non_null() +static void tox_event_conference_title_destruct(Tox_Event_Conference_Title *conference_title) +{ + free(conference_title->title); +} + +non_null() +static void tox_event_conference_title_set_conference_number(Tox_Event_Conference_Title *conference_title, + uint32_t conference_number) +{ + assert(conference_title != nullptr); + conference_title->conference_number = conference_number; +} +uint32_t tox_event_conference_title_get_conference_number(const Tox_Event_Conference_Title *conference_title) +{ + assert(conference_title != nullptr); + return conference_title->conference_number; +} + +non_null() +static void tox_event_conference_title_set_peer_number(Tox_Event_Conference_Title *conference_title, + uint32_t peer_number) +{ + assert(conference_title != nullptr); + conference_title->peer_number = peer_number; +} +uint32_t tox_event_conference_title_get_peer_number(const Tox_Event_Conference_Title *conference_title) +{ + assert(conference_title != nullptr); + return conference_title->peer_number; +} + +non_null() +static bool tox_event_conference_title_set_title(Tox_Event_Conference_Title *conference_title, const uint8_t *title, + uint32_t title_length) +{ + assert(conference_title != nullptr); + + if (conference_title->title != nullptr) { + free(conference_title->title); + conference_title->title = nullptr; + conference_title->title_length = 0; + } + + conference_title->title = (uint8_t *)malloc(title_length); + + if (conference_title->title == nullptr) { + return false; + } + + memcpy(conference_title->title, title, title_length); + conference_title->title_length = title_length; + return true; +} +uint32_t tox_event_conference_title_get_title_length(const Tox_Event_Conference_Title *conference_title) +{ + assert(conference_title != nullptr); + return conference_title->title_length; +} +const uint8_t *tox_event_conference_title_get_title(const Tox_Event_Conference_Title *conference_title) +{ + assert(conference_title != nullptr); + return conference_title->title; +} + +non_null() +static bool tox_event_conference_title_pack( + const Tox_Event_Conference_Title *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_CONFERENCE_TITLE) + && bin_pack_array(bp, 3) + && bin_pack_u32(bp, event->conference_number) + && bin_pack_u32(bp, event->peer_number) + && bin_pack_bin(bp, event->title, event->title_length); +} + +non_null() +static bool tox_event_conference_title_unpack( + Tox_Event_Conference_Title *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 3)) { + return false; + } + + return bin_unpack_u32(bu, &event->conference_number) + && bin_unpack_u32(bu, &event->peer_number) + && bin_unpack_bin(bu, &event->title, &event->title_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Conference_Title *tox_events_add_conference_title(Tox_Events *events) +{ + if (events->conference_title_size == UINT32_MAX) { + return nullptr; + } + + if (events->conference_title_size == events->conference_title_capacity) { + const uint32_t new_conference_title_capacity = events->conference_title_capacity * 2 + 1; + Tox_Event_Conference_Title *new_conference_title = (Tox_Event_Conference_Title *)realloc( + events->conference_title, new_conference_title_capacity * sizeof(Tox_Event_Conference_Title)); + + if (new_conference_title == nullptr) { + return nullptr; + } + + events->conference_title = new_conference_title; + events->conference_title_capacity = new_conference_title_capacity; + } + + Tox_Event_Conference_Title *const conference_title = &events->conference_title[events->conference_title_size]; + tox_event_conference_title_construct(conference_title); + ++events->conference_title_size; + return conference_title; +} + +void tox_events_clear_conference_title(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->conference_title_size; ++i) { + tox_event_conference_title_destruct(&events->conference_title[i]); + } + + free(events->conference_title); + events->conference_title = nullptr; + events->conference_title_size = 0; + events->conference_title_capacity = 0; +} + +uint32_t tox_events_get_conference_title_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->conference_title_size; +} + +const Tox_Event_Conference_Title *tox_events_get_conference_title(const Tox_Events *events, uint32_t index) +{ + assert(index < events->conference_title_size); + assert(events->conference_title != nullptr); + return &events->conference_title[index]; +} + +bool tox_events_pack_conference_title(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_conference_title_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_conference_title_pack(tox_events_get_conference_title(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_conference_title(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Conference_Title *event = tox_events_add_conference_title(events); + + if (event == nullptr) { + return false; + } + + return tox_event_conference_title_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_conference_title(Tox *tox, uint32_t conference_number, uint32_t peer_number, + const uint8_t *title, size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Conference_Title *conference_title = tox_events_add_conference_title(state->events); + + if (conference_title == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_conference_title_set_conference_number(conference_title, conference_number); + tox_event_conference_title_set_peer_number(conference_title, peer_number); + tox_event_conference_title_set_title(conference_title, title, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/events_alloc.c b/protocols/Tox/libtox/src/toxcore/events/events_alloc.c new file mode 100644 index 0000000000..f661c5c039 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/events_alloc.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> + +#include "../ccompat.h" + +Tox_Events_State *tox_events_alloc(void *user_data) +{ + Tox_Events_State *state = (Tox_Events_State *)user_data; + assert(state != nullptr); + + if (state->events != nullptr) { + // Already allocated. + return state; + } + + state->events = (Tox_Events *)calloc(1, sizeof(Tox_Events)); + + if (state->events == nullptr) { + // It's still null => allocation failed. + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + } else { + *state->events = (Tox_Events) { + nullptr + }; + } + + return state; +} + +void tox_events_free(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + tox_events_clear_conference_connected(events); + tox_events_clear_conference_invite(events); + tox_events_clear_conference_message(events); + tox_events_clear_conference_peer_list_changed(events); + tox_events_clear_conference_peer_name(events); + tox_events_clear_conference_title(events); + tox_events_clear_file_chunk_request(events); + tox_events_clear_file_recv_chunk(events); + tox_events_clear_file_recv_control(events); + tox_events_clear_file_recv(events); + tox_events_clear_friend_connection_status(events); + tox_events_clear_friend_lossless_packet(events); + tox_events_clear_friend_lossy_packet(events); + tox_events_clear_friend_message(events); + tox_events_clear_friend_name(events); + tox_events_clear_friend_read_receipt(events); + tox_events_clear_friend_request(events); + tox_events_clear_friend_status(events); + tox_events_clear_friend_status_message(events); + tox_events_clear_friend_typing(events); + tox_events_clear_self_connection_status(events); + free(events); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/events_alloc.h b/protocols/Tox/libtox/src/toxcore/events/events_alloc.h new file mode 100644 index 0000000000..6c5a7abd4f --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/events_alloc.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H +#define C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H + +#include "../attributes.h" +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../tox_events.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Tox_Events { + Tox_Event_Conference_Connected *conference_connected; + uint32_t conference_connected_size; + uint32_t conference_connected_capacity; + + Tox_Event_Conference_Invite *conference_invite; + uint32_t conference_invite_size; + uint32_t conference_invite_capacity; + + Tox_Event_Conference_Message *conference_message; + uint32_t conference_message_size; + uint32_t conference_message_capacity; + + Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed; + uint32_t conference_peer_list_changed_size; + uint32_t conference_peer_list_changed_capacity; + + Tox_Event_Conference_Peer_Name *conference_peer_name; + uint32_t conference_peer_name_size; + uint32_t conference_peer_name_capacity; + + Tox_Event_Conference_Title *conference_title; + uint32_t conference_title_size; + uint32_t conference_title_capacity; + + Tox_Event_File_Chunk_Request *file_chunk_request; + uint32_t file_chunk_request_size; + uint32_t file_chunk_request_capacity; + + Tox_Event_File_Recv *file_recv; + uint32_t file_recv_size; + uint32_t file_recv_capacity; + + Tox_Event_File_Recv_Chunk *file_recv_chunk; + uint32_t file_recv_chunk_size; + uint32_t file_recv_chunk_capacity; + + Tox_Event_File_Recv_Control *file_recv_control; + uint32_t file_recv_control_size; + uint32_t file_recv_control_capacity; + + Tox_Event_Friend_Connection_Status *friend_connection_status; + uint32_t friend_connection_status_size; + uint32_t friend_connection_status_capacity; + + Tox_Event_Friend_Lossless_Packet *friend_lossless_packet; + uint32_t friend_lossless_packet_size; + uint32_t friend_lossless_packet_capacity; + + Tox_Event_Friend_Lossy_Packet *friend_lossy_packet; + uint32_t friend_lossy_packet_size; + uint32_t friend_lossy_packet_capacity; + + Tox_Event_Friend_Message *friend_message; + uint32_t friend_message_size; + uint32_t friend_message_capacity; + + Tox_Event_Friend_Name *friend_name; + uint32_t friend_name_size; + uint32_t friend_name_capacity; + + Tox_Event_Friend_Read_Receipt *friend_read_receipt; + uint32_t friend_read_receipt_size; + uint32_t friend_read_receipt_capacity; + + Tox_Event_Friend_Request *friend_request; + uint32_t friend_request_size; + uint32_t friend_request_capacity; + + Tox_Event_Friend_Status *friend_status; + uint32_t friend_status_size; + uint32_t friend_status_capacity; + + Tox_Event_Friend_Status_Message *friend_status_message; + uint32_t friend_status_message_size; + uint32_t friend_status_message_capacity; + + Tox_Event_Friend_Typing *friend_typing; + uint32_t friend_typing_size; + uint32_t friend_typing_capacity; + + Tox_Event_Self_Connection_Status *self_connection_status; + uint32_t self_connection_status_size; + uint32_t self_connection_status_capacity; +}; + +typedef struct Tox_Events_State { + Tox_Err_Events_Iterate error; + Tox_Events *events; +} Tox_Events_State; + +tox_conference_connected_cb tox_events_handle_conference_connected; +tox_conference_invite_cb tox_events_handle_conference_invite; +tox_conference_message_cb tox_events_handle_conference_message; +tox_conference_peer_list_changed_cb tox_events_handle_conference_peer_list_changed; +tox_conference_peer_name_cb tox_events_handle_conference_peer_name; +tox_conference_title_cb tox_events_handle_conference_title; +tox_file_chunk_request_cb tox_events_handle_file_chunk_request; +tox_file_recv_cb tox_events_handle_file_recv; +tox_file_recv_chunk_cb tox_events_handle_file_recv_chunk; +tox_file_recv_control_cb tox_events_handle_file_recv_control; +tox_friend_connection_status_cb tox_events_handle_friend_connection_status; +tox_friend_lossless_packet_cb tox_events_handle_friend_lossless_packet; +tox_friend_lossy_packet_cb tox_events_handle_friend_lossy_packet; +tox_friend_message_cb tox_events_handle_friend_message; +tox_friend_name_cb tox_events_handle_friend_name; +tox_friend_read_receipt_cb tox_events_handle_friend_read_receipt; +tox_friend_request_cb tox_events_handle_friend_request; +tox_friend_status_cb tox_events_handle_friend_status; +tox_friend_status_message_cb tox_events_handle_friend_status_message; +tox_friend_typing_cb tox_events_handle_friend_typing; +tox_self_connection_status_cb tox_events_handle_self_connection_status; + +// non_null() +typedef void tox_events_clear_cb(Tox_Events *events); + +tox_events_clear_cb tox_events_clear_conference_connected; +tox_events_clear_cb tox_events_clear_conference_invite; +tox_events_clear_cb tox_events_clear_conference_message; +tox_events_clear_cb tox_events_clear_conference_peer_list_changed; +tox_events_clear_cb tox_events_clear_conference_peer_name; +tox_events_clear_cb tox_events_clear_conference_title; +tox_events_clear_cb tox_events_clear_file_chunk_request; +tox_events_clear_cb tox_events_clear_file_recv_chunk; +tox_events_clear_cb tox_events_clear_file_recv_control; +tox_events_clear_cb tox_events_clear_file_recv; +tox_events_clear_cb tox_events_clear_friend_connection_status; +tox_events_clear_cb tox_events_clear_friend_lossless_packet; +tox_events_clear_cb tox_events_clear_friend_lossy_packet; +tox_events_clear_cb tox_events_clear_friend_message; +tox_events_clear_cb tox_events_clear_friend_name; +tox_events_clear_cb tox_events_clear_friend_read_receipt; +tox_events_clear_cb tox_events_clear_friend_request; +tox_events_clear_cb tox_events_clear_friend_status_message; +tox_events_clear_cb tox_events_clear_friend_status; +tox_events_clear_cb tox_events_clear_friend_typing; +tox_events_clear_cb tox_events_clear_self_connection_status; + +// non_null() +typedef bool tox_events_pack_cb(const Tox_Events *events, Bin_Pack *bp); + +tox_events_pack_cb tox_events_pack_conference_connected; +tox_events_pack_cb tox_events_pack_conference_invite; +tox_events_pack_cb tox_events_pack_conference_message; +tox_events_pack_cb tox_events_pack_conference_peer_list_changed; +tox_events_pack_cb tox_events_pack_conference_peer_name; +tox_events_pack_cb tox_events_pack_conference_title; +tox_events_pack_cb tox_events_pack_file_chunk_request; +tox_events_pack_cb tox_events_pack_file_recv_chunk; +tox_events_pack_cb tox_events_pack_file_recv_control; +tox_events_pack_cb tox_events_pack_file_recv; +tox_events_pack_cb tox_events_pack_friend_connection_status; +tox_events_pack_cb tox_events_pack_friend_lossless_packet; +tox_events_pack_cb tox_events_pack_friend_lossy_packet; +tox_events_pack_cb tox_events_pack_friend_message; +tox_events_pack_cb tox_events_pack_friend_name; +tox_events_pack_cb tox_events_pack_friend_read_receipt; +tox_events_pack_cb tox_events_pack_friend_request; +tox_events_pack_cb tox_events_pack_friend_status_message; +tox_events_pack_cb tox_events_pack_friend_status; +tox_events_pack_cb tox_events_pack_friend_typing; +tox_events_pack_cb tox_events_pack_self_connection_status; + +tox_events_pack_cb tox_events_pack; + +// non_null() +typedef bool tox_events_unpack_cb(Tox_Events *events, Bin_Unpack *bu); + +tox_events_unpack_cb tox_events_unpack_conference_connected; +tox_events_unpack_cb tox_events_unpack_conference_invite; +tox_events_unpack_cb tox_events_unpack_conference_message; +tox_events_unpack_cb tox_events_unpack_conference_peer_list_changed; +tox_events_unpack_cb tox_events_unpack_conference_peer_name; +tox_events_unpack_cb tox_events_unpack_conference_title; +tox_events_unpack_cb tox_events_unpack_file_chunk_request; +tox_events_unpack_cb tox_events_unpack_file_recv_chunk; +tox_events_unpack_cb tox_events_unpack_file_recv_control; +tox_events_unpack_cb tox_events_unpack_file_recv; +tox_events_unpack_cb tox_events_unpack_friend_connection_status; +tox_events_unpack_cb tox_events_unpack_friend_lossless_packet; +tox_events_unpack_cb tox_events_unpack_friend_lossy_packet; +tox_events_unpack_cb tox_events_unpack_friend_message; +tox_events_unpack_cb tox_events_unpack_friend_name; +tox_events_unpack_cb tox_events_unpack_friend_read_receipt; +tox_events_unpack_cb tox_events_unpack_friend_request; +tox_events_unpack_cb tox_events_unpack_friend_status_message; +tox_events_unpack_cb tox_events_unpack_friend_status; +tox_events_unpack_cb tox_events_unpack_friend_typing; +tox_events_unpack_cb tox_events_unpack_self_connection_status; + +tox_events_unpack_cb tox_events_unpack; + +non_null() +Tox_Events_State *tox_events_alloc(void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif // C_TOXCORE_TOXCORE_TOX_EVENTS_INTERNAL_H diff --git a/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c b/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c new file mode 100644 index 0000000000..f5a9421f58 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/file_chunk_request.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_File_Chunk_Request { + uint32_t friend_number; + uint32_t file_number; + uint64_t position; + uint16_t length; +}; + +non_null() +static void tox_event_file_chunk_request_construct(Tox_Event_File_Chunk_Request *file_chunk_request) +{ + *file_chunk_request = (Tox_Event_File_Chunk_Request) { + 0 + }; +} +non_null() +static void tox_event_file_chunk_request_destruct(Tox_Event_File_Chunk_Request *file_chunk_request) +{ + return; +} + +non_null() +static void tox_event_file_chunk_request_set_friend_number(Tox_Event_File_Chunk_Request *file_chunk_request, + uint32_t friend_number) +{ + assert(file_chunk_request != nullptr); + file_chunk_request->friend_number = friend_number; +} +uint32_t tox_event_file_chunk_request_get_friend_number(const Tox_Event_File_Chunk_Request *file_chunk_request) +{ + assert(file_chunk_request != nullptr); + return file_chunk_request->friend_number; +} + +non_null() +static void tox_event_file_chunk_request_set_file_number(Tox_Event_File_Chunk_Request *file_chunk_request, + uint32_t file_number) +{ + assert(file_chunk_request != nullptr); + file_chunk_request->file_number = file_number; +} +uint32_t tox_event_file_chunk_request_get_file_number(const Tox_Event_File_Chunk_Request *file_chunk_request) +{ + assert(file_chunk_request != nullptr); + return file_chunk_request->file_number; +} + +non_null() +static void tox_event_file_chunk_request_set_position(Tox_Event_File_Chunk_Request *file_chunk_request, + uint64_t position) +{ + assert(file_chunk_request != nullptr); + file_chunk_request->position = position; +} +uint64_t tox_event_file_chunk_request_get_position(const Tox_Event_File_Chunk_Request *file_chunk_request) +{ + assert(file_chunk_request != nullptr); + return file_chunk_request->position; +} + +non_null() +static void tox_event_file_chunk_request_set_length(Tox_Event_File_Chunk_Request *file_chunk_request, uint16_t length) +{ + assert(file_chunk_request != nullptr); + file_chunk_request->length = length; +} +uint16_t tox_event_file_chunk_request_get_length(const Tox_Event_File_Chunk_Request *file_chunk_request) +{ + assert(file_chunk_request != nullptr); + return file_chunk_request->length; +} + +non_null() +static bool tox_event_file_chunk_request_pack( + const Tox_Event_File_Chunk_Request *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FILE_CHUNK_REQUEST) + && bin_pack_array(bp, 4) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->file_number) + && bin_pack_u64(bp, event->position) + && bin_pack_u16(bp, event->length); +} + +non_null() +static bool tox_event_file_chunk_request_unpack( + Tox_Event_File_Chunk_Request *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 4)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_u32(bu, &event->file_number) + && bin_unpack_u64(bu, &event->position) + && bin_unpack_u16(bu, &event->length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_File_Chunk_Request *tox_events_add_file_chunk_request(Tox_Events *events) +{ + if (events->file_chunk_request_size == UINT32_MAX) { + return nullptr; + } + + if (events->file_chunk_request_size == events->file_chunk_request_capacity) { + const uint32_t new_file_chunk_request_capacity = events->file_chunk_request_capacity * 2 + 1; + Tox_Event_File_Chunk_Request *new_file_chunk_request = (Tox_Event_File_Chunk_Request *)realloc( + events->file_chunk_request, new_file_chunk_request_capacity * sizeof(Tox_Event_File_Chunk_Request)); + + if (new_file_chunk_request == nullptr) { + return nullptr; + } + + events->file_chunk_request = new_file_chunk_request; + events->file_chunk_request_capacity = new_file_chunk_request_capacity; + } + + Tox_Event_File_Chunk_Request *const file_chunk_request = &events->file_chunk_request[events->file_chunk_request_size]; + tox_event_file_chunk_request_construct(file_chunk_request); + ++events->file_chunk_request_size; + return file_chunk_request; +} + +void tox_events_clear_file_chunk_request(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->file_chunk_request_size; ++i) { + tox_event_file_chunk_request_destruct(&events->file_chunk_request[i]); + } + + free(events->file_chunk_request); + events->file_chunk_request = nullptr; + events->file_chunk_request_size = 0; + events->file_chunk_request_capacity = 0; +} + +uint32_t tox_events_get_file_chunk_request_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->file_chunk_request_size; +} + +const Tox_Event_File_Chunk_Request *tox_events_get_file_chunk_request(const Tox_Events *events, uint32_t index) +{ + assert(index < events->file_chunk_request_size); + assert(events->file_chunk_request != nullptr); + return &events->file_chunk_request[index]; +} + +bool tox_events_pack_file_chunk_request(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_file_chunk_request_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_file_chunk_request_pack(tox_events_get_file_chunk_request(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_file_chunk_request(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_File_Chunk_Request *event = tox_events_add_file_chunk_request(events); + + if (event == nullptr) { + return false; + } + + return tox_event_file_chunk_request_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, + size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_File_Chunk_Request *file_chunk_request = tox_events_add_file_chunk_request(state->events); + + if (file_chunk_request == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_file_chunk_request_set_friend_number(file_chunk_request, friend_number); + tox_event_file_chunk_request_set_file_number(file_chunk_request, file_number); + tox_event_file_chunk_request_set_position(file_chunk_request, position); + tox_event_file_chunk_request_set_length(file_chunk_request, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv.c b/protocols/Tox/libtox/src/toxcore/events/file_recv.c new file mode 100644 index 0000000000..a907b3fdbe --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/file_recv.c @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_File_Recv { + uint32_t friend_number; + uint32_t file_number; + uint32_t kind; + uint64_t file_size; + uint8_t *filename; + uint32_t filename_length; +}; + +non_null() +static void tox_event_file_recv_construct(Tox_Event_File_Recv *file_recv) +{ + *file_recv = (Tox_Event_File_Recv) { + 0 + }; +} +non_null() +static void tox_event_file_recv_destruct(Tox_Event_File_Recv *file_recv) +{ + free(file_recv->filename); +} + +non_null() +static void tox_event_file_recv_set_friend_number(Tox_Event_File_Recv *file_recv, + uint32_t friend_number) +{ + assert(file_recv != nullptr); + file_recv->friend_number = friend_number; +} +uint32_t tox_event_file_recv_get_friend_number(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->friend_number; +} + +non_null() +static void tox_event_file_recv_set_file_number(Tox_Event_File_Recv *file_recv, + uint32_t file_number) +{ + assert(file_recv != nullptr); + file_recv->file_number = file_number; +} +uint32_t tox_event_file_recv_get_file_number(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->file_number; +} + +non_null() +static void tox_event_file_recv_set_kind(Tox_Event_File_Recv *file_recv, + uint32_t kind) +{ + assert(file_recv != nullptr); + file_recv->kind = kind; +} +uint32_t tox_event_file_recv_get_kind(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->kind; +} + +non_null() +static void tox_event_file_recv_set_file_size(Tox_Event_File_Recv *file_recv, + uint64_t file_size) +{ + assert(file_recv != nullptr); + file_recv->file_size = file_size; +} +uint64_t tox_event_file_recv_get_file_size(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->file_size; +} + +non_null() +static bool tox_event_file_recv_set_filename(Tox_Event_File_Recv *file_recv, const uint8_t *filename, + uint32_t filename_length) +{ + assert(file_recv != nullptr); + + if (file_recv->filename != nullptr) { + free(file_recv->filename); + file_recv->filename = nullptr; + file_recv->filename_length = 0; + } + + file_recv->filename = (uint8_t *)malloc(filename_length); + + if (file_recv->filename == nullptr) { + return false; + } + + memcpy(file_recv->filename, filename, filename_length); + file_recv->filename_length = filename_length; + return true; +} +uint32_t tox_event_file_recv_get_filename_length(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->filename_length; +} +const uint8_t *tox_event_file_recv_get_filename(const Tox_Event_File_Recv *file_recv) +{ + assert(file_recv != nullptr); + return file_recv->filename; +} + +non_null() +static bool tox_event_file_recv_pack( + const Tox_Event_File_Recv *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FILE_RECV) + && bin_pack_array(bp, 5) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->file_number) + && bin_pack_u32(bp, event->kind) + && bin_pack_u64(bp, event->file_size) + && bin_pack_bin(bp, event->filename, event->filename_length); +} + +non_null() +static bool tox_event_file_recv_unpack( + Tox_Event_File_Recv *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 5)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_u32(bu, &event->file_number) + && bin_unpack_u32(bu, &event->kind) + && bin_unpack_u64(bu, &event->file_size) + && bin_unpack_bin(bu, &event->filename, &event->filename_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_File_Recv *tox_events_add_file_recv(Tox_Events *events) +{ + if (events->file_recv_size == UINT32_MAX) { + return nullptr; + } + + if (events->file_recv_size == events->file_recv_capacity) { + const uint32_t new_file_recv_capacity = events->file_recv_capacity * 2 + 1; + Tox_Event_File_Recv *new_file_recv = (Tox_Event_File_Recv *)realloc( + events->file_recv, new_file_recv_capacity * sizeof(Tox_Event_File_Recv)); + + if (new_file_recv == nullptr) { + return nullptr; + } + + events->file_recv = new_file_recv; + events->file_recv_capacity = new_file_recv_capacity; + } + + Tox_Event_File_Recv *const file_recv = &events->file_recv[events->file_recv_size]; + tox_event_file_recv_construct(file_recv); + ++events->file_recv_size; + return file_recv; +} + +void tox_events_clear_file_recv(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->file_recv_size; ++i) { + tox_event_file_recv_destruct(&events->file_recv[i]); + } + + free(events->file_recv); + events->file_recv = nullptr; + events->file_recv_size = 0; + events->file_recv_capacity = 0; +} + +uint32_t tox_events_get_file_recv_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->file_recv_size; +} + +const Tox_Event_File_Recv *tox_events_get_file_recv(const Tox_Events *events, uint32_t index) +{ + assert(index < events->file_recv_size); + assert(events->file_recv != nullptr); + return &events->file_recv[index]; +} + +bool tox_events_pack_file_recv(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_file_recv_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_file_recv_pack(tox_events_get_file_recv(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_file_recv(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_File_Recv *event = tox_events_add_file_recv(events); + + if (event == nullptr) { + return false; + } + + return tox_event_file_recv_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_file_recv(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind, + uint64_t file_size, const uint8_t *filename, size_t filename_length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_File_Recv *file_recv = tox_events_add_file_recv(state->events); + + if (file_recv == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_file_recv_set_friend_number(file_recv, friend_number); + tox_event_file_recv_set_file_number(file_recv, file_number); + tox_event_file_recv_set_kind(file_recv, kind); + tox_event_file_recv_set_file_size(file_recv, file_size); + tox_event_file_recv_set_filename(file_recv, filename, filename_length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c b/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c new file mode 100644 index 0000000000..0fe7784465 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/file_recv_chunk.c @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_File_Recv_Chunk { + uint32_t friend_number; + uint32_t file_number; + uint64_t position; + uint8_t *data; + uint32_t data_length; +}; + +non_null() +static void tox_event_file_recv_chunk_construct(Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + *file_recv_chunk = (Tox_Event_File_Recv_Chunk) { + 0 + }; +} +non_null() +static void tox_event_file_recv_chunk_destruct(Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + free(file_recv_chunk->data); +} + +non_null() +static void tox_event_file_recv_chunk_set_friend_number(Tox_Event_File_Recv_Chunk *file_recv_chunk, + uint32_t friend_number) +{ + assert(file_recv_chunk != nullptr); + file_recv_chunk->friend_number = friend_number; +} +uint32_t tox_event_file_recv_chunk_get_friend_number(const Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + assert(file_recv_chunk != nullptr); + return file_recv_chunk->friend_number; +} + +non_null() +static void tox_event_file_recv_chunk_set_file_number(Tox_Event_File_Recv_Chunk *file_recv_chunk, + uint32_t file_number) +{ + assert(file_recv_chunk != nullptr); + file_recv_chunk->file_number = file_number; +} +uint32_t tox_event_file_recv_chunk_get_file_number(const Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + assert(file_recv_chunk != nullptr); + return file_recv_chunk->file_number; +} + +non_null() +static void tox_event_file_recv_chunk_set_position(Tox_Event_File_Recv_Chunk *file_recv_chunk, + uint64_t position) +{ + assert(file_recv_chunk != nullptr); + file_recv_chunk->position = position; +} +uint64_t tox_event_file_recv_chunk_get_position(const Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + assert(file_recv_chunk != nullptr); + return file_recv_chunk->position; +} + +non_null() +static bool tox_event_file_recv_chunk_set_data(Tox_Event_File_Recv_Chunk *file_recv_chunk, const uint8_t *data, + uint32_t data_length) +{ + assert(file_recv_chunk != nullptr); + + if (file_recv_chunk->data != nullptr) { + free(file_recv_chunk->data); + file_recv_chunk->data = nullptr; + file_recv_chunk->data_length = 0; + } + + file_recv_chunk->data = (uint8_t *)malloc(data_length); + + if (file_recv_chunk->data == nullptr) { + return false; + } + + memcpy(file_recv_chunk->data, data, data_length); + file_recv_chunk->data_length = data_length; + return true; +} +uint32_t tox_event_file_recv_chunk_get_length(const Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + assert(file_recv_chunk != nullptr); + return file_recv_chunk->data_length; +} +const uint8_t *tox_event_file_recv_chunk_get_data(const Tox_Event_File_Recv_Chunk *file_recv_chunk) +{ + assert(file_recv_chunk != nullptr); + return file_recv_chunk->data; +} + +non_null() +static bool tox_event_file_recv_chunk_pack( + const Tox_Event_File_Recv_Chunk *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FILE_RECV_CHUNK) + && bin_pack_array(bp, 4) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->file_number) + && bin_pack_u64(bp, event->position) + && bin_pack_bin(bp, event->data, event->data_length); +} + +non_null() +static bool tox_event_file_recv_chunk_unpack( + Tox_Event_File_Recv_Chunk *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 4)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_u32(bu, &event->file_number) + && bin_unpack_u64(bu, &event->position) + && bin_unpack_bin(bu, &event->data, &event->data_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_File_Recv_Chunk *tox_events_add_file_recv_chunk(Tox_Events *events) +{ + if (events->file_recv_chunk_size == UINT32_MAX) { + return nullptr; + } + + if (events->file_recv_chunk_size == events->file_recv_chunk_capacity) { + const uint32_t new_file_recv_chunk_capacity = events->file_recv_chunk_capacity * 2 + 1; + Tox_Event_File_Recv_Chunk *new_file_recv_chunk = (Tox_Event_File_Recv_Chunk *)realloc( + events->file_recv_chunk, new_file_recv_chunk_capacity * sizeof(Tox_Event_File_Recv_Chunk)); + + if (new_file_recv_chunk == nullptr) { + return nullptr; + } + + events->file_recv_chunk = new_file_recv_chunk; + events->file_recv_chunk_capacity = new_file_recv_chunk_capacity; + } + + Tox_Event_File_Recv_Chunk *const file_recv_chunk = &events->file_recv_chunk[events->file_recv_chunk_size]; + tox_event_file_recv_chunk_construct(file_recv_chunk); + ++events->file_recv_chunk_size; + return file_recv_chunk; +} + +void tox_events_clear_file_recv_chunk(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->file_recv_chunk_size; ++i) { + tox_event_file_recv_chunk_destruct(&events->file_recv_chunk[i]); + } + + free(events->file_recv_chunk); + events->file_recv_chunk = nullptr; + events->file_recv_chunk_size = 0; + events->file_recv_chunk_capacity = 0; +} + +uint32_t tox_events_get_file_recv_chunk_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->file_recv_chunk_size; +} + +const Tox_Event_File_Recv_Chunk *tox_events_get_file_recv_chunk(const Tox_Events *events, uint32_t index) +{ + assert(index < events->file_recv_chunk_size); + assert(events->file_recv_chunk != nullptr); + return &events->file_recv_chunk[index]; +} + +bool tox_events_pack_file_recv_chunk(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_file_recv_chunk_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_file_recv_chunk_pack(tox_events_get_file_recv_chunk(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_file_recv_chunk(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_File_Recv_Chunk *event = tox_events_add_file_recv_chunk(events); + + if (event == nullptr) { + return false; + } + + return tox_event_file_recv_chunk_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_file_recv_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, + const uint8_t *data, size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_File_Recv_Chunk *file_recv_chunk = tox_events_add_file_recv_chunk(state->events); + + if (file_recv_chunk == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_file_recv_chunk_set_friend_number(file_recv_chunk, friend_number); + tox_event_file_recv_chunk_set_file_number(file_recv_chunk, file_number); + tox_event_file_recv_chunk_set_position(file_recv_chunk, position); + tox_event_file_recv_chunk_set_data(file_recv_chunk, data, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c b/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c new file mode 100644 index 0000000000..af04b33dce --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/file_recv_control.c @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_File_Recv_Control { + uint32_t friend_number; + uint32_t file_number; + Tox_File_Control control; +}; + +non_null() +static void tox_event_file_recv_control_construct(Tox_Event_File_Recv_Control *file_recv_control) +{ + *file_recv_control = (Tox_Event_File_Recv_Control) { + 0 + }; +} +non_null() +static void tox_event_file_recv_control_destruct(Tox_Event_File_Recv_Control *file_recv_control) +{ + return; +} + +non_null() +static void tox_event_file_recv_control_set_friend_number(Tox_Event_File_Recv_Control *file_recv_control, + uint32_t friend_number) +{ + assert(file_recv_control != nullptr); + file_recv_control->friend_number = friend_number; +} +uint32_t tox_event_file_recv_control_get_friend_number(const Tox_Event_File_Recv_Control *file_recv_control) +{ + assert(file_recv_control != nullptr); + return file_recv_control->friend_number; +} + +non_null() +static void tox_event_file_recv_control_set_file_number(Tox_Event_File_Recv_Control *file_recv_control, + uint32_t file_number) +{ + assert(file_recv_control != nullptr); + file_recv_control->file_number = file_number; +} +uint32_t tox_event_file_recv_control_get_file_number(const Tox_Event_File_Recv_Control *file_recv_control) +{ + assert(file_recv_control != nullptr); + return file_recv_control->file_number; +} + +non_null() +static void tox_event_file_recv_control_set_control(Tox_Event_File_Recv_Control *file_recv_control, + Tox_File_Control control) +{ + assert(file_recv_control != nullptr); + file_recv_control->control = control; +} +Tox_File_Control tox_event_file_recv_control_get_control(const Tox_Event_File_Recv_Control *file_recv_control) +{ + assert(file_recv_control != nullptr); + return file_recv_control->control; +} + +non_null() +static bool tox_event_file_recv_control_pack( + const Tox_Event_File_Recv_Control *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FILE_RECV_CONTROL) + && bin_pack_array(bp, 3) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->file_number) + && bin_pack_u32(bp, event->control); +} + +non_null() +static bool tox_event_file_recv_control_unpack( + Tox_Event_File_Recv_Control *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 3)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_u32(bu, &event->file_number) + && tox_unpack_file_control(bu, &event->control); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_File_Recv_Control *tox_events_add_file_recv_control(Tox_Events *events) +{ + if (events->file_recv_control_size == UINT32_MAX) { + return nullptr; + } + + if (events->file_recv_control_size == events->file_recv_control_capacity) { + const uint32_t new_file_recv_control_capacity = events->file_recv_control_capacity * 2 + 1; + Tox_Event_File_Recv_Control *new_file_recv_control = (Tox_Event_File_Recv_Control *)realloc( + events->file_recv_control, new_file_recv_control_capacity * sizeof(Tox_Event_File_Recv_Control)); + + if (new_file_recv_control == nullptr) { + return nullptr; + } + + events->file_recv_control = new_file_recv_control; + events->file_recv_control_capacity = new_file_recv_control_capacity; + } + + Tox_Event_File_Recv_Control *const file_recv_control = &events->file_recv_control[events->file_recv_control_size]; + tox_event_file_recv_control_construct(file_recv_control); + ++events->file_recv_control_size; + return file_recv_control; +} + +void tox_events_clear_file_recv_control(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->file_recv_control_size; ++i) { + tox_event_file_recv_control_destruct(&events->file_recv_control[i]); + } + + free(events->file_recv_control); + events->file_recv_control = nullptr; + events->file_recv_control_size = 0; + events->file_recv_control_capacity = 0; +} + +uint32_t tox_events_get_file_recv_control_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->file_recv_control_size; +} + +const Tox_Event_File_Recv_Control *tox_events_get_file_recv_control(const Tox_Events *events, uint32_t index) +{ + assert(index < events->file_recv_control_size); + assert(events->file_recv_control != nullptr); + return &events->file_recv_control[index]; +} + +bool tox_events_pack_file_recv_control(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_file_recv_control_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_file_recv_control_pack(tox_events_get_file_recv_control(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_file_recv_control(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_File_Recv_Control *event = tox_events_add_file_recv_control(events); + + if (event == nullptr) { + return false; + } + + return tox_event_file_recv_control_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_file_recv_control(Tox *tox, uint32_t friend_number, uint32_t file_number, + Tox_File_Control control, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_File_Recv_Control *file_recv_control = tox_events_add_file_recv_control(state->events); + + if (file_recv_control == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_file_recv_control_set_friend_number(file_recv_control, friend_number); + tox_event_file_recv_control_set_file_number(file_recv_control, file_number); + tox_event_file_recv_control_set_control(file_recv_control, control); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c b/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c new file mode 100644 index 0000000000..3d325ef29f --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_connection_status.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Connection_Status { + uint32_t friend_number; + Tox_Connection connection_status; +}; + +non_null() +static void tox_event_friend_connection_status_construct(Tox_Event_Friend_Connection_Status *friend_connection_status) +{ + *friend_connection_status = (Tox_Event_Friend_Connection_Status) { + 0 + }; +} +non_null() +static void tox_event_friend_connection_status_destruct(Tox_Event_Friend_Connection_Status *friend_connection_status) +{ + return; +} + +non_null() +static void tox_event_friend_connection_status_set_friend_number(Tox_Event_Friend_Connection_Status + *friend_connection_status, uint32_t friend_number) +{ + assert(friend_connection_status != nullptr); + friend_connection_status->friend_number = friend_number; +} +uint32_t tox_event_friend_connection_status_get_friend_number(const Tox_Event_Friend_Connection_Status + *friend_connection_status) +{ + assert(friend_connection_status != nullptr); + return friend_connection_status->friend_number; +} + +non_null() +static void tox_event_friend_connection_status_set_connection_status(Tox_Event_Friend_Connection_Status + *friend_connection_status, Tox_Connection connection_status) +{ + assert(friend_connection_status != nullptr); + friend_connection_status->connection_status = connection_status; +} +Tox_Connection tox_event_friend_connection_status_get_connection_status(const Tox_Event_Friend_Connection_Status + *friend_connection_status) +{ + assert(friend_connection_status != nullptr); + return friend_connection_status->connection_status; +} + +non_null() +static bool tox_event_friend_connection_status_pack( + const Tox_Event_Friend_Connection_Status *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_CONNECTION_STATUS) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->connection_status); +} + +non_null() +static bool tox_event_friend_connection_status_unpack( + Tox_Event_Friend_Connection_Status *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && tox_unpack_connection(bu, &event->connection_status); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Connection_Status *tox_events_add_friend_connection_status(Tox_Events *events) +{ + if (events->friend_connection_status_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_connection_status_size == events->friend_connection_status_capacity) { + const uint32_t new_friend_connection_status_capacity = events->friend_connection_status_capacity * 2 + 1; + Tox_Event_Friend_Connection_Status *new_friend_connection_status = (Tox_Event_Friend_Connection_Status *)realloc( + events->friend_connection_status, new_friend_connection_status_capacity * sizeof(Tox_Event_Friend_Connection_Status)); + + if (new_friend_connection_status == nullptr) { + return nullptr; + } + + events->friend_connection_status = new_friend_connection_status; + events->friend_connection_status_capacity = new_friend_connection_status_capacity; + } + + Tox_Event_Friend_Connection_Status *const friend_connection_status = + &events->friend_connection_status[events->friend_connection_status_size]; + tox_event_friend_connection_status_construct(friend_connection_status); + ++events->friend_connection_status_size; + return friend_connection_status; +} + +void tox_events_clear_friend_connection_status(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_connection_status_size; ++i) { + tox_event_friend_connection_status_destruct(&events->friend_connection_status[i]); + } + + free(events->friend_connection_status); + events->friend_connection_status = nullptr; + events->friend_connection_status_size = 0; + events->friend_connection_status_capacity = 0; +} + +uint32_t tox_events_get_friend_connection_status_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_connection_status_size; +} + +const Tox_Event_Friend_Connection_Status *tox_events_get_friend_connection_status(const Tox_Events *events, + uint32_t index) +{ + assert(index < events->friend_connection_status_size); + assert(events->friend_connection_status != nullptr); + return &events->friend_connection_status[index]; +} + +bool tox_events_pack_friend_connection_status(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_connection_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_connection_status_pack(tox_events_get_friend_connection_status(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_connection_status(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Connection_Status *event = tox_events_add_friend_connection_status(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_connection_status_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_connection_status(Tox *tox, uint32_t friend_number, Tox_Connection connection_status, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Connection_Status *friend_connection_status = tox_events_add_friend_connection_status(state->events); + + if (friend_connection_status == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_connection_status_set_friend_number(friend_connection_status, friend_number); + tox_event_friend_connection_status_set_connection_status(friend_connection_status, connection_status); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c b/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c new file mode 100644 index 0000000000..944abd46c7 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_lossless_packet.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Lossless_Packet { + uint32_t friend_number; + uint8_t *data; + uint32_t data_length; +}; + +non_null() +static void tox_event_friend_lossless_packet_construct(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet) +{ + *friend_lossless_packet = (Tox_Event_Friend_Lossless_Packet) { + 0 + }; +} +non_null() +static void tox_event_friend_lossless_packet_destruct(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet) +{ + free(friend_lossless_packet->data); +} + +non_null() +static void tox_event_friend_lossless_packet_set_friend_number(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet, + uint32_t friend_number) +{ + assert(friend_lossless_packet != nullptr); + friend_lossless_packet->friend_number = friend_number; +} +uint32_t tox_event_friend_lossless_packet_get_friend_number(const Tox_Event_Friend_Lossless_Packet + *friend_lossless_packet) +{ + assert(friend_lossless_packet != nullptr); + return friend_lossless_packet->friend_number; +} + +non_null() +static bool tox_event_friend_lossless_packet_set_data(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet, + const uint8_t *data, uint32_t data_length) +{ + assert(friend_lossless_packet != nullptr); + + if (friend_lossless_packet->data != nullptr) { + free(friend_lossless_packet->data); + friend_lossless_packet->data = nullptr; + friend_lossless_packet->data_length = 0; + } + + friend_lossless_packet->data = (uint8_t *)malloc(data_length); + + if (friend_lossless_packet->data == nullptr) { + return false; + } + + memcpy(friend_lossless_packet->data, data, data_length); + friend_lossless_packet->data_length = data_length; + return true; +} +uint32_t tox_event_friend_lossless_packet_get_data_length(const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet) +{ + assert(friend_lossless_packet != nullptr); + return friend_lossless_packet->data_length; +} +const uint8_t *tox_event_friend_lossless_packet_get_data(const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet) +{ + assert(friend_lossless_packet != nullptr); + return friend_lossless_packet->data; +} + +non_null() +static bool tox_event_friend_lossless_packet_pack( + const Tox_Event_Friend_Lossless_Packet *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_LOSSLESS_PACKET) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_bin(bp, event->data, event->data_length); +} + +non_null() +static bool tox_event_friend_lossless_packet_unpack( + Tox_Event_Friend_Lossless_Packet *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_bin(bu, &event->data, &event->data_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Lossless_Packet *tox_events_add_friend_lossless_packet(Tox_Events *events) +{ + if (events->friend_lossless_packet_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_lossless_packet_size == events->friend_lossless_packet_capacity) { + const uint32_t new_friend_lossless_packet_capacity = events->friend_lossless_packet_capacity * 2 + 1; + Tox_Event_Friend_Lossless_Packet *new_friend_lossless_packet = (Tox_Event_Friend_Lossless_Packet *)realloc( + events->friend_lossless_packet, new_friend_lossless_packet_capacity * sizeof(Tox_Event_Friend_Lossless_Packet)); + + if (new_friend_lossless_packet == nullptr) { + return nullptr; + } + + events->friend_lossless_packet = new_friend_lossless_packet; + events->friend_lossless_packet_capacity = new_friend_lossless_packet_capacity; + } + + Tox_Event_Friend_Lossless_Packet *const friend_lossless_packet = + &events->friend_lossless_packet[events->friend_lossless_packet_size]; + tox_event_friend_lossless_packet_construct(friend_lossless_packet); + ++events->friend_lossless_packet_size; + return friend_lossless_packet; +} + +void tox_events_clear_friend_lossless_packet(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_lossless_packet_size; ++i) { + tox_event_friend_lossless_packet_destruct(&events->friend_lossless_packet[i]); + } + + free(events->friend_lossless_packet); + events->friend_lossless_packet = nullptr; + events->friend_lossless_packet_size = 0; + events->friend_lossless_packet_capacity = 0; +} + +uint32_t tox_events_get_friend_lossless_packet_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_lossless_packet_size; +} + +const Tox_Event_Friend_Lossless_Packet *tox_events_get_friend_lossless_packet(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_lossless_packet_size); + assert(events->friend_lossless_packet != nullptr); + return &events->friend_lossless_packet[index]; +} + +bool tox_events_pack_friend_lossless_packet(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_lossless_packet_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_lossless_packet_pack(tox_events_get_friend_lossless_packet(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_lossless_packet(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Lossless_Packet *event = tox_events_add_friend_lossless_packet(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_lossless_packet_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Lossless_Packet *friend_lossless_packet = tox_events_add_friend_lossless_packet(state->events); + + if (friend_lossless_packet == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_lossless_packet_set_friend_number(friend_lossless_packet, friend_number); + tox_event_friend_lossless_packet_set_data(friend_lossless_packet, data, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c b/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c new file mode 100644 index 0000000000..a787fce0cf --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_lossy_packet.c @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Lossy_Packet { + uint32_t friend_number; + uint8_t *data; + uint32_t data_length; +}; + +non_null() +static void tox_event_friend_lossy_packet_construct(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet) +{ + *friend_lossy_packet = (Tox_Event_Friend_Lossy_Packet) { + 0 + }; +} +non_null() +static void tox_event_friend_lossy_packet_destruct(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet) +{ + free(friend_lossy_packet->data); +} + +non_null() +static void tox_event_friend_lossy_packet_set_friend_number(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet, + uint32_t friend_number) +{ + assert(friend_lossy_packet != nullptr); + friend_lossy_packet->friend_number = friend_number; +} +uint32_t tox_event_friend_lossy_packet_get_friend_number(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet) +{ + assert(friend_lossy_packet != nullptr); + return friend_lossy_packet->friend_number; +} + +non_null() +static bool tox_event_friend_lossy_packet_set_data(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet, + const uint8_t *data, uint32_t data_length) +{ + assert(friend_lossy_packet != nullptr); + + if (friend_lossy_packet->data != nullptr) { + free(friend_lossy_packet->data); + friend_lossy_packet->data = nullptr; + friend_lossy_packet->data_length = 0; + } + + friend_lossy_packet->data = (uint8_t *)malloc(data_length); + + if (friend_lossy_packet->data == nullptr) { + return false; + } + + memcpy(friend_lossy_packet->data, data, data_length); + friend_lossy_packet->data_length = data_length; + return true; +} +uint32_t tox_event_friend_lossy_packet_get_data_length(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet) +{ + assert(friend_lossy_packet != nullptr); + return friend_lossy_packet->data_length; +} +const uint8_t *tox_event_friend_lossy_packet_get_data(const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet) +{ + assert(friend_lossy_packet != nullptr); + return friend_lossy_packet->data; +} + +non_null() +static bool tox_event_friend_lossy_packet_pack( + const Tox_Event_Friend_Lossy_Packet *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_LOSSY_PACKET) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_bin(bp, event->data, event->data_length); +} + +non_null() +static bool tox_event_friend_lossy_packet_unpack( + Tox_Event_Friend_Lossy_Packet *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_bin(bu, &event->data, &event->data_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Lossy_Packet *tox_events_add_friend_lossy_packet(Tox_Events *events) +{ + if (events->friend_lossy_packet_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_lossy_packet_size == events->friend_lossy_packet_capacity) { + const uint32_t new_friend_lossy_packet_capacity = events->friend_lossy_packet_capacity * 2 + 1; + Tox_Event_Friend_Lossy_Packet *new_friend_lossy_packet = (Tox_Event_Friend_Lossy_Packet *)realloc( + events->friend_lossy_packet, new_friend_lossy_packet_capacity * sizeof(Tox_Event_Friend_Lossy_Packet)); + + if (new_friend_lossy_packet == nullptr) { + return nullptr; + } + + events->friend_lossy_packet = new_friend_lossy_packet; + events->friend_lossy_packet_capacity = new_friend_lossy_packet_capacity; + } + + Tox_Event_Friend_Lossy_Packet *const friend_lossy_packet = + &events->friend_lossy_packet[events->friend_lossy_packet_size]; + tox_event_friend_lossy_packet_construct(friend_lossy_packet); + ++events->friend_lossy_packet_size; + return friend_lossy_packet; +} + +void tox_events_clear_friend_lossy_packet(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_lossy_packet_size; ++i) { + tox_event_friend_lossy_packet_destruct(&events->friend_lossy_packet[i]); + } + + free(events->friend_lossy_packet); + events->friend_lossy_packet = nullptr; + events->friend_lossy_packet_size = 0; + events->friend_lossy_packet_capacity = 0; +} + +uint32_t tox_events_get_friend_lossy_packet_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_lossy_packet_size; +} + +const Tox_Event_Friend_Lossy_Packet *tox_events_get_friend_lossy_packet(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_lossy_packet_size); + assert(events->friend_lossy_packet != nullptr); + return &events->friend_lossy_packet[index]; +} + +bool tox_events_pack_friend_lossy_packet(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_lossy_packet_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_lossy_packet_pack(tox_events_get_friend_lossy_packet(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_lossy_packet(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Lossy_Packet *event = tox_events_add_friend_lossy_packet(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_lossy_packet_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Lossy_Packet *friend_lossy_packet = tox_events_add_friend_lossy_packet(state->events); + + if (friend_lossy_packet == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_lossy_packet_set_friend_number(friend_lossy_packet, friend_number); + tox_event_friend_lossy_packet_set_data(friend_lossy_packet, data, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_message.c b/protocols/Tox/libtox/src/toxcore/events/friend_message.c new file mode 100644 index 0000000000..8fa6a19e34 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_message.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Message { + uint32_t friend_number; + Tox_Message_Type type; + uint8_t *message; + uint32_t message_length; +}; + +non_null() +static void tox_event_friend_message_construct(Tox_Event_Friend_Message *friend_message) +{ + *friend_message = (Tox_Event_Friend_Message) { + 0 + }; +} +non_null() +static void tox_event_friend_message_destruct(Tox_Event_Friend_Message *friend_message) +{ + free(friend_message->message); +} + +non_null() +static void tox_event_friend_message_set_friend_number(Tox_Event_Friend_Message *friend_message, + uint32_t friend_number) +{ + assert(friend_message != nullptr); + friend_message->friend_number = friend_number; +} +uint32_t tox_event_friend_message_get_friend_number(const Tox_Event_Friend_Message *friend_message) +{ + assert(friend_message != nullptr); + return friend_message->friend_number; +} + +non_null() +static void tox_event_friend_message_set_type(Tox_Event_Friend_Message *friend_message, Tox_Message_Type type) +{ + assert(friend_message != nullptr); + friend_message->type = type; +} +Tox_Message_Type tox_event_friend_message_get_type(const Tox_Event_Friend_Message *friend_message) +{ + assert(friend_message != nullptr); + return friend_message->type; +} + +non_null() +static bool tox_event_friend_message_set_message(Tox_Event_Friend_Message *friend_message, const uint8_t *message, + uint32_t message_length) +{ + assert(friend_message != nullptr); + + if (friend_message->message != nullptr) { + free(friend_message->message); + friend_message->message = nullptr; + friend_message->message_length = 0; + } + + friend_message->message = (uint8_t *)malloc(message_length); + + if (friend_message->message == nullptr) { + return false; + } + + memcpy(friend_message->message, message, message_length); + friend_message->message_length = message_length; + return true; +} +uint32_t tox_event_friend_message_get_message_length(const Tox_Event_Friend_Message *friend_message) +{ + assert(friend_message != nullptr); + return friend_message->message_length; +} +const uint8_t *tox_event_friend_message_get_message(const Tox_Event_Friend_Message *friend_message) +{ + assert(friend_message != nullptr); + return friend_message->message; +} + +non_null() +static bool tox_event_friend_message_pack( + const Tox_Event_Friend_Message *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_MESSAGE) + && bin_pack_array(bp, 3) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->type) + && bin_pack_bin(bp, event->message, event->message_length); +} + +non_null() +static bool tox_event_friend_message_unpack( + Tox_Event_Friend_Message *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 3)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && tox_unpack_message_type(bu, &event->type) + && bin_unpack_bin(bu, &event->message, &event->message_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Message *tox_events_add_friend_message(Tox_Events *events) +{ + if (events->friend_message_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_message_size == events->friend_message_capacity) { + const uint32_t new_friend_message_capacity = events->friend_message_capacity * 2 + 1; + Tox_Event_Friend_Message *new_friend_message = (Tox_Event_Friend_Message *)realloc( + events->friend_message, new_friend_message_capacity * sizeof(Tox_Event_Friend_Message)); + + if (new_friend_message == nullptr) { + return nullptr; + } + + events->friend_message = new_friend_message; + events->friend_message_capacity = new_friend_message_capacity; + } + + Tox_Event_Friend_Message *const friend_message = &events->friend_message[events->friend_message_size]; + tox_event_friend_message_construct(friend_message); + ++events->friend_message_size; + return friend_message; +} + +void tox_events_clear_friend_message(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_message_size; ++i) { + tox_event_friend_message_destruct(&events->friend_message[i]); + } + + free(events->friend_message); + events->friend_message = nullptr; + events->friend_message_size = 0; + events->friend_message_capacity = 0; +} + +uint32_t tox_events_get_friend_message_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_message_size; +} + +const Tox_Event_Friend_Message *tox_events_get_friend_message(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_message_size); + assert(events->friend_message != nullptr); + return &events->friend_message[index]; +} + +bool tox_events_pack_friend_message(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_message_pack(tox_events_get_friend_message(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_message(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Message *event = tox_events_add_friend_message(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_message_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_message(Tox *tox, uint32_t friend_number, Tox_Message_Type type, const uint8_t *message, + size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Message *friend_message = tox_events_add_friend_message(state->events); + + if (friend_message == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_message_set_friend_number(friend_message, friend_number); + tox_event_friend_message_set_type(friend_message, type); + tox_event_friend_message_set_message(friend_message, message, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_name.c b/protocols/Tox/libtox/src/toxcore/events/friend_name.c new file mode 100644 index 0000000000..123918386a --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_name.c @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Name { + uint32_t friend_number; + uint8_t *name; + uint32_t name_length; +}; + +non_null() +static void tox_event_friend_name_construct(Tox_Event_Friend_Name *friend_name) +{ + *friend_name = (Tox_Event_Friend_Name) { + 0 + }; +} +non_null() +static void tox_event_friend_name_destruct(Tox_Event_Friend_Name *friend_name) +{ + free(friend_name->name); +} + +non_null() +static void tox_event_friend_name_set_friend_number(Tox_Event_Friend_Name *friend_name, + uint32_t friend_number) +{ + assert(friend_name != nullptr); + friend_name->friend_number = friend_number; +} +uint32_t tox_event_friend_name_get_friend_number(const Tox_Event_Friend_Name *friend_name) +{ + assert(friend_name != nullptr); + return friend_name->friend_number; +} + +non_null() +static bool tox_event_friend_name_set_name(Tox_Event_Friend_Name *friend_name, const uint8_t *name, + uint32_t name_length) +{ + assert(friend_name != nullptr); + + if (friend_name->name != nullptr) { + free(friend_name->name); + friend_name->name = nullptr; + friend_name->name_length = 0; + } + + friend_name->name = (uint8_t *)malloc(name_length); + + if (friend_name->name == nullptr) { + return false; + } + + memcpy(friend_name->name, name, name_length); + friend_name->name_length = name_length; + return true; +} +uint32_t tox_event_friend_name_get_name_length(const Tox_Event_Friend_Name *friend_name) +{ + assert(friend_name != nullptr); + return friend_name->name_length; +} +const uint8_t *tox_event_friend_name_get_name(const Tox_Event_Friend_Name *friend_name) +{ + assert(friend_name != nullptr); + return friend_name->name; +} + +non_null() +static bool tox_event_friend_name_pack( + const Tox_Event_Friend_Name *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_NAME) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_bin(bp, event->name, event->name_length); +} + +non_null() +static bool tox_event_friend_name_unpack( + Tox_Event_Friend_Name *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_bin(bu, &event->name, &event->name_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Name *tox_events_add_friend_name(Tox_Events *events) +{ + if (events->friend_name_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_name_size == events->friend_name_capacity) { + const uint32_t new_friend_name_capacity = events->friend_name_capacity * 2 + 1; + Tox_Event_Friend_Name *new_friend_name = (Tox_Event_Friend_Name *)realloc( + events->friend_name, new_friend_name_capacity * sizeof(Tox_Event_Friend_Name)); + + if (new_friend_name == nullptr) { + return nullptr; + } + + events->friend_name = new_friend_name; + events->friend_name_capacity = new_friend_name_capacity; + } + + Tox_Event_Friend_Name *const friend_name = &events->friend_name[events->friend_name_size]; + tox_event_friend_name_construct(friend_name); + ++events->friend_name_size; + return friend_name; +} + +void tox_events_clear_friend_name(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_name_size; ++i) { + tox_event_friend_name_destruct(&events->friend_name[i]); + } + + free(events->friend_name); + events->friend_name = nullptr; + events->friend_name_size = 0; + events->friend_name_capacity = 0; +} + +uint32_t tox_events_get_friend_name_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_name_size; +} + +const Tox_Event_Friend_Name *tox_events_get_friend_name(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_name_size); + assert(events->friend_name != nullptr); + return &events->friend_name[index]; +} + +bool tox_events_pack_friend_name(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_name_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_name_pack(tox_events_get_friend_name(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_name(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Name *event = tox_events_add_friend_name(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_name_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_name(Tox *tox, uint32_t friend_number, const uint8_t *name, size_t length, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Name *friend_name = tox_events_add_friend_name(state->events); + + if (friend_name == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_name_set_friend_number(friend_name, friend_number); + tox_event_friend_name_set_name(friend_name, name, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c b/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c new file mode 100644 index 0000000000..1485b67cca --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_read_receipt.c @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Read_Receipt { + uint32_t friend_number; + uint32_t message_id; +}; + +non_null() +static void tox_event_friend_read_receipt_construct(Tox_Event_Friend_Read_Receipt *friend_read_receipt) +{ + *friend_read_receipt = (Tox_Event_Friend_Read_Receipt) { + 0 + }; +} +non_null() +static void tox_event_friend_read_receipt_destruct(Tox_Event_Friend_Read_Receipt *friend_read_receipt) +{ + return; +} + +non_null() +static void tox_event_friend_read_receipt_set_friend_number(Tox_Event_Friend_Read_Receipt *friend_read_receipt, + uint32_t friend_number) +{ + assert(friend_read_receipt != nullptr); + friend_read_receipt->friend_number = friend_number; +} +uint32_t tox_event_friend_read_receipt_get_friend_number(const Tox_Event_Friend_Read_Receipt *friend_read_receipt) +{ + assert(friend_read_receipt != nullptr); + return friend_read_receipt->friend_number; +} + +non_null() +static void tox_event_friend_read_receipt_set_message_id(Tox_Event_Friend_Read_Receipt *friend_read_receipt, + uint32_t message_id) +{ + assert(friend_read_receipt != nullptr); + friend_read_receipt->message_id = message_id; +} +uint32_t tox_event_friend_read_receipt_get_message_id(const Tox_Event_Friend_Read_Receipt *friend_read_receipt) +{ + assert(friend_read_receipt != nullptr); + return friend_read_receipt->message_id; +} + +non_null() +static bool tox_event_friend_read_receipt_pack( + const Tox_Event_Friend_Read_Receipt *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_READ_RECEIPT) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->message_id); +} + +non_null() +static bool tox_event_friend_read_receipt_unpack( + Tox_Event_Friend_Read_Receipt *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_u32(bu, &event->message_id); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Read_Receipt *tox_events_add_friend_read_receipt(Tox_Events *events) +{ + if (events->friend_read_receipt_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_read_receipt_size == events->friend_read_receipt_capacity) { + const uint32_t new_friend_read_receipt_capacity = events->friend_read_receipt_capacity * 2 + 1; + Tox_Event_Friend_Read_Receipt *new_friend_read_receipt = (Tox_Event_Friend_Read_Receipt *)realloc( + events->friend_read_receipt, new_friend_read_receipt_capacity * sizeof(Tox_Event_Friend_Read_Receipt)); + + if (new_friend_read_receipt == nullptr) { + return nullptr; + } + + events->friend_read_receipt = new_friend_read_receipt; + events->friend_read_receipt_capacity = new_friend_read_receipt_capacity; + } + + Tox_Event_Friend_Read_Receipt *const friend_read_receipt = + &events->friend_read_receipt[events->friend_read_receipt_size]; + tox_event_friend_read_receipt_construct(friend_read_receipt); + ++events->friend_read_receipt_size; + return friend_read_receipt; +} + +void tox_events_clear_friend_read_receipt(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_read_receipt_size; ++i) { + tox_event_friend_read_receipt_destruct(&events->friend_read_receipt[i]); + } + + free(events->friend_read_receipt); + events->friend_read_receipt = nullptr; + events->friend_read_receipt_size = 0; + events->friend_read_receipt_capacity = 0; +} + +uint32_t tox_events_get_friend_read_receipt_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_read_receipt_size; +} + +const Tox_Event_Friend_Read_Receipt *tox_events_get_friend_read_receipt(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_read_receipt_size); + assert(events->friend_read_receipt != nullptr); + return &events->friend_read_receipt[index]; +} + +bool tox_events_pack_friend_read_receipt(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_read_receipt_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_read_receipt_pack(tox_events_get_friend_read_receipt(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_read_receipt(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Read_Receipt *event = tox_events_add_friend_read_receipt(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_read_receipt_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_read_receipt(Tox *tox, uint32_t friend_number, uint32_t message_id, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Read_Receipt *friend_read_receipt = tox_events_add_friend_read_receipt(state->events); + + if (friend_read_receipt == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_read_receipt_set_friend_number(friend_read_receipt, friend_number); + tox_event_friend_read_receipt_set_message_id(friend_read_receipt, message_id); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_request.c b/protocols/Tox/libtox/src/toxcore/events/friend_request.c new file mode 100644 index 0000000000..3366f54fe4 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_request.c @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Request { + uint8_t public_key[TOX_PUBLIC_KEY_SIZE]; + uint8_t *message; + uint32_t message_length; +}; + +non_null() +static void tox_event_friend_request_construct(Tox_Event_Friend_Request *friend_request) +{ + *friend_request = (Tox_Event_Friend_Request) { + 0 + }; +} +non_null() +static void tox_event_friend_request_destruct(Tox_Event_Friend_Request *friend_request) +{ + free(friend_request->message); +} + +non_null() +static bool tox_event_friend_request_set_public_key(Tox_Event_Friend_Request *friend_request, const uint8_t *public_key) +{ + assert(friend_request != nullptr); + + memcpy(friend_request->public_key, public_key, TOX_PUBLIC_KEY_SIZE); + return true; +} +const uint8_t *tox_event_friend_request_get_public_key(const Tox_Event_Friend_Request *friend_request) +{ + assert(friend_request != nullptr); + return friend_request->public_key; +} + +non_null() +static bool tox_event_friend_request_set_message(Tox_Event_Friend_Request *friend_request, const uint8_t *message, + uint32_t message_length) +{ + assert(friend_request != nullptr); + + if (friend_request->message != nullptr) { + free(friend_request->message); + friend_request->message = nullptr; + friend_request->message_length = 0; + } + + friend_request->message = (uint8_t *)malloc(message_length); + + if (friend_request->message == nullptr) { + return false; + } + + memcpy(friend_request->message, message, message_length); + friend_request->message_length = message_length; + return true; +} +uint32_t tox_event_friend_request_get_message_length(const Tox_Event_Friend_Request *friend_request) +{ + assert(friend_request != nullptr); + return friend_request->message_length; +} +const uint8_t *tox_event_friend_request_get_message(const Tox_Event_Friend_Request *friend_request) +{ + assert(friend_request != nullptr); + return friend_request->message; +} + +non_null() +static bool tox_event_friend_request_pack( + const Tox_Event_Friend_Request *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_REQUEST) + && bin_pack_array(bp, 2) + && bin_pack_bin(bp, event->public_key, TOX_PUBLIC_KEY_SIZE) + && bin_pack_bin(bp, event->message, event->message_length); +} + +non_null() +static bool tox_event_friend_request_unpack( + Tox_Event_Friend_Request *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_bin_fixed(bu, event->public_key, TOX_PUBLIC_KEY_SIZE) + && bin_unpack_bin(bu, &event->message, &event->message_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Request *tox_events_add_friend_request(Tox_Events *events) +{ + if (events->friend_request_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_request_size == events->friend_request_capacity) { + const uint32_t new_friend_request_capacity = events->friend_request_capacity * 2 + 1; + Tox_Event_Friend_Request *new_friend_request = (Tox_Event_Friend_Request *)realloc( + events->friend_request, new_friend_request_capacity * sizeof(Tox_Event_Friend_Request)); + + if (new_friend_request == nullptr) { + return nullptr; + } + + events->friend_request = new_friend_request; + events->friend_request_capacity = new_friend_request_capacity; + } + + Tox_Event_Friend_Request *const friend_request = &events->friend_request[events->friend_request_size]; + tox_event_friend_request_construct(friend_request); + ++events->friend_request_size; + return friend_request; +} + +void tox_events_clear_friend_request(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_request_size; ++i) { + tox_event_friend_request_destruct(&events->friend_request[i]); + } + + free(events->friend_request); + events->friend_request = nullptr; + events->friend_request_size = 0; + events->friend_request_capacity = 0; +} + +uint32_t tox_events_get_friend_request_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_request_size; +} + +const Tox_Event_Friend_Request *tox_events_get_friend_request(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_request_size); + assert(events->friend_request != nullptr); + return &events->friend_request[index]; +} + +bool tox_events_pack_friend_request(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_request_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_request_pack(tox_events_get_friend_request(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_request(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Request *event = tox_events_add_friend_request(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_request_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_request(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Request *friend_request = tox_events_add_friend_request(state->events); + + if (friend_request == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_request_set_public_key(friend_request, public_key); + tox_event_friend_request_set_message(friend_request, message, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_status.c b/protocols/Tox/libtox/src/toxcore/events/friend_status.c new file mode 100644 index 0000000000..c6eabdaa47 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_status.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include "../bin_unpack.h" + +#include "../bin_pack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Status { + uint32_t friend_number; + Tox_User_Status status; +}; + +non_null() +static void tox_event_friend_status_construct(Tox_Event_Friend_Status *friend_status) +{ + *friend_status = (Tox_Event_Friend_Status) { + 0 + }; +} +non_null() +static void tox_event_friend_status_destruct(Tox_Event_Friend_Status *friend_status) +{ + return; +} + +non_null() +static void tox_event_friend_status_set_friend_number(Tox_Event_Friend_Status *friend_status, + uint32_t friend_number) +{ + assert(friend_status != nullptr); + friend_status->friend_number = friend_number; +} +uint32_t tox_event_friend_status_get_friend_number(const Tox_Event_Friend_Status *friend_status) +{ + assert(friend_status != nullptr); + return friend_status->friend_number; +} + +non_null() +static void tox_event_friend_status_set_status(Tox_Event_Friend_Status *friend_status, + Tox_User_Status status) +{ + assert(friend_status != nullptr); + friend_status->status = status; +} +Tox_User_Status tox_event_friend_status_get_status(const Tox_Event_Friend_Status *friend_status) +{ + assert(friend_status != nullptr); + return friend_status->status; +} + +non_null() +static bool tox_event_friend_status_pack( + const Tox_Event_Friend_Status *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_STATUS) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_u32(bp, event->status); +} + +non_null() +static bool tox_event_friend_status_unpack( + Tox_Event_Friend_Status *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && tox_unpack_user_status(bu, &event->status); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Status *tox_events_add_friend_status(Tox_Events *events) +{ + if (events->friend_status_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_status_size == events->friend_status_capacity) { + const uint32_t new_friend_status_capacity = events->friend_status_capacity * 2 + 1; + Tox_Event_Friend_Status *new_friend_status = (Tox_Event_Friend_Status *)realloc( + events->friend_status, new_friend_status_capacity * sizeof(Tox_Event_Friend_Status)); + + if (new_friend_status == nullptr) { + return nullptr; + } + + events->friend_status = new_friend_status; + events->friend_status_capacity = new_friend_status_capacity; + } + + Tox_Event_Friend_Status *const friend_status = &events->friend_status[events->friend_status_size]; + tox_event_friend_status_construct(friend_status); + ++events->friend_status_size; + return friend_status; +} + +void tox_events_clear_friend_status(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_status_size; ++i) { + tox_event_friend_status_destruct(&events->friend_status[i]); + } + + free(events->friend_status); + events->friend_status = nullptr; + events->friend_status_size = 0; + events->friend_status_capacity = 0; +} + +uint32_t tox_events_get_friend_status_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_status_size; +} + +const Tox_Event_Friend_Status *tox_events_get_friend_status(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_status_size); + assert(events->friend_status != nullptr); + return &events->friend_status[index]; +} + +bool tox_events_pack_friend_status(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_status_pack(tox_events_get_friend_status(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_status(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Status *event = tox_events_add_friend_status(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_status_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_status(Tox *tox, uint32_t friend_number, Tox_User_Status status, + void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Status *friend_status = tox_events_add_friend_status(state->events); + + if (friend_status == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_status_set_friend_number(friend_status, friend_number); + tox_event_friend_status_set_status(friend_status, status); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c b/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c new file mode 100644 index 0000000000..91fa88e321 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_status_message.c @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Status_Message { + uint32_t friend_number; + uint8_t *message; + uint32_t message_length; +}; + +non_null() +static void tox_event_friend_status_message_construct(Tox_Event_Friend_Status_Message *friend_status_message) +{ + *friend_status_message = (Tox_Event_Friend_Status_Message) { + 0 + }; +} +non_null() +static void tox_event_friend_status_message_destruct(Tox_Event_Friend_Status_Message *friend_status_message) +{ + free(friend_status_message->message); +} + +non_null() +static void tox_event_friend_status_message_set_friend_number(Tox_Event_Friend_Status_Message *friend_status_message, + uint32_t friend_number) +{ + assert(friend_status_message != nullptr); + friend_status_message->friend_number = friend_number; +} +uint32_t tox_event_friend_status_message_get_friend_number(const Tox_Event_Friend_Status_Message *friend_status_message) +{ + assert(friend_status_message != nullptr); + return friend_status_message->friend_number; +} + +non_null() +static bool tox_event_friend_status_message_set_message(Tox_Event_Friend_Status_Message *friend_status_message, + const uint8_t *message, uint32_t message_length) +{ + assert(friend_status_message != nullptr); + + if (friend_status_message->message != nullptr) { + free(friend_status_message->message); + friend_status_message->message = nullptr; + friend_status_message->message_length = 0; + } + + friend_status_message->message = (uint8_t *)malloc(message_length); + + if (friend_status_message->message == nullptr) { + return false; + } + + memcpy(friend_status_message->message, message, message_length); + friend_status_message->message_length = message_length; + return true; +} +uint32_t tox_event_friend_status_message_get_message_length(const Tox_Event_Friend_Status_Message + *friend_status_message) +{ + assert(friend_status_message != nullptr); + return friend_status_message->message_length; +} +const uint8_t *tox_event_friend_status_message_get_message(const Tox_Event_Friend_Status_Message + *friend_status_message) +{ + assert(friend_status_message != nullptr); + return friend_status_message->message; +} + +non_null() +static bool tox_event_friend_status_message_pack( + const Tox_Event_Friend_Status_Message *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_STATUS_MESSAGE) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_bin(bp, event->message, event->message_length); +} + +non_null() +static bool tox_event_friend_status_message_unpack( + Tox_Event_Friend_Status_Message *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_bin(bu, &event->message, &event->message_length); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Status_Message *tox_events_add_friend_status_message(Tox_Events *events) +{ + if (events->friend_status_message_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_status_message_size == events->friend_status_message_capacity) { + const uint32_t new_friend_status_message_capacity = events->friend_status_message_capacity * 2 + 1; + Tox_Event_Friend_Status_Message *new_friend_status_message = (Tox_Event_Friend_Status_Message *)realloc( + events->friend_status_message, new_friend_status_message_capacity * sizeof(Tox_Event_Friend_Status_Message)); + + if (new_friend_status_message == nullptr) { + return nullptr; + } + + events->friend_status_message = new_friend_status_message; + events->friend_status_message_capacity = new_friend_status_message_capacity; + } + + Tox_Event_Friend_Status_Message *const friend_status_message = + &events->friend_status_message[events->friend_status_message_size]; + tox_event_friend_status_message_construct(friend_status_message); + ++events->friend_status_message_size; + return friend_status_message; +} + +void tox_events_clear_friend_status_message(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_status_message_size; ++i) { + tox_event_friend_status_message_destruct(&events->friend_status_message[i]); + } + + free(events->friend_status_message); + events->friend_status_message = nullptr; + events->friend_status_message_size = 0; + events->friend_status_message_capacity = 0; +} + +uint32_t tox_events_get_friend_status_message_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_status_message_size; +} + +const Tox_Event_Friend_Status_Message *tox_events_get_friend_status_message(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_status_message_size); + assert(events->friend_status_message != nullptr); + return &events->friend_status_message[index]; +} + +bool tox_events_pack_friend_status_message(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_status_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_status_message_pack(tox_events_get_friend_status_message(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_status_message(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Status_Message *event = tox_events_add_friend_status_message(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_status_message_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_status_message(Tox *tox, uint32_t friend_number, const uint8_t *message, + size_t length, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Status_Message *friend_status_message = tox_events_add_friend_status_message(state->events); + + if (friend_status_message == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_status_message_set_friend_number(friend_status_message, friend_number); + tox_event_friend_status_message_set_message(friend_status_message, message, length); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/friend_typing.c b/protocols/Tox/libtox/src/toxcore/events/friend_typing.c new file mode 100644 index 0000000000..83c4dbac63 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/friend_typing.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Friend_Typing { + uint32_t friend_number; + bool typing; +}; + +non_null() +static void tox_event_friend_typing_construct(Tox_Event_Friend_Typing *friend_typing) +{ + *friend_typing = (Tox_Event_Friend_Typing) { + 0 + }; +} +non_null() +static void tox_event_friend_typing_destruct(Tox_Event_Friend_Typing *friend_typing) +{ + return; +} + +non_null() +static void tox_event_friend_typing_set_friend_number(Tox_Event_Friend_Typing *friend_typing, + uint32_t friend_number) +{ + assert(friend_typing != nullptr); + friend_typing->friend_number = friend_number; +} +uint32_t tox_event_friend_typing_get_friend_number(const Tox_Event_Friend_Typing *friend_typing) +{ + assert(friend_typing != nullptr); + return friend_typing->friend_number; +} + +non_null() +static void tox_event_friend_typing_set_typing(Tox_Event_Friend_Typing *friend_typing, bool typing) +{ + assert(friend_typing != nullptr); + friend_typing->typing = typing; +} +bool tox_event_friend_typing_get_typing(const Tox_Event_Friend_Typing *friend_typing) +{ + assert(friend_typing != nullptr); + return friend_typing->typing; +} + +non_null() +static bool tox_event_friend_typing_pack( + const Tox_Event_Friend_Typing *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_FRIEND_TYPING) + && bin_pack_array(bp, 2) + && bin_pack_u32(bp, event->friend_number) + && bin_pack_bool(bp, event->typing); +} + +non_null() +static bool tox_event_friend_typing_unpack( + Tox_Event_Friend_Typing *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + if (!bin_unpack_array_fixed(bu, 2)) { + return false; + } + + return bin_unpack_u32(bu, &event->friend_number) + && bin_unpack_bool(bu, &event->typing); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Friend_Typing *tox_events_add_friend_typing(Tox_Events *events) +{ + if (events->friend_typing_size == UINT32_MAX) { + return nullptr; + } + + if (events->friend_typing_size == events->friend_typing_capacity) { + const uint32_t new_friend_typing_capacity = events->friend_typing_capacity * 2 + 1; + Tox_Event_Friend_Typing *new_friend_typing = (Tox_Event_Friend_Typing *)realloc( + events->friend_typing, new_friend_typing_capacity * sizeof(Tox_Event_Friend_Typing)); + + if (new_friend_typing == nullptr) { + return nullptr; + } + + events->friend_typing = new_friend_typing; + events->friend_typing_capacity = new_friend_typing_capacity; + } + + Tox_Event_Friend_Typing *const friend_typing = &events->friend_typing[events->friend_typing_size]; + tox_event_friend_typing_construct(friend_typing); + ++events->friend_typing_size; + return friend_typing; +} + +void tox_events_clear_friend_typing(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->friend_typing_size; ++i) { + tox_event_friend_typing_destruct(&events->friend_typing[i]); + } + + free(events->friend_typing); + events->friend_typing = nullptr; + events->friend_typing_size = 0; + events->friend_typing_capacity = 0; +} + +uint32_t tox_events_get_friend_typing_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->friend_typing_size; +} + +const Tox_Event_Friend_Typing *tox_events_get_friend_typing(const Tox_Events *events, uint32_t index) +{ + assert(index < events->friend_typing_size); + assert(events->friend_typing != nullptr); + return &events->friend_typing[index]; +} + +bool tox_events_pack_friend_typing(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_friend_typing_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_friend_typing_pack(tox_events_get_friend_typing(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_friend_typing(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Friend_Typing *event = tox_events_add_friend_typing(events); + + if (event == nullptr) { + return false; + } + + return tox_event_friend_typing_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_friend_typing(Tox *tox, uint32_t friend_number, bool typing, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Friend_Typing *friend_typing = tox_events_add_friend_typing(state->events); + + if (friend_typing == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_friend_typing_set_friend_number(friend_typing, friend_number); + tox_event_friend_typing_set_typing(friend_typing, typing); +} diff --git a/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c b/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c new file mode 100644 index 0000000000..0e381de7fa --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/events/self_connection_status.c @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "events_alloc.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "../bin_pack.h" +#include "../bin_unpack.h" +#include "../ccompat.h" +#include "../tox.h" +#include "../tox_events.h" +#include "../tox_unpack.h" + + +/***************************************************** + * + * :: struct and accessors + * + *****************************************************/ + + +struct Tox_Event_Self_Connection_Status { + Tox_Connection connection_status; +}; + +non_null() +static void tox_event_self_connection_status_construct(Tox_Event_Self_Connection_Status *self_connection_status) +{ + *self_connection_status = (Tox_Event_Self_Connection_Status) { + TOX_CONNECTION_NONE + }; +} +non_null() +static void tox_event_self_connection_status_destruct(Tox_Event_Self_Connection_Status *self_connection_status) +{ + return; +} + +non_null() +static void tox_event_self_connection_status_set_connection_status(Tox_Event_Self_Connection_Status + *self_connection_status, Tox_Connection connection_status) +{ + assert(self_connection_status != nullptr); + self_connection_status->connection_status = connection_status; +} +Tox_Connection tox_event_self_connection_status_get_connection_status(const Tox_Event_Self_Connection_Status + *self_connection_status) +{ + assert(self_connection_status != nullptr); + return self_connection_status->connection_status; +} + +non_null() +static bool tox_event_self_connection_status_pack( + const Tox_Event_Self_Connection_Status *event, Bin_Pack *bp) +{ + assert(event != nullptr); + return bin_pack_array(bp, 2) + && bin_pack_u32(bp, TOX_EVENT_SELF_CONNECTION_STATUS) + && bin_pack_u32(bp, event->connection_status); +} + +non_null() +static bool tox_event_self_connection_status_unpack( + Tox_Event_Self_Connection_Status *event, Bin_Unpack *bu) +{ + assert(event != nullptr); + return tox_unpack_connection(bu, &event->connection_status); +} + + +/***************************************************** + * + * :: add/clear/get + * + *****************************************************/ + + +non_null() +static Tox_Event_Self_Connection_Status *tox_events_add_self_connection_status(Tox_Events *events) +{ + if (events->self_connection_status_size == UINT32_MAX) { + return nullptr; + } + + if (events->self_connection_status_size == events->self_connection_status_capacity) { + const uint32_t new_self_connection_status_capacity = events->self_connection_status_capacity * 2 + 1; + Tox_Event_Self_Connection_Status *new_self_connection_status = (Tox_Event_Self_Connection_Status *)realloc( + events->self_connection_status, new_self_connection_status_capacity * sizeof(Tox_Event_Self_Connection_Status)); + + if (new_self_connection_status == nullptr) { + return nullptr; + } + + events->self_connection_status = new_self_connection_status; + events->self_connection_status_capacity = new_self_connection_status_capacity; + } + + Tox_Event_Self_Connection_Status *const self_connection_status = + &events->self_connection_status[events->self_connection_status_size]; + tox_event_self_connection_status_construct(self_connection_status); + ++events->self_connection_status_size; + return self_connection_status; +} + +void tox_events_clear_self_connection_status(Tox_Events *events) +{ + if (events == nullptr) { + return; + } + + for (uint32_t i = 0; i < events->self_connection_status_size; ++i) { + tox_event_self_connection_status_destruct(&events->self_connection_status[i]); + } + + free(events->self_connection_status); + events->self_connection_status = nullptr; + events->self_connection_status_size = 0; + events->self_connection_status_capacity = 0; +} + +uint32_t tox_events_get_self_connection_status_size(const Tox_Events *events) +{ + if (events == nullptr) { + return 0; + } + + return events->self_connection_status_size; +} + +const Tox_Event_Self_Connection_Status *tox_events_get_self_connection_status(const Tox_Events *events, uint32_t index) +{ + assert(index < events->self_connection_status_size); + assert(events->self_connection_status != nullptr); + return &events->self_connection_status[index]; +} + +bool tox_events_pack_self_connection_status(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t size = tox_events_get_self_connection_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_self_connection_status_pack(tox_events_get_self_connection_status(events, i), bp)) { + return false; + } + } + return true; +} + +bool tox_events_unpack_self_connection_status(Tox_Events *events, Bin_Unpack *bu) +{ + Tox_Event_Self_Connection_Status *event = tox_events_add_self_connection_status(events); + + if (event == nullptr) { + return false; + } + + return tox_event_self_connection_status_unpack(event, bu); +} + + +/***************************************************** + * + * :: event handler + * + *****************************************************/ + + +void tox_events_handle_self_connection_status(Tox *tox, Tox_Connection connection_status, void *user_data) +{ + Tox_Events_State *state = tox_events_alloc(user_data); + assert(state != nullptr); + + if (state->events == nullptr) { + return; + } + + Tox_Event_Self_Connection_Status *self_connection_status = tox_events_add_self_connection_status(state->events); + + if (self_connection_status == nullptr) { + state->error = TOX_ERR_EVENTS_ITERATE_MALLOC; + return; + } + + tox_event_self_connection_status_set_connection_status(self_connection_status, connection_status); +} diff --git a/protocols/Tox/libtox/src/toxcore/forwarding.c b/protocols/Tox/libtox/src/toxcore/forwarding.c new file mode 100644 index 0000000000..5e885abd21 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/forwarding.c @@ -0,0 +1,395 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2019-2022 The TokTok team. + */ + +#include "forwarding.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "DHT.h" +#include "ccompat.h" +#include "timed_auth.h" + +struct Forwarding { + const Logger *log; + const Random *rng; + DHT *dht; + const Mono_Time *mono_time; + Networking_Core *net; + + uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; + + forward_reply_cb *forward_reply_callback; + void *forward_reply_callback_object; + + forwarded_request_cb *forwarded_request_callback; + void *forwarded_request_callback_object; + + forwarded_response_cb *forwarded_response_callback; + void *forwarded_response_callback_object; +}; + +DHT *forwarding_get_dht(Forwarding *forwarding) +{ + return forwarding->dht; +} + +#define SENDBACK_TIMEOUT 3600 + +bool send_forward_request(Networking_Core *net, const IP_Port *forwarder, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length) +{ + if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH + || data_length > MAX_FORWARD_DATA_SIZE) { + return false; + } + + const uint16_t len = forward_chain_packet_size(chain_length, data_length); + VLA(uint8_t, packet, len); + + return create_forward_chain_packet(chain_keys, chain_length, data, data_length, packet) + && sendpacket(net, forwarder, packet, len) == len; +} + +uint16_t forward_chain_packet_size(uint16_t chain_length, uint16_t data_length) +{ + return chain_length * (1 + CRYPTO_PUBLIC_KEY_SIZE) + data_length; +} + +bool create_forward_chain_packet(const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length, + uint8_t *packet) +{ + if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH + || data_length > MAX_FORWARD_DATA_SIZE) { + return false; + } + + uint16_t offset = 0; + + for (uint16_t j = 0; j < chain_length; ++j) { + packet[offset] = NET_PACKET_FORWARD_REQUEST; + ++offset; + memcpy(packet + offset, chain_keys + j * CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE); + offset += CRYPTO_PUBLIC_KEY_SIZE; + } + + memcpy(packet + offset, data, data_length); + return true; +} + +non_null() +static uint16_t forwarding_packet_length(uint16_t sendback_data_len, uint16_t data_length) +{ + const uint16_t sendback_len = sendback_data_len == 0 ? 0 : TIMED_AUTH_SIZE + sendback_data_len; + return 1 + 1 + sendback_len + data_length; +} + +non_null(1, 4, 6) nullable(2) +static bool create_forwarding_packet(const Forwarding *forwarding, + const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *data, uint16_t length, + uint8_t *packet) +{ + packet[0] = NET_PACKET_FORWARDING; + + if (sendback_data_len == 0) { + packet[1] = 0; + memcpy(packet + 1 + 1, data, length); + } else { + const uint16_t sendback_len = TIMED_AUTH_SIZE + sendback_data_len; + + if (sendback_len > MAX_SENDBACK_SIZE) { + return false; + } + + packet[1] = sendback_len; + generate_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data, + sendback_data_len, packet + 1 + 1); + + if (sendback_data_len != 0) { + assert(sendback_data != nullptr); + memcpy(packet + 1 + 1 + TIMED_AUTH_SIZE, sendback_data, sendback_data_len); + } + + memcpy(packet + 1 + 1 + sendback_len, data, length); + } + + return true; +} + +bool send_forwarding(const Forwarding *forwarding, const IP_Port *dest, + const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *data, uint16_t length) +{ + if (length > MAX_FORWARD_DATA_SIZE) { + return false; + } + + const uint16_t len = forwarding_packet_length(sendback_data_len, length); + VLA(uint8_t, packet, len); + create_forwarding_packet(forwarding, sendback_data, sendback_data_len, data, length, packet); + return sendpacket(forwarding->net, dest, packet, len) == len; +} + +#define FORWARD_REQUEST_MIN_PACKET_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE) + +non_null(1) nullable(2, 4) +static bool handle_forward_request_dht(const Forwarding *forwarding, + const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *packet, uint16_t length) +{ + if (length < FORWARD_REQUEST_MIN_PACKET_SIZE) { + return false; + } + + const uint8_t *const public_key = packet + 1; + const uint8_t *const forward_data = packet + (1 + CRYPTO_PUBLIC_KEY_SIZE); + const uint16_t forward_data_len = length - (1 + CRYPTO_PUBLIC_KEY_SIZE); + + if (TIMED_AUTH_SIZE + sendback_data_len > MAX_SENDBACK_SIZE || + forward_data_len > MAX_FORWARD_DATA_SIZE) { + return false; + } + + const uint16_t len = forwarding_packet_length(sendback_data_len, forward_data_len); + VLA(uint8_t, forwarding_packet, len); + + create_forwarding_packet(forwarding, sendback_data, sendback_data_len, forward_data, forward_data_len, + forwarding_packet); + + return route_packet(forwarding->dht, public_key, forwarding_packet, len) == len; +} + +non_null(1, 2) nullable(3, 5) +static int handle_forward_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + const Forwarding *forwarding = (const Forwarding *)object; + + uint8_t sendback_data[1 + MAX_PACKED_IPPORT_SIZE]; + sendback_data[0] = SENDBACK_IPPORT; + + const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source); + + if (ipport_length == -1) { + return 1; + } + + return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length, packet, length) ? 0 : 1; +} + +#define MIN_NONEMPTY_SENDBACK_SIZE TIMED_AUTH_SIZE +#define FORWARD_REPLY_MIN_PACKET_SIZE (1 + 1 + MIN_NONEMPTY_SENDBACK_SIZE) + +non_null(1, 2) nullable(3, 5) +static int handle_forward_reply(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + const Forwarding *forwarding = (const Forwarding *)object; + + if (length < FORWARD_REPLY_MIN_PACKET_SIZE) { + return 1; + } + + const uint8_t sendback_len = packet[1]; + const uint8_t *const sendback_auth = packet + 1 + 1; + const uint8_t *const sendback_data = sendback_auth + TIMED_AUTH_SIZE; + + if (sendback_len > MAX_SENDBACK_SIZE) { + /* value 0xff is reserved for possible future expansion */ + return 1; + } + + if (sendback_len < TIMED_AUTH_SIZE + 1) { + return 1; + } + + const uint16_t sendback_data_len = sendback_len - TIMED_AUTH_SIZE; + + if (length < 1 + 1 + sendback_len) { + return 1; + } + + const uint8_t *const to_forward = packet + (1 + 1 + sendback_len); + const uint16_t to_forward_len = length - (1 + 1 + sendback_len); + + if (!check_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data, sendback_data_len, + sendback_auth)) { + return 1; + } + + if (sendback_data[0] == SENDBACK_IPPORT) { + IP_Port dest; + + if (unpack_ip_port(&dest, sendback_data + 1, sendback_data_len - 1, false) + != sendback_data_len - 1) { + return 1; + } + + return send_forwarding(forwarding, &dest, nullptr, 0, to_forward, to_forward_len) ? 0 : 1; + } + + if (sendback_data[0] == SENDBACK_FORWARD) { + IP_Port forwarder; + const int ipport_length = unpack_ip_port(&forwarder, sendback_data + 1, sendback_data_len - 1, false); + + if (ipport_length == -1) { + return 1; + } + + const uint8_t *const forward_sendback = sendback_data + (1 + ipport_length); + const uint16_t forward_sendback_len = sendback_data_len - (1 + ipport_length); + + return forward_reply(forwarding->net, &forwarder, forward_sendback, forward_sendback_len, to_forward, + to_forward_len) ? 0 : 1; + } + + if (forwarding->forward_reply_callback == nullptr) { + return 1; + } + + return forwarding->forward_reply_callback(forwarding->forward_reply_callback_object, + sendback_data, sendback_data_len, + to_forward, to_forward_len) ? 0 : 1; +} + +#define FORWARDING_MIN_PACKET_SIZE (1 + 1) + +non_null(1, 2) nullable(3, 5) +static int handle_forwarding(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + const Forwarding *forwarding = (const Forwarding *)object; + + if (length < FORWARDING_MIN_PACKET_SIZE) { + return 1; + } + + const uint8_t sendback_len = packet[1]; + + if (length < 1 + 1 + sendback_len) { + return 1; + } + + const uint8_t *const sendback = packet + 1 + 1; + + const uint8_t *const forwarded = sendback + sendback_len; + const uint16_t forwarded_len = length - (1 + 1 + sendback_len); + + if (forwarded_len >= 1 && forwarded[0] == NET_PACKET_FORWARD_REQUEST) { + VLA(uint8_t, sendback_data, 1 + MAX_PACKED_IPPORT_SIZE + sendback_len); + sendback_data[0] = SENDBACK_FORWARD; + + const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source); + + if (ipport_length == -1) { + return 1; + } + + memcpy(sendback_data + 1 + ipport_length, sendback, sendback_len); + + return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length + sendback_len, forwarded, + forwarded_len) ? 0 : 1; + } + + if (sendback_len > 0) { + if (forwarding->forwarded_request_callback == nullptr) { + return 1; + } + + forwarding->forwarded_request_callback(forwarding->forwarded_request_callback_object, + source, sendback, sendback_len, + forwarded, forwarded_len, userdata); + return 0; + } else { + if (forwarding->forwarded_response_callback == nullptr) { + return 1; + } + + forwarding->forwarded_response_callback(forwarding->forwarded_response_callback_object, + forwarded, forwarded_len, userdata); + return 0; + } +} + +bool forward_reply(Networking_Core *net, const IP_Port *forwarder, + const uint8_t *sendback, uint16_t sendback_length, + const uint8_t *data, uint16_t length) +{ + if (sendback_length > MAX_SENDBACK_SIZE || + length > MAX_FORWARD_DATA_SIZE) { + return false; + } + + const uint16_t len = 1 + 1 + sendback_length + length; + VLA(uint8_t, packet, len); + packet[0] = NET_PACKET_FORWARD_REPLY; + packet[1] = (uint8_t) sendback_length; + memcpy(packet + 1 + 1, sendback, sendback_length); + memcpy(packet + 1 + 1 + sendback_length, data, length); + return sendpacket(net, forwarder, packet, len) == len; +} + +void set_callback_forwarded_request(Forwarding *forwarding, forwarded_request_cb *function, void *object) +{ + forwarding->forwarded_request_callback = function; + forwarding->forwarded_request_callback_object = object; +} + +void set_callback_forwarded_response(Forwarding *forwarding, forwarded_response_cb *function, void *object) +{ + forwarding->forwarded_response_callback = function; + forwarding->forwarded_response_callback_object = object; +} + +void set_callback_forward_reply(Forwarding *forwarding, forward_reply_cb *function, void *object) +{ + forwarding->forward_reply_callback = function; + forwarding->forward_reply_callback_object = object; +} + +Forwarding *new_forwarding(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht) +{ + if (log == nullptr || mono_time == nullptr || dht == nullptr) { + return nullptr; + } + + Forwarding *forwarding = (Forwarding *)calloc(1, sizeof(Forwarding)); + + if (forwarding == nullptr) { + return nullptr; + } + + forwarding->log = log; + forwarding->rng = rng; + forwarding->mono_time = mono_time; + forwarding->dht = dht; + forwarding->net = dht_get_net(dht); + + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, &handle_forward_request, forwarding); + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, &handle_forward_reply, forwarding); + networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, &handle_forwarding, forwarding); + + new_hmac_key(forwarding->rng, forwarding->hmac_key); + + return forwarding; +} + +void kill_forwarding(Forwarding *forwarding) +{ + if (forwarding == nullptr) { + return; + } + + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, nullptr, nullptr); + networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, nullptr, nullptr); + networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, nullptr, nullptr); + + crypto_memzero(forwarding->hmac_key, CRYPTO_HMAC_KEY_SIZE); + + free(forwarding); +} diff --git a/protocols/Tox/libtox/src/toxcore/forwarding.h b/protocols/Tox/libtox/src/toxcore/forwarding.h new file mode 100644 index 0000000000..36ce8ad894 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/forwarding.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2019-2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_FORWARDING_H +#define C_TOXCORE_TOXCORE_FORWARDING_H + +#include "DHT.h" +#include "network.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SENDBACK_IPPORT 0 +#define SENDBACK_FORWARD 1 +#define SENDBACK_TCP 2 + +#define MAX_SENDBACK_SIZE (0xff - 1) +#define MAX_FORWARD_DATA_SIZE (MAX_UDP_PACKET_SIZE - (1 + 1 + MAX_SENDBACK_SIZE)) + +#define MAX_FORWARD_CHAIN_LENGTH 4 + +#define MAX_PACKED_IPPORT_SIZE (1 + SIZE_IP6 + sizeof(uint16_t)) + +typedef struct Forwarding Forwarding; + +non_null() +DHT *forwarding_get_dht(Forwarding *forwarding); + +/** + * @brief Send data to forwarder for forwarding via chain of dht nodes. + * Destination is last key in the chain. + * + * @param data Must be of length at most MAX_FORWARD_DATA_SIZE. + * @param chain_length Number of intermediate nodes in chain. + * Must be at least 1 and at most MAX_FORWARD_CHAIN_LENGTH. + * @param chain_keys Public keys of chain nodes. Must be of length + * `chain_length * CRYPTO_PUBLIC_KEY_SIZE`. + * + * @return true on success, false otherwise. + */ +non_null() +bool send_forward_request(Networking_Core *net, const IP_Port *forwarder, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length); + +/** Returns size of packet written by create_forward_chain_packet. */ +uint16_t forward_chain_packet_size(uint16_t chain_length, uint16_t data_length); + +/** + * @brief Create forward request packet for forwarding data via chain of dht nodes. + * Destination is last key in the chain. + * + * @param data Must be of length at most MAX_FORWARD_DATA_SIZE. + * @param chain_length Number of intermediate nodes in chain. + * Must be at least 1 and at most MAX_FORWARD_CHAIN_LENGTH. + * @param chain_keys Public keys of chain nodes. Must be of length + * `chain_length * CRYPTO_PUBLIC_KEY_SIZE`. + * @param packet Must be of size at least + * `forward_chain_packet_size(chain_length, data_length)` bytes. + * + * @return true on success, false otherwise. + */ +non_null() +bool create_forward_chain_packet(const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length, + uint8_t *packet); + +/** + * @brief Send reply to forwarded packet via forwarder. + * @param sendback Must be of size at most MAX_SENDBACK_SIZE. + * @param data Must be of size at most MAX_FORWARD_DATA_SIZE. + * + * @return true on success, false otherwise. + */ +non_null() +bool forward_reply(Networking_Core *net, const IP_Port *forwarder, + const uint8_t *sendback, uint16_t sendback_length, + const uint8_t *data, uint16_t length); + + +/** + * @brief Set callback to handle a forwarded request. + * To reply to the packet, callback should use `forward_reply()` to send a reply + * forwarded via forwarder, passing the provided sendback. + */ +typedef void forwarded_request_cb(void *object, const IP_Port *forwarder, const uint8_t *sendback, + uint16_t sendback_length, const uint8_t *data, + uint16_t length, void *userdata); +non_null(1) nullable(2, 3) +void set_callback_forwarded_request(Forwarding *forwarding, forwarded_request_cb *function, void *object); + +/** @brief Set callback to handle a forwarded response. */ +typedef void forwarded_response_cb(void *object, const uint8_t *data, uint16_t length, void *userdata); +non_null(1) nullable(2, 3) +void set_callback_forwarded_response(Forwarding *forwarding, forwarded_response_cb *function, void *object); + +/** @brief Send forwarding packet to dest with given sendback data and data. */ +non_null(1, 2, 5) nullable(3) +bool send_forwarding(const Forwarding *forwarding, const IP_Port *dest, + const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *data, uint16_t length); + +typedef bool forward_reply_cb(void *object, const uint8_t *sendback_data, uint16_t sendback_data_len, + const uint8_t *data, uint16_t length); + +/** + * @brief Set callback to handle a forward reply with an otherwise unhandled + * sendback. + */ +non_null(1) nullable(2, 3) +void set_callback_forward_reply(Forwarding *forwarding, forward_reply_cb *function, void *object); + +non_null() +Forwarding *new_forwarding(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht); + +nullable(1) +void kill_forwarding(Forwarding *forwarding); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/protocols/Tox/libtox/src/toxcore/friend_connection.c b/protocols/Tox/libtox/src/toxcore/friend_connection.c index 8a46194848..9e5dee7cbd 100644 --- a/protocols/Tox/libtox/src/toxcore/friend_connection.c +++ b/protocols/Tox/libtox/src/toxcore/friend_connection.c @@ -11,6 +11,7 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "mono_time.h" #include "util.h" @@ -48,16 +49,20 @@ struct Friend_Conn { Node_format tcp_relays[FRIEND_MAX_STORED_TCP_RELAYS]; uint16_t tcp_relay_counter; + uint32_t tcp_relay_share_index; bool hosting_tcp_relay; }; +static const Friend_Conn empty_friend_conn = {0}; + struct Friend_Connections { const Mono_Time *mono_time; const Logger *logger; Net_Crypto *net_crypto; DHT *dht; + Broadcast_Info *broadcast; Onion_Client *onion_c; Friend_Conn *conns; @@ -91,9 +96,11 @@ const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc) } -/** return true if the friendcon_id is valid. - * return false if the friendcon_id is not valid. +/** + * @retval true if the friendcon_id is valid. + * @retval false if the friendcon_id is not valid. */ +non_null() static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id) { return (unsigned int)friendcon_id < fr_c->num_cons && @@ -102,11 +109,12 @@ static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id } -/** Set the size of the friend connections list to num. +/** @brief Set the size of the friend connections list to num. * - * return false if realloc fails. - * return true if it succeeds. + * @retval false if realloc fails. + * @retval true if it succeeds. */ +non_null() static bool realloc_friendconns(Friend_Connections *fr_c, uint32_t num) { if (num == 0) { @@ -125,11 +133,12 @@ static bool realloc_friendconns(Friend_Connections *fr_c, uint32_t num) return true; } -/** Create a new empty friend connection. +/** @brief Create a new empty friend connection. * - * return -1 on failure. - * return friendcon_id on success. + * @retval -1 on failure. + * @return friendcon_id on success. */ +non_null() static int create_friend_conn(Friend_Connections *fr_c) { for (uint32_t i = 0; i < fr_c->num_cons; ++i) { @@ -144,23 +153,24 @@ static int create_friend_conn(Friend_Connections *fr_c) const int id = fr_c->num_cons; ++fr_c->num_cons; - memset(&fr_c->conns[id], 0, sizeof(Friend_Conn)); + fr_c->conns[id] = empty_friend_conn; return id; } -/** Wipe a friend connection. +/** @brief Wipe a friend connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id) { if (!friendconn_id_valid(fr_c, friendcon_id)) { return -1; } - memset(&fr_c->conns[friendcon_id], 0, sizeof(Friend_Conn)); + fr_c->conns[friendcon_id] = empty_friend_conn; uint32_t i; @@ -187,16 +197,17 @@ Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id) return &fr_c->conns[friendcon_id]; } -/** return friendcon_id corresponding to the real public key on success. - * return -1 on failure. +/** + * @return friendcon_id corresponding to the real public key on success. + * @retval -1 on failure. */ int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk) { for (uint32_t i = 0; i < fr_c->num_cons; ++i) { const Friend_Conn *friend_con = get_conn(fr_c, i); - if (friend_con) { - if (public_key_cmp(friend_con->real_public_key, real_pk) == 0) { + if (friend_con != nullptr) { + if (pk_equal(friend_con->real_public_key, real_pk)) { return i; } } @@ -205,11 +216,12 @@ int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk) return -1; } -/** Add a TCP relay associated to the friend. +/** @brief Add a TCP relay associated to the friend. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, const IP_Port *ip_port, const uint8_t *public_key) { @@ -217,12 +229,12 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } /* Local ip and same pk means that they are hosting a TCP relay. */ - if (ip_is_local(&ipp_copy.ip) && public_key_cmp(friend_con->dht_temp_pk, public_key) == 0) { + if (ip_is_local(&ipp_copy.ip) && pk_equal(friend_con->dht_temp_pk, public_key)) { if (!net_family_is_unspec(friend_con->dht_ip_port.ip.family)) { ipp_copy.ip = friend_con->dht_ip_port.ip; } else { @@ -234,8 +246,8 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons for (unsigned i = 0; i < FRIEND_MAX_STORED_TCP_RELAYS; ++i) { if (!net_family_is_unspec(friend_con->tcp_relays[i].ip_port.ip.family) - && public_key_cmp(friend_con->tcp_relays[i].public_key, public_key) == 0) { - memset(&friend_con->tcp_relays[i], 0, sizeof(Node_format)); + && pk_equal(friend_con->tcp_relays[i].public_key, public_key)) { + friend_con->tcp_relays[i] = empty_node_format; } } @@ -247,11 +259,12 @@ static int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, cons } /** Connect to number saved relays for friend. */ +non_null() static void connect_to_saved_tcp_relays(Friend_Connections *fr_c, int friendcon_id, unsigned int number) { const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return; } @@ -267,26 +280,30 @@ static void connect_to_saved_tcp_relays(Friend_Connections *fr_c, int friendcon_ } } +non_null() static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return 0; } Node_format nodes[MAX_SHARED_RELAYS] = {{{0}}}; uint8_t data[1024]; - const int n = copy_connected_tcp_relays(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS); + const uint32_t n = copy_connected_tcp_relays_index(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS, + friend_con->tcp_relay_share_index); - for (int i = 0; i < n; ++i) { + friend_con->tcp_relay_share_index += MAX_SHARED_RELAYS; + + for (uint32_t i = 0; i < n; ++i) { /* Associated the relays being sent with this connection. * On receiving the peer will do the same which will establish the connection. */ friend_add_tcp_relay(fr_c, friendcon_id, &nodes[i].ip_port, nodes[i].public_key); } - int length = pack_nodes(data + 1, sizeof(data) - 1, nodes, n); + int length = pack_nodes(fr_c->logger, data + 1, sizeof(data) - 1, nodes, n); if (length <= 0) { return 0; @@ -295,7 +312,7 @@ static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id) data[0] = PACKET_ID_SHARE_RELAYS; ++length; - if (write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, data, length, 0) != -1) { + if (write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, data, length, false) != -1) { friend_con->share_relays_lastsent = mono_time_get(fr_c->mono_time); return 1; } @@ -304,12 +321,13 @@ static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id) } /** callback for recv TCP relay nodes. */ +non_null() static int tcp_relay_node_callback(void *object, uint32_t number, const IP_Port *ip_port, const uint8_t *public_key) { Friend_Connections *fr_c = (Friend_Connections *)object; const Friend_Conn *friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -320,14 +338,17 @@ static int tcp_relay_node_callback(void *object, uint32_t number, const IP_Port return add_tcp_relay(fr_c->net_crypto, ip_port, public_key); } +non_null() static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id); + /** Callback for DHT ip_port changes. */ +non_null() static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port) { Friend_Connections *const fr_c = (Friend_Connections *)object; Friend_Conn *const friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return; } @@ -335,7 +356,7 @@ static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port friend_new_connection(fr_c, number); } - set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, 1); + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, true); friend_con->dht_ip_port = *ip_port; friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time); @@ -345,17 +366,18 @@ static void dht_ip_callback(void *object, int32_t number, const IP_Port *ip_port } } +non_null() static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_public_key) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return; } friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time); - if (friend_con->dht_lock) { + if (friend_con->dht_lock > 0) { if (dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) { LOGGER_ERROR(fr_c->logger, "a. Could not delete dht peer. Please report this."); return; @@ -368,26 +390,27 @@ static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint memcpy(friend_con->dht_temp_pk, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE); } -static int handle_status(void *object, int number, uint8_t status, void *userdata) +non_null() +static int handle_status(void *object, int number, bool status, void *userdata) { Friend_Connections *const fr_c = (Friend_Connections *)object; Friend_Conn *const friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } - bool status_changed = 0; + bool status_changed = false; if (status) { /* Went online. */ - status_changed = 1; + status_changed = true; friend_con->status = FRIENDCONN_STATUS_CONNECTED; friend_con->ping_lastrecv = mono_time_get(fr_c->mono_time); friend_con->share_relays_lastsent = 0; onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status); } else { /* Went offline. */ if (friend_con->status != FRIENDCONN_STATUS_CONNECTING) { - status_changed = 1; + status_changed = true; friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time); onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status); } @@ -398,12 +421,12 @@ static int handle_status(void *object, int number, uint8_t status, void *userdat } if (status_changed) { - if (fr_c->global_status_callback) { + if (fr_c->global_status_callback != nullptr) { fr_c->global_status_callback(fr_c->global_status_callback_object, number, status, userdata); } for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { - if (friend_con->callbacks[i].status_callback) { + if (friend_con->callbacks[i].status_callback != nullptr) { friend_con->callbacks[i].status_callback( friend_con->callbacks[i].callback_object, friend_con->callbacks[i].callback_id, status, userdata); @@ -415,16 +438,17 @@ static int handle_status(void *object, int number, uint8_t status, void *userdat } /** Callback for dht public key changes. */ +non_null() static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata) { Friend_Connections *const fr_c = (Friend_Connections *)object; Friend_Conn *const friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return; } - if (public_key_cmp(friend_con->dht_temp_pk, dht_public_key) == 0) { + if (pk_equal(friend_con->dht_temp_pk, dht_public_key)) { return; } @@ -434,13 +458,14 @@ static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_pub if (friend_con->crypt_connection_id != -1) { crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; - handle_status(object, number, 0, userdata); /* Going offline. */ + handle_status(object, number, false, userdata); /* Going offline. */ } friend_new_connection(fr_c, number); onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key); } +non_null() static int handle_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata) { if (length == 0) { @@ -450,12 +475,12 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t Friend_Connections *const fr_c = (Friend_Connections *)object; Friend_Conn *friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } if (data[0] == PACKET_ID_FRIEND_REQUESTS) { - if (fr_c->fr_request_callback) { + if (fr_c->fr_request_callback != nullptr) { fr_c->fr_request_callback(fr_c->fr_request_object, friend_con->real_public_key, data, length, userdata); } @@ -469,7 +494,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t if (data[0] == PACKET_ID_SHARE_RELAYS) { Node_format nodes[MAX_SHARED_RELAYS]; - const int n = unpack_nodes(nodes, MAX_SHARED_RELAYS, nullptr, data + 1, length - 1, 1); + const int n = unpack_nodes(nodes, MAX_SHARED_RELAYS, nullptr, data + 1, length - 1, true); if (n == -1) { return -1; @@ -483,7 +508,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t } for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { - if (friend_con->callbacks[i].data_callback) { + if (friend_con->callbacks[i].data_callback != nullptr) { friend_con->callbacks[i].data_callback( friend_con->callbacks[i].callback_object, friend_con->callbacks[i].callback_id, data, length, userdata); @@ -491,7 +516,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } } @@ -499,6 +524,7 @@ static int handle_packet(void *object, int number, const uint8_t *data, uint16_t return 0; } +non_null() static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata) { if (length == 0) { @@ -508,12 +534,12 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui const Friend_Connections *const fr_c = (const Friend_Connections *)object; const Friend_Conn *friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) { - if (friend_con->callbacks[i].lossy_data_callback) { + if (friend_con->callbacks[i].lossy_data_callback != nullptr) { friend_con->callbacks[i].lossy_data_callback( friend_con->callbacks[i].callback_object, friend_con->callbacks[i].callback_id, data, length, userdata); @@ -521,7 +547,7 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui friend_con = get_conn(fr_c, number); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } } @@ -529,13 +555,14 @@ static int handle_lossy_packet(void *object, int number, const uint8_t *data, ui return 0; } +non_null() static int handle_new_connections(void *object, const New_Connection *n_c) { Friend_Connections *const fr_c = (Friend_Connections *)object; const int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key); Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -555,13 +582,13 @@ static int handle_new_connections(void *object, const New_Connection *n_c) friend_con->crypt_connection_id = id; if (!net_family_is_ipv4(n_c->source.ip.family) && !net_family_is_ipv6(n_c->source.ip.family)) { - set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, 0); + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false); } else { friend_con->dht_ip_port = n_c->source; friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time); } - if (public_key_cmp(friend_con->dht_temp_pk, n_c->dht_public_key) != 0) { + if (!pk_equal(friend_con->dht_temp_pk, n_c->dht_public_key)) { change_dht_pk(fr_c, friendcon_id, n_c->dht_public_key); } @@ -573,7 +600,7 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -582,7 +609,7 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id) } /* If dht_temp_pk does not contains a pk. */ - if (!friend_con->dht_lock) { + if (friend_con->dht_lock == 0) { return -1; } @@ -601,16 +628,17 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id) return 0; } +non_null() static int send_ping(const Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } const uint8_t ping = PACKET_ID_ALIVE; - const int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0); + const int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), false); if (ret != -1) { friend_con->ping_lastsent = mono_time_get(fr_c->mono_time); @@ -620,16 +648,16 @@ static int send_ping(const Friend_Connections *fr_c, int friendcon_id) return -1; } -/** Increases lock_count for the connection with friendcon_id by 1. +/** @brief Increases lock_count for the connection with friendcon_id by 1. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -637,57 +665,58 @@ int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id) return 0; } -/** return FRIENDCONN_STATUS_CONNECTED if the friend is connected. - * return FRIENDCONN_STATUS_CONNECTING if the friend isn't connected. - * return FRIENDCONN_STATUS_NONE on failure. +/** + * @retval FRIENDCONN_STATUS_CONNECTED if the friend is connected. + * @retval FRIENDCONN_STATUS_CONNECTING if the friend isn't connected. + * @retval FRIENDCONN_STATUS_NONE on failure. */ unsigned int friend_con_connected(const Friend_Connections *fr_c, int friendcon_id) { const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return 0; } return friend_con->status; } -/** Copy public keys associated to friendcon_id. +/** @brief Copy public keys associated to friendcon_id. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, const Friend_Connections *fr_c, int friendcon_id) { const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } - if (real_pk) { + if (real_pk != nullptr) { memcpy(real_pk, friend_con->real_public_key, CRYPTO_PUBLIC_KEY_SIZE); } - if (dht_temp_pk) { + if (dht_temp_pk != nullptr) { memcpy(dht_temp_pk, friend_con->dht_temp_pk, CRYPTO_PUBLIC_KEY_SIZE); } return 0; } -/** Set temp dht key for connection. - */ +/** Set temp dht key for connection. */ void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk, void *userdata) { dht_pk_callback(fr_c, friendcon_id, dht_temp_pk, userdata); } -/** Set the callbacks for the friend connection. - * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. +/** @brief Set the callbacks for the friend connection. + * @param index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we + * want the callback to set in the array. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id, unsigned int index, fc_status_cb *status_callback, @@ -697,7 +726,7 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -705,6 +734,11 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id return -1; } + if (object != nullptr && (status_callback == nullptr || data_callback == nullptr || lossy_data_callback == nullptr)) { + LOGGER_ERROR(fr_c->logger, "non-null user data object but null callbacks"); + return -1; + } + friend_con->callbacks[index].status_callback = status_callback; friend_con->callbacks[index].data_callback = data_callback; friend_con->callbacks[index].lossy_data_callback = lossy_data_callback; @@ -718,31 +752,36 @@ int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id /** Set global status callback for friend connections. */ void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object) { + if (object != nullptr && global_status_callback == nullptr) { + LOGGER_ERROR(fr_c->logger, "non-null user data object but null callback"); + object = nullptr; + } + fr_c->global_status_callback = global_status_callback; fr_c->global_status_callback_object = object; } -/** return the crypt_connection_id for the connection. +/** @brief return the crypt_connection_id for the connection. * - * return crypt_connection_id on success. - * return -1 on failure. + * @return crypt_connection_id on success. + * @retval -1 on failure. */ int friend_connection_crypt_connection_id(const Friend_Connections *fr_c, int friendcon_id) { const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } return friend_con->crypt_connection_id; } -/** Create a new friend connection. +/** @brief Create a new friend connection. * If one to that real public key already exists, increase lock count and return it. * - * return -1 on failure. - * return connection id on success. + * @retval -1 on failure. + * @return connection id on success. */ int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key) { @@ -778,20 +817,20 @@ int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_k return friendcon_id; } -/** Kill a friend connection. +/** @brief Kill a friend connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } - if (friend_con->lock_count) { + if (friend_con->lock_count > 0) { --friend_con->lock_count; return 0; } @@ -799,7 +838,7 @@ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id) onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum); crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); - if (friend_con->dht_lock) { + if (friend_con->dht_lock > 0) { dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); } @@ -807,7 +846,7 @@ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id) } -/** Set friend request callback. +/** @brief Set friend request callback. * * This function will be called every time a friend request packet is received. */ @@ -818,11 +857,11 @@ void set_friend_request_callback(Friend_Connections *fr_c, fr_request_cb *fr_req oniondata_registerhandler(fr_c->onion_c, CRYPTO_PACKET_FRIEND_REQ, fr_request_callback, object); } -/** Send a Friend request packet. +/** @brief Send a Friend request packet. * - * return -1 if failure. - * return 0 if it sent the friend request directly to the friend. - * return the number of peers it was routed through if it did not send it directly. + * @retval -1 if failure. + * @retval 0 if it sent the friend request directly to the friend. + * @return the number of peers it was routed through if it did not send it directly. */ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data, uint16_t length) @@ -833,7 +872,7 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); - if (!friend_con) { + if (friend_con == nullptr) { return -1; } @@ -843,7 +882,7 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3 if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) { packet[0] = PACKET_ID_FRIEND_REQUESTS; - return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, SIZEOF_VLA(packet), 0) != -1; + return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, SIZEOF_VLA(packet), false) != -1 ? 1 : 0; } packet[0] = CRYPTO_PACKET_FRIEND_REQ; @@ -857,8 +896,9 @@ int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint3 } /** Create new friend_connections instance. */ -Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time *mono_time, Onion_Client *onion_c, - bool local_discovery_enabled) +Friend_Connections *new_friend_connections( + const Logger *logger, const Mono_Time *mono_time, const Network *ns, + Onion_Client *onion_c, bool local_discovery_enabled) { if (onion_c == nullptr) { return nullptr; @@ -882,13 +922,18 @@ Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time new_connection_handler(temp->net_crypto, &handle_new_connections, temp); if (temp->local_discovery_enabled) { - lan_discovery_init(temp->dht); + temp->broadcast = lan_discovery_init(ns); + + if (temp->broadcast == nullptr) { + LOGGER_ERROR(logger, "could not initialise LAN discovery"); + } } return temp; } /** Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds. */ +non_null() static void lan_discovery(Friend_Connections *fr_c) { if (fr_c->last_lan_discovery + LAN_DISCOVERY_INTERVAL < mono_time_get(fr_c->mono_time)) { @@ -897,11 +942,12 @@ static void lan_discovery(Friend_Connections *fr_c) last = last > TOX_PORTRANGE_TO ? TOX_PORTRANGE_TO : last; // Always send to default port - lan_discovery_send(dht_get_net(fr_c->dht), dht_get_self_public_key(fr_c->dht), net_htons(TOX_PORT_DEFAULT)); + lan_discovery_send(dht_get_net(fr_c->dht), fr_c->broadcast, dht_get_self_public_key(fr_c->dht), + net_htons(TOX_PORT_DEFAULT)); // And check some extra ports for (uint16_t port = first; port < last; ++port) { - lan_discovery_send(dht_get_net(fr_c->dht), dht_get_self_public_key(fr_c->dht), net_htons(port)); + lan_discovery_send(dht_get_net(fr_c->dht), fr_c->broadcast, dht_get_self_public_key(fr_c->dht), net_htons(port)); } // Don't include default port in port range @@ -918,10 +964,10 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata) for (uint32_t i = 0; i < fr_c->num_cons; ++i) { Friend_Conn *const friend_con = get_conn(fr_c, i); - if (friend_con) { + if (friend_con != nullptr) { if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) { if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { - if (friend_con->dht_lock) { + if (friend_con->dht_lock > 0) { dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); friend_con->dht_lock = 0; memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE); @@ -929,12 +975,12 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata) } if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { - friend_con->dht_ip_port.ip.family = net_family_unspec; + friend_con->dht_ip_port.ip.family = net_family_unspec(); } - if (friend_con->dht_lock) { + if (friend_con->dht_lock > 0) { if (friend_new_connection(fr_c, i) == 0) { - set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, 0); + set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false); connect_to_saved_tcp_relays(fr_c, i, MAX_FRIEND_TCP_CONNECTIONS / 2); /* Only fill it half up. */ } } @@ -951,7 +997,7 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata) /* If we stopped receiving ping packets, kill it. */ crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; - handle_status(fr_c, i, 0, userdata); /* Going offline. */ + handle_status(fr_c, i, false, userdata); /* Going offline. */ } } } @@ -965,7 +1011,7 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata) /** Free everything related with friend_connections. */ void kill_friend_connections(Friend_Connections *fr_c) { - if (!fr_c) { + if (fr_c == nullptr) { return; } @@ -973,9 +1019,6 @@ void kill_friend_connections(Friend_Connections *fr_c) kill_friend_connection(fr_c, i); } - if (fr_c->local_discovery_enabled) { - lan_discovery_kill(fr_c->dht); - } - + lan_discovery_kill(fr_c->broadcast); free(fr_c); } diff --git a/protocols/Tox/libtox/src/toxcore/friend_connection.h b/protocols/Tox/libtox/src/toxcore/friend_connection.h index 4986b3bfb9..93bd5113ac 100644 --- a/protocols/Tox/libtox/src/toxcore/friend_connection.h +++ b/protocols/Tox/libtox/src/toxcore/friend_connection.h @@ -36,8 +36,8 @@ /** Max number of tcp relays sent to friends */ #define MAX_SHARED_RELAYS RECOMMENDED_FRIEND_TCP_CONNECTIONS -/** Interval between the sending of tcp relay information */ -#define SHARE_RELAYS_INTERVAL (5 * 60) +/** How often we share our TCP relays with each friend connection */ +#define SHARE_RELAYS_INTERVAL (60 * 2) typedef enum Friendconn_Status { @@ -48,112 +48,129 @@ typedef enum Friendconn_Status { typedef struct Friend_Connections Friend_Connections; -Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c); +non_null() Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c); -/** return friendcon_id corresponding to the real public key on success. - * return -1 on failure. +/** @return friendcon_id corresponding to the real public key on success. + * @retval -1 on failure. */ +non_null() int getfriend_conn_id_pk(const Friend_Connections *fr_c, const uint8_t *real_pk); -/** Increases lock_count for the connection with friendcon_id by 1. +/** @brief Increases lock_count for the connection with friendcon_id by 1. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null() int friend_connection_lock(const Friend_Connections *fr_c, int friendcon_id); -/** return FRIENDCONN_STATUS_CONNECTED if the friend is connected. - * return FRIENDCONN_STATUS_CONNECTING if the friend isn't connected. - * return FRIENDCONN_STATUS_NONE on failure. +/** + * @retval FRIENDCONN_STATUS_CONNECTED if the friend is connected. + * @retval FRIENDCONN_STATUS_CONNECTING if the friend isn't connected. + * @retval FRIENDCONN_STATUS_NONE on failure. */ +non_null() unsigned int friend_con_connected(const Friend_Connections *fr_c, int friendcon_id); -/** Copy public keys associated to friendcon_id. +/** @brief Copy public keys associated to friendcon_id. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null(3) nullable(1, 2) int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, const Friend_Connections *fr_c, int friendcon_id); -/** Set temp dht key for connection. - */ +/** Set temp dht key for connection. */ +non_null() void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk, void *userdata); -typedef int global_status_cb(void *object, int id, uint8_t status, void *userdata); +typedef int global_status_cb(void *object, int id, bool status, void *userdata); -typedef int fc_status_cb(void *object, int id, uint8_t status, void *userdata); +typedef int fc_status_cb(void *object, int id, bool status, void *userdata); typedef int fc_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); typedef int fc_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); /** Set global status callback for friend connections. */ +non_null(1) nullable(2, 3) void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object); -/** Set the callbacks for the friend connection. - * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array. +/** @brief Set the callbacks for the friend connection. + * @param index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we + * want the callback to set in the array. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ +non_null(1) nullable(4, 5, 6, 7) int friend_connection_callbacks(const Friend_Connections *fr_c, int friendcon_id, unsigned int index, fc_status_cb *status_callback, fc_data_cb *data_callback, fc_lossy_data_cb *lossy_data_callback, void *object, int number); -/** return the crypt_connection_id for the connection. +/** @brief return the crypt_connection_id for the connection. * - * return crypt_connection_id on success. - * return -1 on failure. + * @return crypt_connection_id on success. + * @retval -1 on failure. */ +non_null() int friend_connection_crypt_connection_id(const Friend_Connections *fr_c, int friendcon_id); -/** Create a new friend connection. +/** @brief Create a new friend connection. * If one to that real public key already exists, increase lock count and return it. * - * return -1 on failure. - * return connection id on success. + * @retval -1 on failure. + * @return connection id on success. */ +non_null() int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key); -/** Kill a friend connection. +/** @brief Kill a friend connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id); -/** Send a Friend request packet. +/** @brief Send a Friend request packet. * - * return -1 if failure. - * return 0 if it sent the friend request directly to the friend. - * return the number of peers it was routed through if it did not send it directly. + * @retval -1 if failure. + * @retval 0 if it sent the friend request directly to the friend. + * @return the number of peers it was routed through if it did not send it directly. */ -int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data, - uint16_t length); +non_null() +int send_friend_request_packet( + Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data, uint16_t length); -typedef int fr_request_cb(void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t len, - void *userdata); +typedef int fr_request_cb( + void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t len, void *userdata); -/** Set friend request callback. +/** @brief Set friend request callback. * * This function will be called every time a friend request packet is received. */ +non_null() void set_friend_request_callback(Friend_Connections *fr_c, fr_request_cb *fr_request_callback, void *object); /** Create new friend_connections instance. */ -Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time *mono_time, Onion_Client *onion_c, - bool local_discovery_enabled); +non_null() +Friend_Connections *new_friend_connections( + const Logger *logger, const Mono_Time *mono_time, const Network *ns, + Onion_Client *onion_c, bool local_discovery_enabled); /** main friend_connections loop. */ +non_null() void do_friend_connections(Friend_Connections *fr_c, void *userdata); /** Free everything related with friend_connections. */ +nullable(1) void kill_friend_connections(Friend_Connections *fr_c); typedef struct Friend_Conn Friend_Conn; -Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id); -int friend_conn_get_onion_friendnum(const Friend_Conn *fc); -const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc); +non_null() Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id); +non_null() int friend_conn_get_onion_friendnum(const Friend_Conn *fc); +non_null() const IP_Port *friend_conn_get_dht_ip_port(const Friend_Conn *fc); #endif diff --git a/protocols/Tox/libtox/src/toxcore/friend_requests.c b/protocols/Tox/libtox/src/toxcore/friend_requests.c index f4d572996d..7f18b1ff02 100644 --- a/protocols/Tox/libtox/src/toxcore/friend_requests.c +++ b/protocols/Tox/libtox/src/toxcore/friend_requests.c @@ -11,10 +11,12 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "util.h" -/** NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem. - * TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam.) +/** + * NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem. + * TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam). */ #define MAX_RECEIVED_STORED 32 @@ -47,8 +49,7 @@ uint32_t get_nospam(const Friend_Requests *fr) } -/** Set the function that will be executed when a friend request for us is received. - */ +/** Set the function that will be executed when a friend request for us is received. */ void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object) { fr->handle_friendrequest = function; @@ -56,8 +57,8 @@ void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, fr->handle_friendrequest_object = object; } -/** Set the function used to check if a friend request should be displayed to the user or not. - * It must return 0 if the request is ok (anything else if it is bad.) +/** @brief Set the function used to check if a friend request should be displayed to the user or not. + * It must return 0 if the request is ok (anything else if it is bad). */ void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata) { @@ -66,25 +67,27 @@ void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void } /** Add to list of received friend requests. */ +non_null() static void addto_receivedlist(Friend_Requests *fr, const uint8_t *real_pk) { if (fr->received.requests_index >= MAX_RECEIVED_STORED) { fr->received.requests_index = 0; } - id_copy(fr->received.requests[fr->received.requests_index], real_pk); + pk_copy(fr->received.requests[fr->received.requests_index], real_pk); ++fr->received.requests_index; } -/** Check if a friend request was already received. +/** @brief Check if a friend request was already received. * - * return false if it did not. - * return true if it did. + * @retval false if it did not. + * @retval true if it did. */ +non_null() static bool request_received(const Friend_Requests *fr, const uint8_t *real_pk) { for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) { - if (id_equal(fr->received.requests[i], real_pk)) { + if (pk_equal(fr->received.requests[i], real_pk)) { return true; } } @@ -92,15 +95,15 @@ static bool request_received(const Friend_Requests *fr, const uint8_t *real_pk) return false; } -/** Remove real_pk from received_requests list. +/** @brief Remove real_pk from received_requests list. * - * return 0 if it removed it successfully. - * return -1 if it didn't find it. + * @retval 0 if it removed it successfully. + * @retval -1 if it didn't find it. */ int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk) { for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) { - if (id_equal(fr->received.requests[i], real_pk)) { + if (pk_equal(fr->received.requests[i], real_pk)) { crypto_memzero(fr->received.requests[i], CRYPTO_PUBLIC_KEY_SIZE); return 0; } @@ -110,6 +113,7 @@ int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk) } +non_null() static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length, void *userdata) { @@ -134,7 +138,7 @@ static int friendreq_handlepacket(void *object, const uint8_t *source_pubkey, co return 1; } - if (fr->filter_function) { + if (fr->filter_function != nullptr) { if (fr->filter_function(source_pubkey, fr->filter_function_userdata) != 0) { return 1; } diff --git a/protocols/Tox/libtox/src/toxcore/friend_requests.h b/protocols/Tox/libtox/src/toxcore/friend_requests.h index bb2b10b81c..2614527163 100644 --- a/protocols/Tox/libtox/src/toxcore/friend_requests.h +++ b/protocols/Tox/libtox/src/toxcore/friend_requests.h @@ -16,34 +16,39 @@ typedef struct Friend_Requests Friend_Requests; /** Set and get the nospam variable used to prevent one type of friend request spam. */ -void set_nospam(Friend_Requests *fr, uint32_t num); -uint32_t get_nospam(const Friend_Requests *fr); +non_null() void set_nospam(Friend_Requests *fr, uint32_t num); +non_null() uint32_t get_nospam(const Friend_Requests *fr); -/** Remove real_pk from received_requests list. +/** @brief Remove real_pk from received_requests list. * - * return 0 if it removed it successfully. - * return -1 if it didn't find it. + * @retval 0 if it removed it successfully. + * @retval -1 if it didn't find it. */ +non_null() int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk); typedef void fr_friend_request_cb(void *object, const uint8_t *public_key, const uint8_t *message, size_t length, void *user_data); -/** Set the function that will be executed when a friend request for us is received. - */ +/** Set the function that will be executed when a friend request for us is received. */ +non_null() void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object); typedef int filter_function_cb(const uint8_t *public_key, void *user_data); -/** Set the function used to check if a friend request should be displayed to the user or not. - * It must return 0 if the request is ok (anything else if it is bad.) +/** @brief Set the function used to check if a friend request should be displayed to the user or not. + * It must return 0 if the request is ok (anything else if it is bad). */ +non_null() void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata); /** Sets up friendreq packet handlers. */ +non_null() void friendreq_init(Friend_Requests *fr, Friend_Connections *fr_c); Friend_Requests *friendreq_new(void); + +nullable(1) void friendreq_kill(Friend_Requests *fr); #endif diff --git a/protocols/Tox/libtox/src/toxcore/group.c b/protocols/Tox/libtox/src/toxcore/group.c index fd7c491a1d..0e851b1e34 100644 --- a/protocols/Tox/libtox/src/toxcore/group.c +++ b/protocols/Tox/libtox/src/toxcore/group.c @@ -12,10 +12,153 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "mono_time.h" #include "state.h" #include "util.h" +enum { + /** Connection is to one of the closest DESIRED_CLOSEST peers */ + GROUPCHAT_CONNECTION_REASON_CLOSEST = 1 << 0, + + /** Connection is to a peer we are introducing to the conference */ + GROUPCHAT_CONNECTION_REASON_INTRODUCING = 1 << 1, + + /** Connection is to a peer who is introducing us to the conference */ + GROUPCHAT_CONNECTION_REASON_INTRODUCER = 1 << 2, +}; + +typedef enum Groupchat_Connection_Type { + GROUPCHAT_CONNECTION_NONE, + GROUPCHAT_CONNECTION_CONNECTING, + GROUPCHAT_CONNECTION_ONLINE, +} Groupchat_Connection_Type; + +typedef enum Groupchat_Status { + GROUPCHAT_STATUS_NONE, + GROUPCHAT_STATUS_VALID, + GROUPCHAT_STATUS_CONNECTED, +} Groupchat_Status; + +#define GROUP_ID_LENGTH CRYPTO_SYMMETRIC_KEY_SIZE + +#define DESIRED_CLOSEST 4 +#define MAX_GROUP_CONNECTIONS 16 +#define MAX_LAST_MESSAGE_INFOS 8 +#define MAX_LOSSY_COUNT 256 + +/** Maximum number of frozen peers to store; `group_set_max_frozen()` overrides. */ +#define MAX_FROZEN_DEFAULT 128 + +typedef struct Message_Info { + uint32_t message_number; + uint8_t message_id; +} Message_Info; + +typedef struct Group_Peer { + uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; + bool temp_pk_updated; + bool is_friend; + + uint64_t last_active; + + Message_Info + last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */ + uint8_t num_last_message_infos; + + uint8_t nick[MAX_NAME_LENGTH]; + uint8_t nick_len; + bool nick_updated; + + uint16_t peer_number; + + uint8_t recv_lossy[MAX_LOSSY_COUNT]; + uint16_t bottom_lossy_number; + uint16_t top_lossy_number; + + void *object; +} Group_Peer; + +typedef struct Groupchat_Connection { + uint8_t type; /* `GROUPCHAT_CONNECTION_*` */ + uint8_t reasons; /* bit field with flags `GROUPCHAT_CONNECTION_REASON_*` */ + uint32_t number; + uint16_t group_number; +} Groupchat_Connection; + +typedef struct Groupchat_Closest { + /** + * Whether this peer is active in the closest_peers array. + */ + bool active; + uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; +} Groupchat_Closest; + +typedef struct Group_c { + uint8_t status; + + bool need_send_name; + bool title_fresh; + + Group_Peer *group; + uint32_t numpeers; + + Group_Peer *frozen; + uint32_t numfrozen; + + uint32_t maxfrozen; + + Groupchat_Connection connections[MAX_GROUP_CONNECTIONS]; + + uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; + Groupchat_Closest closest_peers[DESIRED_CLOSEST]; + uint8_t changed; + + uint8_t type; + uint8_t id[GROUP_ID_LENGTH]; + + uint8_t title[MAX_NAME_LENGTH]; + uint8_t title_len; + + uint32_t message_number; + uint16_t lossy_message_number; + uint16_t peer_number; + + uint64_t last_sent_ping; + + uint32_t num_introducer_connections; + + void *object; + + peer_on_join_cb *peer_on_join; + peer_on_leave_cb *peer_on_leave; + group_on_delete_cb *group_on_delete; +} Group_c; + +struct Group_Chats { + const Mono_Time *mono_time; + + Messenger *m; + Friend_Connections *fr_c; + + Group_c *chats; + uint16_t num_chats; + + g_conference_invite_cb *invite_callback; + g_conference_connected_cb *connected_callback; + g_conference_message_cb *message_callback; + peer_name_cb *peer_name_callback; + peer_list_changed_cb *peer_list_changed_callback; + title_cb *title_callback; + + lossy_packet_cb *lossy_packethandlers[256]; +}; + +static const Group_c empty_group_c = {0}; +static const Group_Peer empty_group_peer = {{0}}; + /** * Packet type IDs as per the protocol specification. */ @@ -57,14 +200,34 @@ typedef enum Peer_Id { static_assert(GROUP_ID_LENGTH == CRYPTO_PUBLIC_KEY_SIZE, "GROUP_ID_LENGTH should be equal to CRYPTO_PUBLIC_KEY_SIZE"); +const Mono_Time *g_mono_time(const Group_Chats *g_c) +{ + return g_c->mono_time; +} + +non_null() static bool group_id_eq(const uint8_t *a, const uint8_t *b) { - return public_key_cmp(a, b) == 0; + return pk_equal(a, b); } -/** return false if the groupnumber is not valid. - * return true if the groupnumber is valid. +non_null() +static bool g_title_eq(Group_c *g, const uint8_t *title, uint8_t title_len) +{ + return memeq(g->title, g->title_len, title, title_len); +} + +non_null() +static bool g_peer_nick_eq(Group_Peer *peer, const uint8_t *nick, uint8_t nick_len) +{ + return memeq(peer->nick, peer->nick_len, nick, nick_len); +} + +/** + * @retval false if the groupnumber is not valid. + * @retval true if the groupnumber is valid. */ +non_null() static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber) { return groupnumber < g_c->num_chats @@ -73,11 +236,12 @@ static bool is_groupnumber_valid(const Group_Chats *g_c, uint32_t groupnumber) } -/** Set the size of the groupchat list to num. +/** @brief Set the size of the groupchat list to num. * - * return false if realloc fails. - * return true if it succeeds. + * @retval false if realloc fails. + * @retval true if it succeeds. */ +non_null() static bool realloc_conferences(Group_Chats *g_c, uint16_t num) { if (num == 0) { @@ -96,18 +260,19 @@ static bool realloc_conferences(Group_Chats *g_c, uint16_t num) return true; } +non_null() static void setup_conference(Group_c *g) { - memset(g, 0, sizeof(Group_c)); - + *g = empty_group_c; g->maxfrozen = MAX_FROZEN_DEFAULT; } -/** Create a new empty groupchat connection. +/** @brief Create a new empty groupchat connection. * - * return -1 on failure. - * return groupnumber on success. + * @retval -1 on failure. + * @return groupnumber on success. */ +non_null() static int32_t create_group_chat(Group_Chats *g_c) { for (uint16_t i = 0; i < g_c->num_chats; ++i) { @@ -117,7 +282,7 @@ static int32_t create_group_chat(Group_Chats *g_c) } if (realloc_conferences(g_c, g_c->num_chats + 1)) { - uint16_t id = g_c->num_chats; + const uint16_t id = g_c->num_chats; ++g_c->num_chats; setup_conference(&g_c->chats[id]); return id; @@ -126,19 +291,28 @@ static int32_t create_group_chat(Group_Chats *g_c) return -1; } +non_null() +static void wipe_group_c(Group_c *g) +{ + free(g->frozen); + free(g->group); + crypto_memzero(g, sizeof(Group_c)); +} -/** Wipe a groupchat. +/** @brief Wipe a groupchat. * - * return true on success. + * @retval true on success. */ +non_null() static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber) { - if (!is_groupnumber_valid(g_c, groupnumber)) { + if (groupnumber >= g_c->num_chats || g_c->chats == nullptr) { return false; } + wipe_group_c(&g_c->chats[groupnumber]); + uint16_t i; - crypto_memzero(&g_c->chats[groupnumber], sizeof(Group_c)); for (i = g_c->num_chats; i != 0; --i) { if (g_c->chats[i - 1].status != GROUPCHAT_STATUS_NONE) { @@ -154,6 +328,7 @@ static bool wipe_group_chat(Group_Chats *g_c, uint32_t groupnumber) return true; } +non_null() static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber) { if (!is_groupnumber_valid(g_c, groupnumber)) { @@ -166,15 +341,16 @@ static Group_c *get_group_c(const Group_Chats *g_c, uint32_t groupnumber) /** * check if peer with real_pk is in peer array. * - * return peer index if peer is in group. - * return -1 if peer is not in group. + * @return peer index if peer is in group. + * @retval -1 if peer is not in group. * * TODO(irungentoo): make this more efficient. */ +non_null() static int peer_in_group(const Group_c *g, const uint8_t *real_pk) { for (uint32_t i = 0; i < g->numpeers; ++i) { - if (id_equal(g->group[i].real_pk, real_pk)) { + if (pk_equal(g->group[i].real_pk, real_pk)) { return i; } } @@ -182,10 +358,11 @@ static int peer_in_group(const Group_c *g, const uint8_t *real_pk) return -1; } +non_null() static int frozen_in_group(const Group_c *g, const uint8_t *real_pk) { for (uint32_t i = 0; i < g->numfrozen; ++i) { - if (id_equal(g->frozen[i].real_pk, real_pk)) { + if (pk_equal(g->frozen[i].real_pk, real_pk)) { return i; } } @@ -196,11 +373,12 @@ static int frozen_in_group(const Group_c *g, const uint8_t *real_pk) /** * check if group with the given type and id is in group array. * - * return group number if peer is in list. - * return -1 if group is not in list. + * @return group number if peer is in list. + * @retval -1 if group is not in list. * * TODO(irungentoo): make this more efficient and maybe use constant time comparisons? */ +non_null() static int32_t get_group_num(const Group_Chats *g_c, const uint8_t type, const uint8_t *id) { for (uint16_t i = 0; i < g_c->num_chats; ++i) { @@ -226,11 +404,12 @@ int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id) /** * check if peer with peer_number is in peer array. * - * return peer index if peer is in chat. - * return -1 if peer is not in chat. + * @return peer index if peer is in chat. + * @retval -1 if peer is not in chat. * * TODO(irungentoo): make this more efficient. */ +non_null() static int get_peer_index(const Group_c *g, uint16_t peer_number) { for (uint32_t i = 0; i < g->numpeers; ++i) { @@ -243,6 +422,7 @@ static int get_peer_index(const Group_c *g, uint16_t peer_number) } +non_null() static uint64_t calculate_comp_value(const uint8_t *pk1, const uint8_t *pk2) { uint64_t cmp1 = 0; @@ -262,22 +442,23 @@ typedef enum Groupchat_Closest_Change { GROUPCHAT_CLOSEST_CHANGE_REMOVED, } Groupchat_Closest_Change; +non_null() static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *temp_pk) { - if (public_key_cmp(g->real_pk, real_pk) == 0) { + if (pk_equal(g->real_pk, real_pk)) { return false; } unsigned int index = DESIRED_CLOSEST; for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { - if (g->closest_peers[i].entry && public_key_cmp(real_pk, g->closest_peers[i].real_pk) == 0) { + if (g->closest_peers[i].active && pk_equal(real_pk, g->closest_peers[i].real_pk)) { return true; } } for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { - if (g->closest_peers[i].entry == 0) { + if (!g->closest_peers[i].active) { index = i; break; } @@ -288,7 +469,7 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te uint64_t comp_d = 0; for (unsigned int i = 0; i < (DESIRED_CLOSEST / 2); ++i) { - uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk); + const uint64_t comp = calculate_comp_value(g->real_pk, g->closest_peers[i].real_pk); if (comp > comp_val && comp > comp_d) { index = i; @@ -314,15 +495,15 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te uint8_t old_real_pk[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t old_temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t old = 0; + bool old = false; - if (g->closest_peers[index].entry) { + if (g->closest_peers[index].active) { memcpy(old_real_pk, g->closest_peers[index].real_pk, CRYPTO_PUBLIC_KEY_SIZE); memcpy(old_temp_pk, g->closest_peers[index].temp_pk, CRYPTO_PUBLIC_KEY_SIZE); - old = 1; + old = true; } - g->closest_peers[index].entry = 1; + g->closest_peers[index].active = true; memcpy(g->closest_peers[index].real_pk, real_pk, CRYPTO_PUBLIC_KEY_SIZE); memcpy(g->closest_peers[index].temp_pk, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); @@ -330,21 +511,22 @@ static bool add_to_closest(Group_c *g, const uint8_t *real_pk, const uint8_t *te add_to_closest(g, old_real_pk, old_temp_pk); } - if (!g->changed) { + if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) { g->changed = GROUPCHAT_CLOSEST_CHANGE_ADDED; } return true; } +non_null() static bool pk_in_closest_peers(const Group_c *g, const uint8_t *real_pk) { for (unsigned int i = 0; i < DESIRED_CLOSEST; ++i) { - if (!g->closest_peers[i].entry) { + if (!g->closest_peers[i].active) { continue; } - if (public_key_cmp(g->closest_peers[i].real_pk, real_pk) == 0) { + if (pk_equal(g->closest_peers[i].real_pk, real_pk)) { return true; } } @@ -352,13 +534,15 @@ static bool pk_in_closest_peers(const Group_c *g, const uint8_t *real_pk) return false; } +non_null() static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason); +non_null() static void purge_closest(Group_Chats *g_c, uint32_t groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -367,7 +551,7 @@ static void purge_closest(Group_Chats *g_c, uint32_t groupnumber) continue; } - if (!(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST)) { + if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) == 0) { continue; } @@ -380,32 +564,35 @@ static void purge_closest(Group_Chats *g_c, uint32_t groupnumber) } } -static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, - uint8_t type, const uint8_t *id); +non_null() +static bool send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, + uint8_t type, const uint8_t *id); +non_null() static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason, - uint8_t lock); + bool lock); +non_null(1) nullable(3) static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) { - if (!g->closest_peers[i].entry) { + if (!g->closest_peers[i].active) { continue; } int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->closest_peers[i].real_pk); - uint8_t fresh = 0; + bool fresh = false; if (friendcon_id == -1) { friendcon_id = new_friend_connection(g_c->fr_c, g->closest_peers[i].real_pk); - fresh = 1; + fresh = true; if (friendcon_id == -1) { continue; @@ -432,15 +619,16 @@ static void add_closest_connections(Group_Chats *g_c, uint32_t groupnumber, void } } +non_null(1) nullable(3) static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } - if (!g->changed) { + if (g->changed == GROUPCHAT_CLOSEST_CHANGE_NONE) { return true; } @@ -459,6 +647,7 @@ static bool connect_to_closest(Group_Chats *g_c, uint32_t groupnumber, void *use return true; } +non_null() static int get_frozen_index(const Group_c *g, uint16_t peer_number) { for (uint32_t i = 0; i < g->numfrozen; ++i) { @@ -470,6 +659,7 @@ static int get_frozen_index(const Group_c *g, uint16_t peer_number) return -1; } +non_null() static bool delete_frozen(Group_c *g, uint32_t frozen_index) { if (frozen_index >= g->numfrozen) { @@ -498,16 +688,17 @@ static bool delete_frozen(Group_c *g, uint32_t frozen_index) return true; } -/** Update last_active timestamp on peer, and thaw the peer if it is frozen. +/** @brief Update last_active timestamp on peer, and thaw the peer if it is frozen. * - * return peer index if peer is in the conference. - * return -1 otherwise, and on error. + * @return peer index if peer is in the conference. + * @retval -1 otherwise, and on error. */ +non_null(1) nullable(4) static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_number, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -545,11 +736,11 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee delete_frozen(g, frozen_index); - if (g_c->peer_list_changed_callback) { + if (g_c->peer_list_changed_callback != nullptr) { g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata); } - if (g->peer_on_join) { + if (g->peer_on_join != nullptr) { g->peer_on_join(g->object, groupnumber, thawed_index); } @@ -558,13 +749,15 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee return thawed_index; } +non_null(1) nullable(4) static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata); +non_null(1, 3) nullable(4) static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -581,24 +774,25 @@ static void delete_any_peer_with_pk(Group_Chats *g_c, uint32_t groupnumber, cons } } -/** Add a peer to the group chat, or update an existing peer. +/** @brief Add a peer to the group chat, or update an existing peer. * * fresh indicates whether we should consider this information on the peer to * be current, and so should update temp_pk and consider the peer active. * * do_gc_callback indicates whether we want to trigger callbacks set by the client * via the public API. This should be set to false if this function is called - * from outside of the tox_iterate() loop. + * from outside of the `tox_iterate()` loop. * - * return peer_index if success or peer already in chat. - * return -1 if error. + * @return peer_index if success or peer already in chat. + * @retval -1 if error. */ +non_null(1, 3, 4) nullable(6) static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_pk, const uint8_t *temp_pk, uint16_t peer_number, void *userdata, bool fresh, bool do_gc_callback) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -607,12 +801,12 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p get_peer_index(g, peer_number); if (peer_index != -1) { - if (!id_equal(g->group[peer_index].real_pk, real_pk)) { + if (!pk_equal(g->group[peer_index].real_pk, real_pk)) { return -1; } if (fresh || !g->group[peer_index].temp_pk_updated) { - id_copy(g->group[peer_index].temp_pk, temp_pk); + pk_copy(g->group[peer_index].temp_pk, temp_pk); g->group[peer_index].temp_pk_updated = true; } @@ -623,11 +817,11 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p const int frozen_index = get_frozen_index(g, peer_number); if (frozen_index != -1) { - if (!id_equal(g->frozen[frozen_index].real_pk, real_pk)) { + if (!pk_equal(g->frozen[frozen_index].real_pk, real_pk)) { return -1; } - id_copy(g->frozen[frozen_index].temp_pk, temp_pk); + pk_copy(g->frozen[frozen_index].temp_pk, temp_pk); return -1; } @@ -641,35 +835,36 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p return -1; } - memset(&temp[g->numpeers], 0, sizeof(Group_Peer)); + temp[g->numpeers] = empty_group_peer; g->group = temp; const uint32_t new_index = g->numpeers; - id_copy(g->group[new_index].real_pk, real_pk); - id_copy(g->group[new_index].temp_pk, temp_pk); + pk_copy(g->group[new_index].real_pk, real_pk); + pk_copy(g->group[new_index].temp_pk, temp_pk); g->group[new_index].temp_pk_updated = true; g->group[new_index].peer_number = peer_number; g->group[new_index].last_active = mono_time_get(g_c->mono_time); - g->group[new_index].is_friend = (getfriend_id(g_c->m, real_pk) != -1); + g->group[new_index].is_friend = getfriend_id(g_c->m, real_pk) != -1; ++g->numpeers; add_to_closest(g, real_pk, temp_pk); - if (do_gc_callback && g_c->peer_list_changed_callback) { + if (do_gc_callback && g_c->peer_list_changed_callback != nullptr) { g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata); } - if (g->peer_on_join) { + if (g->peer_on_join != nullptr) { g->peer_on_join(g->object, groupnumber, new_index); } return new_index; } +non_null() static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i) { - if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) { + if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) { --g->num_introducer_connections; } @@ -677,12 +872,13 @@ static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i) g->connections[i].type = GROUPCHAT_CONNECTION_NONE; } +non_null() static void remove_from_closest(Group_c *g, int peer_index) { for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) { - if (g->closest_peers[i].entry - && id_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) { - g->closest_peers[i].entry = 0; + if (g->closest_peers[i].active + && pk_equal(g->closest_peers[i].real_pk, g->group[peer_index].real_pk)) { + g->closest_peers[i].active = false; g->changed = GROUPCHAT_CLOSEST_CHANGE_REMOVED; break; } @@ -698,7 +894,7 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -740,11 +936,11 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void g->group = temp; } - if (g_c->peer_list_changed_callback) { + if (g_c->peer_list_changed_callback != nullptr) { g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata); } - if (g->peer_on_leave) { + if (g->peer_on_leave != nullptr) { g->peer_on_leave(g->object, groupnumber, peer_object); } @@ -753,10 +949,11 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void static int cmp_u64(uint64_t a, uint64_t b) { - return (a > b) - (a < b); + return (a > b ? 1 : 0) - (a < b ? 1 : 0); } /** Order peers with friends first and with more recently active earlier */ +non_null() static int cmp_frozen(const void *a, const void *b) { const Group_Peer *pa = (const Group_Peer *)a; @@ -769,10 +966,11 @@ static int cmp_frozen(const void *a, const void *b) return cmp_u64(pb->last_active, pa->last_active); } -/** Delete frozen peers as necessary to ensure at most g->maxfrozen remain. +/** @brief Delete frozen peers as necessary to ensure at most `g->maxfrozen` remain. * - * return true if any frozen peers are removed. + * @retval true if any frozen peers are removed. */ +non_null() static bool delete_old_frozen(Group_c *g) { if (g->numfrozen <= g->maxfrozen) { @@ -801,13 +999,15 @@ static bool delete_old_frozen(Group_c *g) return true; } +non_null() static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk); +non_null(1) nullable(4) static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -835,14 +1035,15 @@ static bool freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, } -/** Set the nick for a peer. +/** @brief Set the nick for a peer. * * do_gc_callback indicates whether we want to trigger callbacks set by the client * via the public API. This should be set to false if this function is called - * from outside of the tox_iterate() loop. + * from outside of the `tox_iterate()` loop. * - * return true on success. + * @retval true on success. */ +non_null(1, 4) nullable(6) static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len, void *userdata, bool do_gc_callback) { @@ -852,35 +1053,35 @@ static bool setnick(Group_Chats *g_c, uint32_t groupnumber, int peer_index, cons Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } g->group[peer_index].nick_updated = true; - if (g->group[peer_index].nick_len == nick_len - && (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))) { + if (g_peer_nick_eq(&g->group[peer_index], nick, nick_len)) { /* same name as already stored */ return true; } - if (nick_len) { + if (nick_len > 0) { memcpy(g->group[peer_index].nick, nick, nick_len); } g->group[peer_index].nick_len = nick_len; - if (do_gc_callback && g_c->peer_name_callback) { + if (do_gc_callback && g_c->peer_name_callback != nullptr) { g_c->peer_name_callback(g_c->m, groupnumber, peer_index, nick, nick_len, userdata); } return true; } -/** Set the title for a group. +/** @brief Set the title for a group. * - * return true on success. + * @retval true on success. */ +non_null(1, 4) nullable(6) static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, const uint8_t *title, uint8_t title_len, void *userdata) { @@ -890,11 +1091,11 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } - if (g->title_len == title_len && !memcmp(g->title, title, title_len)) { + if (g_title_eq(g, title, title_len)) { /* same title as already set */ return true; } @@ -904,7 +1105,7 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con g->title_fresh = true; - if (g_c->title_callback) { + if (g_c->title_callback != nullptr) { g_c->title_callback(g_c->m, groupnumber, peer_index, title, title_len, userdata); } @@ -912,11 +1113,12 @@ static bool settitle(Group_Chats *g_c, uint32_t groupnumber, int peer_index, con } /** Check if the group has no online connection, and freeze all peers if so */ +non_null(1) nullable(3) static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *userdata) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -927,18 +1129,19 @@ static void check_disconnected(Group_Chats *g_c, uint32_t groupnumber, void *use } for (uint32_t i = 0; i < g->numpeers; ++i) { - while (i < g->numpeers && !id_equal(g->group[i].real_pk, g->real_pk)) { + while (i < g->numpeers && !pk_equal(g->group[i].real_pk, g->real_pk)) { freeze_peer(g_c, groupnumber, i, userdata); } } } +non_null(1) nullable(5) static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, int friendcon_id, uint8_t type, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -961,6 +1164,7 @@ static void set_conns_type_connections(Group_Chats *g_c, uint32_t groupnumber, i } /** Set the type for all connections with friendcon_id */ +non_null(1) nullable(4) static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t type, void *userdata) { for (uint16_t i = 0; i < g_c->num_chats; ++i) { @@ -968,6 +1172,7 @@ static void set_conns_status_groups(Group_Chats *g_c, int friendcon_id, uint8_t } } +non_null() static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id) { uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; @@ -976,12 +1181,12 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id) for (uint16_t i = 0; i < g_c->num_chats; ++i) { Group_c *g = get_group_c(g_c, i); - if (!g) { + if (g == nullptr) { continue; } for (uint32_t j = 0; j < g->numfrozen; ++j) { - if (id_equal(g->frozen[j].real_pk, real_pk)) { + if (pk_equal(g->frozen[j].real_pk, real_pk)) { try_send_rejoin(g_c, g, real_pk); break; } @@ -989,7 +1194,8 @@ static void rejoin_frozen_friend(Group_Chats *g_c, int friendcon_id) } } -static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, void *userdata) +non_null(1) nullable(4) +static int g_handle_any_status(void *object, int friendcon_id, bool status, void *userdata) { Group_Chats *g_c = (Group_Chats *)object; @@ -1000,7 +1206,8 @@ static int g_handle_any_status(void *object, int friendcon_id, uint8_t status, v return 0; } -static int g_handle_status(void *object, int friendcon_id, uint8_t status, void *userdata) +non_null(1) nullable(4) +static int g_handle_status(void *object, int friendcon_id, bool status, void *userdata) { Group_Chats *g_c = (Group_Chats *)object; @@ -1014,16 +1221,18 @@ static int g_handle_status(void *object, int friendcon_id, uint8_t status, void return 0; } +non_null(1, 3) nullable(5) static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata); +non_null(1, 3) nullable(5) static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata); -/** Add friend to group chat. +/** @brief Add friend to group chat. * - * return connections index on success - * return -1 on failure. + * @return connections index on success + * @retval -1 on failure. */ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, uint8_t reason, - uint8_t lock) + bool lock) { uint16_t empty = MAX_GROUP_CONNECTIONS; uint16_t ind = MAX_GROUP_CONNECTIONS; @@ -1058,7 +1267,7 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, ind = empty; } - if (!(g->connections[ind].reasons & reason)) { + if ((g->connections[ind].reasons & reason) == 0) { g->connections[ind].reasons |= reason; if (reason == GROUPCHAT_CONNECTION_REASON_INTRODUCER) { @@ -1069,15 +1278,16 @@ static int add_conn_to_groupchat(Group_Chats *g_c, int friendcon_id, Group_c *g, return ind; } -static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num); +non_null() +static bool send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num); -/** Removes reason for keeping connection. +/** @brief Removes reason for keeping connection. * * Kills connection if this was the last reason. */ static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, uint8_t reason) { - if (!(g->connections[i].reasons & reason)) { + if ((g->connections[i].reasons & reason) == 0) { return; } @@ -1097,14 +1307,15 @@ static void remove_connection_reason(Group_Chats *g_c, Group_c *g, uint16_t i, u } } -/** Creates a new groupchat and puts it in the chats array. +/** @brief Creates a new groupchat and puts it in the chats array. * - * type is one of `GROUPCHAT_TYPE_*` + * @param rng Random number generator used for generating the group ID. + * @param type is one of `GROUPCHAT_TYPE_*` * - * return group number on success. - * return -1 on failure. + * @return group number on success. + * @retval -1 on failure. */ -int add_groupchat(Group_Chats *g_c, uint8_t type) +int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type) { const int32_t groupnumber = create_group_chat(g_c); @@ -1116,7 +1327,7 @@ int add_groupchat(Group_Chats *g_c, uint8_t type) g->status = GROUPCHAT_STATUS_CONNECTED; g->type = type; - new_symmetric_key(g->id); + new_symmetric_key(rng, g->id); g->peer_number = 0; /* Founder is peer 0. */ memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), 0, nullptr, true, @@ -1131,20 +1342,21 @@ int add_groupchat(Group_Chats *g_c, uint8_t type) return groupnumber; } +non_null() static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent); -/** Delete a groupchat from the chats array, informing the group first as +/** @brief Delete a groupchat from the chats array, informing the group first as * appropriate. * - * return 0 on success. - * return -1 if groupnumber is invalid. + * @retval true on success. + * @retval false if groupnumber is invalid. */ -int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently) +bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { - return -1; + if (g == nullptr) { + return false; } group_leave(g_c, groupnumber, leave_permanently); @@ -1159,21 +1371,19 @@ int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently } for (uint32_t i = 0; i < g->numpeers; ++i) { - if (g->peer_on_leave) { + if (g->peer_on_leave != nullptr) { g->peer_on_leave(g->object, groupnumber, g->group[i].object); } } - free(g->group); - free(g->frozen); - - if (g->group_on_delete) { + if (g->group_on_delete != nullptr) { g->group_on_delete(g->object, groupnumber); } return wipe_group_chat(g_c, groupnumber); } +non_null() static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, bool frozen) { const Group_Peer *list = frozen ? g->frozen : g->group; @@ -1187,18 +1397,21 @@ static const Group_Peer *peer_in_list(const Group_c *g, uint32_t peernumber, boo } -/** Copy the public key of (frozen, if frozen is true) peernumber who is in - * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long. +/** + * @brief Copy the public key of (frozen, if frozen is true) peernumber who is in + * groupnumber to pk. + * + * @param pk must be CRYPTO_PUBLIC_KEY_SIZE long. * - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval 0 on success + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1213,16 +1426,16 @@ int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t pee } /** - * Return the size of (frozen, if frozen is true) peernumber's name. + * @brief Return the size of (frozen, if frozen is true) peernumber's name. * - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1235,18 +1448,21 @@ int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t p return peer->nick_len; } -/** Copy the name of (frozen, if frozen is true) peernumber who is in - * groupnumber to name. name must be at least MAX_NAME_LENGTH long. +/** + * @brief Copy the name of (frozen, if frozen is true) peernumber who is in + * groupnumber to name. + * + * @param name must be at least MAX_NAME_LENGTH long. * - * return length of name if success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @return length of name if success + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1263,19 +1479,20 @@ int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernu return peer->nick_len; } -/** Copy last active timestamp of frozen peernumber who is in groupnumber to - * last_active. +/** + * @brief Copy last active timestamp of frozen peernumber who is in groupnumber to + * last_active. * - * return 0 on success. - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint64_t *last_active) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1287,16 +1504,16 @@ int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint3 return 0; } -/** Set maximum number of frozen peers. +/** @brief Set maximum number of frozen peers. * - * return 0 on success. - * return -1 if groupnumber is invalid. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. */ int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1305,32 +1522,33 @@ int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t return 0; } -/** Return the number of (frozen, if frozen is true) peers in the group chat on - * success. - * return -1 if groupnumber is invalid. +/** + * @return the number of (frozen, if frozen is true) peers in the group chat on success. + * @retval -1 if groupnumber is invalid. */ int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } return frozen ? g->numfrozen : g->numpeers; } -/** return 1 if the peernumber corresponds to ours. - * return 0 if the peernumber is not ours. - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. - * return -3 if we are not connected to the group chat. +/** + * @retval 1 if the peernumber corresponds to ours. + * @retval 0 if the peernumber is not ours. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. + * @retval -3 if we are not connected to the group chat. */ int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1342,35 +1560,35 @@ int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint3 return -3; } - return g->peer_number == g->group[peernumber].peer_number; + return (g->peer_number == g->group[peernumber].peer_number) ? 1 : 0; } -/** return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is. +/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is. * - * return -1 on failure. - * return type on success. + * @retval -1 on failure. + * @return type on success. */ int group_get_type(const Group_Chats *g_c, uint32_t groupnumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } return g->type; } -/** Copies the unique id of `group_chat[groupnumber]` into `id`. +/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`. * - * return false on failure. - * return true on success. + * @retval false on failure. + * @retval true on success. */ bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -1381,16 +1599,17 @@ bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id return true; } -/** Send a group packet to friendcon_id. +/** @brief Send a group packet to friendcon_id. * - * return 1 on success - * return 0 on failure + * @retval true on success + * @retval false on failure */ -static unsigned int send_packet_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, - uint16_t group_num, const uint8_t *data, uint16_t length) +non_null() +static bool send_packet_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, + uint16_t group_num, const uint8_t *data, uint16_t length) { if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) { - return 0; + return false; } group_num = net_htons(group_num); @@ -1399,19 +1618,20 @@ static unsigned int send_packet_group_peer(const Friend_Connections *fr_c, int f memcpy(packet + 1, &group_num, sizeof(uint16_t)); memcpy(packet + 1 + sizeof(uint16_t), data, length); return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, - SIZEOF_VLA(packet), 0) != -1; + SIZEOF_VLA(packet), false) != -1; } -/** Send a group lossy packet to friendcon_id. +/** @brief Send a group lossy packet to friendcon_id. * - * return 1 on success - * return 0 on failure + * @retval true on success + * @retval false on failure */ -static unsigned int send_lossy_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, - uint16_t group_num, const uint8_t *data, uint16_t length) +non_null() +static bool send_lossy_group_peer(const Friend_Connections *fr_c, int friendcon_id, uint8_t packet_id, + uint16_t group_num, const uint8_t *data, uint16_t length) { if (1 + sizeof(uint16_t) + length > MAX_CRYPTO_DATA_SIZE) { - return 0; + return false; } group_num = net_htons(group_num); @@ -1423,18 +1643,18 @@ static unsigned int send_lossy_group_peer(const Friend_Connections *fr_c, int fr packet, SIZEOF_VLA(packet)) != -1; } -/** invite friendnumber to groupnumber. +/** @brief invite friendnumber to groupnumber. * - * return 0 on success. - * return -1 if groupnumber is invalid. - * return -2 if invite packet failed to send. - * return -3 if we are not connected to the group chat. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if invite packet failed to send. + * @retval -3 if we are not connected to the group chat. */ int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1456,9 +1676,9 @@ int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupn return -2; } -/** Send a rejoin packet to a peer if we have a friend connection to the peer. - * return true if a packet was sent. - * return false otherwise. +/** @brief Send a rejoin packet to a peer if we have a friend connection to the peer. + * @retval true if a packet was sent. + * @retval false otherwise. */ static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk) { @@ -1474,32 +1694,34 @@ static bool try_send_rejoin(Group_Chats *g_c, Group_c *g, const uint8_t *real_pk memcpy(packet + 2, g->id, GROUP_ID_LENGTH); if (write_cryptpacket(friendconn_net_crypto(g_c->fr_c), friend_connection_crypt_connection_id(g_c->fr_c, friendcon_id), - packet, sizeof(packet), 0) == -1) { + packet, sizeof(packet), false) == -1) { return false; } - add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1); + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true); return true; } -static unsigned int send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num); +non_null() +static bool send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num); +non_null() static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t friendnumber, const uint8_t *data, uint16_t length); -/** Join a group (we need to have been invited first.) +/** @brief Join a group (we need to have been invited first). * - * expected_type is the groupchat type we expect the chat we are joining to - * have. + * @param expected_type is the groupchat type we expect the chat we are joining + * to have. * - * return group number on success. - * return -1 if data length is invalid. - * return -2 if group is not the expected type. - * return -3 if friendnumber is invalid. - * return -4 if client is already in this group. - * return -5 if group instance failed to initialize. - * return -6 if join packet fails to send. + * @return group number on success. + * @retval -1 if data length is invalid. + * @retval -2 if group is not the expected type. + * @retval -3 if friendnumber is invalid. + * @retval -4 if client is already in this group. + * @retval -5 if group instance failed to initialize. + * @retval -6 if join packet fails to send. */ int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length) { @@ -1578,10 +1800,10 @@ static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t fri return false; } - const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 1); + const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, true); if (member) { - add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, 0); + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCING, false); } if (connection_index != -1) { @@ -1597,7 +1819,7 @@ static bool send_invite_response(Group_Chats *g_c, int groupnumber, uint32_t fri /** Set handlers for custom lossy packets. */ void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function) { - g_c->lossy_packethandlers[byte].function = function; + g_c->lossy_packethandlers[byte] = function; } /** Set the callback for group invites. */ @@ -1618,7 +1840,7 @@ void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *functio g_c->message_callback = function; } -/** Set callback function for peer nickname changes. +/** @brief Set callback function for peer nickname changes. * * It gets called every time a peer changes their nickname. */ @@ -1627,7 +1849,7 @@ void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function) g_c->peer_name_callback = function; } -/** Set callback function for peer list changes. +/** @brief Set callback function for peer list changes. * * It gets called every time the name list changes(new peer, deleted peer) */ @@ -1642,16 +1864,16 @@ void g_callback_group_title(Group_Chats *g_c, title_cb *function) g_c->title_callback = function; } -/** Set a function to be called when a new peer joins a group chat. +/** @brief Set a function to be called when a new peer joins a group chat. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1659,16 +1881,16 @@ int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, pe return 0; } -/** Set a function to be called when a peer leaves a group chat. +/** @brief Set a function to be called when a peer leaves a group chat. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1676,16 +1898,16 @@ int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, return 0; } -/** Set a function to be called when the group chat is deleted. +/** @brief Set a function to be called when the group chat is deleted. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1693,24 +1915,23 @@ int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, grou return 0; } +non_null(1) nullable(4) static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data, uint16_t len); -/** send a ping message +/** @brief send a ping message * return true on success */ +non_null() static bool group_ping_send(const Group_Chats *g_c, uint32_t groupnumber) { - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0) { - return true; - } - - return false; + return send_message_group(g_c, groupnumber, GROUP_MESSAGE_PING_ID, nullptr, 0) > 0; } -/** send a new_peer message +/** @brief send a new_peer message * return true on success */ +non_null() static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num, const uint8_t *real_pk, const uint8_t *temp_pk) { @@ -1721,16 +1942,13 @@ static bool group_new_peer_send(const Group_Chats *g_c, uint32_t groupnumber, ui memcpy(packet + sizeof(uint16_t), real_pk, CRYPTO_PUBLIC_KEY_SIZE); memcpy(packet + sizeof(uint16_t) + CRYPTO_PUBLIC_KEY_SIZE, temp_pk, CRYPTO_PUBLIC_KEY_SIZE); - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0) { - return true; - } - - return false; + return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NEW_PEER_ID, packet, sizeof(packet)) > 0; } -/** send a kill_peer message +/** @brief send a kill_peer message * return true on success */ +non_null() static bool group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num) { uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH]; @@ -1738,16 +1956,13 @@ static bool group_kill_peer_send(const Group_Chats *g_c, uint32_t groupnumber, u peer_num = net_htons(peer_num); memcpy(packet, &peer_num, sizeof(uint16_t)); - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0) { - return true; - } - - return false; + return send_message_group(g_c, groupnumber, GROUP_MESSAGE_KILL_PEER_ID, packet, sizeof(packet)) > 0; } -/** send a freeze_peer message +/** @brief send a freeze_peer message * return true on success */ +non_null() static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_num) { uint8_t packet[GROUP_MESSAGE_KILL_PEER_LENGTH]; @@ -1755,37 +1970,30 @@ static bool group_freeze_peer_send(const Group_Chats *g_c, uint32_t groupnumber, peer_num = net_htons(peer_num); memcpy(packet, &peer_num, sizeof(uint16_t)); - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0) { - return true; - } - - return false; + return send_message_group(g_c, groupnumber, GROUP_MESSAGE_FREEZE_PEER_ID, packet, sizeof(packet)) > 0; } -/** send a name message +/** @brief send a name message * return true on success */ +non_null() static bool group_name_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *nick, uint16_t nick_len) { if (nick_len > MAX_NAME_LENGTH) { return false; } - if (send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0) { - return true; - } - - return false; + return send_message_group(g_c, groupnumber, GROUP_MESSAGE_NAME_ID, nick, nick_len) > 0; } -/** send message to announce leaving group +/** @brief send message to announce leaving group * return true on success */ static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool permanent) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -1797,17 +2005,17 @@ static bool group_leave(const Group_Chats *g_c, uint32_t groupnumber, bool perma } -/** set the group's title, limited to MAX_NAME_LENGTH - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. - * return -3 if packet fails to send. +/** @brief set the group's title, limited to MAX_NAME_LENGTH. + * @retval 0 on success + * @retval -1 if groupnumber is invalid. + * @retval -2 if title is too long or empty. + * @retval -3 if packet fails to send. */ int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1816,7 +2024,7 @@ int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t } /* same as already set? */ - if (g->title_len == title_len && !memcmp(g->title, title, title_len)) { + if (g_title_eq(g, title, title_len)) { return 0; } @@ -1834,15 +2042,15 @@ int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t return -3; } -/** return the group's title size. - * return -1 of groupnumber is invalid. - * return -2 if title is too long or empty. +/** @brief return the group's title size. + * @retval -1 of groupnumber is invalid. + * @retval -2 if title is too long or empty. */ int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1853,18 +2061,19 @@ int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber) return g->title_len; } -/** Get group title from groupnumber and put it in title. +/** @brief Get group title from groupnumber and put it in title. + * * Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. * - * return length of copied title if success. - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. + * @return length of copied title if success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if title is too long or empty. */ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -1876,6 +2085,7 @@ int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title return g->title_len; } +non_null() static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t *peer_number) { const int peer_index = peer_in_group(g, real_pk); @@ -1895,6 +2105,7 @@ static bool get_peer_number(const Group_c *g, const uint8_t *real_pk, uint16_t * return false; } +non_null(1, 3) nullable(5) static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *userdata) { @@ -1904,9 +2115,6 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con return; } - const uint8_t *invite_data = data + 1; - const uint16_t invite_length = length - 1; - switch (data[0]) { case INVITE_ID: { if (length != INVITE_PACKET_SIZE) { @@ -1915,8 +2123,11 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con const int groupnumber = get_group_num(g_c, data[1 + sizeof(uint16_t)], data + 1 + sizeof(uint16_t) + 1); + const uint8_t *invite_data = data + 1; + const uint16_t invite_length = length - 1; + if (groupnumber == -1) { - if (g_c->invite_callback) { + if (g_c->invite_callback != nullptr) { g_c->invite_callback(m, friendnumber, invite_data[sizeof(uint16_t)], invite_data, invite_length, userdata); } @@ -1924,7 +2135,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con } else { const Group_c *g = get_group_c(g_c, groupnumber); - if (g && g->status == GROUPCHAT_STATUS_CONNECTED) { + if (g != nullptr && g->status == GROUPCHAT_STATUS_CONNECTED) { send_invite_response(g_c, groupnumber, friendnumber, invite_data, invite_length); } } @@ -1947,7 +2158,7 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con Group_c *g = get_group_c(g_c, groupnum); - if (!g) { + if (g == nullptr) { return; } @@ -1967,12 +2178,12 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con /* TODO(irungentoo): what if two people enter the group at the * same time and are given the same peer_number by different * nodes? */ - peer_number = random_u16(); + peer_number = random_u16(m->rng); unsigned int tries = 0; while (get_peer_index(g, peer_number) != -1 || get_frozen_index(g, peer_number) != -1) { - peer_number = random_u16(); + peer_number = random_u16(m->rng); ++tries; if (tries > 32) { @@ -1994,10 +2205,10 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true); const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, - GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1); + GROUPCHAT_CONNECTION_REASON_INTRODUCING, true); if (member) { - add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, 0); + add_conn_to_groupchat(g_c, friendcon_id, g, GROUPCHAT_CONNECTION_REASON_INTRODUCER, false); send_peer_query(g_c, friendcon_id, other_groupnum); } @@ -2017,11 +2228,12 @@ static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, con } } -/** Find index of friend in the connections list. +/** @brief Find index of friend in the connections list. * * return index on success * return -1 on failure. */ +non_null() static int friend_in_connections(const Group_c *g, int friendcon_id) { for (unsigned int i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { @@ -2037,8 +2249,8 @@ static int friend_in_connections(const Group_c *g, int friendcon_id) return -1; } -/** return number of connections. - */ +/** return number of connections. */ +non_null() static unsigned int count_connected(const Group_c *g) { unsigned int count = 0; @@ -2052,8 +2264,8 @@ static unsigned int count_connected(const Group_c *g) return count; } -static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, - uint8_t type, const uint8_t *id) +static bool send_packet_online(const Friend_Connections *fr_c, int friendcon_id, uint16_t group_num, + uint8_t type, const uint8_t *id) { uint8_t packet[1 + ONLINE_PACKET_DATA_SIZE]; group_num = net_htons(group_num); @@ -2062,11 +2274,13 @@ static int send_packet_online(const Friend_Connections *fr_c, int friendcon_id, packet[1 + sizeof(uint16_t)] = type; memcpy(packet + 1 + sizeof(uint16_t) + 1, id, GROUP_ID_LENGTH); return write_cryptpacket(friendconn_net_crypto(fr_c), friend_connection_crypt_connection_id(fr_c, friendcon_id), packet, - sizeof(packet), 0) != -1; + sizeof(packet), false) != -1; } +non_null() static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber); +non_null() static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length) { if (length != ONLINE_PACKET_DATA_SIZE) { @@ -2085,7 +2299,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2099,7 +2313,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const return -1; } - if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER)) { + if (count_connected(g) == 0 || (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) != 0) { send_peer_query(g_c, friendcon_id, other_groupnum); } @@ -2107,7 +2321,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const g->connections[index].type = GROUPCHAT_CONNECTION_ONLINE; send_packet_online(g_c->fr_c, friendcon_id, groupnumber, g->type, g->id); - if (g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) { + if ((g->connections[index].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCING) != 0) { uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; get_friendcon_public_keys(real_pk, temp_pk, g_c->fr_c, friendcon_id); @@ -2126,6 +2340,7 @@ static int handle_packet_online(const Group_Chats *g_c, int friendcon_id, const return 0; } +non_null(1, 3) nullable(5) static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata) { @@ -2137,7 +2352,7 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_ Group_c *g = get_group_c(g_c, groupnum); - if (!g) { + if (g == nullptr) { return -1; } @@ -2153,7 +2368,7 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_ addpeer(g_c, groupnum, real_pk, temp_pk, peer_number, userdata, true, true); const int connection_index = add_conn_to_groupchat(g_c, friendcon_id, g, - GROUPCHAT_CONNECTION_REASON_INTRODUCING, 1); + GROUPCHAT_CONNECTION_REASON_INTRODUCING, true); if (connection_index != -1) { send_packet_online(g_c->fr_c, friendcon_id, groupnum, g->type, g->id); @@ -2165,10 +2380,11 @@ static int handle_packet_rejoin(Group_Chats *g_c, int friendcon_id, const uint8_ // we could send title with invite, but then if it changes between sending and accepting inv, joinee won't see it -/** return 1 on success. - * return 0 on failure +/** + * @retval true on success. + * @retval false on failure */ -static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num) +static bool send_peer_introduced(const Group_Chats *g_c, int friendcon_id, uint16_t group_num) { uint8_t packet[1]; packet[0] = PEER_INTRODUCED_ID; @@ -2176,19 +2392,22 @@ static unsigned int send_peer_introduced(const Group_Chats *g_c, int friendcon_i } -/** return 1 on success. - * return 0 on failure +/** + * @retval true on success. + * @retval false on failure */ -static unsigned int send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num) +static bool send_peer_query(const Group_Chats *g_c, int friendcon_id, uint16_t group_num) { uint8_t packet[1]; packet[0] = PEER_QUERY_ID; return send_packet_group_peer(g_c->fr_c, friendcon_id, PACKET_ID_DIRECT_CONFERENCE, group_num, packet, sizeof(packet)); } -/** return number of peers sent on success. - * return 0 on failure. +/** + * @return number of peers sent on success. + * @retval 0 on failure. */ +non_null() static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int friendcon_id, uint16_t group_num) { uint8_t response_packet[MAX_CRYPTO_DATA_SIZE - (1 + sizeof(uint16_t))]; @@ -2228,7 +2447,7 @@ static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int fri p += g->group[i].nick_len; } - if (g->title_len) { + if (g->title_len > 0) { VLA(uint8_t, title_packet, 1 + g->title_len); title_packet[0] = PEER_TITLE_ID; memcpy(title_packet + 1, g->title, g->title_len); @@ -2239,6 +2458,7 @@ static unsigned int send_peers(const Group_Chats *g_c, const Group_c *g, int fri return sent; } +non_null(1, 3) nullable(5) static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, void *userdata) { @@ -2248,7 +2468,7 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8 Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2261,11 +2481,11 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8 d += sizeof(uint16_t); if (g->status == GROUPCHAT_STATUS_VALID - && public_key_cmp(d, nc_get_self_public_key(g_c->m->net_crypto)) == 0) { + && pk_equal(d, nc_get_self_public_key(g_c->m->net_crypto))) { g->peer_number = peer_num; g->status = GROUPCHAT_STATUS_CONNECTED; - if (g_c->connected_callback) { + if (g_c->connected_callback != nullptr) { g_c->connected_callback(g_c->m, groupnumber, userdata); } @@ -2296,6 +2516,7 @@ static int handle_send_peers(Group_Chats *g_c, uint32_t groupnumber, const uint8 return 0; } +non_null(1, 3) nullable(6) static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, int connection_index, void *userdata) { @@ -2305,7 +2526,7 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -2341,11 +2562,13 @@ static void handle_direct_packet(Group_Chats *g_c, uint32_t groupnumber, const u } } -/** Send message to all connections except receiver (if receiver isn't -1) +/** @brief Send message to all connections except receiver (if receiver isn't -1) + * * NOTE: this function appends the group chat number to the data passed to it. * - * return number of messages sent. + * @return number of messages sent. */ +non_null() static unsigned int send_message_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data, uint16_t length, int receiver) { @@ -2369,11 +2592,13 @@ static unsigned int send_message_all_connections(const Group_Chats *g_c, const G return sent; } -/** Send lossy message to all connections except receiver (if receiver isn't -1) +/** @brief Send lossy message to all connections except receiver (if receiver isn't -1) + * * NOTE: this function appends the group chat number to the data passed to it. * - * return number of messages sent. + * @return number of messages sent. */ +non_null() static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Group_c *g, const uint8_t *data, uint16_t length, int receiver) { @@ -2390,7 +2615,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro continue; } - if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) { + if ((g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0) { connected_closest[num_connected_closest] = i; ++num_connected_closest; continue; @@ -2402,7 +2627,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro } } - if (!num_connected_closest) { + if (num_connected_closest == 0) { return sent; } @@ -2415,7 +2640,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro const uint64_t comp_val = calculate_comp_value(g->real_pk, real_pk); for (uint8_t j = 0; j < 2; ++j) { - if (j ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) { + if (j > 0 ? (comp_val > comp_val_old[j]) : (comp_val < comp_val_old[j])) { to_send[j] = connected_closest[i]; comp_val_old[j] = comp_val; } @@ -2423,7 +2648,7 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro } for (uint8_t j = 0; j < 2; ++j) { - if (j && to_send[1] == to_send[0]) { + if (j > 0 && to_send[1] == to_send[0]) { break; } @@ -2436,20 +2661,21 @@ static unsigned int send_lossy_all_connections(const Group_Chats *g_c, const Gro return sent; } -/** Send data of len with message_id to groupnumber. +/** @brief Send data of len with message_id to groupnumber. * - * return number of peers it was sent to on success. - * return -1 if groupnumber is invalid. - * return -2 if message is too long. - * return -3 if we are not connected to the group. - * return -4 if message failed to send. + * @return number of peers it was sent to on success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if message is too long. + * @retval -3 if we are not connected to the group. + * @retval -4 if message failed to send. */ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint8_t message_id, const uint8_t *data, uint16_t len) { + assert(len == 0 || data != nullptr); Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2467,7 +2693,7 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint ++g->message_number; - if (!g->message_number) { + if (g->message_number == 0) { ++g->message_number; } @@ -2476,11 +2702,11 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint packet[sizeof(uint16_t) + sizeof(uint32_t)] = message_id; - if (len) { + if (len != 0) { memcpy(packet + sizeof(uint16_t) + sizeof(uint32_t) + 1, data, len); } - unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1); + const unsigned int ret = send_message_all_connections(g_c, g, packet, SIZEOF_VLA(packet), -1); if (ret == 0) { return -4; @@ -2489,9 +2715,9 @@ static int send_message_group(const Group_Chats *g_c, uint32_t groupnumber, uint return ret; } -/** send a group message - * return 0 on success - * see: send_message_group() for error codes. +/** @brief send a group message + * @retval 0 on success + * @see send_message_group for error codes. */ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length) { @@ -2504,9 +2730,9 @@ int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8 return ret; } -/** send a group action - * return 0 on success - * see: send_message_group() for error codes. +/** @brief send a group action + * @retval 0 on success + * @see send_message_group for error codes. */ int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length) { @@ -2519,17 +2745,17 @@ int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_ return ret; } -/** High level function to send custom lossy packets. +/** @brief High level function to send custom lossy packets. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length) { // TODO(irungentoo): length check here? Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2548,6 +2774,7 @@ int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const return 0; } +non_null() static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_t message_id, Group_Peer *peer) { const bool ignore_older = message_id == GROUP_MESSAGE_NAME_ID || message_id == GROUP_MESSAGE_TITLE_ID; @@ -2571,11 +2798,12 @@ static Message_Info *find_message_slot_or_reject(uint32_t message_number, uint8_ return i; } -/** Stores message info in peer->last_message_infos. +/** @brief Stores message info in `peer->last_message_infos`. * - * return true if message should be processed. - * return false otherwise. + * @retval true if message should be processed. + * @retval false otherwise. */ +non_null() static bool check_message_info(uint32_t message_number, uint8_t message_id, Group_Peer *peer) { Message_Info *const i = find_message_slot_or_reject(message_number, message_id, peer); @@ -2592,7 +2820,7 @@ static bool check_message_info(uint32_t message_number, uint8_t message_id, Grou ++peer->num_last_message_infos; } - memmove(i + 1, i, ((peer->last_message_infos + peer->num_last_message_infos - 1) - i) * sizeof(Message_Info)); + memmove(i + 1, i, (&peer->last_message_infos[peer->num_last_message_infos - 1] - i) * sizeof(Message_Info)); i->message_number = message_number; i->message_id = message_id; @@ -2600,6 +2828,7 @@ static bool check_message_info(uint32_t message_number, uint8_t message_id, Grou return true; } +non_null(1, 3) nullable(6) static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length, int connection_index, void *userdata) { @@ -2609,7 +2838,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return; } @@ -2649,7 +2878,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, if (g->num_introducer_connections > 0 && count_connected(g) > DESIRED_CLOSEST) { for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE - || !(g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) == 0 || i == connection_index) { continue; } @@ -2657,7 +2886,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[i].number); - if (id_equal(g->group[index].real_pk, real_pk)) { + if (pk_equal(g->group[index].real_pk, real_pk)) { /* Received message from peer relayed via another peer, so * the introduction was successful */ remove_connection_reason(g_c, g, i, GROUPCHAT_CONNECTION_REASON_INTRODUCER); @@ -2671,7 +2900,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; get_friendcon_public_keys(real_pk, nullptr, g_c->fr_c, g->connections[connection_index].number); - const bool direct_from_sender = id_equal(g->group[index].real_pk, real_pk); + const bool direct_from_sender = pk_equal(g->group[index].real_pk, real_pk); switch (message_id) { case GROUP_MESSAGE_PING_ID: { @@ -2741,7 +2970,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, newmsg[msg_data_len] = 0; // TODO(irungentoo): - if (g_c->message_callback) { + if (g_c->message_callback != nullptr) { g_c->message_callback(g_c->m, groupnumber, index, 0, newmsg, msg_data_len, userdata); } @@ -2758,7 +2987,7 @@ static void handle_message_packet_group(Group_Chats *g_c, uint32_t groupnumber, newmsg[msg_data_len] = 0; // TODO(irungentoo): - if (g_c->message_callback) { + if (g_c->message_callback != nullptr) { g_c->message_callback(g_c->m, groupnumber, index, 1, newmsg, msg_data_len, userdata); } @@ -2798,7 +3027,7 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, groupnumber = net_ntohs(groupnumber); const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2823,14 +3052,15 @@ static int g_handle_packet(void *object, int friendcon_id, const uint8_t *data, return -1; } -/** Did we already receive the lossy packet or not. +/** @brief Did we already receive the lossy packet or not. * - * return -1 on failure. - * return 0 if packet was not received. - * return 1 if packet was received. + * @retval -1 on failure. + * @retval 0 if packet was not received. + * @retval 1 if packet was received. * * TODO(irungentoo): test this */ +non_null() static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t message_number) { if (peer_index == -1) { @@ -2845,7 +3075,7 @@ static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t } if ((uint16_t)(message_number - g->group[peer_index].bottom_lossy_number) < MAX_LOSSY_COUNT) { - if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT]) { + if (g->group[peer_index].recv_lossy[message_number % MAX_LOSSY_COUNT] != 0) { return 1; } @@ -2880,7 +3110,7 @@ static int lossy_packet_not_received(const Group_c *g, int peer_index, uint16_t /** Does this group type make use of lossy packets? */ static bool type_uses_lossy(uint8_t type) { - return (type == GROUPCHAT_TYPE_AV); + return type == GROUPCHAT_TYPE_AV; } static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata) @@ -2907,7 +3137,7 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2943,11 +3173,11 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin send_lossy_all_connections(g_c, g, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index); - if (!g_c->lossy_packethandlers[message_id].function) { + if (g_c->lossy_packethandlers[message_id] == nullptr) { return -1; } - if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object, + if (g_c->lossy_packethandlers[message_id](g->object, groupnumber, peer_index, g->group[peer_index].object, lossy_data, lossy_length) == -1) { return -1; } @@ -2955,16 +3185,16 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin return 0; } -/** Set the object that is tied to the group chat. +/** @brief Set the object that is tied to the group chat. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2972,16 +3202,16 @@ int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object) return 0; } -/** Set the object that is tied to the group peer. +/** @brief Set the object that is tied to the group peer. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return -1; } @@ -2993,32 +3223,32 @@ int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t return 0; } -/** Return the object tied to the group chat previously set by group_set_object. +/** @brief Return the object tied to the group chat previously set by group_set_object. * - * return NULL on failure. - * return object on success. + * @retval NULL on failure. + * @return object on success. */ void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return nullptr; } return g->object; } -/** Return the object tied to the group chat peer previously set by group_peer_set_object. +/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object. * - * return NULL on failure. - * return object on success. + * @retval NULL on failure. + * @return object on success. */ void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber) { const Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return nullptr; } @@ -3036,7 +3266,7 @@ static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -3052,11 +3282,12 @@ static bool ping_groupchat(const Group_Chats *g_c, uint32_t groupnumber) /** Seconds of inactivity after which to freeze a peer */ #define FREEZE_TIMEOUT (GROUP_PING_INTERVAL * 3) +non_null(1) nullable(3) static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, void *userdata) { Group_c *g = get_group_c(g_c, groupnumber); - if (!g) { + if (g == nullptr) { return false; } @@ -3078,6 +3309,7 @@ static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, vo } /** Push non-empty slots to start. */ +non_null() static void squash_connections(Group_c *g) { uint16_t num_connected = 0; @@ -3096,6 +3328,7 @@ static void squash_connections(Group_c *g) #define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10) +non_null() static uint16_t empty_connection_count(const Group_c *g) { uint16_t to_clear = MIN_EMPTY_CONNECTIONS; @@ -3113,10 +3346,14 @@ static uint16_t empty_connection_count(const Group_c *g) return to_clear; } -/** Remove old connections as necessary to ensure we have space for new - * connections. This invalidates connections array indices (which is +/** + * @brief Remove old connections as necessary to ensure we have space for new + * connections. + * + * This invalidates connections array indices (which is * why we do this periodically rather than on adding a connection). */ +non_null() static void clean_connections(Group_Chats *g_c, Group_c *g) { for (uint16_t to_clear = empty_connection_count(g); to_clear > 0; --to_clear) { @@ -3127,7 +3364,7 @@ static void clean_connections(Group_Chats *g_c, Group_c *g) while (i < MAX_GROUP_CONNECTIONS && (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING - || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) { ++i; } @@ -3136,7 +3373,7 @@ static void clean_connections(Group_Chats *g_c, Group_c *g) while (i < MAX_GROUP_CONNECTIONS - to_clear && (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE - || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST) != 0)) { ++i; } } @@ -3149,14 +3386,13 @@ static void clean_connections(Group_Chats *g_c, Group_c *g) squash_connections(g); } -/** Send current name (set in messenger) to all online groups. - */ +/** Send current name (set in messenger) to all online groups. */ void send_name_all_groups(const Group_Chats *g_c) { for (uint16_t i = 0; i < g_c->num_chats; ++i) { Group_c *g = get_group_c(g_c, i); - if (!g) { + if (g == nullptr) { continue; } @@ -3169,11 +3405,13 @@ void send_name_all_groups(const Group_Chats *g_c) #define SAVED_PEER_SIZE_CONSTANT (2 * CRYPTO_PUBLIC_KEY_SIZE + sizeof(uint16_t) + sizeof(uint64_t) + 1) +non_null() static uint32_t saved_peer_size(const Group_Peer *peer) { return SAVED_PEER_SIZE_CONSTANT + peer->nick_len; } +non_null() static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data) { memcpy(data, peer->real_pk, CRYPTO_PUBLIC_KEY_SIZE); @@ -3188,6 +3426,7 @@ static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data) host_to_lendian_bytes64(data, peer->last_active); data += sizeof(uint64_t); + // TODO(iphydf): This looks broken: nick_len can be > 255. *data = peer->nick_len; ++data; @@ -3200,6 +3439,7 @@ static uint8_t *save_peer(const Group_Peer *peer, uint8_t *data) #define SAVED_CONF_SIZE_CONSTANT (1 + GROUP_ID_LENGTH + sizeof(uint32_t) \ + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + 1) +non_null() static uint32_t saved_conf_size(const Group_c *g) { uint32_t len = SAVED_CONF_SIZE_CONSTANT + g->title_len; @@ -3207,7 +3447,7 @@ static uint32_t saved_conf_size(const Group_c *g) for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) { const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers]; - if (id_equal(peer->real_pk, g->real_pk)) { + if (pk_equal(peer->real_pk, g->real_pk)) { continue; } @@ -3217,11 +3457,14 @@ static uint32_t saved_conf_size(const Group_c *g) return len; } -/** Save a future message number. The save will remain valid until we have sent - * this many more messages. */ +/** + * Save a future message number. The save will remain valid until we have sent + * this many more messages. + */ #define SAVE_OFFSET_MESSAGE_NUMBER (1 << 16) #define SAVE_OFFSET_LOSSY_MESSAGE_NUMBER (1 << 13) +non_null() static uint8_t *save_conf(const Group_c *g, uint8_t *data) { *data = g->type; @@ -3253,7 +3496,7 @@ static uint8_t *save_conf(const Group_c *g, uint8_t *data) for (uint32_t j = 0; j < g->numpeers + g->numfrozen; ++j) { const Group_Peer *peer = (j < g->numpeers) ? &g->group[j] : &g->frozen[j - g->numpeers]; - if (id_equal(peer->real_pk, g->real_pk)) { + if (pk_equal(peer->real_pk, g->real_pk)) { continue; } @@ -3266,6 +3509,7 @@ static uint8_t *save_conf(const Group_c *g, uint8_t *data) return data; } +non_null() static uint32_t conferences_section_size(const Group_Chats *g_c) { uint32_t len = 0; @@ -3273,7 +3517,7 @@ static uint32_t conferences_section_size(const Group_Chats *g_c) for (uint16_t i = 0; i < g_c->num_chats; ++i) { const Group_c *g = get_group_c(g_c, i); - if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) { + if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) { continue; } @@ -3296,7 +3540,7 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data) for (uint16_t i = 0; i < g_c->num_chats; ++i) { const Group_c *g = get_group_c(g_c, i); - if (!g || g->status != GROUPCHAT_STATUS_CONNECTED) { + if (g == nullptr || g->status != GROUPCHAT_STATUS_CONNECTED) { continue; } @@ -3306,104 +3550,157 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data) return data; } -static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length) +/** + * @brief load_group Load a Group section from a save file + * @param g Group to load + * @param g_c Reference to all groupchats, need for utility functions + * @param data Start of the data to deserialze + * @param length Length of data + * @return 0 on error, number of consumed bytes otherwise + */ +non_null() +static uint32_t load_group(Group_c *g, const Group_Chats *g_c, const uint8_t *data, uint32_t length) { const uint8_t *init_data = data; - while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) { - const int groupnumber = create_group_chat(g_c); + // Initialize to default values so we can unconditionally free in case of an error + setup_conference(g); - if (groupnumber == -1) { - return STATE_LOAD_STATUS_ERROR; - } + g->type = *data; + ++data; - Group_c *g = &g_c->chats[groupnumber]; + memcpy(g->id, data, GROUP_ID_LENGTH); + data += GROUP_ID_LENGTH; - g->type = *data; - ++data; + lendian_bytes_to_host32(&g->message_number, data); + data += sizeof(uint32_t); - memcpy(g->id, data, GROUP_ID_LENGTH); - data += GROUP_ID_LENGTH; + lendian_bytes_to_host16(&g->lossy_message_number, data); + data += sizeof(uint16_t); - lendian_bytes_to_host32(&g->message_number, data); - data += sizeof(uint32_t); + lendian_bytes_to_host16(&g->peer_number, data); + data += sizeof(uint16_t); - lendian_bytes_to_host16(&g->lossy_message_number, data); - data += sizeof(uint16_t); + lendian_bytes_to_host32(&g->numfrozen, data); + data += sizeof(uint32_t); - lendian_bytes_to_host16(&g->peer_number, data); - data += sizeof(uint16_t); + g->title_len = *data; + + if (g->title_len > MAX_NAME_LENGTH) { + return 0; + } - lendian_bytes_to_host32(&g->numfrozen, data); - data += sizeof(uint32_t); + ++data; - if (g->numfrozen > 0) { - g->frozen = (Group_Peer *)calloc(g->numfrozen, sizeof(Group_Peer)); + assert((data - init_data) < UINT32_MAX); - if (g->frozen == nullptr) { - return STATE_LOAD_STATUS_ERROR; - } + if (length < (uint32_t)(data - init_data) + g->title_len) { + return 0; + } + + memcpy(g->title, data, g->title_len); + data += g->title_len; + + for (uint32_t j = 0; j < g->numfrozen; ++j) { + + assert((data - init_data) < UINT32_MAX); + + if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) { + return 0; } - g->title_len = *data; + // This is inefficient, but allows us to check data consistency before allocating memory + Group_Peer *tmp_frozen = (Group_Peer *)realloc(g->frozen, (j + 1) * sizeof(Group_Peer)); - if (g->title_len > MAX_NAME_LENGTH) { - return STATE_LOAD_STATUS_ERROR; + if (tmp_frozen == nullptr) { + // Memory allocation failure + return 0; } - ++data; + g->frozen = tmp_frozen; - if (length < (uint32_t)(data - init_data) + g->title_len) { - return STATE_LOAD_STATUS_ERROR; + Group_Peer *peer = &g->frozen[j]; + *peer = empty_group_peer; + + pk_copy(peer->real_pk, data); + data += CRYPTO_PUBLIC_KEY_SIZE; + pk_copy(peer->temp_pk, data); + data += CRYPTO_PUBLIC_KEY_SIZE; + + lendian_bytes_to_host16(&peer->peer_number, data); + data += sizeof(uint16_t); + + lendian_bytes_to_host64(&peer->last_active, data); + data += sizeof(uint64_t); + + peer->nick_len = *data; + + if (peer->nick_len > MAX_NAME_LENGTH) { + return 0; } - memcpy(g->title, data, g->title_len); - data += g->title_len; + ++data; + assert((data - init_data) < UINT32_MAX); - for (uint32_t j = 0; j < g->numfrozen; ++j) { - if (length < (uint32_t)(data - init_data) + SAVED_PEER_SIZE_CONSTANT) { - return STATE_LOAD_STATUS_ERROR; - } + if (length < (uint32_t)(data - init_data) + peer->nick_len) { + return 0; + } - Group_Peer *peer = &g->frozen[j]; - memset(peer, 0, sizeof(Group_Peer)); + memcpy(peer->nick, data, peer->nick_len); + data += peer->nick_len; - id_copy(peer->real_pk, data); - data += CRYPTO_PUBLIC_KEY_SIZE; - id_copy(peer->temp_pk, data); - data += CRYPTO_PUBLIC_KEY_SIZE; + // NOTE: this relies on friends being loaded before conferences. + peer->is_friend = getfriend_id(g_c->m, peer->real_pk) != -1; + } - lendian_bytes_to_host16(&peer->peer_number, data); - data += sizeof(uint16_t); + if (g->numfrozen > g->maxfrozen) { + g->maxfrozen = g->numfrozen; + } - lendian_bytes_to_host64(&peer->last_active, data); - data += sizeof(uint64_t); + g->status = GROUPCHAT_STATUS_CONNECTED; - peer->nick_len = *data; + pk_copy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto)); - if (peer->nick_len > MAX_NAME_LENGTH) { - return STATE_LOAD_STATUS_ERROR; - } + assert((data - init_data) < UINT32_MAX); - ++data; + return (uint32_t)(data - init_data); +} - if (length < (uint32_t)(data - init_data) + peer->nick_len) { - return STATE_LOAD_STATUS_ERROR; - } +non_null() +static State_Load_Status load_conferences_helper(Group_Chats *g_c, const uint8_t *data, uint32_t length) +{ + const uint8_t *init_data = data; - memcpy(peer->nick, data, peer->nick_len); - data += peer->nick_len; + while (length >= (uint32_t)(data - init_data) + SAVED_CONF_SIZE_CONSTANT) { + const int groupnumber = create_group_chat(g_c); + + // Helpful for testing + assert(groupnumber != -1); - // NOTE: this relies on friends being loaded before conferences. - peer->is_friend = (getfriend_id(g_c->m, peer->real_pk) != -1); + if (groupnumber == -1) { + // If this fails there's a serious problem, don't bother with cleanup + return STATE_LOAD_STATUS_ERROR; } - if (g->numfrozen > g->maxfrozen) { - g->maxfrozen = g->numfrozen; + Group_c *g = &g_c->chats[groupnumber]; + + const uint32_t consumed = load_group(g, g_c, data, length - (uint32_t)(data - init_data)); + + if (consumed == 0) { + // remove partially loaded stuff, wipe_group_chat must be able to wipe a partially loaded group + const bool ret = wipe_group_chat(g_c, groupnumber); + + // HACK: suppress unused variable warning + if (!ret) { + // wipe_group_chat(...) must be able to wipe partially allocated groups + assert(ret); + } + + return STATE_LOAD_STATUS_ERROR; } - g->status = GROUPCHAT_STATUS_CONNECTED; - memcpy(g->real_pk, nc_get_self_public_key(g_c->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); + data += consumed; + const int peer_index = addpeer(g_c, groupnumber, g->real_pk, dht_get_self_public_key(g_c->m->dht), g->peer_number, nullptr, true, false); @@ -3417,6 +3714,27 @@ static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, return STATE_LOAD_STATUS_CONTINUE; } +non_null() +static State_Load_Status load_conferences(Group_Chats *g_c, const uint8_t *data, uint32_t length) +{ + const State_Load_Status res = load_conferences_helper(g_c, data, length); + + if (res == STATE_LOAD_STATUS_CONTINUE) { + return res; + } + + // Loading failed, cleanup all Group_c + + // save locally, because wipe_group_chat(...) modifies it + const uint16_t num_groups = g_c->num_chats; + + for (uint16_t i = 0; i < num_groups; ++i) { + wipe_group_chat(g_c, i); + } + + return res; +} + bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, State_Load_Status *status) { @@ -3432,7 +3750,7 @@ bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint3 /** Create new groupchat instance. */ Group_Chats *new_groupchats(const Mono_Time *mono_time, Messenger *m) { - if (!m) { + if (m == nullptr) { return nullptr; } @@ -3459,7 +3777,7 @@ void do_groupchats(Group_Chats *g_c, void *userdata) for (uint16_t i = 0; i < g_c->num_chats; ++i) { Group_c *g = get_group_c(g_c, i); - if (!g) { + if (g == nullptr) { continue; } @@ -3482,6 +3800,10 @@ void do_groupchats(Group_Chats *g_c, void *userdata) /** Free everything related with group chats. */ void kill_groupchats(Group_Chats *g_c) { + if (g_c == nullptr) { + return; + } + for (uint16_t i = 0; i < g_c->num_chats; ++i) { del_groupchat(g_c, i, false); } @@ -3492,7 +3814,9 @@ void kill_groupchats(Group_Chats *g_c) free(g_c); } -/** Return the number of chats in the instance m. +/** + * @brief Return the number of chats in the instance m. + * * You should use this to determine how much memory to allocate * for copy_chatlist. */ @@ -3509,14 +3833,16 @@ uint32_t count_chatlist(const Group_Chats *g_c) return ret; } -/** Copy a list of valid chat IDs into the array out_list. +/** @brief Copy a list of valid chat IDs into the array out_list. + * * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. * If the array was too small, the contents - * of out_list will be truncated to list_size. */ + * of out_list will be truncated to list_size. + */ uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size) { - if (!out_list) { + if (out_list == nullptr) { return 0; } diff --git a/protocols/Tox/libtox/src/toxcore/group.h b/protocols/Tox/libtox/src/toxcore/group.h index a2e51cd443..e6f6c44733 100644 --- a/protocols/Tox/libtox/src/toxcore/group.h +++ b/protocols/Tox/libtox/src/toxcore/group.h @@ -11,134 +11,18 @@ #include "Messenger.h" -typedef enum Groupchat_Status { - GROUPCHAT_STATUS_NONE, - GROUPCHAT_STATUS_VALID, - GROUPCHAT_STATUS_CONNECTED, -} Groupchat_Status; - typedef enum Groupchat_Type { GROUPCHAT_TYPE_TEXT, GROUPCHAT_TYPE_AV, } Groupchat_Type; -#define MAX_LOSSY_COUNT 256 - -typedef struct Message_Info { - uint32_t message_number; - uint8_t message_id; -} Message_Info; - -#define MAX_LAST_MESSAGE_INFOS 8 - -typedef struct Group_Peer { - uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; - bool temp_pk_updated; - bool is_friend; - - uint64_t last_active; - - Message_Info - last_message_infos[MAX_LAST_MESSAGE_INFOS]; /* received messages, strictly decreasing in message_number */ - uint8_t num_last_message_infos; - - uint8_t nick[MAX_NAME_LENGTH]; - uint8_t nick_len; - bool nick_updated; - - uint16_t peer_number; - - uint8_t recv_lossy[MAX_LOSSY_COUNT]; - uint16_t bottom_lossy_number; - uint16_t top_lossy_number; - - void *object; -} Group_Peer; - -#define DESIRED_CLOSEST 4 -#define MAX_GROUP_CONNECTIONS 16 -#define GROUP_ID_LENGTH CRYPTO_SYMMETRIC_KEY_SIZE - -typedef enum Groupchat_Connection_Type { - GROUPCHAT_CONNECTION_NONE, - GROUPCHAT_CONNECTION_CONNECTING, - GROUPCHAT_CONNECTION_ONLINE, -} Groupchat_Connection_Type; - -/** Connection is to one of the closest DESIRED_CLOSEST peers */ -#define GROUPCHAT_CONNECTION_REASON_CLOSEST (1 << 0) - -/** Connection is to a peer we are introducing to the conference */ -#define GROUPCHAT_CONNECTION_REASON_INTRODUCING (1 << 1) - -/** Connection is to a peer who is introducing us to the conference */ -#define GROUPCHAT_CONNECTION_REASON_INTRODUCER (1 << 2) - -typedef struct Groupchat_Connection { - uint8_t type; /* `GROUPCHAT_CONNECTION_*` */ - uint8_t reasons; /* bit field with flags `GROUPCHAT_CONNECTION_REASON_*` */ - uint32_t number; - uint16_t group_number; -} Groupchat_Connection; - -typedef struct Groupchat_Closest { - uint8_t entry; - uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t temp_pk[CRYPTO_PUBLIC_KEY_SIZE]; -} Groupchat_Closest; - typedef void peer_on_join_cb(void *object, uint32_t conference_number, uint32_t peer_number); typedef void peer_on_leave_cb(void *object, uint32_t conference_number, void *peer_object); typedef void group_on_delete_cb(void *object, uint32_t conference_number); -// maximum number of frozen peers to store; group_set_max_frozen() overrides. -#define MAX_FROZEN_DEFAULT 128 - -typedef struct Group_c { - uint8_t status; - - bool need_send_name; - bool title_fresh; - - Group_Peer *group; - uint32_t numpeers; - - Group_Peer *frozen; - uint32_t numfrozen; - - uint32_t maxfrozen; - - Groupchat_Connection connections[MAX_GROUP_CONNECTIONS]; - - uint8_t real_pk[CRYPTO_PUBLIC_KEY_SIZE]; - Groupchat_Closest closest_peers[DESIRED_CLOSEST]; - uint8_t changed; - - uint8_t type; - uint8_t id[GROUP_ID_LENGTH]; - - uint8_t title[MAX_NAME_LENGTH]; - uint8_t title_len; - - uint32_t message_number; - uint16_t lossy_message_number; - uint16_t peer_number; - - uint64_t last_sent_ping; - - uint32_t num_introducer_connections; - - void *object; - - peer_on_join_cb *peer_on_join; - peer_on_leave_cb *peer_on_leave; - group_on_delete_cb *group_on_delete; -} Group_c; - -/** Callback for group invites. +/** @brief Callback for group invites. * - * data of length is what needs to be passed to join_groupchat(). + * data of length is what needs to be passed to `join_groupchat()`. */ typedef void g_conference_invite_cb(Messenger *m, uint32_t friend_number, int type, const uint8_t *cookie, size_t length, void *user_data); @@ -157,299 +41,333 @@ typedef void peer_name_cb(Messenger *m, uint32_t conference_number, uint32_t pee /** Set callback function for peer list changes. */ typedef void peer_list_changed_cb(Messenger *m, uint32_t conference_number, void *user_data); -/** Callback for title changes. +/** @brief Callback for title changes. * * If peer_number == -1, then author is unknown (e.g. initial joining the group). */ typedef void title_cb(Messenger *m, uint32_t conference_number, uint32_t peer_number, const uint8_t *title, size_t length, void *user_data); -/** Callback for lossy packets. +/** @brief Callback for lossy packets. * * NOTE: Handler must return 0 if packet is to be relayed, -1 if the packet should not be relayed. */ typedef int lossy_packet_cb(void *object, uint32_t conference_number, uint32_t peer_number, void *peer_object, const uint8_t *packet, uint16_t length); -typedef struct Group_Lossy_Handler { - lossy_packet_cb *function; -} Group_Lossy_Handler; - -typedef struct Group_Chats { - const Mono_Time *mono_time; +typedef struct Group_Chats Group_Chats; - Messenger *m; - Friend_Connections *fr_c; - - Group_c *chats; - uint16_t num_chats; - - g_conference_invite_cb *invite_callback; - g_conference_connected_cb *connected_callback; - g_conference_message_cb *message_callback; - peer_name_cb *peer_name_callback; - peer_list_changed_cb *peer_list_changed_callback; - title_cb *title_callback; - - Group_Lossy_Handler lossy_packethandlers[256]; -} Group_Chats; +non_null() +const Mono_Time *g_mono_time(const Group_Chats *g_c); /** Set the callback for group invites. */ +non_null() void g_callback_group_invite(Group_Chats *g_c, g_conference_invite_cb *function); /** Set the callback for group connection. */ +non_null() void g_callback_group_connected(Group_Chats *g_c, g_conference_connected_cb *function); /** Set the callback for group messages. */ +non_null() void g_callback_group_message(Group_Chats *g_c, g_conference_message_cb *function); /** Set callback function for title changes. */ +non_null() void g_callback_group_title(Group_Chats *g_c, title_cb *function); -/** Set callback function for peer nickname changes. +/** @brief Set callback function for peer nickname changes. * * It gets called every time a peer changes their nickname. */ +non_null() void g_callback_peer_name(Group_Chats *g_c, peer_name_cb *function); -/** Set callback function for peer list changes. +/** @brief Set callback function for peer list changes. * * It gets called every time the name list changes(new peer, deleted peer) */ +non_null() void g_callback_peer_list_changed(Group_Chats *g_c, peer_list_changed_cb *function); -/** Creates a new groupchat and puts it in the chats array. +/** @brief Creates a new groupchat and puts it in the chats array. * - * type is one of `GROUPCHAT_TYPE_*` + * @param rng Random number generator used for generating the group ID. + * @param type is one of `GROUPCHAT_TYPE_*` * - * return group number on success. - * return -1 on failure. + * @return group number on success. + * @retval -1 on failure. */ -int add_groupchat(Group_Chats *g_c, uint8_t type); +non_null() +int add_groupchat(Group_Chats *g_c, const Random *rng, uint8_t type); -/** Delete a groupchat from the chats array, informing the group first as +/** @brief Delete a groupchat from the chats array, informing the group first as * appropriate. * - * return 0 on success. - * return -1 if groupnumber is invalid. + * @retval true on success. + * @retval false if groupnumber is invalid. */ -int del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently); +non_null() +bool del_groupchat(Group_Chats *g_c, uint32_t groupnumber, bool leave_permanently); -/** Copy the public key of (frozen, if frozen is true) peernumber who is in - * groupnumber to pk. pk must be CRYPTO_PUBLIC_KEY_SIZE long. +/** + * @brief Copy the public key of (frozen, if frozen is true) peernumber who is in + * groupnumber to pk. + * + * @param pk must be CRYPTO_PUBLIC_KEY_SIZE long. * - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval 0 on success + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ +non_null() int group_peer_pubkey(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *pk, bool frozen); /** - * Return the size of (frozen, if frozen is true) peernumber's name. + * @brief Return the size of (frozen, if frozen is true) peernumber's name. * - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ +non_null() int group_peername_size(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, bool frozen); -/** Copy the name of (frozen, if frozen is true) peernumber who is in - * groupnumber to name. name must be at least MAX_NAME_LENGTH long. +/** + * @brief Copy the name of (frozen, if frozen is true) peernumber who is in + * groupnumber to name. * - * return length of name if success - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @param name must be at least MAX_NAME_LENGTH long. + * + * @return length of name if success + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ -int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, bool frozen); +non_null() +int group_peername(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint8_t *name, + bool frozen); -/** Copy last active timestamp of frozen peernumber who is in groupnumber to - * last_active. +/** + * @brief Copy last active timestamp of frozen peernumber who is in groupnumber to + * last_active. * - * return 0 on success. - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. */ -int group_frozen_last_active(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, - uint64_t *last_active); +non_null() +int group_frozen_last_active( + const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, uint64_t *last_active); -/** Set maximum number of frozen peers. +/** @brief Set maximum number of frozen peers. * - * return 0 on success. - * return -1 if groupnumber is invalid. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. */ +non_null() int group_set_max_frozen(const Group_Chats *g_c, uint32_t groupnumber, uint32_t maxfrozen); -/** invite friendnumber to groupnumber. +/** @brief invite friendnumber to groupnumber. * - * return 0 on success. - * return -1 if groupnumber is invalid. - * return -2 if invite packet failed to send. - * return -3 if we are not connected to the group chat. + * @retval 0 on success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if invite packet failed to send. + * @retval -3 if we are not connected to the group chat. */ +non_null() int invite_friend(const Group_Chats *g_c, uint32_t friendnumber, uint32_t groupnumber); -/** Join a group (we need to have been invited first.) +/** @brief Join a group (we need to have been invited first). * - * expected_type is the groupchat type we expect the chat we are joining to - * have. + * @param expected_type is the groupchat type we expect the chat we are joining + * to have. * - * return group number on success. - * return -1 if data length is invalid. - * return -2 if group is not the expected type. - * return -3 if friendnumber is invalid. - * return -4 if client is already in this group. - * return -5 if group instance failed to initialize. - * return -6 if join packet fails to send. - */ -int join_groupchat(Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, - uint16_t length); - -/** send a group message - * return 0 on success - * see: send_message_group() for error codes. - */ + * @return group number on success. + * @retval -1 if data length is invalid. + * @retval -2 if group is not the expected type. + * @retval -3 if friendnumber is invalid. + * @retval -4 if client is already in this group. + * @retval -5 if group instance failed to initialize. + * @retval -6 if join packet fails to send. + */ +non_null() +int join_groupchat( + Group_Chats *g_c, uint32_t friendnumber, uint8_t expected_type, const uint8_t *data, uint16_t length); + +/** @brief send a group message + * @retval 0 on success + * @see send_message_group for error codes. + */ +non_null() int group_message_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *message, uint16_t length); -/** send a group action - * return 0 on success - * see: send_message_group() for error codes. +/** @brief send a group action + * @retval 0 on success + * @see send_message_group for error codes. */ +non_null() int group_action_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *action, uint16_t length); -/** set the group's title, limited to MAX_NAME_LENGTH - * return 0 on success - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. - * return -3 if packet fails to send. +/** @brief set the group's title, limited to MAX_NAME_LENGTH. + * @retval 0 on success + * @retval -1 if groupnumber is invalid. + * @retval -2 if title is too long or empty. + * @retval -3 if packet fails to send. */ +non_null() int group_title_send(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *title, uint8_t title_len); -/** return the group's title size. - * return -1 of groupnumber is invalid. - * return -2 if title is too long or empty. +/** @brief return the group's title size. + * @retval -1 of groupnumber is invalid. + * @retval -2 if title is too long or empty. */ +non_null() int group_title_get_size(const Group_Chats *g_c, uint32_t groupnumber); -/** Get group title from groupnumber and put it in title. +/** @brief Get group title from groupnumber and put it in title. + * * Title needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. * - * return length of copied title if success. - * return -1 if groupnumber is invalid. - * return -2 if title is too long or empty. + * @return length of copied title if success. + * @retval -1 if groupnumber is invalid. + * @retval -2 if title is too long or empty. */ +non_null() int group_title_get(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *title); -/** Return the number of (frozen, if frozen is true) peers in the group chat on - * success. - * return -1 if groupnumber is invalid. +/** + * @return the number of (frozen, if frozen is true) peers in the group chat on success. + * @retval -1 if groupnumber is invalid. */ +non_null() int group_number_peers(const Group_Chats *g_c, uint32_t groupnumber, bool frozen); -/** return 1 if the peernumber corresponds to ours. - * return 0 if the peernumber is not ours. - * return -1 if groupnumber is invalid. - * return -2 if peernumber is invalid. - * return -3 if we are not connected to the group chat. +/** + * @retval 1 if the peernumber corresponds to ours. + * @retval 0 if the peernumber is not ours. + * @retval -1 if groupnumber is invalid. + * @retval -2 if peernumber is invalid. + * @retval -3 if we are not connected to the group chat. */ +non_null() int group_peernumber_is_ours(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber); /** Set handlers for custom lossy packets. */ +non_null() void group_lossy_packet_registerhandler(Group_Chats *g_c, uint8_t byte, lossy_packet_cb *function); -/** High level function to send custom lossy packets. +/** @brief High level function to send custom lossy packets. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() int send_group_lossy_packet(const Group_Chats *g_c, uint32_t groupnumber, const uint8_t *data, uint16_t length); -/** Return the number of chats in the instance m. +/** + * @brief Return the number of chats in the instance m. + * * You should use this to determine how much memory to allocate * for copy_chatlist. */ +non_null() uint32_t count_chatlist(const Group_Chats *g_c); -/** Copy a list of valid chat IDs into the array out_list. +/** @brief Copy a list of valid chat IDs into the array out_list. + * * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. * If the array was too small, the contents - * of out_list will be truncated to list_size. */ + * of out_list will be truncated to list_size. + */ +non_null() uint32_t copy_chatlist(const Group_Chats *g_c, uint32_t *out_list, uint32_t list_size); -/** return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is. +/** @brief return the type of groupchat (GROUPCHAT_TYPE_) that groupnumber is. * - * return -1 on failure. - * return type on success. + * @retval -1 on failure. + * @return type on success. */ +non_null() int group_get_type(const Group_Chats *g_c, uint32_t groupnumber); -/** Copies the unique id of `group_chat[groupnumber]` into `id`. +/** @brief Copies the unique id of `group_chat[groupnumber]` into `id`. * - * return false on failure. - * return true on success. + * @retval false on failure. + * @retval true on success. */ +non_null() bool conference_get_id(const Group_Chats *g_c, uint32_t groupnumber, uint8_t *id); -int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id); +non_null() int32_t conference_by_id(const Group_Chats *g_c, const uint8_t *id); -/** Send current name (set in messenger) to all online groups. - */ +/** Send current name (set in messenger) to all online groups. */ +non_null() void send_name_all_groups(const Group_Chats *g_c); -/** Set the object that is tied to the group chat. +/** @brief Set the object that is tied to the group chat. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ +non_null(1) nullable(3) int group_set_object(const Group_Chats *g_c, uint32_t groupnumber, void *object); -/** Set the object that is tied to the group peer. +/** @brief Set the object that is tied to the group peer. * - * return 0 on success. - * return -1 on failure + * @retval 0 on success. + * @retval -1 on failure */ +non_null(1) nullable(4) int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber, void *object); -/** Return the object tied to the group chat previously set by group_set_object. +/** @brief Return the object tied to the group chat previously set by group_set_object. * - * return NULL on failure. - * return object on success. + * @retval NULL on failure. + * @return object on success. */ +non_null() void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber); -/** Return the object tied to the group chat peer previously set by group_peer_set_object. +/** @brief Return the object tied to the group chat peer previously set by group_peer_set_object. * - * return NULL on failure. - * return object on success. + * @retval NULL on failure. + * @return object on success. */ +non_null() void *group_peer_get_object(const Group_Chats *g_c, uint32_t groupnumber, uint32_t peernumber); -/** Set a function to be called when a new peer joins a group chat. +/** @brief Set a function to be called when a new peer joins a group chat. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null(1) nullable(3) int callback_groupchat_peer_new(const Group_Chats *g_c, uint32_t groupnumber, peer_on_join_cb *function); -/** Set a function to be called when a peer leaves a group chat. +/** @brief Set a function to be called when a peer leaves a group chat. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null(1) nullable(3) int callback_groupchat_peer_delete(const Group_Chats *g_c, uint32_t groupnumber, peer_on_leave_cb *function); -/** Set a function to be called when the group chat is deleted. +/** @brief Set a function to be called when the group chat is deleted. * - * return 0 on success. - * return -1 on failure. + * @retval 0 on success. + * @retval -1 on failure. */ +non_null(1) nullable(3) int callback_groupchat_delete(const Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function); /** Return size of the conferences data (for saving). */ +non_null() uint32_t conferences_size(const Group_Chats *g_c); -/** Save the conferences in data (must be allocated memory of size at least conferences_size()) */ +/** Save the conferences in data (must be allocated memory of size at least `conferences_size()`). */ +non_null() uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data); /** @@ -461,16 +379,20 @@ uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data); * @param status Result of loading section is stored here if the section is handled. * @return true iff section handled. */ -bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, - State_Load_Status *status); +non_null() +bool conferences_load_state_section( + Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type, State_Load_Status *status); /** Create new groupchat instance. */ +non_null() Group_Chats *new_groupchats(const Mono_Time *mono_time, Messenger *m); /** main groupchats loop. */ +non_null(1) nullable(2) void do_groupchats(Group_Chats *g_c, void *userdata); /** Free everything related with group chats. */ +nullable(1) void kill_groupchats(Group_Chats *g_c); #endif diff --git a/protocols/Tox/libtox/src/toxcore/group_announce.c b/protocols/Tox/libtox/src/toxcore/group_announce.c new file mode 100644 index 0000000000..83dd3acf54 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/group_announce.c @@ -0,0 +1,462 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2020 The TokTok team. + * Copyright © 2015 Tox project. + */ + +#include "group_announce.h" + +#include <stdlib.h> +#include <string.h> + +#include "LAN_discovery.h" +#include "ccompat.h" +#include "mono_time.h" +#include "util.h" + +/** + * Removes `announces` from `gc_announces_list`. + */ +non_null() +static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces *announces) +{ + if (announces == nullptr || gc_announces_list == nullptr) { + return; + } + + if (announces->prev_announce != nullptr) { + announces->prev_announce->next_announce = announces->next_announce; + } else { + gc_announces_list->root_announces = announces->next_announce; + } + + if (announces->next_announce != nullptr) { + announces->next_announce->prev_announce = announces->prev_announce; + } + + free(announces); +} + +/** + * Returns the announce designated by `chat_id`. + * Returns null if no announce is found. + */ +non_null() +static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announces_list, const uint8_t *chat_id) +{ + GC_Announces *announces = gc_announces_list->root_announces; + + while (announces != nullptr) { + if (memcmp(announces->chat_id, chat_id, CHAT_ID_SIZE) == 0) { + return announces; + } + + announces = announces->next_announce; + } + + return nullptr; +} + +int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes, + const uint8_t *chat_id, const uint8_t *except_public_key) +{ + if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr || max_nodes == 0 + || except_public_key == nullptr) { + return -1; + } + + const GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); + + if (announces == nullptr) { + return 0; + } + + uint16_t added_count = 0; + + for (size_t i = 0; i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes; ++i) { + const size_t index = i % GCA_MAX_SAVED_ANNOUNCES_PER_GC; + + if (memcmp(except_public_key, &announces->peer_announces[index].base_announce.peer_public_key, + ENC_PUBLIC_KEY_SIZE) == 0) { + continue; + } + + bool already_added = false; + + for (size_t j = 0; j < added_count; ++j) { + if (memcmp(&gc_announces[j].peer_public_key, &announces->peer_announces[index].base_announce.peer_public_key, + ENC_PUBLIC_KEY_SIZE) == 0) { + already_added = true; + break; + } + } + + if (!already_added) { + gc_announces[added_count] = announces->peer_announces[index].base_announce; + ++added_count; + } + } + + return added_count; +} + +uint16_t gca_pack_announces_list_size(uint16_t count) +{ + return count * GCA_ANNOUNCE_MAX_SIZE; +} + +int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce) +{ + if (length < GCA_ANNOUNCE_MAX_SIZE) { + LOGGER_ERROR(log, "Invalid announce length: %u", length); + return -1; + } + + if (data == nullptr) { + LOGGER_ERROR(log, "data is null"); + return -1; + } + + if (announce == nullptr) { + LOGGER_ERROR(log, "announce is null"); + return -1; + } + + uint16_t offset = 0; + memcpy(data + offset, announce->peer_public_key, ENC_PUBLIC_KEY_SIZE); + offset += ENC_PUBLIC_KEY_SIZE; + + data[offset] = announce->ip_port_is_set ? 1 : 0; + ++offset; + + data[offset] = announce->tcp_relays_count; + ++offset; + + if (!announce->ip_port_is_set && announce->tcp_relays_count == 0) { + LOGGER_ERROR(log, "Failed to pack announce: no valid ip_port or tcp relay"); + return -1; + } + + if (announce->ip_port_is_set) { + const int ip_port_length = pack_ip_port(log, data + offset, length - offset, &announce->ip_port); + + if (ip_port_length == -1) { + LOGGER_ERROR(log, "Failed to pack ip_port"); + return -1; + } + + offset += ip_port_length; + } + + const int nodes_length = pack_nodes(log, data + offset, length - offset, announce->tcp_relays, + announce->tcp_relays_count); + + if (nodes_length == -1) { + LOGGER_ERROR(log, "Failed to pack TCP nodes"); + return -1; + } + + return nodes_length + offset; +} + +/** + * Unpacks `announce` into `data` buffer of size `length`. + * + * Returns the size of the unpacked data on success. + * Returns -1 on failure. + */ +non_null() +static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announce) +{ + if (length < ENC_PUBLIC_KEY_SIZE + 2) { + LOGGER_ERROR(log, "Invalid announce length: %u", length); + return -1; + } + + if (data == nullptr) { + LOGGER_ERROR(log, "data is null"); + return -1; + } + + if (announce == nullptr) { + LOGGER_ERROR(log, "announce is null"); + return -1; + } + + uint16_t offset = 0; + memcpy(announce->peer_public_key, data + offset, ENC_PUBLIC_KEY_SIZE); + offset += ENC_PUBLIC_KEY_SIZE; + + announce->ip_port_is_set = data[offset] == 1; + ++offset; + + announce->tcp_relays_count = data[offset]; + ++offset; + + if (announce->tcp_relays_count > GCA_MAX_ANNOUNCED_TCP_RELAYS) { + return -1; + } + + if (announce->ip_port_is_set) { + if (length - offset == 0) { + return -1; + } + + const int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false); + + if (ip_port_length == -1) { + LOGGER_ERROR(log, "Failed to unpack ip_port"); + return -1; + } + + offset += ip_port_length; + } + + uint16_t nodes_length; + const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length, + data + offset, length - offset, true); + + if (nodes_count != announce->tcp_relays_count) { + LOGGER_ERROR(log, "Failed to unpack TCP nodes"); + return -1; + } + + return offset + nodes_length; +} + +int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length, + const GC_Public_Announce *public_announce) +{ + if (public_announce == nullptr || data == nullptr || length < CHAT_ID_SIZE) { + return -1; + } + + memcpy(data, public_announce->chat_public_key, CHAT_ID_SIZE); + + const int packed_size = gca_pack_announce(log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, + &public_announce->base_announce); + + if (packed_size < 0) { + LOGGER_ERROR(log, "Failed to pack public group announce"); + return -1; + } + + return packed_size + CHAT_ID_SIZE; +} + +int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length, + GC_Public_Announce *public_announce) +{ + if (length < CHAT_ID_SIZE) { + LOGGER_ERROR(log, "invalid public announce length: %u", length); + return -1; + } + + if (data == nullptr) { + LOGGER_ERROR(log, "data is null"); + return -1; + } + + if (public_announce == nullptr) { + LOGGER_ERROR(log, "public_announce is null"); + return -1; + } + + memcpy(public_announce->chat_public_key, data, CHAT_ID_SIZE); + + const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE, length - ENC_PUBLIC_KEY_SIZE, + &public_announce->base_announce); + + if (base_announce_size == -1) { + LOGGER_ERROR(log, "Failed to unpack group announce"); + return -1; + } + + return base_announce_size + CHAT_ID_SIZE; +} + +int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces, + uint8_t announces_count, size_t *processed) +{ + if (data == nullptr) { + LOGGER_ERROR(log, "data is null"); + return -1; + } + + if (announces == nullptr) { + LOGGER_ERROR(log, "announces is null"); + return -1; + } + + uint16_t offset = 0; + + for (size_t i = 0; i < announces_count; ++i) { + const int packed_length = gca_pack_announce(log, data + offset, length - offset, &announces[i]); + + if (packed_length < 0) { + LOGGER_ERROR(log, "Failed to pack group announce"); + return -1; + } + + offset += packed_length; + } + + if (processed != nullptr) { + *processed = offset; + } + + return announces_count; +} + +int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces, + uint8_t max_count) +{ + if (data == nullptr) { + LOGGER_ERROR(log, "data is null"); + return -1; + } + + if (announces == nullptr) { + LOGGER_ERROR(log, "announces is null"); + return -1; + } + + uint16_t offset = 0; + int announces_count = 0; + + for (size_t i = 0; i < max_count && length > offset; ++i) { + const int unpacked_length = gca_unpack_announce(log, data + offset, length - offset, &announces[i]); + + if (unpacked_length == -1) { + LOGGER_WARNING(log, "Failed to unpack group announce: %d %d", length, offset); + return -1; + } + + offset += unpacked_length; + ++announces_count; + } + + return announces_count; +} + +GC_Peer_Announce *gca_add_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, + const GC_Public_Announce *public_announce) +{ + if (gc_announces_list == nullptr || public_announce == nullptr) { + return nullptr; + } + + GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key); + + // No entry for this chat_id exists so we create one + if (announces == nullptr) { + announces = (GC_Announces *)calloc(1, sizeof(GC_Announces)); + + if (announces == nullptr) { + return nullptr; + } + + announces->index = 0; + announces->prev_announce = nullptr; + + if (gc_announces_list->root_announces != nullptr) { + gc_announces_list->root_announces->prev_announce = announces; + } + + announces->next_announce = gc_announces_list->root_announces; + gc_announces_list->root_announces = announces; + memcpy(announces->chat_id, public_announce->chat_public_key, CHAT_ID_SIZE); + } + + const uint64_t cur_time = mono_time_get(mono_time); + + announces->last_announce_received_timestamp = cur_time; + + const uint64_t index = announces->index % GCA_MAX_SAVED_ANNOUNCES_PER_GC; + + GC_Peer_Announce *gc_peer_announce = &announces->peer_announces[index]; + + gc_peer_announce->base_announce = public_announce->base_announce; + + gc_peer_announce->timestamp = cur_time; + + ++announces->index; + + return gc_peer_announce; +} + +bool gca_is_valid_announce(const GC_Announce *announce) +{ + if (announce == nullptr) { + return false; + } + + return announce->tcp_relays_count > 0 || announce->ip_port_is_set; +} + +GC_Announces_List *new_gca_list(void) +{ + GC_Announces_List *announces_list = (GC_Announces_List *)calloc(1, sizeof(GC_Announces_List)); + return announces_list; +} + +void kill_gca(GC_Announces_List *announces_list) +{ + if (announces_list == nullptr) { + return; + } + + GC_Announces *root = announces_list->root_announces; + + while (root != nullptr) { + GC_Announces *next = root->next_announce; + free(root); + root = next; + } + + free(announces_list); +} + +/* How long we save a peer's announce before we consider it stale and remove it. */ +#define GCA_ANNOUNCE_SAVE_TIMEOUT 30 + +/* How often we run do_gca() */ +#define GCA_DO_GCA_TIMEOUT 1 + +void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list) +{ + if (gc_announces_list == nullptr) { + return; + } + + if (!mono_time_is_timeout(mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) { + return; + } + + gc_announces_list->last_timeout_check = mono_time_get(mono_time); + + GC_Announces *announces = gc_announces_list->root_announces; + + while (announces != nullptr) { + if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp, GCA_ANNOUNCE_SAVE_TIMEOUT)) { + GC_Announces *to_delete = announces; + announces = announces->next_announce; + remove_announces(gc_announces_list, to_delete); + continue; + } + + announces = announces->next_announce; + } +} + +void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id) +{ + if (gc_announces_list == nullptr || chat_id == nullptr) { + return; + } + + GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); + + if (announces != nullptr) { + remove_announces(gc_announces_list, announces); + } +} diff --git a/protocols/Tox/libtox/src/toxcore/group_announce.h b/protocols/Tox/libtox/src/toxcore/group_announce.h new file mode 100644 index 0000000000..11bba7d530 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/group_announce.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2020 The TokTok team. + * Copyright © 2015 Tox project. + */ + +/** + * Similar to ping.h, but designed for group chat purposes + */ +#ifndef GROUP_ANNOUNCE_H +#define GROUP_ANNOUNCE_H + +#include <stdbool.h> + +#include "DHT.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The maximum number of announces to save for a particular group chat. */ +#define GCA_MAX_SAVED_ANNOUNCES_PER_GC 16 + +/* Maximum number of TCP relays that can be in an annoucne. */ +#define GCA_MAX_ANNOUNCED_TCP_RELAYS 1 + +/* Maximum number of announces we can send in an announce response. */ +#define GCA_MAX_SENT_ANNOUNCES 4 + +/* Maximum size of an announce. */ +#define GCA_ANNOUNCE_MAX_SIZE (ENC_PUBLIC_KEY_SIZE + 1 + 1 + (PACKED_NODE_SIZE_IP6 * 2)) + +/* Maximum size of a public announce. */ +#define GCA_PUBLIC_ANNOUNCE_MAX_SIZE (ENC_PUBLIC_KEY_SIZE + GCA_ANNOUNCE_MAX_SIZE) + +typedef struct GC_Announce GC_Announce; +typedef struct GC_Peer_Announce GC_Peer_Announce; +typedef struct GC_Announces GC_Announces; +typedef struct GC_Announces_List GC_Announces_List; +typedef struct GC_Public_Announce GC_Public_Announce; + +/* Base announce. */ +struct GC_Announce { + Node_format tcp_relays[GCA_MAX_ANNOUNCED_TCP_RELAYS]; + uint8_t tcp_relays_count; + bool ip_port_is_set; + IP_Port ip_port; + uint8_t peer_public_key[ENC_PUBLIC_KEY_SIZE]; +}; + +/* Peer announce for specific group. */ +struct GC_Peer_Announce { + GC_Announce base_announce; + uint64_t timestamp; +}; + +/* Used for announces in public groups. */ +struct GC_Public_Announce { + GC_Announce base_announce; + uint8_t chat_public_key[ENC_PUBLIC_KEY_SIZE]; +}; + +/* A linked list that holds all announces for a particular group. */ +struct GC_Announces { + uint8_t chat_id[CHAT_ID_SIZE]; + uint64_t index; + uint64_t last_announce_received_timestamp; + + GC_Peer_Announce peer_announces[GCA_MAX_SAVED_ANNOUNCES_PER_GC]; + + GC_Announces *next_announce; + GC_Announces *prev_announce; +}; + +/* A list of all announces. */ +struct GC_Announces_List { + GC_Announces *root_announces; + uint64_t last_timeout_check; +}; + + +/** @brief Returns a new group announces list. + * + * The caller is responsible for freeing the memory with `kill_gca`. + */ +GC_Announces_List *new_gca_list(void); + +/** @brief Frees all dynamically allocated memory associated with `announces_list`. */ +nullable(1) +void kill_gca(GC_Announces_List *announces_list); + +/** @brief Iterates through the announces list and removes announces that are considered stale. + * + * @param gc_announces_list The list of announces to iterate. + * + * This function should be called from the main loop, and will iterate the list a + * maxmimum of once per second. + */ +non_null() +void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list); + +/** @brief Frees all dynamically allocated memory associated with an announces list entry. + * + * @param gc_announces_list The announces list we want to search through. + * @param chat_id The chat ID that designates the entry we want to remove. + */ +non_null() +void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id); + +/** @brief Puts a set of announces from the announces list in supplied list. + * + * @param gc_announces_list The announces list we want to search for entries in. + * @param gc_announces An empty announces list that will be filled with matches. + * @param max_nodes The maximum number of matches that we want to add to the list. + * @param chat_id The chat ID associated with the announces that we want to add. + * @param except_public_key The public key associated with announces that we want to ignore. + * + * @return the number of added nodes on success. + * @retval -1 on failure. + */ +non_null() +int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes, + const uint8_t *chat_id, const uint8_t *except_public_key); + +/** @brief Adds a public_announce to list of announces. + * + * @param gc_announces_list The announces list that we want to add an entry to. + * @param public_announce The public announce that we want to add. + * + * @return the peer announce on success. + * @retval null on failure. + */ +non_null() +GC_Peer_Announce *gca_add_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, + const GC_Public_Announce *public_announce); + +/** @brief Packs an announce into a data buffer. + * + * @param data The data buffer being packed. + * @param length The size in bytes of the data buffer. Must be at least GCA_ANNOUNCE_MAX_SIZE. + * @param announce The announce being packed into the data buffer. + * + * @return the size of the packed data on success. + * @retval -1 on failure. + */ +non_null() +int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce); + +/** @brief Returns the number of bytes needed for a buff in which to pack `count` announces. */ +uint16_t gca_pack_announces_list_size(uint16_t count); + +/** @brief Packs a list of announces into a data buffer. + * + * @param data The data buffer being packed. + * @param length The size in bytes of the data buffer. Use gca_pack_announces_list_size to get the + * required length. + * @param announces The announces to be packed into the data buffer. + * @param announces_count The number of announces in the announces list. + * @param processed If non-null, will contain the number of bytes packed (only on success). + * + * @return the number of packed announces on success. + * @retval -1 on failure. + */ +non_null(1, 2, 4) nullable(6) +int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces, + uint8_t announces_count, size_t *processed); + +/** @brief Unpacks packed announces from a data buffer into a supplied list. + * + * @param data The data buffer to unpack from. + * @param length The size of the data buffer. + * @param announces The announces list that the data buffer will be unpacked to. + * @param max_count The maximum number of announces to unpack. + * + * @return the number of unpacked announces on success. + * @retval -1 on failure. + */ +non_null() +int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces, + uint8_t max_count); + +/** @brief Packs a public announce into a data buffer. + * + * @param data The data buffer being packed. + * @param length The size in bytes of the data buffer. Must be at least GCA_PUBLIC_ANNOUNCE_MAX_SIZE. + * @param public_announce The public announce being packed into the data buffer. + * + * @return the size of the packed data on success. + * @retval -1 on failure. + */ +non_null() +int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length, + const GC_Public_Announce *public_announce); + +/** @brief Unpacks a public announce from a data buffer into a supplied public announce. + * + * @param data The data buffer to unpack from. + * @param length The size of the data buffer. + * @param public_announce The public announce to unpack the data buffer into. + * + * @return the size of the unpacked data on success. + * @retval -1 on failure. + */ +non_null() +int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length, + GC_Public_Announce *public_announce); + +/** @brief Returns true if the announce is valid. + * + * An announce is considered valid if there is at least one TCP relay, or the ip_port is set. + */ +non_null() +bool gca_is_valid_announce(const GC_Announce *announce); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // GROUP_ANNOUNCE_H diff --git a/protocols/Tox/libtox/src/toxcore/group_moderation.c b/protocols/Tox/libtox/src/toxcore/group_moderation.c new file mode 100644 index 0000000000..dea38a648b --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/group_moderation.c @@ -0,0 +1,864 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2020 The TokTok team. + * Copyright © 2015 Tox project. + */ + +/** + * An implementation of massive text only group chats. + */ + +#include "group_moderation.h" + +#include <assert.h> + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "ccompat.h" +#include "crypto_core.h" +#include "mono_time.h" +#include "network.h" +#include "util.h" + +static_assert(MOD_SANCTIONS_CREDS_SIZE <= MAX_PACKET_SIZE_NO_HEADERS, + "MOD_SANCTIONS_CREDS_SIZE must be <= the maximum allowed payload size"); +static_assert(MOD_MAX_NUM_SANCTIONS * MOD_SANCTION_PACKED_SIZE + MOD_SANCTIONS_CREDS_SIZE <= MAX_PACKET_SIZE_NO_HEADERS, + "MOD_MAX_NUM_SANCTIONS must be able to fit inside the maximum allowed payload size"); +static_assert(MOD_MAX_NUM_MODERATORS * MOD_LIST_ENTRY_SIZE <= MAX_PACKET_SIZE_NO_HEADERS, + "MOD_MAX_NUM_MODERATORS must be able to fit insize the maximum allowed payload size"); + +uint16_t mod_list_packed_size(const Moderation *moderation) +{ + return moderation->num_mods * MOD_LIST_ENTRY_SIZE; +} + +int mod_list_unpack(Moderation *moderation, const uint8_t *data, uint16_t length, uint16_t num_mods) +{ + if (length < num_mods * MOD_LIST_ENTRY_SIZE) { + return -1; + } + + mod_list_cleanup(moderation); + + if (num_mods == 0) { + return 0; + } + + uint8_t **tmp_list = (uint8_t **)calloc(num_mods, sizeof(uint8_t *)); + + if (tmp_list == nullptr) { + return -1; + } + + uint16_t unpacked_len = 0; + + for (uint16_t i = 0; i < num_mods; ++i) { + tmp_list[i] = (uint8_t *)malloc(sizeof(uint8_t) * MOD_LIST_ENTRY_SIZE); + + if (tmp_list[i] == nullptr) { + free_uint8_t_pointer_array(tmp_list, i); + return -1; + } + + memcpy(tmp_list[i], &data[i * MOD_LIST_ENTRY_SIZE], MOD_LIST_ENTRY_SIZE); + unpacked_len += MOD_LIST_ENTRY_SIZE; + } + + moderation->mod_list = tmp_list; + moderation->num_mods = num_mods; + + return unpacked_len; +} + +void mod_list_pack(const Moderation *moderation, uint8_t *data) +{ + for (uint16_t i = 0; i < moderation->num_mods; ++i) { + memcpy(&data[i * MOD_LIST_ENTRY_SIZE], moderation->mod_list[i], MOD_LIST_ENTRY_SIZE); + } +} + +void mod_list_get_data_hash(uint8_t *hash, const uint8_t *packed_mod_list, uint16_t length) +{ + crypto_sha256(hash, packed_mod_list, length); +} + +bool mod_list_make_hash(const Moderation *moderation, uint8_t *hash) +{ + if (moderation->num_mods == 0) { + memset(hash, 0, MOD_MODERATION_HASH_SIZE); + return true; + } + + const size_t data_buf_size = mod_list_packed_size(moderation); + + assert(data_buf_size > 0); + + uint8_t *data = (uint8_t *)malloc(data_buf_size); + + if (data == nullptr) { + return false; + } + + mod_list_pack(moderation, data); + + mod_list_get_data_hash(hash, data, data_buf_size); + + free(data); + + return true; +} + +/** + * Returns moderator list index for public_sig_key. + * Returns -1 if key is not in the list. + */ +non_null() +static int mod_list_index_of_sig_pk(const Moderation *moderation, const uint8_t *public_sig_key) +{ + for (uint16_t i = 0; i < moderation->num_mods; ++i) { + if (memcmp(moderation->mod_list[i], public_sig_key, SIG_PUBLIC_KEY_SIZE) == 0) { + return i; + } + } + + return -1; +} + +bool mod_list_verify_sig_pk(const Moderation *moderation, const uint8_t *sig_pk) +{ + if (memcmp(moderation->founder_public_sig_key, sig_pk, SIG_PUBLIC_KEY_SIZE) == 0) { + return true; + } + + for (uint16_t i = 0; i < moderation->num_mods; ++i) { + if (memcmp(moderation->mod_list[i], sig_pk, SIG_PUBLIC_KEY_SIZE) == 0) { + return true; + } + } + + return false; +} + +bool mod_list_remove_index(Moderation *moderation, uint16_t index) +{ + if (index >= moderation->num_mods) { + return false; + } + + if ((moderation->num_mods - 1) == 0) { + mod_list_cleanup(moderation); + return true; + } + + --moderation->num_mods; + + if (index != moderation->num_mods) { + memcpy(moderation->mod_list[index], moderation->mod_list[moderation->num_mods], + MOD_LIST_ENTRY_SIZE); + } + + free(moderation->mod_list[moderation->num_mods]); + moderation->mod_list[moderation->num_mods] = nullptr; + + uint8_t **tmp_list = (uint8_t **)realloc(moderation->mod_list, moderation->num_mods * sizeof(uint8_t *)); + + if (tmp_list == nullptr) { + return false; + } + + moderation->mod_list = tmp_list; + + return true; +} + +bool mod_list_remove_entry(Moderation *moderation, const uint8_t *public_sig_key) +{ + if (moderation->num_mods == 0) { + return false; + } + + const int idx = mod_list_index_of_sig_pk(moderation, public_sig_key); + + if (idx == -1) { + return false; + } + + assert(idx <= UINT16_MAX); + + return mod_list_remove_index(moderation, (uint16_t)idx); +} + +bool mod_list_add_entry(Moderation *moderation, const uint8_t *mod_data) +{ + if (moderation->num_mods >= MOD_MAX_NUM_MODERATORS) { + return false; + } + + uint8_t **tmp_list = (uint8_t **)realloc(moderation->mod_list, (moderation->num_mods + 1) * sizeof(uint8_t *)); + + if (tmp_list == nullptr) { + return false; + } + + moderation->mod_list = tmp_list; + + tmp_list[moderation->num_mods] = (uint8_t *)malloc(sizeof(uint8_t) * MOD_LIST_ENTRY_SIZE); + + if (tmp_list[moderation->num_mods] == nullptr) { + return false; + } + + memcpy(tmp_list[moderation->num_mods], mod_data, MOD_LIST_ENTRY_SIZE); + ++moderation->num_mods; + + return true; +} + +void mod_list_cleanup(Moderation *moderation) +{ + free_uint8_t_pointer_array(moderation->mod_list, moderation->num_mods); + moderation->num_mods = 0; + moderation->mod_list = nullptr; +} + +uint16_t sanctions_creds_pack(const Mod_Sanction_Creds *creds, uint8_t *data) +{ + uint16_t packed_len = 0; + + net_pack_u32(data + packed_len, creds->version); + packed_len += sizeof(uint32_t); + memcpy(data + packed_len, creds->hash, MOD_SANCTION_HASH_SIZE); + packed_len += MOD_SANCTION_HASH_SIZE; + net_pack_u16(data + packed_len, creds->checksum); + packed_len += sizeof(uint16_t); + memcpy(data + packed_len, creds->sig_pk, SIG_PUBLIC_KEY_SIZE); + packed_len += SIG_PUBLIC_KEY_SIZE; + memcpy(data + packed_len, creds->sig, SIGNATURE_SIZE); + packed_len += SIGNATURE_SIZE; + + return packed_len; +} + +uint16_t sanctions_list_packed_size(uint16_t num_sanctions) +{ + return MOD_SANCTION_PACKED_SIZE * num_sanctions; +} + +int sanctions_list_pack(uint8_t *data, uint16_t length, const Mod_Sanction *sanctions, uint16_t num_sanctions, + const Mod_Sanction_Creds *creds) +{ + assert(sanctions != nullptr || num_sanctions == 0); + assert(sanctions != nullptr || creds != nullptr); + + uint16_t packed_len = 0; + + for (uint16_t i = 0; i < num_sanctions; ++i) { + if (packed_len + sizeof(uint8_t) + SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE > length) { + return -1; + } + + memcpy(data + packed_len, &sanctions[i].type, sizeof(uint8_t)); + packed_len += sizeof(uint8_t); + memcpy(data + packed_len, sanctions[i].setter_public_sig_key, SIG_PUBLIC_KEY_SIZE); + packed_len += SIG_PUBLIC_KEY_SIZE; + net_pack_u64(data + packed_len, sanctions[i].time_set); + packed_len += TIME_STAMP_SIZE; + + const uint8_t sanctions_type = sanctions[i].type; + + if (sanctions_type == SA_OBSERVER) { + if (packed_len + ENC_PUBLIC_KEY_SIZE > length) { + return -1; + } + + memcpy(data + packed_len, sanctions[i].target_public_enc_key, ENC_PUBLIC_KEY_SIZE); + packed_len += ENC_PUBLIC_KEY_SIZE; + } else { + return -1; + } + + if (packed_len + SIGNATURE_SIZE > length) { + return -1; + } + + /* Signature must be packed last */ + memcpy(data + packed_len, sanctions[i].signature, SIGNATURE_SIZE); + packed_len += SIGNATURE_SIZE; + } + + if (creds == nullptr) { + return packed_len; + } + + if (length < packed_len || length - packed_len < MOD_SANCTIONS_CREDS_SIZE) { + return -1; + } + + const uint16_t cred_len = sanctions_creds_pack(creds, data + packed_len); + + if (cred_len != MOD_SANCTIONS_CREDS_SIZE) { + return -1; + } + + return (int)(packed_len + cred_len); +} + +uint16_t sanctions_creds_unpack(Mod_Sanction_Creds *creds, const uint8_t *data) +{ + uint16_t len_processed = 0; + + net_unpack_u32(data + len_processed, &creds->version); + len_processed += sizeof(uint32_t); + memcpy(creds->hash, data + len_processed, MOD_SANCTION_HASH_SIZE); + len_processed += MOD_SANCTION_HASH_SIZE; + net_unpack_u16(data + len_processed, &creds->checksum); + len_processed += sizeof(uint16_t); + memcpy(creds->sig_pk, data + len_processed, SIG_PUBLIC_KEY_SIZE); + len_processed += SIG_PUBLIC_KEY_SIZE; + memcpy(creds->sig, data + len_processed, SIGNATURE_SIZE); + len_processed += SIGNATURE_SIZE; + + return len_processed; +} + +int sanctions_list_unpack(Mod_Sanction *sanctions, Mod_Sanction_Creds *creds, uint16_t max_sanctions, + const uint8_t *data, uint16_t length, uint16_t *processed_data_len) +{ + uint16_t num = 0; + uint16_t len_processed = 0; + + while (num < max_sanctions && num < MOD_MAX_NUM_SANCTIONS && len_processed < length) { + if (len_processed + sizeof(uint8_t) + SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE > length) { + return -1; + } + + memcpy(&sanctions[num].type, data + len_processed, sizeof(uint8_t)); + len_processed += sizeof(uint8_t); + memcpy(sanctions[num].setter_public_sig_key, data + len_processed, SIG_PUBLIC_KEY_SIZE); + len_processed += SIG_PUBLIC_KEY_SIZE; + net_unpack_u64(data + len_processed, &sanctions[num].time_set); + len_processed += TIME_STAMP_SIZE; + + if (sanctions[num].type == SA_OBSERVER) { + if (len_processed + ENC_PUBLIC_KEY_SIZE > length) { + return -1; + } + + memcpy(sanctions[num].target_public_enc_key, data + len_processed, ENC_PUBLIC_KEY_SIZE); + len_processed += ENC_PUBLIC_KEY_SIZE; + } else { + return -1; + } + + if (len_processed + SIGNATURE_SIZE > length) { + return -1; + } + + memcpy(sanctions[num].signature, data + len_processed, SIGNATURE_SIZE); + len_processed += SIGNATURE_SIZE; + + ++num; + } + + if (length <= len_processed || length - len_processed < MOD_SANCTIONS_CREDS_SIZE) { + if (length != len_processed) { + return -1; + } + + if (processed_data_len != nullptr) { + *processed_data_len = len_processed; + } + + return num; + } + + const uint16_t creds_len = sanctions_creds_unpack(creds, data + len_processed); + + if (creds_len != MOD_SANCTIONS_CREDS_SIZE) { + return -1; + } + + if (processed_data_len != nullptr) { + *processed_data_len = len_processed + creds_len; + } + + return num; +} + + +/** @brief Creates a new sanction list hash and puts it in hash. + * + * The hash is derived from the signature of all entries plus the version number. + * hash must have room for at least MOD_SANCTION_HASH_SIZE bytes. + * + * If num_sanctions is 0 the hash is zeroed. + * + * Return true on success. + */ +non_null(4) nullable(1) +static bool sanctions_list_make_hash(const Mod_Sanction *sanctions, uint32_t new_version, uint16_t num_sanctions, + uint8_t *hash) +{ + if (num_sanctions == 0 || sanctions == nullptr) { + memset(hash, 0, MOD_SANCTION_HASH_SIZE); + return true; + } + + const size_t sig_data_size = num_sanctions * SIGNATURE_SIZE; + const size_t data_buf_size = sig_data_size + sizeof(uint32_t); + + // check for integer overflower + if (data_buf_size < num_sanctions) { + return false; + } + + uint8_t *data = (uint8_t *)malloc(data_buf_size); + + if (data == nullptr) { + return false; + } + + for (uint16_t i = 0; i < num_sanctions; ++i) { + memcpy(&data[i * SIGNATURE_SIZE], sanctions[i].signature, SIGNATURE_SIZE); + } + + memcpy(&data[sig_data_size], &new_version, sizeof(uint32_t)); + crypto_sha256(hash, data, data_buf_size); + + free(data); + + return true; +} + +/** @brief Verifies that sanction contains valid info and was assigned by a current mod or group founder. + * + * Returns true on success. + */ +non_null() +static bool sanctions_list_validate_entry(const Moderation *moderation, const Mod_Sanction *sanction) +{ + if (!mod_list_verify_sig_pk(moderation, sanction->setter_public_sig_key)) { + return false; + } + + if (sanction->type >= SA_INVALID) { + return false; + } + + if (sanction->time_set == 0) { + return false; + } + + uint8_t packed_data[MOD_SANCTION_PACKED_SIZE]; + const int packed_len = sanctions_list_pack(packed_data, sizeof(packed_data), sanction, 1, nullptr); + + if (packed_len <= (int) SIGNATURE_SIZE) { + return false; + } + + return crypto_signature_verify(sanction->signature, packed_data, packed_len - SIGNATURE_SIZE, + sanction->setter_public_sig_key); +} + +non_null() +static uint16_t sanctions_creds_get_checksum(const Mod_Sanction_Creds *creds) +{ + return data_checksum(creds->hash, sizeof(creds->hash)); +} + +non_null() +static void sanctions_creds_set_checksum(Mod_Sanction_Creds *creds) +{ + creds->checksum = sanctions_creds_get_checksum(creds); +} + +bool sanctions_list_make_creds(Moderation *moderation) +{ + const Mod_Sanction_Creds old_creds = moderation->sanctions_creds; + + ++moderation->sanctions_creds.version; + + memcpy(moderation->sanctions_creds.sig_pk, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE); + + uint8_t hash[MOD_SANCTION_HASH_SIZE]; + + if (!sanctions_list_make_hash(moderation->sanctions, moderation->sanctions_creds.version, + moderation->num_sanctions, hash)) { + moderation->sanctions_creds = old_creds; + return false; + } + + memcpy(moderation->sanctions_creds.hash, hash, MOD_SANCTION_HASH_SIZE); + + sanctions_creds_set_checksum(&moderation->sanctions_creds); + + if (!crypto_signature_create(moderation->sanctions_creds.sig, moderation->sanctions_creds.hash, + MOD_SANCTION_HASH_SIZE, moderation->self_secret_sig_key)) { + moderation->sanctions_creds = old_creds; + return false; + } + + return true; +} + +/** @brief Validates sanction list credentials. + * + * Verifies that: + * - the public signature key belongs to a mod or the founder + * - the signature for the hash was made by the owner of the public signature key. + * - the received hash matches our own hash of the new sanctions list + * - the received checksum matches the received hash + * - the new version is >= our current version + * + * Returns true on success. + */ +non_null(1, 3) nullable(2) +static bool sanctions_creds_validate(const Moderation *moderation, const Mod_Sanction *sanctions, + const Mod_Sanction_Creds *creds, uint16_t num_sanctions) +{ + if (!mod_list_verify_sig_pk(moderation, creds->sig_pk)) { + LOGGER_WARNING(moderation->log, "Invalid credentials signature pk"); + return false; + } + + uint8_t hash[MOD_SANCTION_HASH_SIZE]; + + if (!sanctions_list_make_hash(sanctions, creds->version, num_sanctions, hash)) { + return false; + } + + if (memcmp(hash, creds->hash, MOD_SANCTION_HASH_SIZE) != 0) { + LOGGER_WARNING(moderation->log, "Invalid credentials hash"); + return false; + } + + if (creds->checksum != sanctions_creds_get_checksum(creds)) { + LOGGER_WARNING(moderation->log, "Invalid credentials checksum"); + return false; + } + + if (moderation->shared_state_version > 0) { + if ((creds->version < moderation->sanctions_creds.version) + && !(creds->version == 0 && moderation->sanctions_creds.version == UINT32_MAX)) { + LOGGER_WARNING(moderation->log, "Invalid version"); + return false; + } + } + + if (!crypto_signature_verify(creds->sig, hash, MOD_SANCTION_HASH_SIZE, creds->sig_pk)) { + LOGGER_WARNING(moderation->log, "Invalid signature"); + return false; + } + + return true; +} + +bool sanctions_list_check_integrity(const Moderation *moderation, const Mod_Sanction_Creds *creds, + const Mod_Sanction *sanctions, uint16_t num_sanctions) +{ + for (uint16_t i = 0; i < num_sanctions; ++i) { + if (!sanctions_list_validate_entry(moderation, &sanctions[i])) { + LOGGER_WARNING(moderation->log, "Invalid entry"); + return false; + } + } + + return sanctions_creds_validate(moderation, sanctions, creds, num_sanctions); +} + +/** @brief Validates a sanctions list if credentials are supplied. If successful, + * or if no credentials are supplid, assigns new sanctions list and credentials + * to moderation object. + * + * @param moderation The moderation object being operated on. + * @param new_sanctions The sanctions list to validate and assign to moderation object. + * @param new_creds The new sanctions credentials to be assigned to moderation object. + * @param num_sanctions The number of sanctions in the sanctions list. + * + * @retval false if sanctions credentials validation fails. + */ +non_null(1, 2) nullable(3) +static bool sanctions_apply_new(Moderation *moderation, Mod_Sanction *new_sanctions, + const Mod_Sanction_Creds *new_creds, + uint16_t num_sanctions) +{ + if (new_creds != nullptr) { + if (!sanctions_creds_validate(moderation, new_sanctions, new_creds, num_sanctions)) { + LOGGER_WARNING(moderation->log, "Failed to validate credentials"); + return false; + } + + moderation->sanctions_creds = *new_creds; + } + + sanctions_list_cleanup(moderation); + moderation->sanctions = new_sanctions; + moderation->num_sanctions = num_sanctions; + + return true; +} + +/** @brief Returns a copy of the sanctions list. The caller is responsible for freeing the + * memory returned by this function. + */ +non_null() +static Mod_Sanction *sanctions_list_copy(const Mod_Sanction *sanctions, uint16_t num_sanctions) +{ + Mod_Sanction *copy = (Mod_Sanction *)calloc(num_sanctions, sizeof(Mod_Sanction)); + + if (copy == nullptr) { + return nullptr; + } + + memcpy(copy, sanctions, num_sanctions * sizeof(Mod_Sanction)); + + return copy; +} + +/** @brief Removes index-th sanction list entry. + * + * New credentials will be validated if creds is non-null. + * + * Returns true on success. + */ +non_null(1) nullable(3) +static bool sanctions_list_remove_index(Moderation *moderation, uint16_t index, const Mod_Sanction_Creds *creds) +{ + if (index >= moderation->num_sanctions) { + return false; + } + + const uint16_t new_num = moderation->num_sanctions - 1; + + if (new_num == 0) { + if (creds != nullptr) { + if (!sanctions_creds_validate(moderation, nullptr, creds, 0)) { + return false; + } + + moderation->sanctions_creds = *creds; + } + + sanctions_list_cleanup(moderation); + + return true; + } + + /* Operate on a copy of the list in case something goes wrong. */ + Mod_Sanction *sanctions_copy = sanctions_list_copy(moderation->sanctions, moderation->num_sanctions); + + if (sanctions_copy == nullptr) { + return false; + } + + if (index != new_num) { + sanctions_copy[index] = sanctions_copy[new_num]; + } + + Mod_Sanction *new_list = (Mod_Sanction *)realloc(sanctions_copy, new_num * sizeof(Mod_Sanction)); + + if (new_list == nullptr) { + free(sanctions_copy); + return false; + } + + if (!sanctions_apply_new(moderation, new_list, creds, new_num)) { + free(new_list); + return false; + } + + return true; +} + +bool sanctions_list_remove_observer(Moderation *moderation, const uint8_t *public_key, + const Mod_Sanction_Creds *creds) +{ + for (uint16_t i = 0; i < moderation->num_sanctions; ++i) { + const Mod_Sanction *curr_sanction = &moderation->sanctions[i]; + + if (curr_sanction->type != SA_OBSERVER) { + continue; + } + + if (memcmp(public_key, curr_sanction->target_public_enc_key, ENC_PUBLIC_KEY_SIZE) == 0) { + if (!sanctions_list_remove_index(moderation, i, creds)) { + return false; + } + + if (creds == nullptr) { + return sanctions_list_make_creds(moderation); + } + + return true; + } + } + + return false; +} + +bool sanctions_list_is_observer(const Moderation *moderation, const uint8_t *public_key) +{ + for (uint16_t i = 0; i < moderation->num_sanctions; ++i) { + const Mod_Sanction *curr_sanction = &moderation->sanctions[i]; + + if (curr_sanction->type != SA_OBSERVER) { + continue; + } + + if (memcmp(curr_sanction->target_public_enc_key, public_key, ENC_PUBLIC_KEY_SIZE) == 0) { + return true; + } + } + + return false; +} + +bool sanctions_list_entry_exists(const Moderation *moderation, const Mod_Sanction *sanction) +{ + if (sanction->type == SA_OBSERVER) { + return sanctions_list_is_observer(moderation, sanction->target_public_enc_key); + } + + return false; +} + +bool sanctions_list_add_entry(Moderation *moderation, const Mod_Sanction *sanction, const Mod_Sanction_Creds *creds) +{ + if (moderation->num_sanctions >= MOD_MAX_NUM_SANCTIONS) { + LOGGER_WARNING(moderation->log, "num_sanctions %d exceeds maximum", moderation->num_sanctions); + return false; + } + + if (!sanctions_list_validate_entry(moderation, sanction)) { + LOGGER_ERROR(moderation->log, "Failed to validate sanction"); + return false; + } + + if (sanctions_list_entry_exists(moderation, sanction)) { + LOGGER_WARNING(moderation->log, "Attempted to add duplicate sanction"); + return false; + } + + /* Operate on a copy of the list in case something goes wrong. */ + Mod_Sanction *sanctions_copy = nullptr; + + if (moderation->num_sanctions > 0) { + sanctions_copy = sanctions_list_copy(moderation->sanctions, moderation->num_sanctions); + + if (sanctions_copy == nullptr) { + return false; + } + } + + const uint16_t index = moderation->num_sanctions; + Mod_Sanction *new_list = (Mod_Sanction *)realloc(sanctions_copy, (index + 1) * sizeof(Mod_Sanction)); + + if (new_list == nullptr) { + free(sanctions_copy); + return false; + } + + new_list[index] = *sanction; + + if (!sanctions_apply_new(moderation, new_list, creds, index + 1)) { + free(new_list); + return false; + } + + return true; +} + +/** @brief Signs packed sanction data. + * + * This function must be called by the owner of the entry's public_sig_key. + * + * Returns true on success. + */ +non_null() +static bool sanctions_list_sign_entry(const Moderation *moderation, Mod_Sanction *sanction) +{ + uint8_t packed_data[MOD_SANCTION_PACKED_SIZE]; + const int packed_len = sanctions_list_pack(packed_data, sizeof(packed_data), sanction, 1, nullptr); + + if (packed_len <= (int) SIGNATURE_SIZE) { + LOGGER_ERROR(moderation->log, "Failed to pack sanctions list: %d", packed_len); + return false; + } + + return crypto_signature_create(sanction->signature, packed_data, packed_len - SIGNATURE_SIZE, + moderation->self_secret_sig_key); +} + +bool sanctions_list_make_entry(Moderation *moderation, const uint8_t *public_key, Mod_Sanction *sanction, + uint8_t type) +{ + *sanction = (Mod_Sanction) { + 0 + }; + + if (type == SA_OBSERVER) { + memcpy(sanction->target_public_enc_key, public_key, ENC_PUBLIC_KEY_SIZE); + } else { + LOGGER_ERROR(moderation->log, "Tried to create sanction with invalid type: %u", type); + return false; + } + + memcpy(sanction->setter_public_sig_key, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE); + + sanction->time_set = (uint64_t)time(nullptr); + sanction->type = type; + + if (!sanctions_list_sign_entry(moderation, sanction)) { + LOGGER_ERROR(moderation->log, "Failed to sign sanction"); + return false; + } + + if (!sanctions_list_add_entry(moderation, sanction, nullptr)) { + return false; + } + + if (!sanctions_list_make_creds(moderation)) { + LOGGER_ERROR(moderation->log, "Failed to make credentials for new sanction"); + return false; + } + + return true; +} +uint16_t sanctions_list_replace_sig(Moderation *moderation, const uint8_t *public_sig_key) +{ + uint16_t count = 0; + + for (uint16_t i = 0; i < moderation->num_sanctions; ++i) { + if (memcmp(moderation->sanctions[i].setter_public_sig_key, public_sig_key, SIG_PUBLIC_KEY_SIZE) != 0) { + continue; + } + + memcpy(moderation->sanctions[i].setter_public_sig_key, moderation->self_public_sig_key, SIG_PUBLIC_KEY_SIZE); + + if (!sanctions_list_sign_entry(moderation, &moderation->sanctions[i])) { + LOGGER_ERROR(moderation->log, "Failed to sign sanction"); + continue; + } + + ++count; + } + + if (count > 0) { + if (!sanctions_list_make_creds(moderation)) { + return 0; + } + } + + return count; +} + +void sanctions_list_cleanup(Moderation *moderation) +{ + if (moderation->sanctions != nullptr) { + free(moderation->sanctions); + } + + moderation->sanctions = nullptr; + moderation->num_sanctions = 0; +} diff --git a/protocols/Tox/libtox/src/toxcore/group_moderation.h b/protocols/Tox/libtox/src/toxcore/group_moderation.h new file mode 100644 index 0000000000..36b44a42f0 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/group_moderation.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2020 The TokTok team. + * Copyright © 2015 Tox project. + */ + +/** + * An implementation of massive text only group chats. + */ + +#ifndef C_TOXCORE_TOXCORE_GROUP_MODERATION_H +#define C_TOXCORE_TOXCORE_GROUP_MODERATION_H + +#include <stdbool.h> +#include <stdint.h> + +#include "DHT.h" +#include "logger.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MOD_MODERATION_HASH_SIZE CRYPTO_SHA256_SIZE +#define MOD_LIST_ENTRY_SIZE SIG_PUBLIC_KEY_SIZE +#define MOD_SANCTION_HASH_SIZE CRYPTO_SHA256_SIZE + +#define TIME_STAMP_SIZE sizeof(uint64_t) + +/* The packed size of a Mod_Sanction_Creds */ +#define MOD_SANCTIONS_CREDS_SIZE (sizeof(uint32_t) + MOD_SANCTION_HASH_SIZE + sizeof(uint16_t) +\ + SIG_PUBLIC_KEY_SIZE + SIGNATURE_SIZE) + +/* The packed size of a Mod_Sanction */ +#define MOD_SANCTION_PACKED_SIZE (SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE + 1 + ENC_PUBLIC_KEY_SIZE + SIGNATURE_SIZE) + +/* The max size of a groupchat packet with 100 bytes reserved for header data */ +#define MAX_PACKET_SIZE_NO_HEADERS 49900 + +/* These values must take into account the maximum allowed packet size and headers. */ +#define MOD_MAX_NUM_MODERATORS (((MAX_PACKET_SIZE_NO_HEADERS) / (MOD_LIST_ENTRY_SIZE))) +#define MOD_MAX_NUM_SANCTIONS (((MAX_PACKET_SIZE_NO_HEADERS - (MOD_SANCTIONS_CREDS_SIZE)) / (MOD_SANCTION_PACKED_SIZE))) + +typedef enum Mod_Sanction_Type { + SA_OBSERVER = 0x00, + SA_INVALID = 0x01, +} Mod_Sanction_Type; + +typedef struct Mod_Sanction_Creds { + uint32_t version; + uint8_t hash[MOD_SANCTION_HASH_SIZE]; // hash of all sanctions list signatures + version + uint16_t checksum; // a sum of the hash + uint8_t sig_pk[SIG_PUBLIC_KEY_SIZE]; // Last mod to have modified the sanctions list + uint8_t sig[SIGNATURE_SIZE]; // signature of hash, signed by sig_pk +} Mod_Sanction_Creds; + +/** Holds data pertaining to a peer who has been sanctioned. */ +typedef struct Mod_Sanction { + uint8_t setter_public_sig_key[SIG_PUBLIC_KEY_SIZE]; + + // TODO(Jfreegman): This timestamp can potentially be used to track a user across + // different group chats if they're a moderator and set many sanctions across the + // different groups. This should be addressed in the future. + uint64_t time_set; + + uint8_t type; + uint8_t target_public_enc_key[ENC_PUBLIC_KEY_SIZE]; + + /* Signature of all above packed data signed by the owner of public_sig_key */ + uint8_t signature[SIGNATURE_SIZE]; +} Mod_Sanction; + +typedef struct Moderation { + const Logger *log; + + Mod_Sanction *sanctions; + uint16_t num_sanctions; + + Mod_Sanction_Creds sanctions_creds; + + uint8_t **mod_list; // array of public signature keys of all the mods + uint16_t num_mods; + + // copies from parent/sibling chat/shared state objects + uint8_t founder_public_sig_key[SIG_PUBLIC_KEY_SIZE]; + uint8_t self_public_sig_key[SIG_PUBLIC_KEY_SIZE]; + uint8_t self_secret_sig_key[SIG_SECRET_KEY_SIZE]; + uint32_t shared_state_version; +} Moderation; + +/** @brief Returns the size in bytes of the packed moderation list. */ +non_null() +uint16_t mod_list_packed_size(const Moderation *moderation); + +/** @brief Unpacks data into the moderator list. + * + * @param data should contain num_mods entries of size MOD_LIST_ENTRY_SIZE. + * + * Returns length of unpacked data on success. + * Returns -1 on failure. + */ +non_null() +int mod_list_unpack(Moderation *moderation, const uint8_t *data, uint16_t length, uint16_t num_mods); + +/** @brief Packs moderator list into data. + * @param data must have room for the number of bytes returned by `mod_list_packed_size`. + */ +non_null() +void mod_list_pack(const Moderation *moderation, uint8_t *data); + +/** @brief Creates a new moderator list hash and puts it in `hash`. + * + * @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes. + * + * If num_mods is 0 the hash is zeroed. + * + * Returns true on sucess. + */ +non_null() +bool mod_list_make_hash(const Moderation *moderation, uint8_t *hash); + +/** @brief Puts a sha256 hash of `packed_mod_list` of `length` bytes in `hash`. + * + * @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes. + */ +non_null() +void mod_list_get_data_hash(uint8_t *hash, const uint8_t *packed_mod_list, uint16_t length); + +/** @brief Removes moderator at index-th position in the moderator list. + * + * Returns true on success. + */ +non_null() +bool mod_list_remove_index(Moderation *moderation, uint16_t index); + +/** @brief Removes public_sig_key from the moderator list. + * + * Returns true on success. + */ +non_null() +bool mod_list_remove_entry(Moderation *moderation, const uint8_t *public_sig_key); + +/** @brief Adds a mod to the moderator list. + * + * @param mod_data must be MOD_LIST_ENTRY_SIZE bytes. + * + * Returns true on success. + */ +non_null() +bool mod_list_add_entry(Moderation *moderation, const uint8_t *mod_data); + +/** @return true if the public signature key belongs to a moderator or the founder */ +non_null() +bool mod_list_verify_sig_pk(const Moderation *moderation, const uint8_t *sig_pk); + +/** @brief Frees all memory associated with the moderator list and sets num_mods to 0. */ +nullable(1) +void mod_list_cleanup(Moderation *moderation); + +/** @brief Returns the size in bytes of num_sanctions packed sanctions. */ +uint16_t sanctions_list_packed_size(uint16_t num_sanctions); + +/** @brief Packs sanctions into data. Additionally packs the sanctions credentials into creds. + * + * @param data The byte array being packed. Must have room for the number of bytes returned + * by `sanctions_list_packed_size`. + * @param length The size of the byte array. + * @param sanctions The sanctions list. + * @param num_sanctions The number of sanctions in the sanctions list. This value must be the same + * value used when calling `sanctions_list_packed_size`. + * @param creds The credentials object to fill. + * + * @retval The length of packed data on success. + * @retval -1 on failure. + */ +non_null(1) nullable(3, 5) +int sanctions_list_pack(uint8_t *data, uint16_t length, const Mod_Sanction *sanctions, uint16_t num_sanctions, + const Mod_Sanction_Creds *creds); + +/** @brief Unpacks sanctions and new sanctions credentials. + * + * @param sanctions The sanctions array the sanctions data is unpacked into. + * @param creds The creds object the creds data is unpacked into. + * @param max_sanctions The maximum number of sanctions that the sanctions array can hold. + * @param data The packed data array. + * @param length The size of the packed data. + * @param processed_data_len If non-null, will contain the number of processed bytes on success. + * + * @retval The number of unpacked entries on success. + * @retval -1 on failure. + */ +non_null(1, 2, 4) nullable(6) +int sanctions_list_unpack(Mod_Sanction *sanctions, Mod_Sanction_Creds *creds, uint16_t max_sanctions, + const uint8_t *data, uint16_t length, uint16_t *processed_data_len); + +/** @brief Packs sanction list credentials into data. + * + * @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes. + * + * Returns length of packed data. + */ +non_null() +uint16_t sanctions_creds_pack(const Mod_Sanction_Creds *creds, uint8_t *data); + +/** @brief Unpacks sanctions credentials into creds from data. + * + * @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes. + * + * Returns the length of the data processed. + */ +non_null() +uint16_t sanctions_creds_unpack(Mod_Sanction_Creds *creds, const uint8_t *data); + +/** @brief Updates sanction list credentials. + * + * Increment version, replace sig_pk with your own, update hash to reflect new + * sanction list, and sign new hash signature. + * + * Returns true on success. + */ +non_null() +bool sanctions_list_make_creds(Moderation *moderation); + +/** @brief Validates all sanctions list entries as well as the list itself. + * + * Returns true if all entries are valid. + * Returns false if one or more entries are invalid. + */ +non_null() +bool sanctions_list_check_integrity(const Moderation *moderation, const Mod_Sanction_Creds *creds, + const Mod_Sanction *sanctions, uint16_t num_sanctions); + +/** @brief Adds an entry to the sanctions list. + * + * The entry is first validated and the resulting new sanction list is + * compared against the new credentials. + * + * Entries must be unique. + * + * Returns true on success. + */ +non_null(1, 2) nullable(3) +bool sanctions_list_add_entry(Moderation *moderation, const Mod_Sanction *sanction, const Mod_Sanction_Creds *creds); + +/** @brief Creates a new sanction entry for `public_key` where type is one of Mod_Sanction_Type. + * + * New entry is signed and placed in the sanctions list. + * + * Returns true on success. + */ +non_null() +bool sanctions_list_make_entry(Moderation *moderation, const uint8_t *public_key, Mod_Sanction *sanction, + uint8_t type); + +/** @return true if public key is in the observer list. */ +non_null() +bool sanctions_list_is_observer(const Moderation *moderation, const uint8_t *public_key); + +/** @return true if sanction already exists in the sanctions list. */ +non_null() +bool sanctions_list_entry_exists(const Moderation *moderation, const Mod_Sanction *sanction); + +/** @brief Removes observer entry for public key from sanction list. + * + * If creds is NULL we make new credentials (this should only be done by a moderator or founder) + * + * Returns false on failure or if entry was not found. + */ +non_null(1, 2) nullable(3) +bool sanctions_list_remove_observer(Moderation *moderation, const uint8_t *public_key, + const Mod_Sanction_Creds *creds); + +/** @brief Replaces all sanctions list signatures made by public_sig_key with the caller's. + * + * This is called whenever the founder demotes a moderator. + * + * Returns the number of entries re-signed. + */ +non_null() +uint16_t sanctions_list_replace_sig(Moderation *moderation, const uint8_t *public_sig_key); + +non_null() +void sanctions_list_cleanup(Moderation *moderation); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // C_TOXCORE_TOXCORE_GROUP_MODERATION_H diff --git a/protocols/Tox/libtox/src/toxcore/list.c b/protocols/Tox/libtox/src/toxcore/list.c index 02a0b1e536..e367487380 100644 --- a/protocols/Tox/libtox/src/toxcore/list.c +++ b/protocols/Tox/libtox/src/toxcore/list.c @@ -5,8 +5,8 @@ /** * Simple struct with functions to create a list which associates ids with data - * -Allows for finding ids associated with data such as IPs or public keys in a short time - * -Should only be used if there are relatively few add/remove calls to the list + * - Allows for finding ids associated with data such as IPs or public keys in a short time + * - Should only be used if there are relatively few add/remove calls to the list */ #include "list.h" @@ -16,15 +16,16 @@ #include "ccompat.h" -/** Basically, the elements in the list are placed in order so that they can be searched for easily - * -each element is seen as a big-endian integer when ordering them - * -the ids array is maintained so that each id always matches - * -the search algorithm cuts down the time to find the id associated with a piece of data +/** + * Basically, the elements in the list are placed in order so that they can be searched for easily + * - each element is seen as a big-endian integer when ordering them + * - the ids array is maintained so that each id always matches + * - the search algorithm cuts down the time to find the id associated with a piece of data * at the cost of slow add/remove functions for large lists - * -Starts at 1/2 of the array, compares the element in the array with the data, - * then moves +/- 1/4 of the array depending on whether the value is greater or lower, - * then +- 1/8, etc, until the value is matched or its position where it should be in the array is found - * -some considerations since the array size is never perfect + * - Starts at `1/2` of the array, compares the element in the array with the data, + * then moves `+/- 1/4` of the array depending on whether the value is greater or lower, + * then `+- 1/8`, etc, until the value is matched or its position where it should be in the array is found + * - some considerations since the array size is never perfect */ static int32_t @@ -33,13 +34,13 @@ list_index(uint32_t i) return ~i; } -/** Find data in list +/** @brief Find data in list * - * return value: - * >= 0 : index of data in array - * < 0 : no match, returns index (return value is list_index(index)) where - * the data should be inserted + * @retval >=0 index of data in array + * @retval <0 no match, returns index (return value is `list_index(index)`) where + * the data should be inserted */ +non_null() static int find(const BS_List *list, const uint8_t *data) { // should work well, but could be improved @@ -50,15 +51,15 @@ static int find(const BS_List *list, const uint8_t *data) uint32_t i = list->n / 2; // current position in the array uint32_t delta = i / 2; // how much we move in the array - if (!delta) { + if (delta == 0) { delta = 1; } int d = -1; // used to determine if closest match is found // closest match is found if we move back to where we have already been - while (1) { - int r = memcmp(data, list->data + list->element_size * i, list->element_size); + while (true) { + const int r = memcmp(data, list->data + list->element_size * i, list->element_size); if (r == 0) { return i; @@ -105,6 +106,7 @@ static int find(const BS_List *list, const uint8_t *data) * * @return true on success. */ +non_null() static bool resize(BS_List *list, uint32_t new_size) { if (new_size == 0) { @@ -114,7 +116,7 @@ static bool resize(BS_List *list, uint32_t new_size) uint8_t *data = (uint8_t *)realloc(list->data, list->element_size * new_size); - if (!data) { + if (data == nullptr) { return false; } @@ -122,7 +124,7 @@ static bool resize(BS_List *list, uint32_t new_size) int *ids = (int *)realloc(list->ids, sizeof(int) * new_size); - if (!ids) { + if (ids == nullptr) { return false; } @@ -154,6 +156,10 @@ int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity void bs_list_free(BS_List *list) { + if (list == nullptr) { + return; + } + // free both arrays free(list->data); list->data = nullptr; @@ -164,7 +170,7 @@ void bs_list_free(BS_List *list) int bs_list_find(const BS_List *list, const uint8_t *data) { - int r = find(list, data); + const int r = find(list, data); // return only -1 and positive values if (r < 0) { @@ -174,7 +180,7 @@ int bs_list_find(const BS_List *list, const uint8_t *data) return list->ids[r]; } -int bs_list_add(BS_List *list, const uint8_t *data, int id) +bool bs_list_add(BS_List *list, const uint8_t *data, int id) { // find where the new element should be inserted // see: return value of find() @@ -182,7 +188,7 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id) if (i >= 0) { // already in list - return 0; + return false; } i = ~i; @@ -193,7 +199,7 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id) const uint32_t new_capacity = list->n + list->n / 2 + 1; if (!resize(list, new_capacity)) { - return 0; + return false; } list->capacity = new_capacity; @@ -211,20 +217,20 @@ int bs_list_add(BS_List *list, const uint8_t *data, int id) // increase n ++list->n; - return 1; + return true; } -int bs_list_remove(BS_List *list, const uint8_t *data, int id) +bool bs_list_remove(BS_List *list, const uint8_t *data, int id) { - int i = find(list, data); + const int i = find(list, data); if (i < 0) { - return 0; + return false; } if (list->ids[i] != id) { // this should never happen - return 0; + return false; } // decrease the size of the arrays if needed @@ -242,5 +248,5 @@ int bs_list_remove(BS_List *list, const uint8_t *data, int id) (list->n - i) * list->element_size); memmove(&list->ids[i], &list->ids[i + 1], (list->n - i) * sizeof(int)); - return 1; + return true; } diff --git a/protocols/Tox/libtox/src/toxcore/list.h b/protocols/Tox/libtox/src/toxcore/list.h index 1cbf142282..a7c0e56c06 100644 --- a/protocols/Tox/libtox/src/toxcore/list.h +++ b/protocols/Tox/libtox/src/toxcore/list.h @@ -11,8 +11,11 @@ #ifndef C_TOXCORE_TOXCORE_LIST_H #define C_TOXCORE_TOXCORE_LIST_H +#include <stdbool.h> #include <stdint.h> +#include "attributes.h" + #ifdef __cplusplus extern "C" { #endif @@ -25,41 +28,44 @@ typedef struct BS_List { int *ids; // array of element ids } BS_List; -/** Initialize a list, element_size is the size of the elements in the list and - * initial_capacity is the number of elements the memory will be initially allocated for +/** @brief Initialize a list. + * + * @param element_size is the size of the elements in the list. + * @param initial_capacity is the number of elements the memory will be initially allocated for. * - * return value: - * 1 : success - * 0 : failure + * @retval 1 success + * @retval 0 failure */ +non_null() int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity); /** Free a list initiated with list_init */ +nullable(1) void bs_list_free(BS_List *list); -/** Retrieve the id of an element in the list +/** @brief Retrieve the id of an element in the list * - * return value: - * >= 0 : id associated with data - * -1 : failure + * @retval >=0 id associated with data + * @retval -1 failure */ +non_null() int bs_list_find(const BS_List *list, const uint8_t *data); -/** Add an element with associated id to the list +/** @brief Add an element with associated id to the list * - * return value: - * 1 : success - * 0 : failure (data already in list) + * @retval true success + * @retval false failure (data already in list) */ -int bs_list_add(BS_List *list, const uint8_t *data, int id); +non_null() +bool bs_list_add(BS_List *list, const uint8_t *data, int id); -/** Remove element from the list +/** @brief Remove element from the list * - * return value: - * 1 : success - * 0 : failure (element not found or id does not match) + * @retval true success + * @retval false failure (element not found or id does not match) */ -int bs_list_remove(BS_List *list, const uint8_t *data, int id); +non_null() +bool bs_list_remove(BS_List *list, const uint8_t *data, int id); #ifdef __cplusplus } // extern "C" diff --git a/protocols/Tox/libtox/src/toxcore/logger.c b/protocols/Tox/libtox/src/toxcore/logger.c index 67e4a3e5ce..d281a66085 100644 --- a/protocols/Tox/libtox/src/toxcore/logger.c +++ b/protocols/Tox/libtox/src/toxcore/logger.c @@ -14,6 +14,7 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" struct Logger { logger_cb *callback; @@ -21,7 +22,7 @@ struct Logger { void *userdata; }; -#ifdef USE_STDERR_LOGGER +#ifndef NDEBUG static const char *logger_level_name(Logger_Level level) { switch (level) { @@ -43,12 +44,18 @@ static const char *logger_level_name(Logger_Level level) return "<unknown>"; } +#endif +non_null(1, 3, 5, 6) nullable(7) static void logger_stderr_handler(void *context, Logger_Level level, const char *file, int line, const char *func, const char *message, void *userdata) { +#ifndef NDEBUG // GL stands for "global logger". fprintf(stderr, "[GL] %s %s:%d(%s): %s\n", logger_level_name(level), file, line, func, message); + fprintf(stderr, "Default stderr logger triggered; aborting program\n"); + abort(); +#endif } static const Logger logger_stderr = { @@ -56,7 +63,6 @@ static const Logger logger_stderr = { nullptr, nullptr, }; -#endif /* * Public Functions @@ -82,16 +88,11 @@ void logger_callback_log(Logger *log, logger_cb *function, void *context, void * void logger_write(const Logger *log, Logger_Level level, const char *file, int line, const char *func, const char *format, ...) { - if (!log) { -#ifdef USE_STDERR_LOGGER + if (log == nullptr) { log = &logger_stderr; -#else - fprintf(stderr, "NULL logger not permitted.\n"); - abort(); -#endif } - if (!log->callback) { + if (log->callback == nullptr) { return; } @@ -104,7 +105,7 @@ void logger_write(const Logger *log, Logger_Level level, const char *file, int l // On Windows, the path separator *may* be a backslash, so we look for that // one too. const char *windows_filename = strrchr(file, '\\'); - file = windows_filename ? windows_filename + 1 : file; + file = windows_filename != nullptr ? windows_filename + 1 : file; #endif // Format message diff --git a/protocols/Tox/libtox/src/toxcore/logger.h b/protocols/Tox/libtox/src/toxcore/logger.h index c3d8f66d61..ee5838ae73 100644 --- a/protocols/Tox/libtox/src/toxcore/logger.h +++ b/protocols/Tox/libtox/src/toxcore/logger.h @@ -11,7 +11,11 @@ #include <stdint.h> -#include "ccompat.h" +#include "attributes.h" + +#ifdef __cplusplus +extern "C" { +#endif #ifndef MIN_LOGGER_LEVEL #define MIN_LOGGER_LEVEL LOGGER_LEVEL_INFO @@ -39,55 +43,61 @@ Logger *logger_new(void); /** * Frees all resources associated with the logger. */ +nullable(1) void logger_kill(Logger *log); /** * Sets the logger callback. Disables logging if set to NULL. * The context parameter is passed to the callback as first argument. */ +non_null(1) nullable(2, 3, 4) void logger_callback_log(Logger *log, logger_cb *function, void *context, void *userdata); -/** - * Main write function. If logging is disabled, this does nothing. +/** @brief Main write function. If logging is disabled, this does nothing. + * + * If the logger is NULL and `NDEBUG` is not defined, this writes to stderr. + * This behaviour should not be used in production code, but can be useful for + * temporarily debugging a function that does not have a logger available. It's + * essentially `fprintf(stderr, ...)`, but with source location. * - * If the logger is NULL, this writes to stderr. This behaviour should not be - * used in production code, but can be useful for temporarily debugging a - * function that does not have a logger available. It's essentially - * fprintf(stderr, ...), but with timestamps and source location. Toxcore must - * be built with -DUSE_STDERR_LOGGER for this to work. It will cause an - * assertion failure otherwise. + * If `NDEBUG` is defined, the NULL logger does nothing. */ +non_null(3, 5, 6) nullable(1) GNU_PRINTF(6, 7) void logger_write( const Logger *log, Logger_Level level, const char *file, int line, const char *func, - const char *format, ...) GNU_PRINTF(6, 7); + const char *format, ...); -#define LOGGER_WRITE(log, level, ...) \ - do { \ - if (level >= MIN_LOGGER_LEVEL) { \ +#define LOGGER_WRITE(log, level, ...) \ + do { \ + if (level >= MIN_LOGGER_LEVEL) { \ logger_write(log, level, __FILE__, __LINE__, __func__, __VA_ARGS__); \ - } \ + } \ } while (0) /* To log with an logger */ -#define LOGGER_TRACE(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_TRACE , __VA_ARGS__) -#define LOGGER_DEBUG(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_DEBUG , __VA_ARGS__) -#define LOGGER_INFO(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_INFO , __VA_ARGS__) +#define LOGGER_TRACE(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_TRACE, __VA_ARGS__) +#define LOGGER_DEBUG(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_DEBUG, __VA_ARGS__) +#define LOGGER_INFO(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_INFO, __VA_ARGS__) #define LOGGER_WARNING(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_WARNING, __VA_ARGS__) -#define LOGGER_ERROR(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_ERROR , __VA_ARGS__) +#define LOGGER_ERROR(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_ERROR, __VA_ARGS__) -#define LOGGER_FATAL(log, ...) \ - do { \ +#define LOGGER_FATAL(log, ...) \ + do { \ LOGGER_ERROR(log, __VA_ARGS__); \ - abort(); \ - } while(0) + abort(); \ + } while (0) -#define LOGGER_ASSERT(log, cond, ...) \ - do { \ - if (!(cond)) { \ +#define LOGGER_ASSERT(log, cond, ...) \ + do { \ + if (!(cond)) { \ LOGGER_ERROR(log, "Assertion failed"); \ - LOGGER_FATAL(log, __VA_ARGS__); \ - } \ - } while(0) + LOGGER_FATAL(log, __VA_ARGS__); \ + } \ + } while (0) + +#ifdef __cplusplus +} // extern "C" +#endif #endif // C_TOXCORE_TOXCORE_LOGGER_H diff --git a/protocols/Tox/libtox/src/toxcore/mono_time.c b/protocols/Tox/libtox/src/toxcore/mono_time.c index e207996df6..df64f2246c 100644 --- a/protocols/Tox/libtox/src/toxcore/mono_time.c +++ b/protocols/Tox/libtox/src/toxcore/mono_time.c @@ -26,21 +26,16 @@ #include <sys/time.h> #endif +#include <assert.h> #include <pthread.h> #include <stdlib.h> #include <time.h> #include "ccompat.h" -//!TOKSTYLE- -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#include "../testing/fuzzing/fuzz_adapter.h" -#endif -//!TOKSTYLE+ - /** don't call into system billions of times for no reason */ struct Mono_Time { - uint64_t time; + uint64_t cur_time; uint64_t base_time; #ifdef OS_WIN32 /* protect `last_clock_update` and `last_clock_mono` from concurrent access */ @@ -49,27 +44,31 @@ struct Mono_Time { bool last_clock_update; #endif +#ifndef ESP_PLATFORM /* protect `time` from concurrent access */ pthread_rwlock_t *time_update_lock; +#endif mono_time_current_time_cb *current_time_callback; void *user_data; }; -static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_data) -{ - uint64_t time = 0; #ifdef OS_WIN32 +non_null() +static uint64_t current_time_monotonic_default(void *user_data) +{ + Mono_Time *const mono_time = (Mono_Time *)user_data; + /* Must hold mono_time->last_clock_lock here */ /* GetTickCount provides only a 32 bit counter, but we can't use * GetTickCount64 for backwards compatibility, so we handle wraparound * ourselves. */ - uint32_t ticks = GetTickCount(); + const uint32_t ticks = GetTickCount(); /* the higher 32 bits count the number of wrap arounds */ - uint64_t old_ovf = mono_time->time & ~((uint64_t)UINT32_MAX); + uint64_t old_ovf = mono_time->cur_time & ~((uint64_t)UINT32_MAX); /* Check if time has decreased because of 32 bit wrap from GetTickCount() */ if (ticks < mono_time->last_clock_mono) { @@ -83,10 +82,18 @@ static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_ } /* splice the low and high bits back together */ - time = old_ovf + ticks; -#else + return old_ovf + ticks; +} +#else // !OS_WIN32 +static uint64_t timespec_to_u64(struct timespec clock_mono) +{ + return 1000ULL * clock_mono.tv_sec + (clock_mono.tv_nsec / 1000000ULL); +} +#ifdef __APPLE__ +non_null() +static uint64_t current_time_monotonic_default(void *user_data) +{ struct timespec clock_mono; -#if defined(__APPLE__) clock_serv_t muhclock; mach_timespec_t machtime; @@ -96,23 +103,26 @@ static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_ clock_mono.tv_sec = machtime.tv_sec; clock_mono.tv_nsec = machtime.tv_nsec; -#else - clock_gettime(CLOCK_MONOTONIC, &clock_mono); -#endif - time = 1000ULL * clock_mono.tv_sec + (clock_mono.tv_nsec / 1000000ULL); -#endif - return time; + return timespec_to_u64(clock_mono); } - - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -static uint64_t current_time_monotonic_dummy(Mono_Time *mono_time, void *user_data) +#else // !__APPLE__ +non_null() +static uint64_t current_time_monotonic_default(void *user_data) { - return fuzz_get_count(); -} +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // This assert should always fail. If it does, the fuzzing harness didn't + // override the mono time callback. + assert(user_data == nullptr); #endif + struct timespec clock_mono; + clock_gettime(CLOCK_MONOTONIC, &clock_mono); + return timespec_to_u64(clock_mono); +} +#endif // !__APPLE__ +#endif // !OS_WIN32 + -Mono_Time *mono_time_new(void) +Mono_Time *mono_time_new(mono_time_current_time_cb *current_time_callback, void *user_data) { Mono_Time *mono_time = (Mono_Time *)calloc(1, sizeof(Mono_Time)); @@ -120,6 +130,7 @@ Mono_Time *mono_time_new(void) return nullptr; } +#ifndef ESP_PLATFORM mono_time->time_update_lock = (pthread_rwlock_t *)calloc(1, sizeof(pthread_rwlock_t)); if (mono_time->time_update_lock == nullptr) { @@ -132,13 +143,9 @@ Mono_Time *mono_time_new(void) free(mono_time); return nullptr; } - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - mono_time->current_time_callback = current_time_monotonic_dummy; -#else - mono_time->current_time_callback = current_time_monotonic_default; #endif - mono_time->user_data = nullptr; + + mono_time_set_current_time_callback(mono_time, current_time_callback, user_data); #ifdef OS_WIN32 @@ -153,9 +160,10 @@ Mono_Time *mono_time_new(void) #endif - mono_time->time = 0; + mono_time->cur_time = 0; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - mono_time->base_time = 0; // Maximum reproducibility + // Maximum reproducibility. Never return time = 0. + mono_time->base_time = 1; #else mono_time->base_time = (uint64_t)time(nullptr) - (current_time_monotonic(mono_time) / 1000ULL); #endif @@ -167,44 +175,56 @@ Mono_Time *mono_time_new(void) void mono_time_free(Mono_Time *mono_time) { + if (mono_time == nullptr) { + return; + } #ifdef OS_WIN32 pthread_mutex_destroy(&mono_time->last_clock_lock); #endif +#ifndef ESP_PLATFORM pthread_rwlock_destroy(mono_time->time_update_lock); free(mono_time->time_update_lock); +#endif free(mono_time); } void mono_time_update(Mono_Time *mono_time) { - uint64_t time = 0; + uint64_t cur_time = 0; #ifdef OS_WIN32 /* we actually want to update the overflow state of mono_time here */ pthread_mutex_lock(&mono_time->last_clock_lock); mono_time->last_clock_update = true; #endif - time = mono_time->current_time_callback(mono_time, mono_time->user_data) / 1000ULL; - time += mono_time->base_time; + cur_time = mono_time->current_time_callback(mono_time->user_data) / 1000ULL; + cur_time += mono_time->base_time; #ifdef OS_WIN32 pthread_mutex_unlock(&mono_time->last_clock_lock); #endif +#ifndef ESP_PLATFORM pthread_rwlock_wrlock(mono_time->time_update_lock); - mono_time->time = time; +#endif + mono_time->cur_time = cur_time; +#ifndef ESP_PLATFORM pthread_rwlock_unlock(mono_time->time_update_lock); +#endif } uint64_t mono_time_get(const Mono_Time *mono_time) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Fuzzing is only single thread for now, no locking needed */ - return mono_time->time; + return mono_time->cur_time; #else - uint64_t time = 0; +#ifndef ESP_PLATFORM pthread_rwlock_rdlock(mono_time->time_update_lock); - time = mono_time->time; +#endif + const uint64_t cur_time = mono_time->cur_time; +#ifndef ESP_PLATFORM pthread_rwlock_unlock(mono_time->time_update_lock); - return time; +#endif + return cur_time; #endif } @@ -218,7 +238,7 @@ void mono_time_set_current_time_callback(Mono_Time *mono_time, { if (current_time_callback == nullptr) { mono_time->current_time_callback = current_time_monotonic_default; - mono_time->user_data = nullptr; + mono_time->user_data = mono_time; } else { mono_time->current_time_callback = current_time_callback; mono_time->user_data = user_data; @@ -237,9 +257,9 @@ uint64_t current_time_monotonic(Mono_Time *mono_time) * but must protect against other threads */ pthread_mutex_lock(&mono_time->last_clock_lock); #endif - uint64_t time = mono_time->current_time_callback(mono_time, mono_time->user_data); + const uint64_t cur_time = mono_time->current_time_callback(mono_time->user_data); #ifdef OS_WIN32 pthread_mutex_unlock(&mono_time->last_clock_lock); #endif - return time; + return cur_time; } diff --git a/protocols/Tox/libtox/src/toxcore/mono_time.h b/protocols/Tox/libtox/src/toxcore/mono_time.h index b27e9aaf13..5a36724715 100644 --- a/protocols/Tox/libtox/src/toxcore/mono_time.h +++ b/protocols/Tox/libtox/src/toxcore/mono_time.h @@ -8,6 +8,8 @@ #include <stdbool.h> #include <stdint.h> +#include "attributes.h" + #ifdef __cplusplus extern "C" { #endif @@ -43,39 +45,47 @@ extern "C" { */ typedef struct Mono_Time Mono_Time; -Mono_Time *mono_time_new(void); +typedef uint64_t mono_time_current_time_cb(void *user_data); + +nullable(1, 2) +Mono_Time *mono_time_new(mono_time_current_time_cb *current_time_callback, void *user_data); + +nullable(1) void mono_time_free(Mono_Time *mono_time); /** * Update mono_time; subsequent calls to mono_time_get or mono_time_is_timeout * will use the time at the call to mono_time_update. */ +non_null() void mono_time_update(Mono_Time *mono_time); /** * Return unix time since epoch in seconds. */ +non_null() uint64_t mono_time_get(const Mono_Time *mono_time); /** * Return true iff timestamp is at least timeout seconds in the past. */ +non_null() bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64_t timeout); /** * Return current monotonic time in milliseconds (ms). The starting point is * unspecified. */ +non_null() uint64_t current_time_monotonic(Mono_Time *mono_time); -typedef uint64_t mono_time_current_time_cb(Mono_Time *mono_time, void *user_data); - /** - * Override implementation of current_time_monotonic() (for tests). + * Override implementation of `current_time_monotonic()` (for tests). * - * The caller is obligated to ensure that current_time_monotonic() continues + * The caller is obligated to ensure that `current_time_monotonic()` continues * to increase monotonically. */ +non_null(1) nullable(2, 3) void mono_time_set_current_time_callback(Mono_Time *mono_time, mono_time_current_time_cb *current_time_callback, void *user_data); diff --git a/protocols/Tox/libtox/src/toxcore/net_crypto.c b/protocols/Tox/libtox/src/toxcore/net_crypto.c index 77ba2294ce..c34a45734c 100644 --- a/protocols/Tox/libtox/src/toxcore/net_crypto.c +++ b/protocols/Tox/libtox/src/toxcore/net_crypto.c @@ -14,6 +14,7 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "list.h" #include "mono_time.h" #include "util.h" @@ -112,7 +113,7 @@ typedef struct Crypto_Connection { /* TCP_connection connection_number */ unsigned int connection_number_tcp; - uint8_t maximum_speed_reached; + bool maximum_speed_reached; /* Must be a pointer, because the struct is moved in memory */ pthread_mutex_t *mutex; @@ -122,9 +123,13 @@ typedef struct Crypto_Connection { uint32_t dht_pk_callback_number; } Crypto_Connection; +static const Crypto_Connection empty_crypto_connection = {{0}}; + struct Net_Crypto { const Logger *log; + const Random *rng; Mono_Time *mono_time; + const Network *ns; DHT *dht; TCP_Connections *tcp_c; @@ -173,6 +178,7 @@ DHT *nc_get_dht(const Net_Crypto *c) return c->dht; } +non_null() static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connection_id) { if ((uint32_t)crypt_connection_id >= c->crypto_connections_length) { @@ -185,11 +191,7 @@ static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connecti const Crypto_Conn_State status = c->crypto_connections[crypt_connection_id].status; - if (status == CRYPTO_CONN_NO_CONNECTION || status == CRYPTO_CONN_FREE) { - return false; - } - - return true; + return status != CRYPTO_CONN_NO_CONNECTION && status != CRYPTO_CONN_FREE; } /** cookie timeout in seconds */ @@ -202,14 +204,16 @@ static bool crypt_connection_id_is_valid(const Net_Crypto *c, int crypt_connecti #define COOKIE_REQUEST_LENGTH (uint16_t)(1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE) #define COOKIE_RESPONSE_LENGTH (uint16_t)(1 + CRYPTO_NONCE_SIZE + COOKIE_LENGTH + sizeof(uint64_t) + CRYPTO_MAC_SIZE) -/** Create a cookie request packet and put it in packet. +/** @brief Create a cookie request packet and put it in packet. + * * dht_public_key is the dht public key of the other * * packet must be of size COOKIE_REQUEST_LENGTH or bigger. * - * return -1 on failure. - * return COOKIE_REQUEST_LENGTH on success. + * @retval -1 on failure. + * @retval COOKIE_REQUEST_LENGTH on success. */ +non_null() static int create_cookie_request(const Net_Crypto *c, uint8_t *packet, const uint8_t *dht_public_key, uint64_t number, uint8_t *shared_key) { @@ -222,34 +226,35 @@ static int create_cookie_request(const Net_Crypto *c, uint8_t *packet, const uin dht_get_shared_key_sent(c->dht, shared_key, dht_public_key); uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(c->rng, nonce); packet[0] = NET_PACKET_COOKIE_REQUEST; memcpy(packet + 1, dht_get_self_public_key(c->dht), CRYPTO_PUBLIC_KEY_SIZE); memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE); - int len = encrypt_data_symmetric(shared_key, nonce, plain, sizeof(plain), + const int len = encrypt_data_symmetric(shared_key, nonce, plain, sizeof(plain), packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE); if (len != COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE) { return -1; } - return (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + len); + return 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + len; } -/** Create cookie of length COOKIE_LENGTH from bytes of length COOKIE_DATA_LENGTH using encryption_key +/** @brief Create cookie of length COOKIE_LENGTH from bytes of length COOKIE_DATA_LENGTH using encryption_key * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ -static int create_cookie(const Mono_Time *mono_time, uint8_t *cookie, const uint8_t *bytes, +non_null() +static int create_cookie(const Random *rng, const Mono_Time *mono_time, uint8_t *cookie, const uint8_t *bytes, const uint8_t *encryption_key) { uint8_t contents[COOKIE_CONTENTS_LENGTH]; const uint64_t temp_time = mono_time_get(mono_time); memcpy(contents, &temp_time, sizeof(temp_time)); memcpy(contents + sizeof(temp_time), bytes, COOKIE_DATA_LENGTH); - random_nonce(cookie); - int len = encrypt_data_symmetric(encryption_key, cookie, contents, sizeof(contents), cookie + CRYPTO_NONCE_SIZE); + random_nonce(rng, cookie); + const int len = encrypt_data_symmetric(encryption_key, cookie, contents, sizeof(contents), cookie + CRYPTO_NONCE_SIZE); if (len != COOKIE_LENGTH - CRYPTO_NONCE_SIZE) { return -1; @@ -258,11 +263,12 @@ static int create_cookie(const Mono_Time *mono_time, uint8_t *cookie, const uint return 0; } -/** Open cookie of length COOKIE_LENGTH to bytes of length COOKIE_DATA_LENGTH using encryption_key +/** @brief Open cookie of length COOKIE_LENGTH to bytes of length COOKIE_DATA_LENGTH using encryption_key * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int open_cookie(const Mono_Time *mono_time, uint8_t *bytes, const uint8_t *cookie, const uint8_t *encryption_key) { @@ -287,13 +293,14 @@ static int open_cookie(const Mono_Time *mono_time, uint8_t *bytes, const uint8_t } -/** Create a cookie response packet and put it in packet. - * request_plain must be COOKIE_REQUEST_PLAIN_LENGTH bytes. - * packet must be of size COOKIE_RESPONSE_LENGTH or bigger. +/** @brief Create a cookie response packet and put it in packet. + * @param request_plain must be COOKIE_REQUEST_PLAIN_LENGTH bytes. + * @param packet must be of size COOKIE_RESPONSE_LENGTH or bigger. * - * return -1 on failure. - * return COOKIE_RESPONSE_LENGTH on success. + * @retval -1 on failure. + * @retval COOKIE_RESPONSE_LENGTH on success. */ +non_null() static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const uint8_t *request_plain, const uint8_t *shared_key, const uint8_t *dht_public_key) { @@ -302,14 +309,14 @@ static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const ui memcpy(cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE); uint8_t plain[COOKIE_LENGTH + sizeof(uint64_t)]; - if (create_cookie(c->mono_time, plain, cookie_plain, c->secret_symmetric_key) != 0) { + if (create_cookie(c->rng, c->mono_time, plain, cookie_plain, c->secret_symmetric_key) != 0) { return -1; } memcpy(plain + COOKIE_LENGTH, request_plain + COOKIE_DATA_LENGTH, sizeof(uint64_t)); packet[0] = NET_PACKET_COOKIE_RESPONSE; - random_nonce(packet + 1); - int len = encrypt_data_symmetric(shared_key, packet + 1, plain, sizeof(plain), packet + 1 + CRYPTO_NONCE_SIZE); + random_nonce(c->rng, packet + 1); + const int len = encrypt_data_symmetric(shared_key, packet + 1, plain, sizeof(plain), packet + 1 + CRYPTO_NONCE_SIZE); if (len != COOKIE_RESPONSE_LENGTH - (1 + CRYPTO_NONCE_SIZE)) { return -1; @@ -318,13 +325,14 @@ static int create_cookie_response(const Net_Crypto *c, uint8_t *packet, const ui return COOKIE_RESPONSE_LENGTH; } -/** Handle the cookie request packet of length length. +/** @brief Handle the cookie request packet of length length. * Put what was in the request in request_plain (must be of size COOKIE_REQUEST_PLAIN_LENGTH) * Put the key used to decrypt the request into shared_key (of size CRYPTO_SHARED_KEY_SIZE) for use in the response. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, uint8_t *shared_key, uint8_t *dht_public_key, const uint8_t *packet, uint16_t length) { @@ -334,7 +342,7 @@ static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, ui memcpy(dht_public_key, packet + 1, CRYPTO_PUBLIC_KEY_SIZE); dht_get_shared_key_sent(c->dht, shared_key, dht_public_key); - int len = decrypt_data_symmetric(shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, + const int len = decrypt_data_symmetric(shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, COOKIE_REQUEST_PLAIN_LENGTH + CRYPTO_MAC_SIZE, request_plain); @@ -345,8 +353,8 @@ static int handle_cookie_request(const Net_Crypto *c, uint8_t *request_plain, ui return 0; } -/** Handle the cookie request packet (for raw UDP) - */ +/** Handle the cookie request packet (for raw UDP) */ +non_null(1, 2, 3) nullable(5) static int udp_handle_cookie_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -372,8 +380,8 @@ static int udp_handle_cookie_request(void *object, const IP_Port *source, const return 0; } -/** Handle the cookie request packet (for TCP) - */ +/** Handle the cookie request packet (for TCP) */ +non_null() static int tcp_handle_cookie_request(const Net_Crypto *c, int connections_number, const uint8_t *packet, uint16_t length) { @@ -391,12 +399,12 @@ static int tcp_handle_cookie_request(const Net_Crypto *c, int connections_number return -1; } - int ret = send_packet_tcp_connection(c->tcp_c, connections_number, data, sizeof(data)); + const int ret = send_packet_tcp_connection(c->tcp_c, connections_number, data, sizeof(data)); return ret; } -/** Handle the cookie request packet (for TCP oob packets) - */ +/** Handle the cookie request packet (for TCP oob packets) */ +non_null() static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_connections_number, const uint8_t *dht_public_key, const uint8_t *packet, uint16_t length) { @@ -408,7 +416,7 @@ static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_c return -1; } - if (public_key_cmp(dht_public_key, dht_public_key_temp) != 0) { + if (!pk_equal(dht_public_key, dht_public_key_temp)) { return -1; } @@ -418,18 +426,19 @@ static int tcp_oob_handle_cookie_request(const Net_Crypto *c, unsigned int tcp_c return -1; } - int ret = tcp_send_oob_packet(c->tcp_c, tcp_connections_number, dht_public_key, data, sizeof(data)); + const int ret = tcp_send_oob_packet(c->tcp_c, tcp_connections_number, dht_public_key, data, sizeof(data)); return ret; } -/** Handle a cookie response packet of length encrypted with shared_key. +/** @brief Handle a cookie response packet of length encrypted with shared_key. * put the cookie in the response in cookie * - * cookie must be of length COOKIE_LENGTH. + * @param cookie must be of length COOKIE_LENGTH. * - * return -1 on failure. - * return COOKIE_LENGTH on success. + * @retval -1 on failure. + * @retval COOKIE_LENGTH on success. */ +non_null() static int handle_cookie_response(uint8_t *cookie, uint64_t *number, const uint8_t *packet, uint16_t length, const uint8_t *shared_key) @@ -453,13 +462,14 @@ static int handle_cookie_response(uint8_t *cookie, uint64_t *number, #define HANDSHAKE_PACKET_LENGTH (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE + COOKIE_LENGTH + CRYPTO_MAC_SIZE) -/** Create a handshake packet and put it in packet. - * cookie must be COOKIE_LENGTH bytes. - * packet must be of size HANDSHAKE_PACKET_LENGTH or bigger. +/** @brief Create a handshake packet and put it in packet. + * @param cookie must be COOKIE_LENGTH bytes. + * @param packet must be of size HANDSHAKE_PACKET_LENGTH or bigger. * - * return -1 on failure. - * return HANDSHAKE_PACKET_LENGTH on success. + * @retval -1 on failure. + * @retval HANDSHAKE_PACKET_LENGTH on success. */ +non_null() static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const uint8_t *cookie, const uint8_t *nonce, const uint8_t *session_pk, const uint8_t *peer_real_pk, const uint8_t *peer_dht_pubkey) { @@ -471,13 +481,13 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u memcpy(cookie_plain, peer_real_pk, CRYPTO_PUBLIC_KEY_SIZE); memcpy(cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, peer_dht_pubkey, CRYPTO_PUBLIC_KEY_SIZE); - if (create_cookie(c->mono_time, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE, + if (create_cookie(c->rng, c->mono_time, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE, cookie_plain, c->secret_symmetric_key) != 0) { return -1; } - random_nonce(packet + 1 + COOKIE_LENGTH); - int len = encrypt_data(peer_real_pk, c->self_secret_key, packet + 1 + COOKIE_LENGTH, plain, sizeof(plain), + random_nonce(c->rng, packet + 1 + COOKIE_LENGTH); + const int len = encrypt_data(peer_real_pk, c->self_secret_key, packet + 1 + COOKIE_LENGTH, plain, sizeof(plain), packet + 1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE); if (len != HANDSHAKE_PACKET_LENGTH - (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE)) { @@ -490,7 +500,7 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u return HANDSHAKE_PACKET_LENGTH; } -/** Handle a crypto handshake packet of length. +/** @brief Handle a crypto handshake packet of length. * put the nonce contained in the packet in nonce, * the session public key in session_pk * the real public key of the peer in peer_real_pk @@ -505,40 +515,41 @@ static int create_crypto_handshake(const Net_Crypto *c, uint8_t *packet, const u * peer_real_pk must be at least CRYPTO_PUBLIC_KEY_SIZE * cookie must be at least COOKIE_LENGTH * - * return -1 on failure. - * return 0 on success. + * @retval false on failure. + * @retval true on success. */ -static int handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t *session_pk, uint8_t *peer_real_pk, - uint8_t *dht_public_key, uint8_t *cookie, const uint8_t *packet, uint16_t length, const uint8_t *expected_real_pk) +non_null(1, 2, 3, 4, 5, 6, 7) nullable(9) +static bool handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t *session_pk, uint8_t *peer_real_pk, + uint8_t *dht_public_key, uint8_t *cookie, const uint8_t *packet, uint16_t length, const uint8_t *expected_real_pk) { if (length != HANDSHAKE_PACKET_LENGTH) { - return -1; + return false; } uint8_t cookie_plain[COOKIE_DATA_LENGTH]; if (open_cookie(c->mono_time, cookie_plain, packet + 1, c->secret_symmetric_key) != 0) { - return -1; + return false; } - if (expected_real_pk && public_key_cmp(cookie_plain, expected_real_pk) != 0) { - return -1; + if (expected_real_pk != nullptr && !pk_equal(cookie_plain, expected_real_pk)) { + return false; } uint8_t cookie_hash[CRYPTO_SHA512_SIZE]; crypto_sha512(cookie_hash, packet + 1, COOKIE_LENGTH); uint8_t plain[CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE + COOKIE_LENGTH]; - int len = decrypt_data(cookie_plain, c->self_secret_key, packet + 1 + COOKIE_LENGTH, + const int len = decrypt_data(cookie_plain, c->self_secret_key, packet + 1 + COOKIE_LENGTH, packet + 1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE, HANDSHAKE_PACKET_LENGTH - (1 + COOKIE_LENGTH + CRYPTO_NONCE_SIZE), plain); if (len != sizeof(plain)) { - return -1; + return false; } - if (crypto_sha512_cmp(cookie_hash, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE) != 0) { - return -1; + if (!crypto_sha512_eq(cookie_hash, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE)) { + return false; } memcpy(nonce, plain, CRYPTO_NONCE_SIZE); @@ -546,10 +557,11 @@ static int handle_crypto_handshake(const Net_Crypto *c, uint8_t *nonce, uint8_t memcpy(cookie, plain + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SHA512_SIZE, COOKIE_LENGTH); memcpy(peer_real_pk, cookie_plain, CRYPTO_PUBLIC_KEY_SIZE); memcpy(dht_public_key, cookie_plain + CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE); - return 0; + return true; } +non_null() static Crypto_Connection *get_crypto_connection(const Net_Crypto *c, int crypt_connection_id) { if (!crypt_connection_id_is_valid(c, crypt_connection_id)) { @@ -560,11 +572,12 @@ static Crypto_Connection *get_crypto_connection(const Net_Crypto *c, int crypt_c } -/** Associate an ip_port to a connection. +/** @brief Associate an ip_port to a connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int add_ip_port_connection(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -598,14 +611,15 @@ static int add_ip_port_connection(Net_Crypto *c, int crypt_connection_id, const return -1; } -/** Return the IP_Port that should be used to send packets to the other peer. +/** @brief Return the IP_Port that should be used to send packets to the other peer. * - * return IP_Port with family 0 on failure. - * return IP_Port on success. + * @retval IP_Port with family 0 on failure. + * @return IP_Port on success. */ +non_null() static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connection_id) { - const IP_Port empty = {0}; + const IP_Port empty = {{{0}}}; const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -614,15 +628,15 @@ static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connecti } const uint64_t current_time = mono_time_get(c->mono_time); - bool v6 = 0; - bool v4 = 0; + bool v6 = false; + bool v4 = false; if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev4) > current_time) { - v4 = 1; + v4 = true; } if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev6) > current_time) { - v6 = 1; + v6 = true; } /* Prefer IP_Ports which haven't timed out to those which have. @@ -655,11 +669,12 @@ static IP_Port return_ip_port_connection(const Net_Crypto *c, int crypt_connecti return empty; } -/** Sends a packet to the peer using the fastest route. +/** @brief Sends a packet to the peer using the fastest route. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length) { // TODO(irungentoo): TCP, etc... @@ -669,14 +684,14 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t return -1; } - int direct_send_attempt = 0; + bool direct_send_attempt = false; pthread_mutex_lock(conn->mutex); IP_Port ip_port = return_ip_port_connection(c, crypt_connection_id); // TODO(irungentoo): on bad networks, direct connections might not last indefinitely. if (!net_family_is_unspec(ip_port.ip.family)) { - bool direct_connected = 0; + bool direct_connected = false; // FIXME(sudden6): handle return value crypto_connection_status(c, crypt_connection_id, &direct_connected, nullptr); @@ -688,6 +703,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t } pthread_mutex_unlock(conn->mutex); + LOGGER_WARNING(c->log, "sending packet of length %d failed", length); return -1; } @@ -697,7 +713,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t if ((((UDP_DIRECT_TIMEOUT / 2) + conn->direct_send_attempt_time) < current_time && length < 96) || data[0] == NET_PACKET_COOKIE_REQUEST || data[0] == NET_PACKET_CRYPTO_HS) { if ((uint32_t)sendpacket(dht_get_net(c->dht), &ip_port, data, length) == length) { - direct_send_attempt = 1; + direct_send_attempt = true; conn->direct_send_attempt_time = mono_time_get(c->mono_time); } } @@ -705,7 +721,7 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t pthread_mutex_unlock(conn->mutex); pthread_mutex_lock(&c->tcp_mutex); - int ret = send_packet_tcp_connection(c->tcp_c, conn->connection_number_tcp, data, length); + const int ret = send_packet_tcp_connection(c->tcp_c, conn->connection_number_tcp, data, length); pthread_mutex_unlock(&c->tcp_mutex); pthread_mutex_lock(conn->mutex); @@ -716,38 +732,40 @@ static int send_packet_to(Net_Crypto *c, int crypt_connection_id, const uint8_t pthread_mutex_unlock(conn->mutex); - if (ret == 0 || direct_send_attempt) { + if (direct_send_attempt) { return 0; } - return -1; + return ret; } /*** START: Array Related functions */ -/** Return number of packets in array +/** @brief Return number of packets in array * Note that holes are counted too. */ +non_null() static uint32_t num_packets_array(const Packets_Array *array) { return array->buffer_end - array->buffer_start; } -/** Add data with packet number to array. +/** @brief Add data with packet number to array. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int add_data_to_buffer(Packets_Array *array, uint32_t number, const Packet_Data *data) { if (number - array->buffer_start >= CRYPTO_PACKET_BUFFER_SIZE) { return -1; } - uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE; - if (array->buffer[num]) { + if (array->buffer[num] != nullptr) { return -1; } @@ -767,12 +785,13 @@ static int add_data_to_buffer(Packets_Array *array, uint32_t number, const Packe return 0; } -/** Get pointer of data with packet number. +/** @brief Get pointer of data with packet number. * - * return -1 on failure. - * return 0 if data at number is empty. - * return 1 if data pointer was put in data. + * @retval -1 on failure. + * @retval 0 if data at number is empty. + * @retval 1 if data pointer was put in data. */ +non_null() static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint32_t number) { const uint32_t num_spots = num_packets_array(array); @@ -781,9 +800,9 @@ static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint return -1; } - uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = number % CRYPTO_PACKET_BUFFER_SIZE; - if (!array->buffer[num]) { + if (array->buffer[num] == nullptr) { return 0; } @@ -791,37 +810,41 @@ static int get_data_pointer(const Packets_Array *array, Packet_Data **data, uint return 1; } -/** Add data to end of array. +/** @brief Add data to end of array. * - * return -1 on failure. - * return packet number on success. + * @retval -1 on failure. + * @return packet number on success. */ -static int64_t add_data_end_of_buffer(Packets_Array *array, const Packet_Data *data) +non_null() +static int64_t add_data_end_of_buffer(const Logger *logger, Packets_Array *array, const Packet_Data *data) { const uint32_t num_spots = num_packets_array(array); if (num_spots >= CRYPTO_PACKET_BUFFER_SIZE) { + LOGGER_WARNING(logger, "crypto packet buffer size exceeded; rejecting packet of length %d", data->length); return -1; } Packet_Data *new_d = (Packet_Data *)calloc(1, sizeof(Packet_Data)); if (new_d == nullptr) { + LOGGER_ERROR(logger, "packet data allocation failed"); return -1; } *new_d = *data; - uint32_t id = array->buffer_end; + const uint32_t id = array->buffer_end; array->buffer[id % CRYPTO_PACKET_BUFFER_SIZE] = new_d; ++array->buffer_end; return id; } -/** Read data from beginning of array. +/** @brief Read data from beginning of array. * - * return -1 on failure. - * return packet number on success. + * @retval -1 on failure. + * @return packet number on success. */ +non_null() static int64_t read_data_beg_buffer(Packets_Array *array, Packet_Data *data) { if (array->buffer_end == array->buffer_start) { @@ -830,23 +853,24 @@ static int64_t read_data_beg_buffer(Packets_Array *array, Packet_Data *data) const uint32_t num = array->buffer_start % CRYPTO_PACKET_BUFFER_SIZE; - if (!array->buffer[num]) { + if (array->buffer[num] == nullptr) { return -1; } *data = *array->buffer[num]; - uint32_t id = array->buffer_start; + const uint32_t id = array->buffer_start; ++array->buffer_start; free(array->buffer[num]); array->buffer[num] = nullptr; return id; } -/** Delete all packets in array before number (but not number) +/** @brief Delete all packets in array before number (but not number) * - * return -1 on failure. - * return 0 on success + * @retval -1 on failure. + * @retval 0 on success */ +non_null() static int clear_buffer_until(Packets_Array *array, uint32_t number) { const uint32_t num_spots = num_packets_array(array); @@ -858,9 +882,9 @@ static int clear_buffer_until(Packets_Array *array, uint32_t number) uint32_t i; for (i = array->buffer_start; i != number; ++i) { - uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; - if (array->buffer[num]) { + if (array->buffer[num] != nullptr) { free(array->buffer[num]); array->buffer[num] = nullptr; } @@ -870,14 +894,15 @@ static int clear_buffer_until(Packets_Array *array, uint32_t number) return 0; } +non_null() static int clear_buffer(Packets_Array *array) { uint32_t i; for (i = array->buffer_start; i != array->buffer_end; ++i) { - uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; - if (array->buffer[num]) { + if (array->buffer[num] != nullptr) { free(array->buffer[num]); array->buffer[num] = nullptr; } @@ -887,11 +912,12 @@ static int clear_buffer(Packets_Array *array) return 0; } -/** Set array buffer end to number. +/** @brief Set array buffer end to number. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int set_buffer_end(Packets_Array *array, uint32_t number) { if (number - array->buffer_start > CRYPTO_PACKET_BUFFER_SIZE) { @@ -906,12 +932,14 @@ static int set_buffer_end(Packets_Array *array, uint32_t number) return 0; } -/** Create a packet request packet from recv_array and send_buffer_end into - * data of length. +/** + * @brief Create a packet request packet from recv_array and send_buffer_end into + * data of length. * - * return -1 on failure. - * return length of packet on success. + * @retval -1 on failure. + * @return length of packet on success. */ +non_null() static int generate_request_packet(uint8_t *data, uint16_t length, const Packets_Array *recv_array) { if (length == 0) { @@ -933,9 +961,9 @@ static int generate_request_packet(uint8_t *data, uint16_t length, const Packets uint32_t n = 1; for (uint32_t i = recv_array->buffer_start; i != recv_array->buffer_end; ++i) { - uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; - if (!recv_array->buffer[num]) { + if (recv_array->buffer[num] == nullptr) { data[cur_len] = n; n = 0; ++cur_len; @@ -959,12 +987,13 @@ static int generate_request_packet(uint8_t *data, uint16_t length, const Packets return cur_len; } -/** Handle a request data packet. +/** @brief Handle a request data packet. * Remove all the packets the other received from the array. * - * return -1 on failure. - * return number of requested packets on success. + * @retval -1 on failure. + * @return number of requested packets on success. */ +non_null() static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array, const uint8_t *data, uint16_t length, uint64_t *latest_send_time, uint64_t rtt_time) @@ -988,18 +1017,18 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array uint32_t requested = 0; const uint64_t temp_time = current_time_monotonic(mono_time); - uint64_t l_sent_time = -1; + uint64_t l_sent_time = 0; for (uint32_t i = send_array->buffer_start; i != send_array->buffer_end; ++i) { if (length == 0) { break; } - uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; + const uint32_t num = i % CRYPTO_PACKET_BUFFER_SIZE; if (n == data[0]) { - if (send_array->buffer[num]) { - uint64_t sent_time = send_array->buffer[num]->sent_time; + if (send_array->buffer[num] != nullptr) { + const uint64_t sent_time = send_array->buffer[num]->sent_time; if ((sent_time + rtt_time) < temp_time) { send_array->buffer[num]->sent_time = 0; @@ -1011,7 +1040,7 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array n = 0; ++requested; } else { - if (send_array->buffer[num]) { + if (send_array->buffer[num] != nullptr) { l_sent_time = max_u64(l_sent_time, send_array->buffer[num]->sent_time); free(send_array->buffer[num]); @@ -1042,32 +1071,37 @@ static int handle_request_packet(Mono_Time *mono_time, Packets_Array *send_array #define MAX_DATA_DATA_PACKET_SIZE (MAX_CRYPTO_PACKET_SIZE - (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE)) -/** Creates and sends a data packet to the peer using the fastest route. +/** @brief Creates and sends a data packet to the peer using the fastest route. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_data_packet(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length) { const uint16_t max_length = MAX_CRYPTO_PACKET_SIZE - (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE); if (length == 0 || length > max_length) { + LOGGER_ERROR(c->log, "zero-length or too large data packet: %d (max: %d)", length, max_length); return -1; } Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); if (conn == nullptr) { + LOGGER_ERROR(c->log, "connection id %d not found", crypt_connection_id); return -1; } pthread_mutex_lock(conn->mutex); - VLA(uint8_t, packet, 1 + sizeof(uint16_t) + length + CRYPTO_MAC_SIZE); + const uint16_t packet_size = 1 + sizeof(uint16_t) + length + CRYPTO_MAC_SIZE; + VLA(uint8_t, packet, packet_size); packet[0] = NET_PACKET_CRYPTO_DATA; memcpy(packet + 1, conn->sent_nonce + (CRYPTO_NONCE_SIZE - sizeof(uint16_t)), sizeof(uint16_t)); const int len = encrypt_data_symmetric(conn->shared_key, conn->sent_nonce, data, length, packet + 1 + sizeof(uint16_t)); - if (len + 1 + sizeof(uint16_t) != SIZEOF_VLA(packet)) { + if (len + 1 + sizeof(uint16_t) != packet_size) { + LOGGER_ERROR(c->log, "encryption failed: %d", len); pthread_mutex_unlock(conn->mutex); return -1; } @@ -1078,21 +1112,23 @@ static int send_data_packet(Net_Crypto *c, int crypt_connection_id, const uint8_ return send_packet_to(c, crypt_connection_id, packet, SIZEOF_VLA(packet)); } -/** Creates and sends a data packet with buffer_start and num to the peer using the fastest route. +/** @brief Creates and sends a data packet with buffer_start and num to the peer using the fastest route. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint32_t buffer_start, uint32_t num, const uint8_t *data, uint16_t length) { if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) { + LOGGER_ERROR(c->log, "zero-length or too large data packet: %d (max: %d)", length, MAX_CRYPTO_PACKET_SIZE); return -1; } num = net_htonl(num); buffer_start = net_htonl(buffer_start); - uint16_t padding_length = (MAX_CRYPTO_DATA_SIZE - length) % CRYPTO_MAX_PADDING; + const uint16_t padding_length = (MAX_CRYPTO_DATA_SIZE - length) % CRYPTO_MAX_PADDING; VLA(uint8_t, packet, sizeof(uint32_t) + sizeof(uint32_t) + padding_length + length); memcpy(packet, &buffer_start, sizeof(uint32_t)); memcpy(packet + sizeof(uint32_t), &num, sizeof(uint32_t)); @@ -1102,6 +1138,7 @@ static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint3 return send_data_packet(c, crypt_connection_id, packet, SIZEOF_VLA(packet)); } +non_null() static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1126,19 +1163,23 @@ static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id) dt->sent_time = current_time_monotonic(c->mono_time); } - conn->maximum_speed_reached = 0; + conn->maximum_speed_reached = false; } return 0; } -/** return -1 if data could not be put in packet queue. - * return positive packet number if data was put into the queue. +/** + * @retval -1 if data could not be put in packet queue. + * @return positive packet number if data was put into the queue. */ +non_null() static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length, - uint8_t congestion_control) + bool congestion_control) { if (length == 0 || length > MAX_CRYPTO_DATA_SIZE) { + LOGGER_ERROR(c->log, "rejecting too large (or empty) packet of size %d on crypt connection %d", length, + crypt_connection_id); return -1; } @@ -1153,6 +1194,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons reset_max_speed_reached(c, crypt_connection_id); if (conn->maximum_speed_reached && congestion_control) { + LOGGER_INFO(c->log, "congestion control: maximum speed reached on crypt connection %d", crypt_connection_id); return -1; } @@ -1161,7 +1203,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons dt.length = length; memcpy(dt.data, data, length); pthread_mutex_lock(conn->mutex); - int64_t packet_num = add_data_end_of_buffer(&conn->send_array, &dt); + const int64_t packet_num = add_data_end_of_buffer(c->log, &conn->send_array, &dt); pthread_mutex_unlock(conn->mutex); if (packet_num == -1) { @@ -1179,16 +1221,18 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons dt1->sent_time = current_time_monotonic(c->mono_time); } } else { - conn->maximum_speed_reached = 1; - LOGGER_DEBUG(c->log, "send_data_packet failed"); + conn->maximum_speed_reached = true; + LOGGER_DEBUG(c->log, "send_data_packet failed (packet_num = %ld)", (long)packet_num); } return packet_num; } -/** Get the lowest 2 bytes from the nonce and convert - * them to host byte format before returning them. +/** + * @brief Get the lowest 2 bytes from the nonce and convert + * them to host byte format before returning them. */ +non_null() static uint16_t get_nonce_uint16(const uint8_t *nonce) { uint16_t num; @@ -1198,13 +1242,14 @@ static uint16_t get_nonce_uint16(const uint8_t *nonce) #define DATA_NUM_THRESHOLD 21845 -/** Handle a data packet. +/** @brief Handle a data packet. * Decrypt packet of length and put it into data. * data must be at least MAX_DATA_DATA_PACKET_SIZE big. * - * return -1 on failure. - * return length of data on success. + * @retval -1 on failure. + * @return length of data on success. */ +non_null() static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint8_t *data, const uint8_t *packet, uint16_t length) { @@ -1222,12 +1267,12 @@ static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint uint8_t nonce[CRYPTO_NONCE_SIZE]; memcpy(nonce, conn->recv_nonce, CRYPTO_NONCE_SIZE); - uint16_t num_cur_nonce = get_nonce_uint16(nonce); + const uint16_t num_cur_nonce = get_nonce_uint16(nonce); uint16_t num; net_unpack_u16(packet + 1, &num); - uint16_t diff = num - num_cur_nonce; + const uint16_t diff = num - num_cur_nonce; increment_nonce_number(nonce, diff); - int len = decrypt_data_symmetric(conn->shared_key, nonce, packet + 1 + sizeof(uint16_t), + const int len = decrypt_data_symmetric(conn->shared_key, nonce, packet + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), data); if ((unsigned int)len != length - crypto_packet_overhead) { @@ -1241,11 +1286,12 @@ static int handle_data_packet(const Net_Crypto *c, int crypt_connection_id, uint return len; } -/** Send a request packet. +/** @brief Send a request packet. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_request_packet(Net_Crypto *c, int crypt_connection_id) { const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1255,7 +1301,7 @@ static int send_request_packet(Net_Crypto *c, int crypt_connection_id) } uint8_t data[MAX_CRYPTO_DATA_SIZE]; - int len = generate_request_packet(data, sizeof(data), &conn->recv_array); + const int len = generate_request_packet(data, sizeof(data), &conn->recv_array); if (len == -1) { return -1; @@ -1265,11 +1311,12 @@ static int send_request_packet(Net_Crypto *c, int crypt_connection_id) len); } -/** Send up to max num previously requested data packets. +/** @brief Send up to max num previously requested data packets. * - * return -1 on failure. - * return number of packets sent on success. + * @retval -1 on failure. + * @return number of packets sent on success. */ +non_null() static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32_t max_num) { if (max_num == 0) { @@ -1299,7 +1346,7 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32 continue; } - if (dt->sent_time) { + if (dt->sent_time != 0) { continue; } @@ -1318,11 +1365,12 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint32 } -/** Add a new temp packet to send repeatedly. +/** @brief Add a new temp packet to send repeatedly. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length) { if (length == 0 || length > MAX_CRYPTO_PACKET_SIZE) { @@ -1341,7 +1389,7 @@ static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const u return -1; } - if (conn->temp_packet) { + if (conn->temp_packet != nullptr) { free(conn->temp_packet); } @@ -1353,11 +1401,12 @@ static int new_temp_packet(const Net_Crypto *c, int crypt_connection_id, const u return 0; } -/** Clear the temp packet. +/** @brief Clear the temp packet. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1366,7 +1415,7 @@ static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id) return -1; } - if (conn->temp_packet) { + if (conn->temp_packet != nullptr) { free(conn->temp_packet); } @@ -1378,11 +1427,12 @@ static int clear_temp_packet(const Net_Crypto *c, int crypt_connection_id) } -/** Send the temp packet. +/** @brief Send the temp packet. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_temp_packet(Net_Crypto *c, int crypt_connection_id) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1391,7 +1441,7 @@ static int send_temp_packet(Net_Crypto *c, int crypt_connection_id) return -1; } - if (!conn->temp_packet) { + if (conn->temp_packet == nullptr) { return -1; } @@ -1404,12 +1454,13 @@ static int send_temp_packet(Net_Crypto *c, int crypt_connection_id) return 0; } -/** Create a handshake packet and set it as a temp packet. - * cookie must be COOKIE_LENGTH. +/** @brief Create a handshake packet and set it as a temp packet. + * @param cookie must be COOKIE_LENGTH. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int create_send_handshake(Net_Crypto *c, int crypt_connection_id, const uint8_t *cookie, const uint8_t *dht_public_key) { @@ -1434,11 +1485,12 @@ static int create_send_handshake(Net_Crypto *c, int crypt_connection_id, const u return 0; } -/** Send a kill packet. +/** @brief Send a kill packet. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int send_kill_packet(Net_Crypto *c, int crypt_connection_id) { const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1452,6 +1504,7 @@ static int send_kill_packet(Net_Crypto *c, int crypt_connection_id) &kill_packet, sizeof(kill_packet)); } +non_null(1) nullable(3) static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userdata) { const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1460,15 +1513,15 @@ static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userda return; } - if (conn->connection_status_callback) { - conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, 0, - userdata); + if (conn->connection_status_callback != nullptr) { + conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, + false, userdata); } - while (1) { /* TODO(irungentoo): is this really the best way to do this? */ + while (true) { /* TODO(irungentoo): is this really the best way to do this? */ pthread_mutex_lock(&c->connections_mutex); - if (!c->connection_use_counter) { + if (c->connection_use_counter == 0) { break; } @@ -1479,11 +1532,12 @@ static void connection_kill(Net_Crypto *c, int crypt_connection_id, void *userda pthread_mutex_unlock(&c->connections_mutex); } -/** Handle a received data packet. +/** @brief Handle a received data packet. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null(1, 3) nullable(6) static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length, bool udp, void *userdata) { @@ -1498,7 +1552,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const } uint8_t data[MAX_DATA_DATA_PACKET_SIZE]; - int len = handle_data_packet(c, crypt_connection_id, data, packet, length); + const int len = handle_data_packet(c, crypt_connection_id, data, packet, length); if (len <= (int)(sizeof(uint32_t) * 2)) { return -1; @@ -1546,9 +1600,9 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const clear_temp_packet(c, crypt_connection_id); conn->status = CRYPTO_CONN_ESTABLISHED; - if (conn->connection_status_callback) { - conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, 1, - userdata); + if (conn->connection_status_callback != nullptr) { + conn->connection_status_callback(conn->connection_status_callback_object, conn->connection_status_callback_id, + true, userdata); } } @@ -1561,7 +1615,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const rtt_time = DEFAULT_TCP_PING_CONNECTION; } - int requested = handle_request_packet(c->mono_time, &conn->send_array, + const int requested = handle_request_packet(c->mono_time, &conn->send_array, real_data, real_length, &rtt_calc_time, rtt_time); @@ -1579,7 +1633,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const return -1; } - while (1) { + while (true) { pthread_mutex_lock(conn->mutex); const int ret = read_data_beg_buffer(&conn->recv_array, &dt); pthread_mutex_unlock(conn->mutex); @@ -1588,7 +1642,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const break; } - if (conn->connection_data_callback) { + if (conn->connection_data_callback != nullptr) { conn->connection_data_callback(conn->connection_data_callback_object, conn->connection_data_callback_id, dt.data, dt.length, userdata); } @@ -1607,7 +1661,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const set_buffer_end(&conn->recv_array, num); - if (conn->connection_lossy_data_callback) { + if (conn->connection_lossy_data_callback != nullptr) { conn->connection_lossy_data_callback(conn->connection_lossy_data_callback_object, conn->connection_lossy_data_callback_id, real_data, real_length, userdata); } @@ -1626,6 +1680,7 @@ static int handle_data_packet_core(Net_Crypto *c, int crypt_connection_id, const return 0; } +non_null() static int handle_packet_cookie_response(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1657,6 +1712,7 @@ static int handle_packet_cookie_response(Net_Crypto *c, int crypt_connection_id, return 0; } +non_null(1, 3) nullable(5) static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length, void *userdata) { @@ -1676,12 +1732,12 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t cookie[COOKIE_LENGTH]; - if (handle_crypto_handshake(c, conn->recv_nonce, conn->peersessionpublic_key, peer_real_pk, dht_public_key, cookie, - packet, length, conn->public_key) != 0) { + if (!handle_crypto_handshake(c, conn->recv_nonce, conn->peersessionpublic_key, peer_real_pk, dht_public_key, cookie, + packet, length, conn->public_key)) { return -1; } - if (public_key_cmp(dht_public_key, conn->dht_public_key) == 0) { + if (pk_equal(dht_public_key, conn->dht_public_key)) { encrypt_precompute(conn->peersessionpublic_key, conn->sessionsecret_key, conn->shared_key); if (conn->status == CRYPTO_CONN_COOKIE_REQUESTING) { @@ -1692,7 +1748,7 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const conn->status = CRYPTO_CONN_NOT_CONFIRMED; } else { - if (conn->dht_pk_callback) { + if (conn->dht_pk_callback != nullptr) { conn->dht_pk_callback(conn->dht_pk_callback_object, conn->dht_pk_callback_number, dht_public_key, userdata); } } @@ -1700,6 +1756,7 @@ static int handle_packet_crypto_hs(Net_Crypto *c, int crypt_connection_id, const return 0; } +non_null(1, 3) nullable(6) static int handle_packet_crypto_data(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length, bool udp, void *userdata) { @@ -1716,11 +1773,12 @@ static int handle_packet_crypto_data(Net_Crypto *c, int crypt_connection_id, con return handle_data_packet_core(c, crypt_connection_id, packet, length, udp, userdata); } -/** Handle a packet that was received for the connection. +/** @brief Handle a packet that was received for the connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null(1, 3) nullable(6) static int handle_packet_connection(Net_Crypto *c, int crypt_connection_id, const uint8_t *packet, uint16_t length, bool udp, void *userdata) { @@ -1743,11 +1801,12 @@ static int handle_packet_connection(Net_Crypto *c, int crypt_connection_id, cons } } -/** Set the size of the friend list to numfriends. +/** @brief Set the size of the friend list to numfriends. * - * return -1 if realloc fails. - * return 0 if it succeeds. + * @retval -1 if realloc fails. + * @retval 0 if it succeeds. */ +non_null() static int realloc_cryptoconnection(Net_Crypto *c, uint32_t num) { if (num == 0) { @@ -1768,17 +1827,18 @@ static int realloc_cryptoconnection(Net_Crypto *c, uint32_t num) } -/** Create a new empty crypto connection. +/** @brief Create a new empty crypto connection. * - * return -1 on failure. - * return connection id on success. + * @retval -1 on failure. + * @return connection id on success. */ +non_null() static int create_crypto_connection(Net_Crypto *c) { - while (1) { /* TODO(irungentoo): is this really the best way to do this? */ + while (true) { /* TODO(irungentoo): is this really the best way to do this? */ pthread_mutex_lock(&c->connections_mutex); - if (!c->connection_use_counter) { + if (c->connection_use_counter == 0) { break; } @@ -1798,7 +1858,7 @@ static int create_crypto_connection(Net_Crypto *c) if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == 0) { id = c->crypto_connections_length; ++c->crypto_connections_length; - memset(&c->crypto_connections[id], 0, sizeof(Crypto_Connection)); + c->crypto_connections[id] = empty_crypto_connection; } } @@ -1829,11 +1889,12 @@ static int create_crypto_connection(Net_Crypto *c) return id; } -/** Wipe a crypto connection. +/** @brief Wipe a crypto connection. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null() static int wipe_crypto_connection(Net_Crypto *c, int crypt_connection_id) { if ((uint32_t)crypt_connection_id >= c->crypto_connections_length) { @@ -1871,11 +1932,12 @@ static int wipe_crypto_connection(Net_Crypto *c, int crypt_connection_id) return 0; } -/** Get crypto connection id from public key of peer. +/** @brief Get crypto connection id from public key of peer. * - * return -1 if there are no connections like we are looking for. - * return id if it found it. + * @retval -1 if there are no connections like we are looking for. + * @return id if it found it. */ +non_null() static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key) { for (uint32_t i = 0; i < c->crypto_connections_length; ++i) { @@ -1883,7 +1945,7 @@ static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key) continue; } - if (public_key_cmp(public_key, c->crypto_connections[i].public_key) == 0) { + if (pk_equal(public_key, c->crypto_connections[i].public_key)) { return i; } } @@ -1891,13 +1953,14 @@ static int getcryptconnection_id(const Net_Crypto *c, const uint8_t *public_key) return -1; } -/** Add a source to the crypto connection. +/** @brief Add a source to the crypto connection. * This is to be used only when we have received a packet from that source. * - * return -1 on failure. - * return positive number on success. - * 0 if source was a direct UDP connection. + * @retval -1 on failure. + * @retval 0 if source was a direct UDP connection. + * @return positive number on success. */ +non_null() static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id, const IP_Port *source) { Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -1920,8 +1983,10 @@ static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id, return 0; } - if (net_family_is_tcp_family(source->ip.family)) { - if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, source->ip.ip.v6.uint32[0]) == 0) { + unsigned int tcp_connections_number; + + if (ip_port_to_tcp_connections_number(source, &tcp_connections_number)) { + if (add_tcp_number_relay_connection(c->tcp_c, conn->connection_number_tcp, tcp_connections_number) == 0) { return 1; } } @@ -1930,7 +1995,7 @@ static int crypto_connection_add_source(Net_Crypto *c, int crypt_connection_id, } -/** Set function to be called when someone requests a new connection to us. +/** @brief Set function to be called when someone requests a new connection to us. * * The set function should return -1 on failure and 0 on success. * @@ -1942,12 +2007,13 @@ void new_connection_handler(Net_Crypto *c, new_connection_cb *new_connection_cal c->new_connection_callback_object = object; } -/** Handle a handshake packet by someone who wants to initiate a new connection with us. - * This calls the callback set by new_connection_handler() if the handshake is ok. +/** @brief Handle a handshake packet by someone who wants to initiate a new connection with us. + * This calls the callback set by `new_connection_handler()` if the handshake is ok. * - * return -1 on failure. - * return 0 on success. + * @retval -1 on failure. + * @retval 0 on success. */ +non_null(1, 2, 3) nullable(5) static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source, const uint8_t *data, uint16_t length, void *userdata) { @@ -1961,8 +2027,8 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source, n_c.source = *source; n_c.cookie_length = COOKIE_LENGTH; - if (handle_crypto_handshake(c, n_c.recv_nonce, n_c.peersessionpublic_key, n_c.public_key, n_c.dht_public_key, - n_c.cookie, data, length, nullptr) != 0) { + if (!handle_crypto_handshake(c, n_c.recv_nonce, n_c.peersessionpublic_key, n_c.public_key, n_c.dht_public_key, + n_c.cookie, data, length, nullptr)) { free(n_c.cookie); return -1; } @@ -1976,7 +2042,7 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source, return -1; } - if (public_key_cmp(n_c.dht_public_key, conn->dht_public_key) != 0) { + if (!pk_equal(n_c.dht_public_key, conn->dht_public_key)) { connection_kill(c, crypt_connection_id, userdata); } else { if (conn->status != CRYPTO_CONN_COOKIE_REQUESTING && conn->status != CRYPTO_CONN_HANDSHAKE_SENT) { @@ -2001,12 +2067,12 @@ static int handle_new_connection_handshake(Net_Crypto *c, const IP_Port *source, } } - int ret = c->new_connection_callback(c->new_connection_callback_object, &n_c); + const int ret = c->new_connection_callback(c->new_connection_callback_object, &n_c); free(n_c.cookie); return ret; } -/** Accept a crypto connection. +/** @brief Accept a crypto connection. * * return -1 on failure. * return connection id on success. @@ -2044,8 +2110,8 @@ int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c) memcpy(conn->public_key, n_c->public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(conn->recv_nonce, n_c->recv_nonce, CRYPTO_NONCE_SIZE); memcpy(conn->peersessionpublic_key, n_c->peersessionpublic_key, CRYPTO_PUBLIC_KEY_SIZE); - random_nonce(conn->sent_nonce); - crypto_new_keypair(conn->sessionpublic_key, conn->sessionsecret_key); + random_nonce(c->rng, conn->sent_nonce); + crypto_new_keypair(c->rng, conn->sessionpublic_key, conn->sessionsecret_key); encrypt_precompute(conn->peersessionpublic_key, conn->sessionsecret_key, conn->shared_key); conn->status = CRYPTO_CONN_NOT_CONFIRMED; @@ -2066,7 +2132,7 @@ int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c) return crypt_connection_id; } -/** Create a crypto connection. +/** @brief Create a crypto connection. * If one to that real public key already exists, return it. * * return -1 on failure. @@ -2099,8 +2165,8 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u conn->connection_number_tcp = connection_number_tcp; memcpy(conn->public_key, real_public_key, CRYPTO_PUBLIC_KEY_SIZE); - random_nonce(conn->sent_nonce); - crypto_new_keypair(conn->sessionpublic_key, conn->sessionsecret_key); + random_nonce(c->rng, conn->sent_nonce); + crypto_new_keypair(c->rng, conn->sessionpublic_key, conn->sessionsecret_key); conn->status = CRYPTO_CONN_COOKIE_REQUESTING; conn->packet_send_rate = CRYPTO_PACKET_MIN_RATE; conn->packet_send_rate_requested = CRYPTO_PACKET_MIN_RATE; @@ -2108,7 +2174,7 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u conn->rtt_time = DEFAULT_PING_CONNECTION; memcpy(conn->dht_public_key, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE); - conn->cookie_request_number = random_u64(); + conn->cookie_request_number = random_u64(c->rng); uint8_t cookie_request[COOKIE_REQUEST_LENGTH]; if (create_cookie_request(c, cookie_request, conn->dht_public_key, conn->cookie_request_number, @@ -2124,7 +2190,7 @@ int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const u return crypt_connection_id; } -/** Set the direct ip of the crypto connection. +/** @brief Set the direct ip of the crypto connection. * * Connected is 0 if we are not sure we are connected to that person, 1 if we are sure. * @@ -2155,6 +2221,7 @@ int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip } +non_null(1, 3) nullable(5) static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_t *data, uint16_t length, void *userdata) { @@ -2177,7 +2244,7 @@ static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_ // This unlocks the mutex that at this point is locked by do_tcp before // calling do_tcp_connections. pthread_mutex_unlock(&c->tcp_mutex); - int ret = handle_packet_connection(c, crypt_connection_id, data, length, 0, userdata); + const int ret = handle_packet_connection(c, crypt_connection_id, data, length, false, userdata); pthread_mutex_lock(&c->tcp_mutex); if (ret != 0) { @@ -2188,6 +2255,7 @@ static int tcp_data_callback(void *object, int crypt_connection_id, const uint8_ return 0; } +non_null(1, 2, 4) nullable(6) static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length, void *userdata) { @@ -2202,10 +2270,7 @@ static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned in } if (data[0] == NET_PACKET_CRYPTO_HS) { - IP_Port source; - source.port = 0; - source.ip.family = net_family_tcp_family; - source.ip.ip.v6.uint32[0] = tcp_connections_number; + IP_Port source = tcp_connections_number_to_ip_port(tcp_connections_number); if (handle_new_connection_handshake(c, &source, data, length, userdata) != 0) { return -1; @@ -2217,7 +2282,7 @@ static int tcp_oob_callback(void *object, const uint8_t *public_key, unsigned in return -1; } -/** Add a tcp relay, associating it to a crypt_connection_id. +/** @brief Add a tcp relay, associating it to a crypt_connection_id. * * return 0 if it was added. * return -1 if it wasn't. @@ -2231,12 +2296,12 @@ int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip } pthread_mutex_lock(&c->tcp_mutex); - int ret = add_tcp_relay_connection(c->tcp_c, conn->connection_number_tcp, ip_port, public_key); + const int ret = add_tcp_relay_connection(c->tcp_c, conn->connection_number_tcp, ip_port, public_key); pthread_mutex_unlock(&c->tcp_mutex); return ret; } -/** Add a tcp relay to the array. +/** @brief Add a tcp relay to the array. * * return 0 if it was added. * return -1 if it wasn't. @@ -2244,12 +2309,12 @@ int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_key) { pthread_mutex_lock(&c->tcp_mutex); - int ret = add_tcp_relay_global(c->tcp_c, ip_port, public_key); + const int ret = add_tcp_relay_global(c->tcp_c, ip_port, public_key); pthread_mutex_unlock(&c->tcp_mutex); return ret; } -/** Return a random TCP connection number for use in send_tcp_onion_request. +/** @brief Return a random TCP connection number for use in send_tcp_onion_request. * * TODO(irungentoo): This number is just the index of an array that the elements can * change without warning. @@ -2260,13 +2325,27 @@ int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_k int get_random_tcp_con_number(Net_Crypto *c) { pthread_mutex_lock(&c->tcp_mutex); - int ret = get_random_tcp_onion_conn_number(c->tcp_c); + const int ret = get_random_tcp_onion_conn_number(c->tcp_c); pthread_mutex_unlock(&c->tcp_mutex); return ret; } -/** Send an onion packet via the TCP relay corresponding to tcp_connections_number. +/** @brief Put IP_Port of a random onion TCP connection in ip_port. + * + * return true on success. + * return false on failure. + */ +bool get_random_tcp_conn_ip_port(Net_Crypto *c, IP_Port *ip_port) +{ + pthread_mutex_lock(&c->tcp_mutex); + const bool ret = tcp_get_random_conn_ip_port(c->tcp_c, ip_port); + pthread_mutex_unlock(&c->tcp_mutex); + + return ret; +} + +/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number. * * return 0 on success. * return -1 on failure. @@ -2274,13 +2353,34 @@ int get_random_tcp_con_number(Net_Crypto *c) int send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length) { pthread_mutex_lock(&c->tcp_mutex); - int ret = tcp_send_onion_request(c->tcp_c, tcp_connections_number, data, length); + const int ret = tcp_send_onion_request(c->tcp_c, tcp_connections_number, data, length); + pthread_mutex_unlock(&c->tcp_mutex); + + return ret; +} + +/** + * Send a forward request to the TCP relay with IP_Port tcp_forwarder, + * requesting to forward data via a chain of dht nodes starting with dht_node. + * A chain_length of 0 means that dht_node is the final destination of data. + * + * return 0 on success. + * return -1 on failure. + */ +int send_tcp_forward_request(const Logger *logger, Net_Crypto *c, const IP_Port *tcp_forwarder, const IP_Port *dht_node, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length) +{ + pthread_mutex_lock(&c->tcp_mutex); + const int ret = tcp_send_forward_request(logger, c->tcp_c, tcp_forwarder, dht_node, + chain_keys, chain_length, data, data_length); pthread_mutex_unlock(&c->tcp_mutex); return ret; } -/** Copy a maximum of num TCP relays we are connected to to tcp_relays. +/** @brief Copy a maximum of num random TCP relays we are connected to to tcp_relays. + * * NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6. * * return number of relays copied to tcp_relays on success. @@ -2293,12 +2393,26 @@ unsigned int copy_connected_tcp_relays(Net_Crypto *c, Node_format *tcp_relays, u } pthread_mutex_lock(&c->tcp_mutex); - unsigned int ret = tcp_copy_connected_relays(c->tcp_c, tcp_relays, num); + const unsigned int ret = tcp_copy_connected_relays(c->tcp_c, tcp_relays, num); pthread_mutex_unlock(&c->tcp_mutex); return ret; } +uint32_t copy_connected_tcp_relays_index(Net_Crypto *c, Node_format *tcp_relays, uint16_t num, uint32_t idx) +{ + if (num == 0) { + return 0; + } + + pthread_mutex_lock(&c->tcp_mutex); + const uint32_t ret = tcp_copy_connected_relays_index(c->tcp_c, tcp_relays, num, idx); + pthread_mutex_unlock(&c->tcp_mutex); + + return ret; +} + +non_null() static void do_tcp(Net_Crypto *c, void *userdata) { pthread_mutex_lock(&c->tcp_mutex); @@ -2328,7 +2442,7 @@ static void do_tcp(Net_Crypto *c, void *userdata) } } -/** Set function to be called when connection with crypt_connection_id goes connects/disconnects. +/** @brief Set function to be called when connection with crypt_connection_id goes connects/disconnects. * * The set function should return -1 on failure and 0 on success. * Note that if this function is set, the connection will clear itself on disconnect. @@ -2353,7 +2467,7 @@ int connection_status_handler(const Net_Crypto *c, int crypt_connection_id, return 0; } -/** Set function to be called when connection with crypt_connection_id receives a lossless data packet of length. +/** @brief Set function to be called when connection with crypt_connection_id receives a lossless data packet of length. * * The set function should return -1 on failure and 0 on success. * Object and id will be passed to this function untouched. @@ -2376,7 +2490,7 @@ int connection_data_handler(const Net_Crypto *c, int crypt_connection_id, return 0; } -/** Set function to be called when connection with crypt_connection_id receives a lossy data packet of length. +/** @brief Set function to be called when connection with crypt_connection_id receives a lossy data packet of length. * * The set function should return -1 on failure and 0 on success. * Object and id will be passed to this function untouched. @@ -2401,7 +2515,7 @@ int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id, } -/** Set the function for this friend that will be callbacked with object and number if +/** @brief Set the function for this friend that will be callbacked with object and number if * the friend sends us a different dht public key than we have associated to him. * * If this function is called, the connection should be recreated with the new public key. @@ -2425,11 +2539,12 @@ int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id, dht_pk_cb * return 0; } -/** Get the crypto connection id from the ip_port. +/** @brief Get the crypto connection id from the ip_port. * * return -1 on failure. * return connection id on success. */ +non_null() static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port) { return bs_list_find(&c->ip_port_list, (const uint8_t *)ip_port); @@ -2437,7 +2552,7 @@ static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port) #define CRYPTO_MIN_PACKET_SIZE (1 + sizeof(uint16_t) + CRYPTO_MAC_SIZE) -/** Handle raw UDP packets coming directly from the socket. +/** @brief Handle raw UDP packets coming directly from the socket. * * Handles: * Cookie response packets. @@ -2445,6 +2560,7 @@ static int crypto_id_ip_port(const Net_Crypto *c, const IP_Port *ip_port) * Crypto data packets. * */ +non_null(1, 2, 3) nullable(5) static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -2468,7 +2584,7 @@ static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t return 0; } - if (handle_packet_connection(c, crypt_connection_id, packet, length, 1, userdata) != 0) { + if (handle_packet_connection(c, crypt_connection_id, packet, length, true, userdata) != 0) { return 1; } @@ -2490,24 +2606,27 @@ static int udp_handle_packet(void *object, const IP_Port *source, const uint8_t return 0; } -/** The dT for the average packet receiving rate calculations. - * Also used as the */ +/** @brief The dT for the average packet receiving rate calculations. + * Also used as the + */ #define PACKET_COUNTER_AVERAGE_INTERVAL 50 -/** Ratio of recv queue size / recv packet rate (in seconds) times +/** @brief Ratio of recv queue size / recv packet rate (in seconds) times * the number of ms between request packets to send at that ratio */ #define REQUEST_PACKETS_COMPARE_CONSTANT (0.125 * 100.0) -/** Timeout for increasing speed after congestion event (in ms). */ +/** @brief Timeout for increasing speed after congestion event (in ms). */ #define CONGESTION_EVENT_TIMEOUT 1000 -/** If the send queue is SEND_QUEUE_RATIO times larger than the +/** + * If the send queue is SEND_QUEUE_RATIO times larger than the * calculated link speed the packet send speed will be reduced * by a value depending on this number. */ #define SEND_QUEUE_RATIO 2.0 +non_null() static void send_crypto_packets(Net_Crypto *c) { const uint64_t temp_time = current_time_monotonic(c->mono_time); @@ -2537,7 +2656,7 @@ static void send_crypto_packets(Net_Crypto *c) double request_packet_interval = REQUEST_PACKETS_COMPARE_CONSTANT / ((num_packets_array( &conn->recv_array) + 1.0) / (conn->packet_recv_rate + 1.0)); - double request_packet_interval2 = ((CRYPTO_PACKET_MIN_RATE / conn->packet_recv_rate) * + const double request_packet_interval2 = ((CRYPTO_PACKET_MIN_RATE / conn->packet_recv_rate) * (double)CRYPTO_SEND_PACKET_INTERVAL) + (double)PACKET_COUNTER_AVERAGE_INTERVAL; if (request_packet_interval2 < request_packet_interval) { @@ -2564,37 +2683,37 @@ static void send_crypto_packets(Net_Crypto *c) } if ((PACKET_COUNTER_AVERAGE_INTERVAL + conn->packet_counter_set) < temp_time) { - const double dt = temp_time - conn->packet_counter_set; + const double dt = (double)(temp_time - conn->packet_counter_set); conn->packet_recv_rate = (double)conn->packet_counter / (dt / 1000.0); conn->packet_counter = 0; conn->packet_counter_set = temp_time; - uint32_t packets_sent = conn->packets_sent; + const uint32_t packets_sent = conn->packets_sent; conn->packets_sent = 0; - uint32_t packets_resent = conn->packets_resent; + const uint32_t packets_resent = conn->packets_resent; conn->packets_resent = 0; /* conjestion control * calculate a new value of conn->packet_send_rate based on some data */ - unsigned int pos = conn->last_sendqueue_counter % CONGESTION_QUEUE_ARRAY_SIZE; + const unsigned int pos = conn->last_sendqueue_counter % CONGESTION_QUEUE_ARRAY_SIZE; conn->last_sendqueue_size[pos] = num_packets_array(&conn->send_array); long signed int sum = 0; sum = (long signed int)conn->last_sendqueue_size[pos] - (long signed int)conn->last_sendqueue_size[(pos + 1) % CONGESTION_QUEUE_ARRAY_SIZE]; - unsigned int n_p_pos = conn->last_sendqueue_counter % CONGESTION_LAST_SENT_ARRAY_SIZE; + const unsigned int n_p_pos = conn->last_sendqueue_counter % CONGESTION_LAST_SENT_ARRAY_SIZE; conn->last_num_packets_sent[n_p_pos] = packets_sent; conn->last_num_packets_resent[n_p_pos] = packets_resent; conn->last_sendqueue_counter = (conn->last_sendqueue_counter + 1) % (CONGESTION_QUEUE_ARRAY_SIZE * CONGESTION_LAST_SENT_ARRAY_SIZE); - bool direct_connected = 0; + bool direct_connected = false; /* return value can be ignored since the `if` above ensures the connection is established */ crypto_connection_status(c, i, &direct_connected, nullptr); @@ -2605,14 +2724,14 @@ static void send_crypto_packets(Net_Crypto *c) // TODO(irungentoo): use real delay unsigned int delay = (unsigned int)(((double)conn->rtt_time / PACKET_COUNTER_AVERAGE_INTERVAL) + 0.5); - unsigned int packets_set_rem_array = CONGESTION_LAST_SENT_ARRAY_SIZE - CONGESTION_QUEUE_ARRAY_SIZE; + const unsigned int packets_set_rem_array = CONGESTION_LAST_SENT_ARRAY_SIZE - CONGESTION_QUEUE_ARRAY_SIZE; if (delay > packets_set_rem_array) { delay = packets_set_rem_array; } for (unsigned j = 0; j < CONGESTION_QUEUE_ARRAY_SIZE; ++j) { - unsigned int ind = (j + (packets_set_rem_array - delay) + n_p_pos) % CONGESTION_LAST_SENT_ARRAY_SIZE; + const unsigned int ind = (j + (packets_set_rem_array - delay) + n_p_pos) % CONGESTION_LAST_SENT_ARRAY_SIZE; total_sent += conn->last_num_packets_sent[ind]; total_resent += conn->last_num_packets_resent[ind]; } @@ -2626,18 +2745,18 @@ static void send_crypto_packets(Net_Crypto *c) } /* if queue is too big only allow resending packets. */ - uint32_t npackets = num_packets_array(&conn->send_array); + const uint32_t npackets = num_packets_array(&conn->send_array); double min_speed = 1000.0 * (((double)total_sent) / ((double)CONGESTION_QUEUE_ARRAY_SIZE * PACKET_COUNTER_AVERAGE_INTERVAL)); - double min_speed_request = 1000.0 * (((double)(total_sent + total_resent)) / ( + const double min_speed_request = 1000.0 * (((double)(total_sent + total_resent)) / ( (double)CONGESTION_QUEUE_ARRAY_SIZE * PACKET_COUNTER_AVERAGE_INTERVAL)); if (min_speed < CRYPTO_PACKET_MIN_RATE) { min_speed = CRYPTO_PACKET_MIN_RATE; } - double send_array_ratio = (double)npackets / min_speed; + const double send_array_ratio = (double)npackets / min_speed; // TODO(irungentoo): Improve formula? if (send_array_ratio > SEND_QUEUE_RATIO && CRYPTO_MIN_QUEUE_LENGTH < npackets) { @@ -2670,8 +2789,8 @@ static void send_crypto_packets(Net_Crypto *c) double n_packets = conn->packet_send_rate * (((double)(temp_time - conn->last_packets_left_set)) / 1000.0); n_packets += conn->last_packets_left_rem; - uint32_t num_packets = n_packets; - double rem = n_packets - (double)num_packets; + const uint32_t num_packets = n_packets; + const double rem = n_packets - (double)num_packets; if (conn->packets_left > num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH) { conn->packets_left = num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH; @@ -2702,7 +2821,7 @@ static void send_crypto_packets(Net_Crypto *c) } } - int ret = send_requested_packets(c, i, conn->packets_left_requested); + const int ret = send_requested_packets(c, i, conn->packets_left_requested); if (ret != -1) { conn->packets_left_requested -= ret; @@ -2730,7 +2849,7 @@ static void send_crypto_packets(Net_Crypto *c) } if (total_send_rate > CRYPTO_PACKET_MIN_RATE) { - sleep_time = (1000.0 / total_send_rate); + sleep_time = 1000.0 / total_send_rate; if (c->current_sleep_time > sleep_time) { c->current_sleep_time = sleep_time + 1; @@ -2744,16 +2863,18 @@ static void send_crypto_packets(Net_Crypto *c) } } -/** Return 1 if max speed was reached for this connection (no more data can be physically through the pipe). - * Return 0 if it wasn't reached. +/** + * @retval 1 if max speed was reached for this connection (no more data can be physically through the pipe). + * @retval 0 if it wasn't reached. */ bool max_speed_reached(Net_Crypto *c, int crypt_connection_id) { return reset_max_speed_reached(c, crypt_connection_id) != 0; } -/** returns the number of packet slots left in the sendbuffer. - * return 0 if failure. +/** + * @return the number of packet slots left in the sendbuffer. + * @retval 0 if failure. */ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id) { @@ -2763,7 +2884,7 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti return 0; } - uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array); + const uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array); if (conn->packets_left < max_packets) { return conn->packets_left; @@ -2772,7 +2893,7 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti return max_packets; } -/** Sends a lossless cryptopacket. +/** @brief Sends a lossless cryptopacket. * * return -1 if data could not be put in packet queue. * return positive packet number if data was put into the queue. @@ -2782,31 +2903,38 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti * congestion_control: should congestion control apply to this packet? */ int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length, - uint8_t congestion_control) + bool congestion_control) { if (length == 0) { + // We need at least a packet id. + LOGGER_ERROR(c->log, "rejecting empty packet for crypto connection %d", crypt_connection_id); return -1; } if (data[0] < PACKET_ID_RANGE_LOSSLESS_START || data[0] > PACKET_ID_RANGE_LOSSLESS_END) { + LOGGER_ERROR(c->log, "rejecting lossless packet with out-of-range id %d", data[0]); return -1; } Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); if (conn == nullptr) { + LOGGER_WARNING(c->log, "invalid crypt connection id %d", crypt_connection_id); return -1; } if (conn->status != CRYPTO_CONN_ESTABLISHED) { + LOGGER_WARNING(c->log, "attempted to send packet to non-established connection %d", crypt_connection_id); return -1; } if (congestion_control && conn->packets_left == 0) { + LOGGER_ERROR(c->log, "congestion control: rejecting packet of length %d on crypt connection %d", length, + crypt_connection_id); return -1; } - int64_t ret = send_lossless_packet(c, crypt_connection_id, data, length, congestion_control); + const int64_t ret = send_lossless_packet(c, crypt_connection_id, data, length, congestion_control); if (ret == -1) { return -1; @@ -2821,7 +2949,7 @@ int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t return ret; } -/** Check if packet_number was received by the other side. +/** @brief Check if packet_number was received by the other side. * * packet_number must be a valid packet number of a packet sent on this connection. * @@ -2843,8 +2971,8 @@ int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t return -1; } - uint32_t num = num_packets_array(&conn->send_array); - uint32_t num1 = packet_number - conn->send_array.buffer_start; + const uint32_t num = num_packets_array(&conn->send_array); + const uint32_t num1 = packet_number - conn->send_array.buffer_start; if (num >= num1) { return -1; @@ -2853,7 +2981,7 @@ int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t return 0; } -/** Sends a lossy cryptopacket. +/** @brief Sends a lossy cryptopacket. * * return -1 on failure. * return 0 on success. @@ -2878,10 +3006,10 @@ int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t int ret = -1; - if (conn) { + if (conn != nullptr) { pthread_mutex_lock(conn->mutex); - uint32_t buffer_start = conn->recv_array.buffer_start; - uint32_t buffer_end = conn->send_array.buffer_end; + const uint32_t buffer_start = conn->recv_array.buffer_start; + const uint32_t buffer_end = conn->send_array.buffer_end; pthread_mutex_unlock(conn->mutex); ret = send_data_packet_helper(c, crypt_connection_id, buffer_start, buffer_end, data, length); } @@ -2893,7 +3021,7 @@ int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t return ret; } -/** Kill a crypto connection. +/** @brief Kill a crypto connection. * * return -1 on failure. * return 0 on success. @@ -2904,7 +3032,7 @@ int crypto_kill(Net_Crypto *c, int crypt_connection_id) int ret = -1; - if (conn) { + if (conn != nullptr) { if (conn->status == CRYPTO_CONN_ESTABLISHED) { send_kill_packet(c, crypt_connection_id); } @@ -2925,7 +3053,7 @@ int crypto_kill(Net_Crypto *c, int crypt_connection_id) } bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool *direct_connected, - unsigned int *online_tcp_relays) + uint32_t *online_tcp_relays) { const Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id); @@ -2933,19 +3061,19 @@ bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool return false; } - if (direct_connected) { - *direct_connected = 0; + if (direct_connected != nullptr) { + *direct_connected = false; const uint64_t current_time = mono_time_get(c->mono_time); if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev4) > current_time) { - *direct_connected = 1; + *direct_connected = true; } else if ((UDP_DIRECT_TIMEOUT + conn->direct_lastrecv_timev6) > current_time) { - *direct_connected = 1; + *direct_connected = true; } } - if (online_tcp_relays) { + if (online_tcp_relays != nullptr) { *online_tcp_relays = tcp_connection_to_online_tcp_relays(c->tcp_c, conn->connection_number_tcp); } @@ -2954,11 +3082,11 @@ bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool void new_keys(Net_Crypto *c) { - crypto_new_keypair(c->self_public_key, c->self_secret_key); + crypto_new_keypair(c->rng, c->self_public_key, c->self_secret_key); } -/** Save the public and private keys to the keys array. - * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE. +/** @brief Save the public and private keys to the keys array. + * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE. * * TODO(irungentoo): Save only secret key. */ @@ -2968,7 +3096,7 @@ void save_keys(const Net_Crypto *c, uint8_t *keys) memcpy(keys + CRYPTO_PUBLIC_KEY_SIZE, c->self_secret_key, CRYPTO_SECRET_KEY_SIZE); } -/** Load the secret key. +/** @brief Load the secret key. * Length must be CRYPTO_SECRET_KEY_SIZE. */ void load_secret_key(Net_Crypto *c, const uint8_t *sk) @@ -2977,10 +3105,10 @@ void load_secret_key(Net_Crypto *c, const uint8_t *sk) crypto_derive_public_key(c->self_public_key, c->self_secret_key); } -/** Create new instance of Net_Crypto. - * Sets all the global connection variables to their default values. +/** @brief Create new instance of Net_Crypto. + * Sets all the global connection variables to their default values. */ -Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info) +Net_Crypto *new_net_crypto(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info) { if (dht == nullptr) { return nullptr; @@ -2993,9 +3121,11 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co } temp->log = log; + temp->rng = rng; temp->mono_time = mono_time; + temp->ns = ns; - temp->tcp_c = new_tcp_connections(log, mono_time, dht_get_self_secret_key(dht), proxy_info); + temp->tcp_c = new_tcp_connections(log, rng, ns, mono_time, dht_get_self_secret_key(dht), proxy_info); if (temp->tcp_c == nullptr) { free(temp); @@ -3015,7 +3145,7 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co temp->dht = dht; new_keys(temp); - new_symmetric_key(temp->secret_symmetric_key); + new_symmetric_key(rng, temp->secret_symmetric_key); temp->current_sleep_time = CRYPTO_SEND_PACKET_INTERVAL; @@ -3029,6 +3159,7 @@ Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, co return temp; } +non_null(1) nullable(2) static void kill_timedout(Net_Crypto *c, void *userdata) { for (uint32_t i = 0; i < c->crypto_connections_length; ++i) { @@ -3051,15 +3182,14 @@ static void kill_timedout(Net_Crypto *c, void *userdata) if (conn->status == CRYPTO_CONN_ESTABLISHED) { // TODO(irungentoo): add a timeout here? - do_timeout_here(); + /* do_timeout_here(); */ } #endif } } -/** return the optimal interval in ms for running do_net_crypto. - */ +/** return the optimal interval in ms for running do_net_crypto. */ uint32_t crypto_run_interval(const Net_Crypto *c) { return c->current_sleep_time; @@ -3075,6 +3205,10 @@ void do_net_crypto(Net_Crypto *c, void *userdata) void kill_net_crypto(Net_Crypto *c) { + if (c == nullptr) { + return; + } + for (uint32_t i = 0; i < c->crypto_connections_length; ++i) { crypto_kill(c, i); } diff --git a/protocols/Tox/libtox/src/toxcore/net_crypto.h b/protocols/Tox/libtox/src/toxcore/net_crypto.h index 9a3bd1e21c..f6e062f3be 100644 --- a/protocols/Tox/libtox/src/toxcore/net_crypto.h +++ b/protocols/Tox/libtox/src/toxcore/net_crypto.h @@ -20,7 +20,7 @@ /*** Ranges. */ -/** Packets in this range are reserved for net_crypto internal use. */ +/** Packets in this range are reserved for net_crypto events_alloc use. */ #define PACKET_ID_RANGE_RESERVED_START 0 #define PACKET_ID_RANGE_RESERVED_END 15 /** Packets in this range are reserved for Messenger use. */ @@ -86,8 +86,10 @@ /** Interval in ms between sending cookie request/handshake packets. */ #define CRYPTO_SEND_PACKET_INTERVAL 1000 -/** The maximum number of times we try to send the cookie request and handshake - * before giving up. */ +/** + * The maximum number of times we try to send the cookie request and handshake + * before giving up. + */ #define MAX_NUM_SENDPACKET_TRIES 8 /** The timeout of no received UDP packets before the direct UDP connection is considered dead. */ @@ -99,8 +101,10 @@ /** All packets will be padded a number of bytes based on this number. */ #define CRYPTO_MAX_PADDING 8 -/** Base current transfer speed on last CONGESTION_QUEUE_ARRAY_SIZE number of points taken - * at the dT defined in net_crypto.c */ +/** + * Base current transfer speed on last CONGESTION_QUEUE_ARRAY_SIZE number of points taken + * at the dT defined in net_crypto.c + */ #define CONGESTION_QUEUE_ARRAY_SIZE 12 #define CONGESTION_LAST_SENT_ARRAY_SIZE (CONGESTION_QUEUE_ARRAY_SIZE * 2) @@ -110,10 +114,10 @@ typedef struct Net_Crypto Net_Crypto; -const uint8_t *nc_get_self_public_key(const Net_Crypto *c); -const uint8_t *nc_get_self_secret_key(const Net_Crypto *c); -TCP_Connections *nc_get_tcp_c(const Net_Crypto *c); -DHT *nc_get_dht(const Net_Crypto *c); +non_null() const uint8_t *nc_get_self_public_key(const Net_Crypto *c); +non_null() const uint8_t *nc_get_self_secret_key(const Net_Crypto *c); +non_null() TCP_Connections *nc_get_tcp_c(const Net_Crypto *c); +non_null() DHT *nc_get_dht(const Net_Crypto *c); typedef struct New_Connection { IP_Port source; @@ -125,45 +129,49 @@ typedef struct New_Connection { uint8_t cookie_length; } New_Connection; -typedef int connection_status_cb(void *object, int id, uint8_t status, void *userdata); +typedef int connection_status_cb(void *object, int id, bool status, void *userdata); typedef int connection_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); typedef int connection_lossy_data_cb(void *object, int id, const uint8_t *data, uint16_t length, void *userdata); typedef void dht_pk_cb(void *data, int32_t number, const uint8_t *dht_public_key, void *userdata); typedef int new_connection_cb(void *object, const New_Connection *n_c); -/** Set function to be called when someone requests a new connection to us. +/** @brief Set function to be called when someone requests a new connection to us. * * The set function should return -1 on failure and 0 on success. * * n_c is only valid for the duration of the function call. */ +non_null() void new_connection_handler(Net_Crypto *c, new_connection_cb *new_connection_callback, void *object); -/** Accept a crypto connection. +/** @brief Accept a crypto connection. * * return -1 on failure. * return connection id on success. */ +non_null() int accept_crypto_connection(Net_Crypto *c, const New_Connection *n_c); -/** Create a crypto connection. +/** @brief Create a crypto connection. * If one to that real public key already exists, return it. * * return -1 on failure. * return connection id on success. */ +non_null() int new_crypto_connection(Net_Crypto *c, const uint8_t *real_public_key, const uint8_t *dht_public_key); -/** Set the direct ip of the crypto connection. +/** @brief Set the direct ip of the crypto connection. * * Connected is 0 if we are not sure we are connected to that person, 1 if we are sure. * * return -1 on failure. * return 0 on success. */ +non_null() int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port, bool connected); -/** Set function to be called when connection with crypt_connection_id goes connects/disconnects. +/** @brief Set function to be called when connection with crypt_connection_id goes connects/disconnects. * * The set function should return -1 on failure and 0 on success. * Note that if this function is set, the connection will clear itself on disconnect. @@ -173,10 +181,11 @@ int set_direct_ip_port(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip * return -1 on failure. * return 0 on success. */ +non_null() int connection_status_handler(const Net_Crypto *c, int crypt_connection_id, connection_status_cb *connection_status_callback, void *object, int id); -/** Set function to be called when connection with crypt_connection_id receives a lossless data packet of length. +/** @brief Set function to be called when connection with crypt_connection_id receives a lossless data packet of length. * * The set function should return -1 on failure and 0 on success. * Object and id will be passed to this function untouched. @@ -184,11 +193,12 @@ int connection_status_handler(const Net_Crypto *c, int crypt_connection_id, * return -1 on failure. * return 0 on success. */ +non_null() int connection_data_handler(const Net_Crypto *c, int crypt_connection_id, connection_data_cb *connection_data_callback, void *object, int id); -/** Set function to be called when connection with crypt_connection_id receives a lossy data packet of length. +/** @brief Set function to be called when connection with crypt_connection_id receives a lossy data packet of length. * * The set function should return -1 on failure and 0 on success. * Object and id will be passed to this function untouched. @@ -196,10 +206,11 @@ int connection_data_handler(const Net_Crypto *c, int crypt_connection_id, * return -1 on failure. * return 0 on success. */ +non_null() int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id, connection_lossy_data_cb *connection_lossy_data_callback, void *object, int id); -/** Set the function for this friend that will be callbacked with object and number if +/** @brief Set the function for this friend that will be callbacked with object and number if * the friend sends us a different dht public key than we have associated to him. * * If this function is called, the connection should be recreated with the new public key. @@ -209,20 +220,25 @@ int connection_lossy_data_handler(const Net_Crypto *c, int crypt_connection_id, * return -1 on failure. * return 0 on success. */ -int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id, dht_pk_cb *function, void *object, - uint32_t number); +non_null() +int nc_dht_pk_callback(const Net_Crypto *c, int crypt_connection_id, + dht_pk_cb *function, void *object, uint32_t number); -/** returns the number of packet slots left in the sendbuffer. - * return 0 if failure. +/** + * @return the number of packet slots left in the sendbuffer. + * @retval 0 if failure. */ +non_null() uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id); -/** Return 1 if max speed was reached for this connection (no more data can be physically through the pipe). - * Return 0 if it wasn't reached. +/** + * @retval 1 if max speed was reached for this connection (no more data can be physically through the pipe). + * @retval 0 if it wasn't reached. */ +non_null() bool max_speed_reached(Net_Crypto *c, int crypt_connection_id); -/** Sends a lossless cryptopacket. +/** @brief Sends a lossless cryptopacket. * * return -1 if data could not be put in packet queue. * return positive packet number if data was put into the queue. @@ -231,10 +247,11 @@ bool max_speed_reached(Net_Crypto *c, int crypt_connection_id); * * congestion_control: should congestion control apply to this packet? */ -int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length, - uint8_t congestion_control); +non_null() +int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, + const uint8_t *data, uint16_t length, bool congestion_control); -/** Check if packet_number was received by the other side. +/** @brief Check if packet_number was received by the other side. * * packet_number must be a valid packet number of a packet sent on this connection. * @@ -248,32 +265,37 @@ int64_t write_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t * It CANNOT be simplified to `packet_number < buffer_start`, as it will fail * when `buffer_end < buffer_start`. */ +non_null() int cryptpacket_received(const Net_Crypto *c, int crypt_connection_id, uint32_t packet_number); -/** Sends a lossy cryptopacket. +/** @brief Sends a lossy cryptopacket. * * return -1 on failure. * return 0 on success. * * The first byte of data must be in the PACKET_ID_RANGE_LOSSY. */ +non_null() int send_lossy_cryptpacket(Net_Crypto *c, int crypt_connection_id, const uint8_t *data, uint16_t length); -/** Add a tcp relay, associating it to a crypt_connection_id. +/** @brief Add a tcp relay, associating it to a crypt_connection_id. * * return 0 if it was added. * return -1 if it wasn't. */ -int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port, const uint8_t *public_key); +non_null() +int add_tcp_relay_peer(Net_Crypto *c, int crypt_connection_id, const IP_Port *ip_port, + const uint8_t *public_key); -/** Add a tcp relay to the array. +/** @brief Add a tcp relay to the array. * * return 0 if it was added. * return -1 if it wasn't. */ +non_null() int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_key); -/** Return a random TCP connection number for use in send_tcp_onion_request. +/** @brief Return a random TCP connection number for use in send_tcp_onion_request. * * TODO(irungentoo): This number is just the index of an array that the elements can * change without warning. @@ -281,69 +303,111 @@ int add_tcp_relay(Net_Crypto *c, const IP_Port *ip_port, const uint8_t *public_k * return TCP connection number on success. * return -1 on failure. */ +non_null() int get_random_tcp_con_number(Net_Crypto *c); -/** Send an onion packet via the TCP relay corresponding to tcp_connections_number. +/** @brief Put IP_Port of a random onion TCP connection in ip_port. + * + * return true on success. + * return false on failure. + */ +non_null() +bool get_random_tcp_conn_ip_port(Net_Crypto *c, IP_Port *ip_port); + +/** @brief Send an onion packet via the TCP relay corresponding to tcp_connections_number. + * + * return 0 on success. + * return -1 on failure. + */ +non_null() +int send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number, + const uint8_t *data, uint16_t length); + +/** + * Send a forward request to the TCP relay with IP_Port tcp_forwarder, + * requesting to forward data via a chain of dht nodes starting with dht_node. + * A chain_length of 0 means that dht_node is the final destination of data. * * return 0 on success. * return -1 on failure. */ -int send_tcp_onion_request(Net_Crypto *c, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length); +non_null() +int send_tcp_forward_request(const Logger *logger, Net_Crypto *c, const IP_Port *tcp_forwarder, const IP_Port *dht_node, + const uint8_t *chain_keys, uint16_t chain_length, + const uint8_t *data, uint16_t data_length); -/** Copy a maximum of num TCP relays we are connected to to tcp_relays. +/** @brief Copy a maximum of num random TCP relays we are connected to to tcp_relays. + * * NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6. * * return number of relays copied to tcp_relays on success. * return 0 on failure. */ +non_null() unsigned int copy_connected_tcp_relays(Net_Crypto *c, Node_format *tcp_relays, uint16_t num); -/** Kill a crypto connection. +/** + * Copy a maximum of `max_num` TCP relays we are connected to starting at the index in the TCP relay array + * for `tcp_c` designated by `idx`. If idx is greater than the array length a modulo operation is performed. + * + * Returns the number of relays successfully copied. + */ +non_null() +uint32_t copy_connected_tcp_relays_index(Net_Crypto *c, Node_format *tcp_relays, uint16_t num, uint32_t idx); + +/** @brief Kill a crypto connection. * * return -1 on failure. * return 0 on success. */ +non_null() int crypto_kill(Net_Crypto *c, int crypt_connection_id); -/** return true if connection is valid, false otherwise +/** + * @retval true if connection is valid, false otherwise * * sets direct_connected to 1 if connection connects directly to other, 0 if it isn't. * sets online_tcp_relays to the number of connected tcp relays this connection has. */ -bool crypto_connection_status(const Net_Crypto *c, int crypt_connection_id, bool *direct_connected, - unsigned int *online_tcp_relays); +non_null(1, 3) nullable(4) +bool crypto_connection_status( + const Net_Crypto *c, int crypt_connection_id, bool *direct_connected, uint32_t *online_tcp_relays); -/** Generate our public and private keys. - * Only call this function the first time the program starts. +/** @brief Generate our public and private keys. + * Only call this function the first time the program starts. */ +non_null() void new_keys(Net_Crypto *c); -/** Save the public and private keys to the keys array. - * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE. +/** @brief Save the public and private keys to the keys array. + * Length must be CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE. * * TODO(irungentoo): Save only secret key. */ +non_null() void save_keys(const Net_Crypto *c, uint8_t *keys); -/** Load the secret key. +/** @brief Load the secret key. * Length must be CRYPTO_SECRET_KEY_SIZE. */ +non_null() void load_secret_key(Net_Crypto *c, const uint8_t *sk); -/** Create new instance of Net_Crypto. - * Sets all the global connection variables to their default values. +/** @brief Create new instance of Net_Crypto. + * Sets all the global connection variables to their default values. */ -Net_Crypto *new_net_crypto(const Logger *log, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info); +non_null() +Net_Crypto *new_net_crypto(const Logger *log, const Random *rng, const Network *ns, Mono_Time *mono_time, DHT *dht, const TCP_Proxy_Info *proxy_info); -/** return the optimal interval in ms for running do_net_crypto. - */ +/** return the optimal interval in ms for running do_net_crypto. */ +non_null() uint32_t crypto_run_interval(const Net_Crypto *c); /** Main loop. */ +non_null(1) nullable(2) void do_net_crypto(Net_Crypto *c, void *userdata); +nullable(1) void kill_net_crypto(Net_Crypto *c); - - #endif diff --git a/protocols/Tox/libtox/src/toxcore/network.c b/protocols/Tox/libtox/src/toxcore/network.c index 8569fc17e5..35893b73dc 100644 --- a/protocols/Tox/libtox/src/toxcore/network.c +++ b/protocols/Tox/libtox/src/toxcore/network.c @@ -92,16 +92,11 @@ #include <sodium.h> #endif +#include "ccompat.h" #include "logger.h" #include "mono_time.h" #include "util.h" -//!TOKSTYLE- -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#include "../testing/fuzzing/fuzz_adapter.h" -#endif -//!TOKSTYLE+ - // Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 @@ -113,6 +108,12 @@ #endif #endif +static_assert(sizeof(IP4) == SIZE_IP4, "IP4 size must be 4"); + +// TODO(iphydf): Stop relying on this. We memcpy this struct (and IP4 above) +// into packets but really should be serialising it properly. +static_assert(sizeof(IP6) == SIZE_IP6, "IP6 size must be 16"); + #if !defined(OS_WIN32) static bool should_ignore_recv_error(int err) @@ -120,21 +121,30 @@ static bool should_ignore_recv_error(int err) return err == EWOULDBLOCK; } +static bool should_ignore_connect_error(int err) +{ + return err == EWOULDBLOCK || err == EINPROGRESS; +} + +non_null() static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufsize) { return inet_ntop(AF_INET, addr, buf, bufsize); } +non_null() static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t bufsize) { return inet_ntop(AF_INET6, addr, buf, bufsize); } +non_null() static int inet_pton4(const char *addrString, struct in_addr *addrbuf) { return inet_pton(AF_INET, addrString, addrbuf); } +non_null() static int inet_pton6(const char *addrString, struct in6_addr *addrbuf) { return inet_pton(AF_INET6, addrString, addrbuf); @@ -152,6 +162,12 @@ static bool should_ignore_recv_error(int err) return err == WSAEWOULDBLOCK || err == WSAECONNRESET; } +static bool should_ignore_connect_error(int err) +{ + return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS; +} + +non_null() static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufsize) { struct sockaddr_in saddr = {0}; @@ -168,6 +184,7 @@ static const char *inet_ntop4(const struct in_addr *addr, char *buf, size_t bufs return buf; } +non_null() static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t bufsize) { struct sockaddr_in6 saddr = {0}; @@ -184,6 +201,7 @@ static const char *inet_ntop6(const struct in6_addr *addr, char *buf, size_t buf return buf; } +non_null() static int inet_pton4(const char *addrString, struct in_addr *addrbuf) { struct sockaddr_in saddr = {0}; @@ -199,6 +217,7 @@ static int inet_pton4(const char *addrString, struct in_addr *addrbuf) return 1; } +non_null() static int inet_pton6(const char *addrString, struct in6_addr *addrbuf) { struct sockaddr_in6 saddr = {0}; @@ -221,7 +240,6 @@ static_assert(TOX_INET6_ADDRSTRLEN >= INET6_ADDRSTRLEN, static_assert(TOX_INET_ADDRSTRLEN >= INET_ADDRSTRLEN, "TOX_INET_ADDRSTRLEN should be greater or equal to INET_ADDRSTRLEN (#INET_ADDRSTRLEN)"); -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static int make_proto(int proto) { switch (proto) { @@ -249,7 +267,6 @@ static int make_socktype(int type) return type; } } -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static int make_family(Family tox_family) { @@ -268,42 +285,58 @@ static int make_family(Family tox_family) } } +static const Family family_unspec = {TOX_AF_UNSPEC}; +static const Family family_ipv4 = {TOX_AF_INET}; +static const Family family_ipv6 = {TOX_AF_INET6}; +static const Family family_tcp_server = {TCP_SERVER_FAMILY}; +static const Family family_tcp_client = {TCP_CLIENT_FAMILY}; +static const Family family_tcp_ipv4 = {TCP_INET}; +static const Family family_tcp_ipv6 = {TCP_INET6}; +static const Family family_tox_tcp_ipv4 = {TOX_TCP_INET}; +static const Family family_tox_tcp_ipv6 = {TOX_TCP_INET6}; + static const Family *make_tox_family(int family) { switch (family) { case AF_INET: - return &net_family_ipv4; + return &family_ipv4; case AF_INET6: - return &net_family_ipv6; + return &family_ipv6; case AF_UNSPEC: - return &net_family_unspec; + return &family_unspec; default: return nullptr; } } +non_null() static void get_ip4(IP4 *result, const struct in_addr *addr) { + static_assert(sizeof(result->uint32) == sizeof(addr->s_addr), + "Tox and operating system don't agree on size of IPv4 addresses"); result->uint32 = addr->s_addr; } +non_null() static void get_ip6(IP6 *result, const struct in6_addr *addr) { - assert(sizeof(result->uint8) == sizeof(addr->s6_addr)); + static_assert(sizeof(result->uint8) == sizeof(addr->s6_addr), + "Tox and operating system don't agree on size of IPv6 addresses"); memcpy(result->uint8, addr->s6_addr, sizeof(result->uint8)); } +non_null() static void fill_addr4(const IP4 *ip, struct in_addr *addr) { addr->s_addr = ip->uint32; } +non_null() static void fill_addr6(const IP6 *ip, struct in6_addr *addr) { - assert(sizeof(ip->uint8) == sizeof(addr->s6_addr)); memcpy(addr->s6_addr, ip->uint8, sizeof(ip->uint8)); } @@ -311,6 +344,9 @@ static void fill_addr6(const IP6 *ip, struct in6_addr *addr) #define INADDR_LOOPBACK 0x7f000001 #endif +static const IP empty_ip = {{0}}; +const IP_Port empty_ip_port = {{{0}}}; + const IP4 ip4_broadcast = { INADDR_BROADCAST }; const IP6 ip6_broadcast = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } @@ -326,7 +362,12 @@ IP4 get_ip4_loopback(void) IP6 get_ip6_loopback(void) { IP6 loopback; +#ifdef ESP_PLATFORM + loopback = empty_ip_port.ip.ip.v6; + loopback.uint8[15] = 1; +#else get_ip6(&loopback, &in6addr_loopback); +#endif return loopback; } @@ -336,134 +377,262 @@ IP6 get_ip6_loopback(void) const Socket net_invalid_socket = { (int)INVALID_SOCKET }; -const Family net_family_unspec = {TOX_AF_UNSPEC}; -const Family net_family_ipv4 = {TOX_AF_INET}; -const Family net_family_ipv6 = {TOX_AF_INET6}; -const Family net_family_tcp_family = {TCP_FAMILY}; -const Family net_family_tcp_onion = {TCP_ONION_FAMILY}; -const Family net_family_tcp_ipv4 = {TCP_INET}; -const Family net_family_tcp_ipv6 = {TCP_INET6}; -const Family net_family_tox_tcp_ipv4 = {TOX_TCP_INET}; -const Family net_family_tox_tcp_ipv6 = {TOX_TCP_INET6}; +Family net_family_unspec() +{ + return family_unspec; +} + +Family net_family_ipv4() +{ + return family_ipv4; +} + +Family net_family_ipv6() +{ + return family_ipv6; +} + +Family net_family_tcp_server() +{ + return family_tcp_server; +} + +Family net_family_tcp_client() +{ + return family_tcp_client; +} + +Family net_family_tcp_ipv4() +{ + return family_tcp_ipv4; +} + +Family net_family_tcp_ipv6() +{ + return family_tcp_ipv6; +} + +Family net_family_tox_tcp_ipv4() +{ + return family_tox_tcp_ipv4; +} + +Family net_family_tox_tcp_ipv6() +{ + return family_tox_tcp_ipv6; +} bool net_family_is_unspec(Family family) { - return family.value == net_family_unspec.value; + return family.value == family_unspec.value; } bool net_family_is_ipv4(Family family) { - return family.value == net_family_ipv4.value; + return family.value == family_ipv4.value; } bool net_family_is_ipv6(Family family) { - return family.value == net_family_ipv6.value; + return family.value == family_ipv6.value; } -bool net_family_is_tcp_family(Family family) +bool net_family_is_tcp_server(Family family) { - return family.value == net_family_tcp_family.value; + return family.value == family_tcp_server.value; } -bool net_family_is_tcp_onion(Family family) +bool net_family_is_tcp_client(Family family) { - return family.value == net_family_tcp_onion.value; + return family.value == family_tcp_client.value; } bool net_family_is_tcp_ipv4(Family family) { - return family.value == net_family_tcp_ipv4.value; + return family.value == family_tcp_ipv4.value; } bool net_family_is_tcp_ipv6(Family family) { - return family.value == net_family_tcp_ipv6.value; + return family.value == family_tcp_ipv6.value; } bool net_family_is_tox_tcp_ipv4(Family family) { - return family.value == net_family_tox_tcp_ipv4.value; + return family.value == family_tox_tcp_ipv4.value; } bool net_family_is_tox_tcp_ipv6(Family family) { - return family.value == net_family_tox_tcp_ipv6.value; + return family.value == family_tox_tcp_ipv6.value; } bool sock_valid(Socket sock) { - return sock.socket != net_invalid_socket.socket; + return sock.sock != net_invalid_socket.sock; } -/** Close the socket. - */ -void kill_sock(Socket sock) +struct Network_Addr { + struct sockaddr_storage addr; + size_t size; +}; + +non_null() +static int sys_close(void *obj, int sock) +{ +#if defined(OS_WIN32) + return closesocket(sock); +#else // !OS_WIN32 + return close(sock); +#endif +} + +non_null() +static int sys_accept(void *obj, int sock) +{ + return accept(sock, nullptr, nullptr); +} + +non_null() +static int sys_bind(void *obj, int sock, const Network_Addr *addr) +{ + return bind(sock, (const struct sockaddr *)&addr->addr, addr->size); +} + +non_null() +static int sys_listen(void *obj, int sock, int backlog) +{ + return listen(sock, backlog); +} + +non_null() +static int sys_recvbuf(void *obj, int sock) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return; -#else #ifdef OS_WIN32 - closesocket(sock.socket); + u_long count = 0; + ioctlsocket(sock, FIONREAD, &count); #else - close(sock.socket); -#endif /* OS_WIN32 */ -#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ + int count = 0; + ioctl(sock, FIONREAD, &count); +#endif + + return count; } -bool set_socket_nonblock(Socket sock) +non_null() +static int sys_recv(void *obj, int sock, uint8_t *buf, size_t len) +{ + return recv(sock, (char *)buf, len, MSG_NOSIGNAL); +} + +non_null() +static int sys_send(void *obj, int sock, const uint8_t *buf, size_t len) +{ + return send(sock, (const char *)buf, len, MSG_NOSIGNAL); +} + +non_null() +static int sys_sendto(void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr) { + return sendto(sock, (const char *)buf, len, 0, (const struct sockaddr *)&addr->addr, addr->size); +} + +non_null() +static int sys_recvfrom(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr) { + socklen_t size = addr->size; + const int ret = recvfrom(sock, (char *)buf, len, 0, (struct sockaddr *)&addr->addr, &size); + addr->size = size; + return ret; +} + +non_null() +static int sys_socket(void *obj, int domain, int type, int proto) +{ + return (int)socket(domain, type, proto); +} + +non_null() +static int sys_socket_nonblock(void *obj, int sock, bool nonblock) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return true; -#else #ifdef OS_WIN32 - u_long mode = 1; - return ioctlsocket(sock.socket, FIONBIO, &mode) == 0; + u_long mode = nonblock ? 1 : 0; + return ioctlsocket(sock, FIONBIO, &mode); #else - return fcntl(sock.socket, F_SETFL, O_NONBLOCK, 1) == 0; + return fcntl(sock, F_SETFL, O_NONBLOCK, nonblock ? 1 : 0); #endif /* OS_WIN32 */ -#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ } -bool set_socket_nosigpipe(Socket sock) +non_null() +static int sys_getsockopt(void *obj, int sock, int level, int optname, void *optval, size_t *optlen) { -#if defined(__APPLE__) - int set = 1; - return setsockopt(sock.socket, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set, sizeof(int)) == 0; -#else - return true; -#endif + socklen_t len = *optlen; + const int ret = getsockopt(sock, level, optname, optval, &len); + *optlen = len; + return ret; } -bool set_socket_reuseaddr(Socket sock) +non_null() +static int sys_setsockopt(void *obj, int sock, int level, int optname, const void *optval, size_t optlen) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return false; -#else - int set = 1; - return setsockopt(sock.socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&set, sizeof(set)) == 0; -#endif + return setsockopt(sock, level, optname, optval, optlen); } -bool set_socket_dualstack(Socket sock) +static const Network_Funcs system_network_funcs = { + sys_close, + sys_accept, + sys_bind, + sys_listen, + sys_recvbuf, + sys_recv, + sys_recvfrom, + sys_send, + sys_sendto, + sys_socket, + sys_socket_nonblock, + sys_getsockopt, + sys_setsockopt, +}; +static const Network system_network_obj = {&system_network_funcs}; + +const Network *system_network(void) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return false; -#else - int ipv6only = 0; - socklen_t optsize = sizeof(ipv6only); - int res = getsockopt(sock.socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only, &optsize); + if ((true)) { + return nullptr; + } +#endif +#ifdef OS_WIN32 + WSADATA wsaData; - if ((res == 0) && (ipv6only == 0)) { - return true; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { + return nullptr; } +#endif + return &system_network_obj; +} - ipv6only = 0; - return setsockopt(sock.socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&ipv6only, sizeof(ipv6only)) == 0; +#if 0 +/* TODO(iphydf): Call this from functions that use `system_network()`. */ +void system_network_deinit(const Network *ns) +{ +#ifdef OS_WIN32 + WSACleanup(); +#endif +} #endif + +non_null() +static int net_setsockopt(const Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen) +{ + return ns->funcs->setsockopt(ns->obj, sock.sock, level, optname, optval, optlen); } +non_null() +static int net_getsockopt(const Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen) +{ + return ns->funcs->getsockopt(ns->obj, sock.sock, level, optname, optval, optlen); +} +non_null() static uint32_t data_0(uint16_t buflen, const uint8_t *buffer) { uint32_t data = 0; @@ -474,43 +643,136 @@ static uint32_t data_0(uint16_t buflen, const uint8_t *buffer) return data; } +non_null() static uint32_t data_1(uint16_t buflen, const uint8_t *buffer) { uint32_t data = 0; - if (buflen > 7) { + if (buflen > 8) { net_unpack_u32(buffer + 5, &data); } return data; } +non_null() static void loglogdata(const Logger *log, const char *message, const uint8_t *buffer, uint16_t buflen, const IP_Port *ip_port, long res) { - char ip_str[IP_NTOA_LEN]; - if (res < 0) { /* Windows doesn't necessarily know `%zu` */ - int error = net_error(); + Ip_Ntoa ip_str; + const int error = net_error(); char *strerror = net_new_strerror(error); LOGGER_TRACE(log, "[%2u] %s %3u%c %s:%u (%u: %s) | %08x%08x...%02x", buffer[0], message, min_u16(buflen, 999), 'E', - ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), error, + net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), error, strerror, data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]); net_kill_strerror(strerror); } else if ((res > 0) && ((size_t)res <= buflen)) { + Ip_Ntoa ip_str; LOGGER_TRACE(log, "[%2u] %s %3u%c %s:%u (%u: %s) | %08x%08x...%02x", buffer[0], message, min_u16(res, 999), (size_t)res < buflen ? '<' : '=', - ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), 0, "OK", + net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), 0, "OK", data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]); } else { /* empty or overwrite */ + Ip_Ntoa ip_str; LOGGER_TRACE(log, "[%2u] %s %lu%c%u %s:%u (%u: %s) | %08x%08x...%02x", - buffer[0], message, res, !res ? '!' : '>', buflen, - ip_ntoa(&ip_port->ip, ip_str, sizeof(ip_str)), net_ntohs(ip_port->port), 0, "OK", + buffer[0], message, res, res == 0 ? '!' : '>', buflen, + net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), 0, "OK", data_0(buflen, buffer), data_1(buflen, buffer), buffer[buflen - 1]); } } +int net_send(const Network *ns, const Logger *log, + Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port) +{ + const int res = ns->funcs->send(ns->obj, sock.sock, buf, len); + loglogdata(log, "T=>", buf, len, ip_port, res); + return res; +} + +non_null() +static int net_sendto( + const Network *ns, + Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr, const IP_Port *ip_port) +{ + return ns->funcs->sendto(ns->obj, sock.sock, buf, len, addr); +} + +int net_recv(const Network *ns, const Logger *log, + Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port) +{ + const int res = ns->funcs->recv(ns->obj, sock.sock, buf, len); + loglogdata(log, "=>T", buf, len, ip_port, res); + return res; +} + +non_null() +static int net_recvfrom(const Network *ns, + Socket sock, uint8_t *buf, size_t len, Network_Addr *addr) +{ + return ns->funcs->recvfrom(ns->obj, sock.sock, buf, len, addr); +} + +int net_listen(const Network *ns, Socket sock, int backlog) +{ + return ns->funcs->listen(ns->obj, sock.sock, backlog); +} + +non_null() +static int net_bind(const Network *ns, Socket sock, const Network_Addr *addr) +{ + return ns->funcs->bind(ns->obj, sock.sock, addr); +} + +Socket net_accept(const Network *ns, Socket sock) +{ + const Socket newsock = {ns->funcs->accept(ns->obj, sock.sock)}; + return newsock; +} + +/** Close the socket. */ +void kill_sock(const Network *ns, Socket sock) +{ + ns->funcs->close(ns->obj, sock.sock); +} + +bool set_socket_nonblock(const Network *ns, Socket sock) +{ + return ns->funcs->socket_nonblock(ns->obj, sock.sock, true) == 0; +} + +bool set_socket_nosigpipe(const Network *ns, Socket sock) +{ +#if defined(__APPLE__) + int set = 1; + return net_setsockopt(ns, sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(int)) == 0; +#else + return true; +#endif +} + +bool set_socket_reuseaddr(const Network *ns, Socket sock) +{ + int set = 1; + return net_setsockopt(ns, sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)) == 0; +} + +bool set_socket_dualstack(const Network *ns, Socket sock) +{ + int ipv6only = 0; + size_t optsize = sizeof(ipv6only); + const int res = net_getsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize); + + if ((res == 0) && (ipv6only == 0)) { + return true; + } + + ipv6only = 0; + return net_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0; +} + + typedef struct Packet_Handler { packet_handler_cb *function; void *object; @@ -519,6 +781,7 @@ typedef struct Packet_Handler { struct Networking_Core { const Logger *log; Packet_Handler packethandlers[256]; + const Network *ns; Family family; uint16_t port; @@ -543,6 +806,12 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe { IP_Port ipp_copy = *ip_port; + if (net_family_is_unspec(ip_port->ip.family)) { + // TODO(iphydf): Make this an error. Currently this fails sometimes when + // called from DHT.c:do_ping_and_sendnode_requests. + return -1; + } + if (net_family_is_unspec(net->family)) { /* Socket not initialized */ // TODO(iphydf): Make this an error. Currently, the onion client calls // this via DHT getnodes. @@ -570,25 +839,23 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe ip6.uint32[2] = net_htonl(0xFFFF); ip6.uint32[3] = ipp_copy.ip.ip.v4.uint32; - ipp_copy.ip.family = net_family_ipv6; + ipp_copy.ip.family = net_family_ipv6(); ipp_copy.ip.ip.v6 = ip6; } - struct sockaddr_storage addr; - - size_t addrsize; + Network_Addr addr; if (net_family_is_ipv4(ipp_copy.ip.family)) { - struct sockaddr_in *const addr4 = (struct sockaddr_in *)&addr; + struct sockaddr_in *const addr4 = (struct sockaddr_in *)&addr.addr; - addrsize = sizeof(struct sockaddr_in); + addr.size = sizeof(struct sockaddr_in); addr4->sin_family = AF_INET; addr4->sin_port = ipp_copy.port; fill_addr4(&ipp_copy.ip.ip.v4, &addr4->sin_addr); } else if (net_family_is_ipv6(ipp_copy.ip.family)) { - struct sockaddr_in6 *const addr6 = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6 *const addr6 = (struct sockaddr_in6 *)&addr.addr; - addrsize = sizeof(struct sockaddr_in6); + addr.size = sizeof(struct sockaddr_in6); addr6->sin6_family = AF_INET6; addr6->sin6_port = ipp_copy.port; fill_addr6(&ipp_copy.ip.ip.v6, &addr6->sin6_addr); @@ -596,21 +863,12 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe addr6->sin6_flowinfo = 0; addr6->sin6_scope_id = 0; } else { - // TODO(iphydf): Make this an error. Currently this fails sometimes when - // called from DHT.c:do_ping_and_sendnode_requests. - LOGGER_WARNING(net->log, "unknown address type: %d", ipp_copy.ip.family.value); + LOGGER_ERROR(net->log, "unknown address type: %d", ipp_copy.ip.family.value); return -1; } -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - const long res = fuzz_sendto(net->sock.socket, (const char *)packet.data, packet.length, 0, - (struct sockaddr *)&addr, addrsize); -#else - const long res = sendto(net->sock.socket, (const char *)packet.data, packet.length, 0, - (struct sockaddr *)&addr, addrsize); -#endif - - loglogdata(net->log, "O=>", packet.data, packet.length, &ipp_copy, res); + const long res = net_sendto(net->ns, net->sock, packet.data, packet.length, &addr, &ipp_copy); + loglogdata(net->log, "O=>", packet.data, packet.length, ip_port, res); assert(res <= INT_MAX); return (int)res; @@ -627,34 +885,27 @@ int sendpacket(const Networking_Core *net, const IP_Port *ip_port, const uint8_t return send_packet(net, ip_port, packet); } -/** Function to receive data - * ip and port of sender is put into ip_port. - * Packet data is put into data. - * Packet length is put into length. +/** @brief Function to receive data + * ip and port of sender is put into ip_port. + * Packet data is put into data. + * Packet length is put into length. */ -static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) +non_null() +static int receivepacket(const Network *ns, const Logger *log, Socket sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) { memset(ip_port, 0, sizeof(IP_Port)); - struct sockaddr_storage addr; -#ifdef OS_WIN32 - int addrlen = sizeof(addr); -#else - socklen_t addrlen = sizeof(addr); -#endif + Network_Addr addr = {{0}}; + addr.size = sizeof(addr.addr); *length = 0; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int fail_or_len = fuzz_recvfrom(sock.socket, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); -#else - int fail_or_len = recvfrom(sock.socket, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); -#endif + const int fail_or_len = net_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, &addr); if (fail_or_len < 0) { - int error = net_error(); + const int error = net_error(); if (!should_ignore_recv_error(error)) { char *strerror = net_new_strerror(error); - LOGGER_ERROR(log, "Unexpected error reading from socket: %u, %s", error, strerror); + LOGGER_ERROR(log, "unexpected error reading from socket: %u, %s", error, strerror); net_kill_strerror(strerror); } @@ -663,8 +914,8 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8 *length = (uint32_t)fail_or_len; - if (addr.ss_family == AF_INET) { - const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr; + if (addr.addr.ss_family == AF_INET) { + const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr.addr; const Family *const family = make_tox_family(addr_in->sin_family); assert(family != nullptr); @@ -676,8 +927,8 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8 ip_port->ip.family = *family; get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr); ip_port->port = addr_in->sin_port; - } else if (addr.ss_family == AF_INET6) { - const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr; + } else if (addr.addr.ss_family == AF_INET6) { + const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr.addr; const Family *const family = make_tox_family(addr_in6->sin6_family); assert(family != nullptr); @@ -690,7 +941,7 @@ static int receivepacket(const Logger *log, Socket sock, IP_Port *ip_port, uint8 ip_port->port = addr_in6->sin6_port; if (ipv6_ipv4_in_v6(&ip_port->ip.ip.v6)) { - ip_port->ip.family = net_family_ipv4; + ip_port->ip.family = net_family_ipv4(); ip_port->ip.ip.v4.uint32 = ip_port->ip.ip.v6.uint32[3]; } } else { @@ -719,89 +970,37 @@ void networking_poll(const Networking_Core *net, void *userdata) uint8_t data[MAX_UDP_PACKET_SIZE]; uint32_t length; - while (receivepacket(net->log, net->sock, &ip_port, data, &length) != -1) { + while (receivepacket(net->ns, net->log, net->sock, &ip_port, data, &length) != -1) { if (length < 1) { continue; } - packet_handler_cb *const cb = net->packethandlers[data[0]].function; - void *const object = net->packethandlers[data[0]].object; + const Packet_Handler *const handler = &net->packethandlers[data[0]]; - if (cb == nullptr) { - LOGGER_WARNING(net->log, "[%02u] -- Packet has no handler", data[0]); + if (handler->function == nullptr) { + // TODO(https://github.com/TokTok/c-toxcore/issues/1115): Make this + // a warning or error again. + LOGGER_DEBUG(net->log, "[%02u] -- Packet has no handler", data[0]); continue; } - cb(object, &ip_port, data, length, userdata); - } -} - -//!TOKSTYLE- -// Global mutable state is not allowed in Tokstyle. -static uint8_t at_startup_ran = 0; -//!TOKSTYLE+ -int networking_at_startup(void) -{ - if (at_startup_ran != 0) { - return 0; - } - -#ifndef VANILLA_NACL - -#ifdef USE_RANDOMBYTES_STIR - randombytes_stir(); -#else - - if (sodium_init() == -1) { - return -1; - } - -#endif /*USE_RANDOMBYTES_STIR*/ - -#endif/*VANILLA_NACL*/ - -#ifdef OS_WIN32 - WSADATA wsaData; - - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) { - return -1; + handler->function(handler->object, &ip_port, data, length, userdata); } - -#endif - at_startup_ran = 1; - return 0; -} - -/* TODO(irungentoo): Put this somewhere */ -#if 0 -static void at_shutdown(void) -{ -#ifdef OS_WIN32 - WSACleanup(); -#endif -} -#endif - -/** Initialize networking. - * Added for reverse compatibility with old new_networking calls. - */ -Networking_Core *new_networking(const Logger *log, const IP *ip, uint16_t port) -{ - return new_networking_ex(log, ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), nullptr); } -/** Initialize networking. +/** @brief Initialize networking. * Bind to ip and port. * ip must be in network order EX: 127.0.0.1 = (7F000001). * port is in host byte order (this means don't worry about it). * - * return Networking_Core object if no problems - * return NULL if there are problems. + * @return Networking_Core object if no problems + * @retval NULL if there are problems. * * If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other. */ -Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t port_from, uint16_t port_to, - unsigned int *error) +Networking_Core *new_networking_ex( + const Logger *log, const Network *ns, const IP *ip, + uint16_t port_from, uint16_t port_to, unsigned int *error) { /* If both from and to are 0, use default port range * If one is 0 and the other is non-0, use the non-0 value as only port @@ -815,12 +1014,12 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por } else if (port_from != 0 && port_to == 0) { port_to = port_from; } else if (port_from > port_to) { - uint16_t temp = port_from; + const uint16_t temp_port = port_from; port_from = port_to; - port_to = temp; + port_to = temp_port; } - if (error) { + if (error != nullptr) { *error = 2; } @@ -830,69 +1029,60 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por return nullptr; } - if (networking_at_startup() != 0) { - return nullptr; - } - Networking_Core *temp = (Networking_Core *)calloc(1, sizeof(Networking_Core)); if (temp == nullptr) { return nullptr; } + temp->ns = ns; temp->log = log; temp->family = ip->family; temp->port = 0; /* Initialize our socket. */ /* add log message what we're creating */ - temp->sock = net_socket(temp->family, TOX_SOCK_DGRAM, TOX_PROTO_UDP); + temp->sock = net_socket(ns, temp->family, TOX_SOCK_DGRAM, TOX_PROTO_UDP); /* Check for socket error. */ if (!sock_valid(temp->sock)) { - int neterror = net_error(); + const int neterror = net_error(); char *strerror = net_new_strerror(neterror); - LOGGER_ERROR(log, "Failed to get a socket?! %d, %s", neterror, strerror); + LOGGER_ERROR(log, "failed to get a socket?! %d, %s", neterror, strerror); net_kill_strerror(strerror); free(temp); - if (error) { + if (error != nullptr) { *error = 1; } return nullptr; } -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Functions to increase the size of the send and receive UDP buffers. */ int n = 1024 * 1024 * 2; - if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_RCVBUF, (const char *)&n, sizeof(n)) != 0) { - LOGGER_WARNING(log, "Failed to set socket option %d", SO_RCVBUF); + if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) != 0) { + LOGGER_ERROR(log, "failed to set socket option %d", SO_RCVBUF); } - if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof(n)) != 0) { - LOGGER_WARNING(log, "Failed to set socket option %d", SO_SNDBUF); + if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) != 0) { + LOGGER_ERROR(log, "failed to set socket option %d", SO_SNDBUF); } -#endif - -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Enable broadcast on socket */ int broadcast = 1; - if (setsockopt(temp->sock.socket, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)) != 0) { - LOGGER_WARNING(log, "Failed to set socket option %d", SO_BROADCAST); + if (net_setsockopt(ns, temp->sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) != 0) { + LOGGER_ERROR(log, "failed to set socket option %d", SO_BROADCAST); } -#endif - /* iOS UDP sockets are weird and apparently can SIGPIPE */ - if (!set_socket_nosigpipe(temp->sock)) { + if (!set_socket_nosigpipe(ns, temp->sock)) { kill_networking(temp); - if (error) { + if (error != nullptr) { *error = 1; } @@ -900,10 +1090,10 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por } /* Set socket nonblocking. */ - if (!set_socket_nonblock(temp->sock)) { + if (!set_socket_nonblock(ns, temp->sock)) { kill_networking(temp); - if (error) { + if (error != nullptr) { *error = 1; } @@ -912,24 +1102,23 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por /* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */ uint16_t *portptr = nullptr; - struct sockaddr_storage addr; - size_t addrsize; + Network_Addr addr; - memset(&addr, 0, sizeof(struct sockaddr_storage)); + memset(&addr.addr, 0, sizeof(struct sockaddr_storage)); if (net_family_is_ipv4(temp->family)) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr; - addrsize = sizeof(struct sockaddr_in); + addr.size = sizeof(struct sockaddr_in); addr4->sin_family = AF_INET; addr4->sin_port = 0; fill_addr4(&ip->ip.v4, &addr4->sin_addr); portptr = &addr4->sin_port; } else if (net_family_is_ipv6(temp->family)) { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr; - addrsize = sizeof(struct sockaddr_in6); + addr.size = sizeof(struct sockaddr_in6); addr6->sin6_family = AF_INET6; addr6->sin6_port = 0; fill_addr6(&ip->ip.v6, &addr6->sin6_addr); @@ -943,12 +1132,16 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por return nullptr; } -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (net_family_is_ipv6(ip->family)) { - const int is_dualstack = set_socket_dualstack(temp->sock); - LOGGER_DEBUG(log, "Dual-stack socket: %s", - is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses"); + const bool is_dualstack = set_socket_dualstack(ns, temp->sock); + + if (is_dualstack) { + LOGGER_TRACE(log, "Dual-stack socket: enabled"); + } else { + LOGGER_ERROR(log, "Dual-stack socket failed to enable, won't be able to receive from/send to IPv4 addresses"); + } + +#ifndef ESP_PLATFORM /* multicast local nodes */ struct ipv6_mreq mreq; memset(&mreq, 0, sizeof(mreq)); @@ -957,21 +1150,20 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; mreq.ipv6mr_interface = 0; - const int res = setsockopt(temp->sock.socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)); + const int res = net_setsockopt(ns, temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); int neterror = net_error(); char *strerror = net_new_strerror(neterror); if (res < 0) { - LOGGER_DEBUG(log, "Failed to activate local multicast membership. (%d, %s)", neterror, strerror); + LOGGER_INFO(log, "Failed to activate local multicast membership in FF02::1. (%d, %s)", neterror, strerror); } else { - LOGGER_DEBUG(log, "Local multicast group FF02::1 joined successfully. (%d, %s)", neterror, strerror); + LOGGER_TRACE(log, "Local multicast group joined successfully. (%d, %s)", neterror, strerror); } net_kill_strerror(strerror); - } - #endif + } /* A hanging program or a different user might block the standard port. * As long as it isn't a parameter coming from the commandline, @@ -993,17 +1185,13 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por *portptr = net_htons(port_to_try); for (uint16_t tries = port_from; tries <= port_to; ++tries) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int res = 0; -#else - int res = bind(temp->sock.socket, (struct sockaddr *)&addr, addrsize); -#endif + const int res = net_bind(ns, temp->sock, &addr); - if (!res) { + if (res == 0) { temp->port = *portptr; - char ip_str[IP_NTOA_LEN]; - LOGGER_DEBUG(log, "Bound successfully to %s:%u", ip_ntoa(ip, ip_str, sizeof(ip_str)), + Ip_Ntoa ip_str; + LOGGER_DEBUG(log, "Bound successfully to %s:%u", net_ip_ntoa(ip, &ip_str), net_ntohs(temp->port)); /* errno isn't reset on success, only set on failure, the failed @@ -1013,7 +1201,7 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por errno = 0; } - if (error) { + if (error != nullptr) { *error = 0; } @@ -1029,27 +1217,23 @@ Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t por *portptr = net_htons(port_to_try); } - char ip_str[IP_NTOA_LEN]; + Ip_Ntoa ip_str; int neterror = net_error(); char *strerror = net_new_strerror(neterror); - LOGGER_ERROR(log, "Failed to bind socket: %d, %s IP: %s port_from: %u port_to: %u", neterror, strerror, - ip_ntoa(ip, ip_str, sizeof(ip_str)), port_from, port_to); + LOGGER_ERROR(log, "failed to bind socket: %d, %s IP: %s port_from: %u port_to: %u", neterror, strerror, + net_ip_ntoa(ip, &ip_str), port_from, port_to); net_kill_strerror(strerror); kill_networking(temp); - if (error) { + if (error != nullptr) { *error = 1; } return nullptr; } -Networking_Core *new_networking_no_udp(const Logger *log) +Networking_Core *new_networking_no_udp(const Logger *log, const Network *ns) { - if (networking_at_startup() != 0) { - return nullptr; - } - /* this is the easiest way to completely disable UDP without changing too much code. */ Networking_Core *net = (Networking_Core *)calloc(1, sizeof(Networking_Core)); @@ -1057,6 +1241,7 @@ Networking_Core *new_networking_no_udp(const Logger *log) return nullptr; } + net->ns = ns; net->log = log; return net; @@ -1065,13 +1250,13 @@ Networking_Core *new_networking_no_udp(const Logger *log) /** Function to cleanup networking stuff (doesn't do much right now). */ void kill_networking(Networking_Core *net) { - if (!net) { + if (net == nullptr) { return; } if (!net_family_is_unspec(net->family)) { /* Socket is initialized, so we close it. */ - kill_sock(net->sock); + kill_sock(net->ns, net->sock); } free(net); @@ -1080,7 +1265,7 @@ void kill_networking(Networking_Core *net) bool ip_equal(const IP *a, const IP *b) { - if (!a || !b) { + if (a == nullptr || b == nullptr) { return false; } @@ -1122,11 +1307,11 @@ bool ip_equal(const IP *a, const IP *b) bool ipport_equal(const IP_Port *a, const IP_Port *b) { - if (!a || !b) { + if (a == nullptr || b == nullptr) { return false; } - if (!a->port || (a->port != b->port)) { + if (a->port == 0 || (a->port != b->port)) { return false; } @@ -1136,38 +1321,38 @@ bool ipport_equal(const IP_Port *a, const IP_Port *b) /** nulls out ip */ void ip_reset(IP *ip) { - if (!ip) { + if (ip == nullptr) { return; } - memset(ip, 0, sizeof(IP)); + *ip = empty_ip; } /** nulls out ip_port */ void ipport_reset(IP_Port *ipport) { - if (!ipport) { + if (ipport == nullptr) { return; } - memset(ipport, 0, sizeof(IP_Port)); + *ipport = empty_ip_port; } /** nulls out ip, sets family according to flag */ void ip_init(IP *ip, bool ipv6enabled) { - if (!ip) { + if (ip == nullptr) { return; } - memset(ip, 0, sizeof(IP)); - ip->family = ipv6enabled ? net_family_ipv6 : net_family_ipv4; + *ip = empty_ip; + ip->family = ipv6enabled ? net_family_ipv6() : net_family_ipv4(); } /** checks if ip is valid */ bool ip_isset(const IP *ip) { - if (!ip) { + if (ip == nullptr) { return false; } @@ -1177,96 +1362,82 @@ bool ip_isset(const IP *ip) /** checks if ip is valid */ bool ipport_isset(const IP_Port *ipport) { - if (!ipport) { + if (ipport == nullptr) { return false; } - if (!ipport->port) { + if (ipport->port == 0) { return false; } return ip_isset(&ipport->ip); } -/** copies an ip structure (careful about direction!) */ +/** copies an ip structure (careful about direction) */ void ip_copy(IP *target, const IP *source) { - if (!source || !target) { + if (source == nullptr || target == nullptr) { return; } *target = *source; } -/** copies an ip_port structure (careful about direction!) */ +/** copies an ip_port structure (careful about direction) */ void ipport_copy(IP_Port *target, const IP_Port *source) { - if (!source || !target) { + if (source == nullptr || target == nullptr) { return; } *target = *source; } -/** ip_ntoa - * converts ip into a string - * ip_str must be of length at least IP_NTOA_LEN +/** @brief Converts IP into a string. + * + * Writes error message into the buffer on error. * - * writes error message into the buffer on error + * @param ip_str contains a buffer of the required size. * - * returns ip_str + * @return Pointer to the buffer inside `ip_str` containing the IP string. */ -const char *ip_ntoa(const IP *ip, char *ip_str, size_t length) +const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str) { - if (length < IP_NTOA_LEN) { - snprintf(ip_str, length, "Bad buf length"); - return ip_str; - } - - if (ip) { - if (net_family_is_ipv4(ip->family)) { - /* returns standard quad-dotted notation */ - struct in_addr addr; - fill_addr4(&ip->ip.v4, &addr); + assert(ip_str != nullptr); - ip_str[0] = '\0'; - assert(make_family(ip->family) == AF_INET); - inet_ntop4(&addr, ip_str, length); - } else if (net_family_is_ipv6(ip->family)) { - /* returns hex-groups enclosed into square brackets */ - struct in6_addr addr; - fill_addr6(&ip->ip.v6, &addr); + if (ip == nullptr) { + snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid: NULL)"); + return ip_str->buf; + } - assert(make_family(ip->family) == AF_INET6); - inet_ntop6(&addr, ip_str, length); - } else { - snprintf(ip_str, length, "(IP invalid, family %u)", ip->family.value); - } - } else { - snprintf(ip_str, length, "(IP invalid: NULL)"); + if (!ip_parse_addr(ip, ip_str->buf, sizeof(ip_str->buf))) { + snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid, family %u)", ip->family.value); + return ip_str->buf; } /* brute force protection against lacking termination */ - ip_str[length - 1] = '\0'; - return ip_str; + ip_str->buf[sizeof(ip_str->buf) - 1] = '\0'; + return ip_str->buf; } bool ip_parse_addr(const IP *ip, char *address, size_t length) { - if (!address || !ip) { + if (address == nullptr || ip == nullptr) { return false; } if (net_family_is_ipv4(ip->family)) { - const struct in_addr *addr = (const struct in_addr *)&ip->ip.v4; + struct in_addr addr; assert(make_family(ip->family) == AF_INET); - return inet_ntop4(addr, address, length) != nullptr; + fill_addr4(&ip->ip.v4, &addr); + return inet_ntop4(&addr, address, length) != nullptr; } if (net_family_is_ipv6(ip->family)) { - const struct in6_addr *addr = (const struct in6_addr *)&ip->ip.v6; + struct in6_addr addr; assert(make_family(ip->family) == AF_INET6); - return inet_ntop6(addr, address, length) != nullptr; + fill_addr6(&ip->ip.v6, &addr); + return inet_ntop6(&addr, address, length) != nullptr; } return false; @@ -1274,14 +1445,14 @@ bool ip_parse_addr(const IP *ip, char *address, size_t length) bool addr_parse_ip(const char *address, IP *to) { - if (!address || !to) { + if (address == nullptr || to == nullptr) { return false; } struct in_addr addr4; if (inet_pton4(address, &addr4) == 1) { - to->family = net_family_ipv4; + to->family = net_family_ipv4(); get_ip4(&to->ip.v4, &addr4); return true; } @@ -1289,7 +1460,7 @@ bool addr_parse_ip(const char *address, IP *to) struct in6_addr addr6; if (inet_pton6(address, &addr6) == 1) { - to->family = net_family_ipv6; + to->family = net_family_ipv6(); get_ip6(&to->ip.v6, &addr6); return true; } @@ -1297,28 +1468,48 @@ bool addr_parse_ip(const char *address, IP *to) return false; } -int addr_resolve(const char *address, IP *to, IP *extra) +/** addr_resolve return values */ +#define TOX_ADDR_RESOLVE_INET 1 +#define TOX_ADDR_RESOLVE_INET6 2 + +/** + * Uses getaddrinfo to resolve an address into an IP address. + * + * Uses the first IPv4/IPv6 addresses returned by getaddrinfo. + * + * @param address a hostname (or something parseable to an IP address) + * @param to to.family MUST be initialized, either set to a specific IP version + * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (0), if both + * IP versions are acceptable + * @param extra can be NULL and is only set in special circumstances, see returns + * + * Returns in `*to` a valid IPAny (v4/v6), + * prefers v6 if `ip.family` was TOX_AF_UNSPEC and both available + * Returns in `*extra` an IPv4 address, if family was TOX_AF_UNSPEC and `*to` is TOX_AF_INET6 + * + * @return 0 on failure, `TOX_ADDR_RESOLVE_*` on success. + */ +non_null(1, 2, 3) nullable(4) +static int addr_resolve(const Network *ns, const char *address, IP *to, IP *extra) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return false; -#else + if ((true)) { + return 0; + } +#endif - if (!address || !to) { + if (address == nullptr || to == nullptr) { return 0; } - Family tox_family = to->family; - int family = make_family(tox_family); + const Family tox_family = to->family; + const int family = make_family(tox_family); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. - if (networking_at_startup() != 0) { - return 0; - } - struct addrinfo *server = nullptr; const int rc = getaddrinfo(address, nullptr, &hints, &server); @@ -1329,9 +1520,9 @@ int addr_resolve(const char *address, IP *to, IP *extra) } IP ip4; - ip_init(&ip4, 0); // ipv6enabled = 0 + ip_init(&ip4, false); // ipv6enabled = false IP ip6; - ip_init(&ip6, 1); // ipv6enabled = 1 + ip_init(&ip6, true); // ipv6enabled = true int result = 0; bool done = false; @@ -1340,12 +1531,12 @@ int addr_resolve(const char *address, IP *to, IP *extra) switch (walker->ai_family) { case AF_INET: { if (walker->ai_family == family) { /* AF_INET requested, done */ - const struct sockaddr_in *addr = (const struct sockaddr_in *)(void *)walker->ai_addr; + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)walker->ai_addr; get_ip4(&to->ip.v4, &addr->sin_addr); result = TOX_ADDR_RESOLVE_INET; done = true; - } else if (!(result & TOX_ADDR_RESOLVE_INET)) { /* AF_UNSPEC requested, store away */ - const struct sockaddr_in *addr = (const struct sockaddr_in *)(void *)walker->ai_addr; + } else if ((result & TOX_ADDR_RESOLVE_INET) == 0) { /* AF_UNSPEC requested, store away */ + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)walker->ai_addr; get_ip4(&ip4.ip.v4, &addr->sin_addr); result |= TOX_ADDR_RESOLVE_INET; } @@ -1361,7 +1552,7 @@ int addr_resolve(const char *address, IP *to, IP *extra) result = TOX_ADDR_RESOLVE_INET6; done = true; } - } else if (!(result & TOX_ADDR_RESOLVE_INET6)) { /* AF_UNSPEC requested, store away */ + } else if ((result & TOX_ADDR_RESOLVE_INET6) == 0) { /* AF_UNSPEC requested, store away */ if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)walker->ai_addr; get_ip6(&ip6.ip.v6, &addr->sin6_addr); @@ -1375,13 +1566,13 @@ int addr_resolve(const char *address, IP *to, IP *extra) } if (family == AF_UNSPEC) { - if (result & TOX_ADDR_RESOLVE_INET6) { + if ((result & TOX_ADDR_RESOLVE_INET6) != 0) { ip_copy(to, &ip6); - if ((result & TOX_ADDR_RESOLVE_INET) && (extra != nullptr)) { + if ((result & TOX_ADDR_RESOLVE_INET) != 0 && (extra != nullptr)) { ip_copy(extra, &ip4); } - } else if (result & TOX_ADDR_RESOLVE_INET) { + } else if ((result & TOX_ADDR_RESOLVE_INET) != 0) { ip_copy(to, &ip4); } else { result = 0; @@ -1390,12 +1581,11 @@ int addr_resolve(const char *address, IP *to, IP *extra) freeaddrinfo(server); return result; -#endif } -bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra) +bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra) { - if (!addr_resolve(address, to, extra)) { + if (addr_resolve(ns, address, to, extra) == 0) { if (!addr_parse_ip(address, to)) { return false; } @@ -1404,7 +1594,7 @@ bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra) return true; } -int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port) +bool net_connect(const Logger *log, Socket sock, const IP_Port *ip_port) { struct sockaddr_storage addr = {0}; size_t addrsize; @@ -1424,30 +1614,55 @@ int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port) fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr); addr6->sin6_port = ip_port->port; } else { - return 0; + Ip_Ntoa ip_str; + LOGGER_ERROR(log, "cannot connect to %s:%d which is neither IPv4 nor IPv6", + net_ip_ntoa(&ip_port->ip, &ip_str), ip_port->port); + return false; } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return 0; -#else - LOGGER_DEBUG(log, "connecting socket %d", (int)sock.socket); - return connect(sock.socket, (struct sockaddr *)&addr, addrsize); + if ((true)) { + return true; + } #endif + + Ip_Ntoa ip_str; + LOGGER_DEBUG(log, "connecting socket %d to %s:%d", + (int)sock.sock, net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port)); + errno = 0; + + if (connect(sock.sock, (struct sockaddr *)&addr, addrsize) == -1) { + const int error = net_error(); + + // Non-blocking socket: "Operation in progress" means it's connecting. + if (!should_ignore_connect_error(error)) { + char *net_strerror = net_new_strerror(error); + LOGGER_ERROR(log, "failed to connect to %s:%d: %d (%s)", + net_ip_ntoa(&ip_port->ip, &ip_str), ip_port->port, error, net_strerror); + net_kill_strerror(net_strerror); + return false; + } + } + + return true; } int32_t net_getipport(const char *node, IP_Port **res, int tox_type) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - *res = (IP_Port *)calloc(1, sizeof(IP_Port)); - assert(*res != nullptr); - IP_Port *ip_port = *res; - ip_port->ip.ip.v4.uint32 = 0x7F000003; // 127.0.0.3 - ip_port->ip.family = *make_tox_family(AF_INET); + if ((true)) { + *res = (IP_Port *)calloc(1, sizeof(IP_Port)); + assert(*res != nullptr); + IP_Port *ip_port = *res; + ip_port->ip.ip.v4.uint32 = 0x7F000003; // 127.0.0.3 + ip_port->ip.family = *make_tox_family(AF_INET); + + return 1; + } +#endif - return 1; -#else // Try parsing as IP address first. - IP_Port parsed = {0}; + IP_Port parsed = {{{0}}}; if (addr_parse_ip(node, &parsed.ip)) { IP_Port *tmp = (IP_Port *)calloc(1, sizeof(IP_Port)); @@ -1534,7 +1749,6 @@ int32_t net_getipport(const char *node, IP_Port **res, int tox_type) freeaddrinfo(infos); return count; -#endif } void net_freeipport(IP_Port *ip_ports) @@ -1542,106 +1756,42 @@ void net_freeipport(IP_Port *ip_ports) free(ip_ports); } -bool bind_to_port(Socket sock, Family family, uint16_t port) +bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port) { - struct sockaddr_storage addr = {0}; - size_t addrsize; + Network_Addr addr = {{0}}; if (net_family_is_ipv4(family)) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr; - addrsize = sizeof(struct sockaddr_in); + addr.size = sizeof(struct sockaddr_in); addr4->sin_family = AF_INET; addr4->sin_port = net_htons(port); } else if (net_family_is_ipv6(family)) { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr; - addrsize = sizeof(struct sockaddr_in6); + addr.size = sizeof(struct sockaddr_in6); addr6->sin6_family = AF_INET6; addr6->sin6_port = net_htons(port); } else { return false; } -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return true; -#else - return bind(sock.socket, (struct sockaddr *)&addr, addrsize) == 0; -#endif + return net_bind(ns, sock, &addr) == 0; } -Socket net_socket(Family domain, int type, int protocol) +Socket net_socket(const Network *ns, Family domain, int type, int protocol) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - const Socket sock = {1}; - return sock; -#else const int platform_domain = make_family(domain); const int platform_type = make_socktype(type); const int platform_prot = make_proto(protocol); - const Socket sock = {(int)socket(platform_domain, platform_type, platform_prot)}; + const Socket sock = {ns->funcs->socket(ns->obj, platform_domain, platform_type, platform_prot)}; return sock; -#endif -} - -int net_send(const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int res = fuzz_send(sock.socket, (const char *)buf, len, MSG_NOSIGNAL); -#else - int res = send(sock.socket, (const char *)buf, len, MSG_NOSIGNAL); -#endif - loglogdata(log, "T=>", buf, len, ip_port, res); - return res; -} - -int net_recv(const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - int res = fuzz_recv(sock.socket, (char *)buf, len, MSG_NOSIGNAL); -#else - int res = recv(sock.socket, (char *)buf, len, MSG_NOSIGNAL); -#endif - loglogdata(log, "=>T", buf, len, ip_port, res); - return res; -} - -int net_listen(Socket sock, int backlog) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return 0; -#else - return listen(sock.socket, backlog); -#endif -} - -Socket net_accept(Socket sock) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - const Socket newsock = {2}; - return newsock; -#else - const Socket newsock = {accept(sock.socket, nullptr, nullptr)}; - return newsock; -#endif } -uint16_t net_socket_data_recv_buffer(Socket sock) +uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return 0; -#else - -#ifdef OS_WIN32 - u_long count = 0; - ioctlsocket(sock.socket, FIONREAD, &count); -#else - int count = 0; - ioctl(sock.socket, FIONREAD, &count); -#endif - - return (uint16_t)count; -#endif + const int count = ns->funcs->recvbuf(ns->obj, sock.sock); + return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX)); } uint32_t net_htonl(uint32_t hostlong) @@ -1689,8 +1839,8 @@ size_t net_pack_u64(uint8_t *bytes, uint64_t v) size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v) { - uint8_t hi = bytes[0]; - uint8_t lo = bytes[1]; + const uint8_t hi = bytes[0]; + const uint8_t lo = bytes[1]; *v = ((uint16_t)hi << 8) | lo; return sizeof(*v); } @@ -1731,9 +1881,9 @@ int net_error(void) #endif } +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) char *net_new_strerror(int error) { -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) char *str = nullptr; // Windows API is weird. The 5th function arg is of char* type, but we // have to pass char** so that it could assign new memory block to our @@ -1745,29 +1895,42 @@ char *net_new_strerror(int error) FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error, 0, (char *)&str, 0, nullptr); return str; +} #else - char tmp[256]; - - errno = 0; - #ifdef _GNU_SOURCE - const char *retstr = strerror_r(error, tmp, sizeof(tmp)); +non_null() +static const char *net_strerror_r(int error, char *tmp, size_t tmp_size) +{ + const char *retstr = strerror_r(error, tmp, tmp_size); if (errno != 0) { - snprintf(tmp, sizeof(tmp), "error %d (strerror_r failed with errno %d)", error, errno); + snprintf(tmp, tmp_size, "error %d (strerror_r failed with errno %d)", error, errno); } + return retstr; +} #else - const int fmt_error = strerror_r(error, tmp, sizeof(tmp)); +non_null() +static const char *net_strerror_r(int error, char *tmp, size_t tmp_size) +{ + const int fmt_error = strerror_r(error, tmp, tmp_size); if (fmt_error != 0) { - snprintf(tmp, sizeof(tmp), "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno); + snprintf(tmp, tmp_size, "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno); } - const char *retstr = tmp; + return tmp; +} #endif +char *net_new_strerror(int error) +{ + char tmp[256]; + + errno = 0; + const char *retstr = net_strerror_r(error, tmp, sizeof(tmp)); const size_t retstr_len = strlen(retstr); + char *str = (char *)malloc(retstr_len + 1); if (str == nullptr) { @@ -1777,8 +1940,8 @@ char *net_new_strerror(int error) memcpy(str, retstr, retstr_len + 1); return str; -#endif } +#endif void net_kill_strerror(char *strerror) { diff --git a/protocols/Tox/libtox/src/toxcore/network.h b/protocols/Tox/libtox/src/toxcore/network.h index 47a93bf0fa..a1fc6a74fc 100644 --- a/protocols/Tox/libtox/src/toxcore/network.h +++ b/protocols/Tox/libtox/src/toxcore/network.h @@ -19,6 +19,57 @@ extern "C" { #endif +/** + * @brief Wrapper for sockaddr_storage and size. + */ +typedef struct Network_Addr Network_Addr; + +typedef int net_close_cb(void *obj, int sock); +typedef int net_accept_cb(void *obj, int sock); +typedef int net_bind_cb(void *obj, int sock, const Network_Addr *addr); +typedef int net_listen_cb(void *obj, int sock, int backlog); +typedef int net_recvbuf_cb(void *obj, int sock); +typedef int net_recv_cb(void *obj, int sock, uint8_t *buf, size_t len); +typedef int net_recvfrom_cb(void *obj, int sock, uint8_t *buf, size_t len, Network_Addr *addr); +typedef int net_send_cb(void *obj, int sock, const uint8_t *buf, size_t len); +typedef int net_sendto_cb(void *obj, int sock, const uint8_t *buf, size_t len, const Network_Addr *addr); +typedef int net_socket_cb(void *obj, int domain, int type, int proto); +typedef int net_socket_nonblock_cb(void *obj, int sock, bool nonblock); +typedef int net_getsockopt_cb(void *obj, int sock, int level, int optname, void *optval, size_t *optlen); +typedef int net_setsockopt_cb(void *obj, int sock, int level, int optname, const void *optval, size_t optlen); +typedef int net_getaddrinfo_cb(void *obj, int family, Network_Addr **addrs); +typedef int net_freeaddrinfo_cb(void *obj, Network_Addr *addrs); + +/** @brief Functions wrapping POSIX network functions. + * + * Refer to POSIX man pages for documentation of what these functions are + * expected to do when providing alternative Network implementations. + */ +typedef struct Network_Funcs { + net_close_cb *close; + net_accept_cb *accept; + net_bind_cb *bind; + net_listen_cb *listen; + net_recvbuf_cb *recvbuf; + net_recv_cb *recv; + net_recvfrom_cb *recvfrom; + net_send_cb *send; + net_sendto_cb *sendto; + net_socket_cb *socket; + net_socket_nonblock_cb *socket_nonblock; + net_getsockopt_cb *getsockopt; + net_setsockopt_cb *setsockopt; + net_getaddrinfo_cb *getaddrinfo; + net_freeaddrinfo_cb *freeaddrinfo; +} Network_Funcs; + +typedef struct Network { + const Network_Funcs *funcs; + void *obj; +} Network; + +const Network *system_network(void); + typedef struct Family { uint8_t value; } Family; @@ -26,25 +77,65 @@ typedef struct Family { bool net_family_is_unspec(Family family); bool net_family_is_ipv4(Family family); bool net_family_is_ipv6(Family family); -bool net_family_is_tcp_family(Family family); -bool net_family_is_tcp_onion(Family family); +bool net_family_is_tcp_server(Family family); +bool net_family_is_tcp_client(Family family); bool net_family_is_tcp_ipv4(Family family); bool net_family_is_tcp_ipv6(Family family); bool net_family_is_tox_tcp_ipv4(Family family); bool net_family_is_tox_tcp_ipv6(Family family); -extern const Family net_family_unspec; -extern const Family net_family_ipv4; -extern const Family net_family_ipv6; -extern const Family net_family_tcp_family; -extern const Family net_family_tcp_onion; -extern const Family net_family_tcp_ipv4; -extern const Family net_family_tcp_ipv6; -extern const Family net_family_tox_tcp_ipv4; -extern const Family net_family_tox_tcp_ipv6; +Family net_family_unspec(void); +Family net_family_ipv4(void); +Family net_family_ipv6(void); +Family net_family_tcp_server(void); +Family net_family_tcp_client(void); +Family net_family_tcp_ipv4(void); +Family net_family_tcp_ipv6(void); +Family net_family_tox_tcp_ipv4(void); +Family net_family_tox_tcp_ipv6(void); #define MAX_UDP_PACKET_SIZE 2048 +#ifdef USE_TEST_NETWORK +typedef enum Net_Packet_Type { + NET_PACKET_PING_REQUEST = 0x05, /* Ping request packet ID. */ + NET_PACKET_PING_RESPONSE = 0x06, /* Ping response packet ID. */ + NET_PACKET_GET_NODES = 0x07, /* Get nodes request packet ID. */ + NET_PACKET_SEND_NODES_IPV6 = 0x08, /* Send nodes response packet ID for other addresses. */ + NET_PACKET_COOKIE_REQUEST = 0x1c, /* Cookie request packet */ + NET_PACKET_COOKIE_RESPONSE = 0x1d, /* Cookie response packet */ + NET_PACKET_CRYPTO_HS = 0x1e, /* Crypto handshake packet */ + NET_PACKET_CRYPTO_DATA = 0x1f, /* Crypto data packet */ + NET_PACKET_CRYPTO = 0x24, /* Encrypted data packet ID. */ + NET_PACKET_LAN_DISCOVERY = 0x25, /* LAN discovery packet ID. */ + + // TODO(Jfreegman): Uncomment these when we merge the rest of new groupchats + // NET_PACKET_GC_HANDSHAKE = 0x62, /* Group chat handshake packet ID */ + // NET_PACKET_GC_LOSSLESS = 0x63, /* Group chat lossless packet ID */ + // NET_PACKET_GC_LOSSY = 0x64, /* Group chat lossy packet ID */ + + /* See: `docs/Prevent_Tracking.txt` and `onion.{c,h}` */ + NET_PACKET_ONION_SEND_INITIAL = 0x8f, + NET_PACKET_ONION_SEND_1 = 0x90, + NET_PACKET_ONION_SEND_2 = 0x91, + + NET_PACKET_ANNOUNCE_REQUEST = 0x92, + NET_PACKET_ANNOUNCE_RESPONSE = 0x93, + NET_PACKET_ONION_DATA_REQUEST = 0x94, + NET_PACKET_ONION_DATA_RESPONSE = 0x95, + + NET_PACKET_ANNOUNCE_REQUEST_OLD = 0x96, /* TODO: DEPRECATE */ + NET_PACKET_ANNOUNCE_RESPONSE_OLD = 0x97, /* TODO: DEPRECATE */ + + NET_PACKET_ONION_RECV_3 = 0x9b, + NET_PACKET_ONION_RECV_2 = 0x9c, + NET_PACKET_ONION_RECV_1 = 0x9d, + + BOOTSTRAP_INFO_PACKET_ID = 0xf1, /* Only used for bootstrap nodes */ + + NET_PACKET_MAX = 0xff, /* This type must remain within a single uint8. */ +} Net_Packet_Type; +#else typedef enum Net_Packet_Type { NET_PACKET_PING_REQUEST = 0x00, /* Ping request packet ID. */ NET_PACKET_PING_RESPONSE = 0x01, /* Ping response packet ID. */ @@ -57,24 +148,44 @@ typedef enum Net_Packet_Type { NET_PACKET_CRYPTO = 0x20, /* Encrypted data packet ID. */ NET_PACKET_LAN_DISCOVERY = 0x21, /* LAN discovery packet ID. */ + // TODO(Jfreegman): Uncomment these when we merge the rest of new groupchats + // NET_PACKET_GC_HANDSHAKE = 0x5a, /* Group chat handshake packet ID */ + // NET_PACKET_GC_LOSSLESS = 0x5b, /* Group chat lossless packet ID */ + // NET_PACKET_GC_LOSSY = 0x5c, /* Group chat lossy packet ID */ + /* See: `docs/Prevent_Tracking.txt` and `onion.{c,h}` */ NET_PACKET_ONION_SEND_INITIAL = 0x80, NET_PACKET_ONION_SEND_1 = 0x81, NET_PACKET_ONION_SEND_2 = 0x82, - NET_PACKET_ANNOUNCE_REQUEST = 0x83, - NET_PACKET_ANNOUNCE_RESPONSE = 0x84, + NET_PACKET_ANNOUNCE_REQUEST_OLD = 0x83, /* TODO: DEPRECATE */ + NET_PACKET_ANNOUNCE_RESPONSE_OLD = 0x84, /* TODO: DEPRECATE */ + NET_PACKET_ONION_DATA_REQUEST = 0x85, NET_PACKET_ONION_DATA_RESPONSE = 0x86, + NET_PACKET_ANNOUNCE_REQUEST = 0x87, + NET_PACKET_ANNOUNCE_RESPONSE = 0x88, NET_PACKET_ONION_RECV_3 = 0x8c, NET_PACKET_ONION_RECV_2 = 0x8d, NET_PACKET_ONION_RECV_1 = 0x8e, + NET_PACKET_FORWARD_REQUEST = 0x90, + NET_PACKET_FORWARDING = 0x91, + NET_PACKET_FORWARD_REPLY = 0x92, + + NET_PACKET_DATA_SEARCH_REQUEST = 0x93, + NET_PACKET_DATA_SEARCH_RESPONSE = 0x94, + NET_PACKET_DATA_RETRIEVE_REQUEST = 0x95, + NET_PACKET_DATA_RETRIEVE_RESPONSE = 0x96, + NET_PACKET_STORE_ANNOUNCE_REQUEST = 0x97, + NET_PACKET_STORE_ANNOUNCE_RESPONSE = 0x98, + BOOTSTRAP_INFO_PACKET_ID = 0xf0, /* Only used for bootstrap nodes */ NET_PACKET_MAX = 0xff, /* This type must remain within a single uint8. */ } Net_Packet_Type; +#endif // test network #define TOX_PORTRANGE_FROM 33445 @@ -95,10 +206,10 @@ typedef enum Net_Packet_Type { #define TOX_PROTO_UDP 2 /** TCP related */ -#define TCP_ONION_FAMILY (TOX_AF_INET6 + 1) +#define TCP_CLIENT_FAMILY (TOX_AF_INET6 + 1) #define TCP_INET (TOX_AF_INET6 + 2) #define TCP_INET6 (TOX_AF_INET6 + 3) -#define TCP_FAMILY (TOX_AF_INET6 + 4) +#define TCP_SERVER_FAMILY (TOX_AF_INET6 + 4) #define SIZE_IP4 4 #define SIZE_IP6 16 @@ -112,8 +223,6 @@ typedef union IP4 { uint8_t uint8[4]; } IP4; -static_assert(sizeof(IP4) == SIZE_IP4, "IP4 size must be 4"); - IP4 get_ip4_loopback(void); extern const IP4 ip4_broadcast; @@ -124,10 +233,6 @@ typedef union IP6 { uint64_t uint64[2]; } IP6; -// TODO(iphydf): Stop relying on this. We memcpy this struct (and IP4 above) -// into packets but really should be serialising it properly. -static_assert(sizeof(IP6) == SIZE_IP6, "IP6 size must be 16"); - IP6 get_ip6_loopback(void); extern const IP6 ip6_broadcast; @@ -146,11 +251,14 @@ typedef struct IP_Port { uint16_t port; } IP_Port; +extern const IP_Port empty_ip_port; + typedef struct Socket { - int socket; + int sock; } Socket; -Socket net_socket(Family domain, int type, int protocol); +non_null() +Socket net_socket(const Network *ns, Family domain, int type, int protocol); /** * Check if socket is valid. @@ -164,64 +272,77 @@ extern const Socket net_invalid_socket; /** * Calls send(sockfd, buf, len, MSG_NOSIGNAL). */ -int net_send(const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port); +non_null() +int net_send(const Network *ns, const Logger *log, Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port); /** * Calls recv(sockfd, buf, len, MSG_NOSIGNAL). */ -int net_recv(const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port); +non_null() +int net_recv(const Network *ns, const Logger *log, Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port); /** * Calls listen(sockfd, backlog). */ -int net_listen(Socket sock, int backlog); +non_null() +int net_listen(const Network *ns, Socket sock, int backlog); /** * Calls accept(sockfd, nullptr, nullptr). */ -Socket net_accept(Socket sock); +non_null() +Socket net_accept(const Network *ns, Socket sock); /** * return the size of data in the tcp recv buffer. * return 0 on failure. */ -uint16_t net_socket_data_recv_buffer(Socket sock); +non_null() +uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock); -/** Convert values between host and network byte order. - */ +/** Convert values between host and network byte order. */ uint32_t net_htonl(uint32_t hostlong); uint16_t net_htons(uint16_t hostshort); uint32_t net_ntohl(uint32_t hostlong); uint16_t net_ntohs(uint16_t hostshort); +non_null() size_t net_pack_u16(uint8_t *bytes, uint16_t v); +non_null() size_t net_pack_u32(uint8_t *bytes, uint32_t v); +non_null() size_t net_pack_u64(uint8_t *bytes, uint64_t v); +non_null() size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v); +non_null() size_t net_unpack_u32(const uint8_t *bytes, uint32_t *v); +non_null() size_t net_unpack_u64(const uint8_t *bytes, uint64_t *v); /** Does the IP6 struct a contain an IPv4 address in an IPv6 one? */ +non_null() bool ipv6_ipv4_in_v6(const IP6 *a); #define TOX_ENABLE_IPV6_DEFAULT true -/** addr_resolve return values */ -#define TOX_ADDR_RESOLVE_INET 1 -#define TOX_ADDR_RESOLVE_INET6 2 - #define TOX_INET6_ADDRSTRLEN 66 #define TOX_INET_ADDRSTRLEN 22 /** this would be TOX_INET6_ADDRSTRLEN, but it might be too short for the error message */ #define IP_NTOA_LEN 96 // TODO(irungentoo): magic number. Why not INET6_ADDRSTRLEN ? -/** ip_ntoa - * converts ip into a string - * ip_str must be of length at least IP_NTOA_LEN + +typedef struct Ip_Ntoa { + char buf[IP_NTOA_LEN]; +} Ip_Ntoa; + +/** @brief Converts IP into a string. + * + * Writes error message into the buffer on error. * - * writes error message into the buffer on error + * @param ip_str contains a buffer of the required size. * - * returns ip_str + * @return Pointer to the buffer inside `ip_str` containing the IP string. */ -const char *ip_ntoa(const IP *ip, char *ip_str, size_t length); +non_null() +const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str); /** * Parses IP structure into an address string. @@ -235,6 +356,7 @@ const char *ip_ntoa(const IP *ip, char *ip_str, size_t length); * * @return true on success, false on failure. */ +non_null() bool ip_parse_addr(const IP *ip, char *address, size_t length); /** @@ -247,6 +369,7 @@ bool ip_parse_addr(const IP *ip, char *address, size_t length); * * @return true on success, false on failure. */ +non_null() bool addr_parse_ip(const char *address, IP *to); /** @@ -256,6 +379,7 @@ bool addr_parse_ip(const char *address, IP *to); * * @return false when not equal or when uninitialized. */ +nullable(1, 2) bool ip_equal(const IP *a, const IP *b); /** @@ -265,59 +389,49 @@ bool ip_equal(const IP *a, const IP *b); * * @return false when not equal or when uninitialized. */ +nullable(1, 2) bool ipport_equal(const IP_Port *a, const IP_Port *b); /** nulls out ip */ +non_null() void ip_reset(IP *ip); /** nulls out ip_port */ +non_null() void ipport_reset(IP_Port *ipport); /** nulls out ip, sets family according to flag */ +non_null() void ip_init(IP *ip, bool ipv6enabled); /** checks if ip is valid */ +non_null() bool ip_isset(const IP *ip); /** checks if ip is valid */ +non_null() bool ipport_isset(const IP_Port *ipport); -/** copies an ip structure (careful about direction!) */ +/** copies an ip structure (careful about direction) */ +non_null() void ip_copy(IP *target, const IP *source); -/** copies an ip_port structure (careful about direction!) */ +/** copies an ip_port structure (careful about direction) */ +non_null() void ipport_copy(IP_Port *target, const IP_Port *source); /** - * Uses getaddrinfo to resolve an address into an IP address. - * - * Uses the first IPv4/IPv6 addresses returned by getaddrinfo. - * - * @param address a hostname (or something parseable to an IP address) - * @param to to.family MUST be initialized, either set to a specific IP version - * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (= 0), if both - * IP versions are acceptable - * @param extra can be NULL and is only set in special circumstances, see returns - * - * returns in `*to` a valid IPAny (v4/v6), - * prefers v6 if `ip.family` was TOX_AF_UNSPEC and both available - * returns in `*extra` an IPv4 address, if family was TOX_AF_UNSPEC and `*to` is TOX_AF_INET6 - * - * @return 0 on failure, `TOX_ADDR_RESOLVE_*` on success. - */ -int addr_resolve(const char *address, IP *to, IP *extra); - -/** * Resolves string into an IP address * * @param address a hostname (or something parseable to an IP address) * @param to to.family MUST be initialized, either set to a specific IP version - * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (= 0), if both - * IP versions are acceptable + * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (0), if both + * IP versions are acceptable * @param extra can be NULL and is only set in special circumstances, see returns * - * returns in `*to` a matching address (IPv6 or IPv4) - * returns in `*extra`, if not NULL, an IPv4 address, if `to->family` was TOX_AF_UNSPEC + * Returns in `*to` a matching address (IPv6 or IPv4) + * Returns in `*extra`, if not NULL, an IPv4 address, if `to->family` was TOX_AF_UNSPEC * * @return true on success, false on failure */ -bool addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra); +non_null(1, 2, 3) nullable(4) +bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra); -/** Function to receive data, ip and port of sender is put into ip_port. +/** @brief Function to receive data, ip and port of sender is put into ip_port. * Packet data is put into data. * Packet length is put into length. */ @@ -325,47 +439,46 @@ typedef int packet_handler_cb(void *object, const IP_Port *ip_port, const uint8_ typedef struct Networking_Core Networking_Core; +non_null() Family net_family(const Networking_Core *net); +non_null() uint16_t net_port(const Networking_Core *net); -/** Run this before creating sockets. - * - * return 0 on success - * return -1 on failure - */ -int networking_at_startup(void); - -/** Close the socket. - */ -void kill_sock(Socket sock); +/** Close the socket. */ +non_null() +void kill_sock(const Network *ns, Socket sock); /** * Set socket as nonblocking * * @return true on success, false on failure. */ -bool set_socket_nonblock(Socket sock); +non_null() +bool set_socket_nonblock(const Network *ns, Socket sock); /** * Set socket to not emit SIGPIPE * * @return true on success, false on failure. */ -bool set_socket_nosigpipe(Socket sock); +non_null() +bool set_socket_nosigpipe(const Network *ns, Socket sock); /** * Enable SO_REUSEADDR on socket. * * @return true on success, false on failure. */ -bool set_socket_reuseaddr(Socket sock); +non_null() +bool set_socket_reuseaddr(const Network *ns, Socket sock); /** * Set socket to dual (IPv4 + IPv6 socket) * * @return true on success, false on failure. */ -bool set_socket_dualstack(Socket sock); +non_null() +bool set_socket_dualstack(const Network *ns, Socket sock); /* Basic network functions: */ @@ -382,6 +495,7 @@ typedef struct Packet { /** * Function to send a network packet to a given IP/port. */ +non_null() int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packet); /** @@ -389,44 +503,51 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe * * @deprecated Use send_packet instead. */ +non_null() int sendpacket(const Networking_Core *net, const IP_Port *ip_port, const uint8_t *data, uint16_t length); /** Function to call when packet beginning with byte is received. */ +non_null(1) nullable(3, 4) void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_cb *cb, void *object); /** Call this several times a second. */ +non_null(1) nullable(2) void networking_poll(const Networking_Core *net, void *userdata); -/** Connect a socket to the address specified by the ip_port. +/** @brief Connect a socket to the address specified by the ip_port. * - * Return 0 on success. - * Return -1 on failure. + * Return true on success. + * Return false on failure. */ -int net_connect(const Logger *log, Socket sock, const IP_Port *ip_port); +non_null() +bool net_connect(const Logger *log, Socket sock, const IP_Port *ip_port); -/** High-level getaddrinfo implementation. - * Given node, which identifies an Internet host, net_getipport() fills an array +/** @brief High-level getaddrinfo implementation. + * + * Given node, which identifies an Internet host, `net_getipport()` fills an array * with one or more IP_Port structures, each of which contains an Internet - * address that can be specified by calling net_connect(), the port is ignored. + * address that can be specified by calling `net_connect()`, the port is ignored. * * Skip all addresses with socktype != type (use type = -1 to get all addresses) - * To correctly deallocate array memory use net_freeipport() + * To correctly deallocate array memory use `net_freeipport()` * * return number of elements in res array * and -1 on error. */ +non_null() int32_t net_getipport(const char *node, IP_Port **res, int tox_type); -/** Deallocates memory allocated by net_getipport - */ +/** Deallocates memory allocated by net_getipport */ +nullable(1) void net_freeipport(IP_Port *ip_ports); /** * @return true on success, false on failure. */ -bool bind_to_port(Socket sock, Family family, uint16_t port); +non_null() +bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port); -/** Get the last networking error code. +/** @brief Get the last networking error code. * * Similar to Unix's errno, but cross-platform, as not all platforms use errno * to indicate networking errors. @@ -434,45 +555,47 @@ bool bind_to_port(Socket sock, Family family, uint16_t port); * Note that different platforms may return different codes for the same error, * so you likely shouldn't be checking the value returned by this function * unless you know what you are doing, you likely just want to use it in - * combination with net_new_strerror() to print the error. + * combination with `net_new_strerror()` to print the error. * * return platform-dependent network error code, if any. */ int net_error(void); -/** Get a text explanation for the error code from net_error(). +/** @brief Get a text explanation for the error code from `net_error()`. * * return NULL on failure. * return pointer to a NULL-terminated string describing the error code on - * success. The returned string must be freed using net_kill_strerror(). + * success. The returned string must be freed using `net_kill_strerror()`. */ char *net_new_strerror(int error); -/** Frees the string returned by net_new_strerror(). +/** @brief Frees the string returned by `net_new_strerror()`. * It's valid to pass NULL as the argument, the function does nothing in this * case. */ +non_null() void net_kill_strerror(char *strerror); -/** Initialize networking. - * Added for reverse compatibility with old new_networking calls. - */ -Networking_Core *new_networking(const Logger *log, const IP *ip, uint16_t port); -/** Initialize networking. +/** @brief Initialize networking. * Bind to ip and port. * ip must be in network order EX: 127.0.0.1 = (7F000001). * port is in host byte order (this means don't worry about it). * - * return Networking_Core object if no problems - * return NULL if there are problems. + * @return Networking_Core object if no problems + * @retval NULL if there are problems. * * If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other. */ -Networking_Core *new_networking_ex(const Logger *log, const IP *ip, uint16_t port_from, uint16_t port_to, - unsigned int *error); -Networking_Core *new_networking_no_udp(const Logger *log); +non_null(1, 2, 3) nullable(6) +Networking_Core *new_networking_ex( + const Logger *log, const Network *ns, const IP *ip, + uint16_t port_from, uint16_t port_to, unsigned int *error); + +non_null() +Networking_Core *new_networking_no_udp(const Logger *log, const Network *ns); /** Function to cleanup networking stuff (doesn't do much right now). */ +nullable(1) void kill_networking(Networking_Core *net); #ifdef __cplusplus diff --git a/protocols/Tox/libtox/src/toxcore/onion.c b/protocols/Tox/libtox/src/toxcore/onion.c index 663c7b1b5c..d7e3f02665 100644 --- a/protocols/Tox/libtox/src/toxcore/onion.c +++ b/protocols/Tox/libtox/src/toxcore/onion.c @@ -8,9 +8,11 @@ */ #include "onion.h" +#include <assert.h> #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "mono_time.h" #include "util.h" @@ -24,16 +26,19 @@ #define SEND_1 ONION_SEND_1 #define KEY_REFRESH_INTERVAL (2 * 60 * 60) + /** Change symmetric keys every 2 hours to make paths expire eventually. */ +non_null() static void change_symmetric_key(Onion *onion) { if (mono_time_is_timeout(onion->mono_time, onion->timestamp, KEY_REFRESH_INTERVAL)) { - new_symmetric_key(onion->secret_symmetric_key); + new_symmetric_key(onion->rng, onion->secret_symmetric_key); onion->timestamp = mono_time_get(onion->mono_time); } } /** packing and unpacking functions */ +non_null() static void ip_pack(uint8_t *data, const IP *source) { data[0] = source->family.value; @@ -47,6 +52,7 @@ static void ip_pack(uint8_t *data, const IP *source) } /** return 0 on success, -1 on failure. */ +non_null() static int ip_unpack(IP *target, const uint8_t *data, unsigned int data_size, bool disable_family_check) { if (data_size < (1 + SIZE_IP6)) { @@ -62,13 +68,14 @@ static int ip_unpack(IP *target, const uint8_t *data, unsigned int data_size, bo memcpy(target->ip.v6.uint8, data + 1, SIZE_IP6); } - bool valid = disable_family_check || + const bool valid = disable_family_check || net_family_is_ipv4(target->family) || net_family_is_ipv6(target->family); return valid ? 0 : -1; } +non_null() static void ipport_pack(uint8_t *data, const IP_Port *source) { ip_pack(data, &source->ip); @@ -76,6 +83,7 @@ static void ipport_pack(uint8_t *data, const IP_Port *source) } /** return 0 on success, -1 on failure. */ +non_null() static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data_size, bool disable_family_check) { if (data_size < (SIZE_IP + SIZE_PORT)) { @@ -91,7 +99,7 @@ static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data } -/** Create a new onion path. +/** @brief Create a new onion path. * * Create a new onion path out of nodes (nodes is a list of ONION_PATH_LENGTH nodes) * @@ -100,9 +108,9 @@ static int ipport_unpack(IP_Port *target, const uint8_t *data, unsigned int data * return -1 on failure. * return 0 on success. */ -int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *nodes) +int create_onion_path(const Random *rng, const DHT *dht, Onion_Path *new_path, const Node_format *nodes) { - if (!new_path || !nodes) { + if (new_path == nullptr || nodes == nullptr) { return -1; } @@ -112,11 +120,11 @@ int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *n uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; - crypto_new_keypair(random_public_key, random_secret_key); + crypto_new_keypair(rng, random_public_key, random_secret_key); encrypt_precompute(nodes[1].public_key, random_secret_key, new_path->shared_key2); memcpy(new_path->public_key2, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); - crypto_new_keypair(random_public_key, random_secret_key); + crypto_new_keypair(rng, random_public_key, random_secret_key); encrypt_precompute(nodes[2].public_key, random_secret_key, new_path->shared_key3); memcpy(new_path->public_key3, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); @@ -133,7 +141,7 @@ int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *n return 0; } -/** Dump nodes in onion path to nodes of length num_nodes. +/** @brief Dump nodes in onion path to nodes of length num_nodes. * * return -1 on failure. * return 0 on success. @@ -154,7 +162,7 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_ return 0; } -/** Create a onion packet. +/** @brief Create a onion packet. * * Use Onion_Path path to create packet for data of length to dest. * Maximum length of data is ONION_MAX_DATA_SIZE. @@ -163,7 +171,8 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_ * return -1 on failure. * return length of created packet on success. */ -int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest, +int create_onion_packet(const Random *rng, uint8_t *packet, uint16_t max_packet_length, + const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length) { if (1 + length + SEND_1 > max_packet_length || length == 0) { @@ -176,7 +185,7 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion memcpy(step1 + SIZE_IPPORT, data, length); uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(rng, nonce); VLA(uint8_t, step2, SIZE_IPPORT + SEND_BASE + length); ipport_pack(step2, &path->ip_port3); @@ -213,7 +222,7 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion return 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len; } -/** Create a onion packet to be sent over tcp. +/** @brief Create a onion packet to be sent over tcp. * * Use Onion_Path path to create packet for data of length to dest. * Maximum length of data is ONION_MAX_DATA_SIZE. @@ -222,7 +231,8 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion * return -1 on failure. * return length of created packet on success. */ -int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest, +int create_onion_packet_tcp(const Random *rng, uint8_t *packet, uint16_t max_packet_length, + const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length) { if (CRYPTO_NONCE_SIZE + SIZE_IPPORT + SEND_BASE * 2 + length > max_packet_length || length == 0) { @@ -235,7 +245,7 @@ int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const O memcpy(step1 + SIZE_IPPORT, data, length); uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(rng, nonce); VLA(uint8_t, step2, SIZE_IPPORT + SEND_BASE + length); ipport_pack(step2, &path->ip_port3); @@ -262,32 +272,7 @@ int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const O return CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE + len; } -/** Create and send a onion packet. - * - * Use Onion_Path path to send data of length to dest. - * Maximum length of data is ONION_MAX_DATA_SIZE. - * - * return -1 on failure. - * return 0 on success. - */ -int send_onion_packet(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, const uint8_t *data, - uint16_t length) -{ - uint8_t packet[ONION_MAX_PACKET_SIZE]; - int len = create_onion_packet(packet, sizeof(packet), path, dest, data, length); - - if (len == -1) { - return -1; - } - - if (sendpacket(net, &path->ip_port1, packet, len) != len) { - return -1; - } - - return 0; -} - -/** Create and send a onion response sent initially to dest with. +/** @brief Create and send a onion response sent initially to dest with. * Maximum length of data is ONION_RESPONSE_MAX_DATA_SIZE. * * return -1 on failure. @@ -312,6 +297,7 @@ int send_onion_response(const Networking_Core *net, const IP_Port *dest, const u return 0; } +non_null() static int handle_send_initial(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -331,7 +317,7 @@ static int handle_send_initial(void *object, const IP_Port *source, const uint8_ uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; get_shared_key(onion->mono_time, &onion->shared_keys_1, shared_key, dht_get_self_secret_key(onion->dht), packet + 1 + CRYPTO_NONCE_SIZE); - int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, + const int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), plain); if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)) { @@ -353,7 +339,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 0) == -1) { + if (ipport_unpack(&send_to, plain, len, false) == -1) { return 1; } @@ -366,7 +352,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); uint8_t *ret_part = data + data_len; - random_nonce(ret_part); + random_nonce(onion->rng, ret_part); len = encrypt_data_symmetric(onion->secret_symmetric_key, ret_part, ip_port, SIZE_IPPORT, ret_part + CRYPTO_NONCE_SIZE); @@ -383,6 +369,7 @@ int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const I return 0; } +non_null() static int handle_send_1(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { Onion *onion = (Onion *)object; @@ -410,7 +397,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 0) == -1) { + if (ipport_unpack(&send_to, plain, len, false) == -1) { return 1; } @@ -420,7 +407,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); uint8_t *ret_part = data + data_len; - random_nonce(ret_part); + random_nonce(onion->rng, ret_part); uint8_t ret_data[RETURN_1 + SIZE_IPPORT]; ipport_pack(ret_data, source); memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_1), RETURN_1); @@ -440,6 +427,7 @@ static int handle_send_1(void *object, const IP_Port *source, const uint8_t *pac return 0; } +non_null() static int handle_send_2(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { Onion *onion = (Onion *)object; @@ -465,19 +453,17 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac return 1; } - if (len <= SIZE_IPPORT) { - return 1; - } + assert(len > SIZE_IPPORT); const uint8_t packet_id = plain[SIZE_IPPORT]; - if (packet_id != NET_PACKET_ANNOUNCE_REQUEST && packet_id != NET_PACKET_ONION_DATA_REQUEST) { + if (packet_id != NET_PACKET_ANNOUNCE_REQUEST_OLD && packet_id != NET_PACKET_ONION_DATA_REQUEST) { return 1; } IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 0) == -1) { + if (ipport_unpack(&send_to, plain, len, false) == -1) { return 1; } @@ -485,7 +471,7 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac memcpy(data, plain + SIZE_IPPORT, len - SIZE_IPPORT); uint16_t data_len = len - SIZE_IPPORT; uint8_t *ret_part = data + (len - SIZE_IPPORT); - random_nonce(ret_part); + random_nonce(onion->rng, ret_part); uint8_t ret_data[RETURN_2 + SIZE_IPPORT]; ipport_pack(ret_data, source); memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_2), RETURN_2); @@ -506,6 +492,7 @@ static int handle_send_2(void *object, const IP_Port *source, const uint8_t *pac } +non_null() static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { Onion *onion = (Onion *)object; @@ -520,14 +507,14 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac const uint8_t packet_id = packet[1 + RETURN_3]; - if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { + if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { return 1; } change_symmetric_key(onion); uint8_t plain[SIZE_IPPORT + RETURN_2]; - int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, + const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, SIZE_IPPORT + RETURN_2 + CRYPTO_MAC_SIZE, plain); if ((uint32_t)len != sizeof(plain)) { @@ -536,7 +523,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 0) == -1) { + if (ipport_unpack(&send_to, plain, len, false) == -1) { return 1; } @@ -544,7 +531,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac data[0] = NET_PACKET_ONION_RECV_2; memcpy(data + 1, plain + SIZE_IPPORT, RETURN_2); memcpy(data + 1 + RETURN_2, packet + 1 + RETURN_3, length - (1 + RETURN_3)); - uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3)); + const uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3)); if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { return 1; @@ -553,6 +540,7 @@ static int handle_recv_3(void *object, const IP_Port *source, const uint8_t *pac return 0; } +non_null() static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { Onion *onion = (Onion *)object; @@ -567,14 +555,14 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac const uint8_t packet_id = packet[1 + RETURN_2]; - if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { + if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { return 1; } change_symmetric_key(onion); uint8_t plain[SIZE_IPPORT + RETURN_1]; - int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, + const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, SIZE_IPPORT + RETURN_1 + CRYPTO_MAC_SIZE, plain); if ((uint32_t)len != sizeof(plain)) { @@ -583,7 +571,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 0) == -1) { + if (ipport_unpack(&send_to, plain, len, false) == -1) { return 1; } @@ -591,7 +579,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac data[0] = NET_PACKET_ONION_RECV_1; memcpy(data + 1, plain + SIZE_IPPORT, RETURN_1); memcpy(data + 1 + RETURN_1, packet + 1 + RETURN_2, length - (1 + RETURN_2)); - uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2)); + const uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2)); if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { return 1; @@ -600,6 +588,7 @@ static int handle_recv_2(void *object, const IP_Port *source, const uint8_t *pac return 0; } +non_null() static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { Onion *onion = (Onion *)object; @@ -614,14 +603,14 @@ static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *pac const uint8_t packet_id = packet[1 + RETURN_1]; - if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { + if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && packet_id != NET_PACKET_ONION_DATA_RESPONSE) { return 1; } change_symmetric_key(onion); uint8_t plain[SIZE_IPPORT]; - int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, + const int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, SIZE_IPPORT + CRYPTO_MAC_SIZE, plain); if ((uint32_t)len != SIZE_IPPORT) { @@ -630,13 +619,13 @@ static int handle_recv_1(void *object, const IP_Port *source, const uint8_t *pac IP_Port send_to; - if (ipport_unpack(&send_to, plain, len, 1) == -1) { + if (ipport_unpack(&send_to, plain, len, true) == -1) { return 1; } - uint16_t data_len = length - (1 + RETURN_1); + const uint16_t data_len = length - (1 + RETURN_1); - if (onion->recv_1_function && + if (onion->recv_1_function != nullptr && !net_family_is_ipv4(send_to.ip.family) && !net_family_is_ipv6(send_to.ip.family)) { return onion->recv_1_function(onion->callback_object, &send_to, packet + (1 + RETURN_1), data_len); @@ -655,7 +644,7 @@ void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *o onion->callback_object = object; } -Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht) +Onion *new_onion(const Logger *log, const Mono_Time *mono_time, const Random *rng, DHT *dht) { if (dht == nullptr) { return nullptr; @@ -671,7 +660,8 @@ Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht) onion->dht = dht; onion->net = dht_get_net(dht); onion->mono_time = mono_time; - new_symmetric_key(onion->secret_symmetric_key); + onion->rng = rng; + new_symmetric_key(rng, onion->secret_symmetric_key); onion->timestamp = mono_time_get(onion->mono_time); networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion); diff --git a/protocols/Tox/libtox/src/toxcore/onion.h b/protocols/Tox/libtox/src/toxcore/onion.h index 970ef5b139..3da2137c44 100644 --- a/protocols/Tox/libtox/src/toxcore/onion.h +++ b/protocols/Tox/libtox/src/toxcore/onion.h @@ -17,7 +17,8 @@ typedef int onion_recv_1_cb(void *object, const IP_Port *dest, const uint8_t *da typedef struct Onion { const Logger *log; - Mono_Time *mono_time; + const Mono_Time *mono_time; + const Random *rng; DHT *dht; Networking_Core *net; uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; @@ -68,7 +69,7 @@ typedef struct Onion_Path { uint32_t path_num; } Onion_Path; -/** Create a new onion path. +/** @brief Create a new onion path. * * Create a new onion path out of nodes (nodes is a list of ONION_PATH_LENGTH nodes) * @@ -77,16 +78,18 @@ typedef struct Onion_Path { * return -1 on failure. * return 0 on success. */ -int create_onion_path(const DHT *dht, Onion_Path *new_path, const Node_format *nodes); +non_null() +int create_onion_path(const Random *rng, const DHT *dht, Onion_Path *new_path, const Node_format *nodes); -/** Dump nodes in onion path to nodes of length num_nodes. +/** @brief Dump nodes in onion path to nodes of length num_nodes. * * return -1 on failure. * return 0 on success. */ +non_null() int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_Path *path); -/** Create a onion packet. +/** @brief Create a onion packet. * * Use Onion_Path path to create packet for data of length to dest. * Maximum length of data is ONION_MAX_DATA_SIZE. @@ -95,11 +98,13 @@ int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_ * return -1 on failure. * return length of created packet on success. */ -int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest, +non_null() +int create_onion_packet(const Random *rng, uint8_t *packet, uint16_t max_packet_length, + const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length); -/** Create a onion packet to be sent over tcp. +/** @brief Create a onion packet to be sent over tcp. * * Use Onion_Path path to create packet for data of length to dest. * Maximum length of data is ONION_MAX_DATA_SIZE. @@ -108,30 +113,22 @@ int create_onion_packet(uint8_t *packet, uint16_t max_packet_length, const Onion * return -1 on failure. * return length of created packet on success. */ -int create_onion_packet_tcp(uint8_t *packet, uint16_t max_packet_length, const Onion_Path *path, const IP_Port *dest, +non_null() +int create_onion_packet_tcp(const Random *rng, uint8_t *packet, uint16_t max_packet_length, + const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length); -/** Create and send a onion packet. - * - * Use Onion_Path path to send data of length to dest. - * Maximum length of data is ONION_MAX_DATA_SIZE. - * - * return -1 on failure. - * return 0 on success. - */ -int send_onion_packet(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, const uint8_t *data, - uint16_t length); - -/** Create and send a onion response sent initially to dest with. +/** @brief Create and send a onion response sent initially to dest with. * Maximum length of data is ONION_RESPONSE_MAX_DATA_SIZE. * * return -1 on failure. * return 0 on success. */ +non_null() int send_onion_response(const Networking_Core *net, const IP_Port *dest, const uint8_t *data, uint16_t length, const uint8_t *ret); -/** Function to handle/send received decrypted versions of the packet sent with send_onion_packet. +/** @brief Function to handle/send received decrypted versions of the packet created by create_onion_packet. * * return 0 on success. * return 1 on failure. @@ -141,14 +138,17 @@ int send_onion_response(const Networking_Core *net, const IP_Port *dest, const u * Source family must be set to something else than TOX_AF_INET6 or TOX_AF_INET so that the callback gets called * when the response is received. */ +non_null() int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const IP_Port *source, const uint8_t *nonce); -/** Set the callback to be called when the dest ip_port doesn't have TOX_AF_INET6 or TOX_AF_INET as the family. - */ +/** Set the callback to be called when the dest ip_port doesn't have TOX_AF_INET6 or TOX_AF_INET as the family. */ +non_null(1) nullable(2, 3) void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *object); -Onion *new_onion(const Logger *log, Mono_Time *mono_time, DHT *dht); +non_null() +Onion *new_onion(const Logger *log, const Mono_Time *mono_time, const Random *rng, DHT *dht); +nullable(1) void kill_onion(Onion *onion); diff --git a/protocols/Tox/libtox/src/toxcore/onion_announce.c b/protocols/Tox/libtox/src/toxcore/onion_announce.c index 5c37fbe18c..f12e560837 100644 --- a/protocols/Tox/libtox/src/toxcore/onion_announce.c +++ b/protocols/Tox/libtox/src/toxcore/onion_announce.c @@ -8,20 +8,29 @@ */ #include "onion_announce.h" +#include <assert.h> #include <stdlib.h> #include <string.h> +#include "DHT.h" #include "LAN_discovery.h" +#include "ccompat.h" #include "mono_time.h" #include "util.h" #define PING_ID_TIMEOUT ONION_ANNOUNCE_TIMEOUT +#define ANNOUNCE_REQUEST_MIN_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_RETURN_3) +#define ANNOUNCE_REQUEST_MAX_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MAX_SIZE + ONION_RETURN_3) + +/* TODO(Jfreegman): DEPRECATE */ #define ANNOUNCE_REQUEST_SIZE_RECV (ONION_ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3) #define DATA_REQUEST_MIN_SIZE ONION_DATA_REQUEST_MIN_SIZE #define DATA_REQUEST_MIN_SIZE_RECV (DATA_REQUEST_MIN_SIZE + ONION_RETURN_3) +#define ONION_MINIMAL_SIZE (ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) + static_assert(ONION_PING_ID_SIZE == CRYPTO_PUBLIC_KEY_SIZE, "announce response packets assume that ONION_PING_ID_SIZE is equal to CRYPTO_PUBLIC_KEY_SIZE"); @@ -30,24 +39,31 @@ typedef struct Onion_Announce_Entry { IP_Port ret_ip_port; uint8_t ret[ONION_RETURN_3]; uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint64_t time; + uint64_t announce_time; } Onion_Announce_Entry; struct Onion_Announce { const Logger *log; - Mono_Time *mono_time; + const Mono_Time *mono_time; + const Random *rng; DHT *dht; Networking_Core *net; Onion_Announce_Entry entries[ONION_ANNOUNCE_MAX_ENTRIES]; - /* This is CRYPTO_SYMMETRIC_KEY_SIZE long just so we can use new_symmetric_key() to fill it */ - uint8_t secret_bytes[CRYPTO_SYMMETRIC_KEY_SIZE]; + uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; Shared_Keys shared_keys_recv; + + uint16_t extra_data_max_size; + pack_extra_data_cb *extra_data_callback; + void *extra_data_object; }; -static bool onion_ping_id_eq(const uint8_t *a, const uint8_t *b) +void onion_announce_extra_data_callback(Onion_Announce *onion_a, uint16_t extra_data_max_size, + pack_extra_data_cb *extra_data_callback, void *extra_data_object) { - return public_key_cmp(a, b) == 0; + onion_a->extra_data_max_size = extra_data_max_size; + onion_a->extra_data_callback = extra_data_callback; + onion_a->extra_data_object = extra_data_object; } uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry) @@ -55,12 +71,14 @@ uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry return onion_a->entries[entry].public_key; } -void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t time) +void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t announce_time) { - onion_a->entries[entry].time = time; + onion_a->entries[entry].announce_time = announce_time; } -/** Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE). +/** @brief Create an onion announce request packet in packet of max_packet_length. + * + * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_MIN_SIZE. * * dest_client_id is the public key of the node the packet will be sent to. * public_key and secret_key is the kepair which will be used to encrypt the request. @@ -73,11 +91,11 @@ void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint * return -1 on failure. * return packet length on success. */ -int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, +int create_announce_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data) { - if (max_packet_length < ONION_ANNOUNCE_REQUEST_SIZE) { + if (max_packet_length < ONION_ANNOUNCE_REQUEST_MIN_SIZE) { return -1; } @@ -89,22 +107,24 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, &sendback_data, sizeof(sendback_data)); - packet[0] = NET_PACKET_ANNOUNCE_REQUEST; - random_nonce(packet + 1); + packet[0] = NET_PACKET_ANNOUNCE_REQUEST_OLD; + random_nonce(rng, packet + 1); - int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, sizeof(plain), - packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + const int len = encrypt_data(dest_client_id, secret_key, packet + 1, plain, sizeof(plain), + packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_SIZE) { + if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_MIN_SIZE) { return -1; } memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE); - return ONION_ANNOUNCE_REQUEST_SIZE; + return ONION_ANNOUNCE_REQUEST_MIN_SIZE; } -/** Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE). +/** @brief Create an onion data request packet in packet of max_packet_length. + * + * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_SIZE. * * public_key is the real public key of the node which we want to send the data of length length to. * encrypt_public_key is the public key used to encrypt the data packet. @@ -114,7 +134,7 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u * return -1 on failure. * return 0 on success. */ -int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key, +int create_data_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length) { if (DATA_REQUEST_MIN_SIZE + length > max_packet_length) { @@ -131,12 +151,12 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8 uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; - crypto_new_keypair(random_public_key, random_secret_key); + crypto_new_keypair(rng, random_public_key, random_secret_key); memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); - int len = encrypt_data(encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length, - packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + const int len = encrypt_data(encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length, + packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); if (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len != DATA_REQUEST_MIN_SIZE + length) { @@ -146,7 +166,7 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8 return DATA_REQUEST_MIN_SIZE + length; } -/** Create and send an onion announce request packet. +/** @brief Create and send an onion announce request packet. * * path is the path the request will take before it is sent to dest. * @@ -160,21 +180,22 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8 * return -1 on failure. * return 0 on success. */ -int send_announce_request(const Networking_Core *net, const Onion_Path *path, Node_format dest, +int send_announce_request(const Networking_Core *net, const Random *rng, + const Onion_Path *path, const Node_format *dest, const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data) { - uint8_t request[ONION_ANNOUNCE_REQUEST_SIZE]; - int len = create_announce_request(request, sizeof(request), dest.public_key, public_key, secret_key, ping_id, client_id, - data_public_key, sendback_data); + uint8_t request[ONION_ANNOUNCE_REQUEST_MIN_SIZE]; + int len = create_announce_request(rng, request, sizeof(request), dest->public_key, public_key, secret_key, ping_id, + client_id, data_public_key, sendback_data); if (len != sizeof(request)) { return -1; } uint8_t packet[ONION_MAX_PACKET_SIZE]; - len = create_onion_packet(packet, sizeof(packet), path, &dest.ip_port, request, sizeof(request)); + len = create_onion_packet(rng, packet, sizeof(packet), path, &dest->ip_port, request, sizeof(request)); if (len == -1) { return -1; @@ -187,7 +208,7 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No return 0; } -/** Create and send an onion data request packet. +/** @brief Create and send an onion data request packet. * * path is the path the request will take before it is sent to dest. * (if dest knows the person with the public_key they should @@ -203,19 +224,19 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No * return -1 on failure. * return 0 on success. */ -int send_data_request(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, - const uint8_t *public_key, - const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length) +int send_data_request(const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest, + const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce, + const uint8_t *data, uint16_t length) { uint8_t request[ONION_MAX_DATA_SIZE]; - int len = create_data_request(request, sizeof(request), public_key, encrypt_public_key, nonce, data, length); + int len = create_data_request(rng, request, sizeof(request), public_key, encrypt_public_key, nonce, data, length); if (len == -1) { return -1; } uint8_t packet[ONION_MAX_PACKET_SIZE]; - len = create_onion_packet(packet, sizeof(packet), path, dest, request, len); + len = create_onion_packet(rng, packet, sizeof(packet), path, dest, request, len); if (len == -1) { return -1; @@ -228,29 +249,17 @@ int send_data_request(const Networking_Core *net, const Onion_Path *path, const return 0; } -/** Generate a ping_id and put it in ping_id */ -static void generate_ping_id(const Onion_Announce *onion_a, uint64_t time, const uint8_t *public_key, - const IP_Port *ret_ip_port, uint8_t *ping_id) -{ - time /= PING_ID_TIMEOUT; - uint8_t data[CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time) + CRYPTO_PUBLIC_KEY_SIZE + sizeof(IP_Port)]; - memcpy(data, onion_a->secret_bytes, CRYPTO_SYMMETRIC_KEY_SIZE); - memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE, &time, sizeof(time)); - memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time), public_key, CRYPTO_PUBLIC_KEY_SIZE); - memcpy(data + CRYPTO_SYMMETRIC_KEY_SIZE + sizeof(time) + CRYPTO_PUBLIC_KEY_SIZE, ret_ip_port, sizeof(IP_Port)); - crypto_sha256(ping_id, data, sizeof(data)); -} - -/** check if public key is in entries list +/** @brief check if public key is in entries list * * return -1 if no * return position in list if yes */ +non_null() static int in_entries(const Onion_Announce *onion_a, const uint8_t *public_key) { for (unsigned int i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { - if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].time, ONION_ANNOUNCE_TIMEOUT) - && public_key_cmp(onion_a->entries[i].public_key, public_key) == 0) { + if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT) + && pk_equal(onion_a->entries[i].public_key, public_key)) { return i; } } @@ -264,6 +273,7 @@ typedef struct Cmp_Data { Onion_Announce_Entry entry; } Cmp_Data; +non_null() static int cmp_entry(const void *a, const void *b) { const Cmp_Data *cmp1 = (const Cmp_Data *)a; @@ -272,8 +282,8 @@ static int cmp_entry(const void *a, const void *b) const Onion_Announce_Entry entry2 = cmp2->entry; const uint8_t *cmp_public_key = cmp1->base_public_key; - const int t1 = mono_time_is_timeout(cmp1->mono_time, entry1.time, ONION_ANNOUNCE_TIMEOUT); - const int t2 = mono_time_is_timeout(cmp1->mono_time, entry2.time, ONION_ANNOUNCE_TIMEOUT); + const bool t1 = mono_time_is_timeout(cmp1->mono_time, entry1.announce_time, ONION_ANNOUNCE_TIMEOUT); + const bool t2 = mono_time_is_timeout(cmp1->mono_time, entry2.announce_time, ONION_ANNOUNCE_TIMEOUT); if (t1 && t2) { return 0; @@ -287,25 +297,30 @@ static int cmp_entry(const void *a, const void *b) return 1; } - const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); + const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); - if (close == 1) { + if (closest == 1) { return 1; } - if (close == 2) { + if (closest == 2) { return -1; } return 0; } +non_null() static void sort_onion_announce_list(Onion_Announce_Entry *list, unsigned int length, const Mono_Time *mono_time, const uint8_t *comp_public_key) { // Pass comp_public_key to qsort with each Client_data entry, so the // comparison function can use it as the base of comparison. - VLA(Cmp_Data, cmp_list, length); + Cmp_Data *cmp_list = (Cmp_Data *)calloc(length, sizeof(Cmp_Data)); + + if (cmp_list == nullptr) { + return; + } for (uint32_t i = 0; i < length; ++i) { cmp_list[i].mono_time = mono_time; @@ -318,13 +333,16 @@ static void sort_onion_announce_list(Onion_Announce_Entry *list, unsigned int le for (uint32_t i = 0; i < length; ++i) { list[i] = cmp_list[i].entry; } + + free(cmp_list); } -/** add entry to entries list +/** @brief add entry to entries list * * return -1 if failure * return position if added */ +non_null() static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, const uint8_t *public_key, const uint8_t *data_public_key, const uint8_t *ret) { @@ -333,7 +351,7 @@ static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, c if (pos == -1) { for (unsigned i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { - if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].time, ONION_ANNOUNCE_TIMEOUT)) { + if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT)) { pos = i; } } @@ -353,114 +371,236 @@ static int add_to_entries(Onion_Announce *onion_a, const IP_Port *ret_ip_port, c onion_a->entries[pos].ret_ip_port = *ret_ip_port; memcpy(onion_a->entries[pos].ret, ret, ONION_RETURN_3); memcpy(onion_a->entries[pos].data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); - onion_a->entries[pos].time = mono_time_get(onion_a->mono_time); + onion_a->entries[pos].announce_time = mono_time_get(onion_a->mono_time); sort_onion_announce_list(onion_a->entries, ONION_ANNOUNCE_MAX_ENTRIES, onion_a->mono_time, dht_get_self_public_key(onion_a->dht)); return in_entries(onion_a, public_key); } -static int handle_announce_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, - void *userdata) +non_null() +static void make_announce_payload_helper(const Onion_Announce *onion_a, const uint8_t *ping_id, + uint8_t *response, int index, const uint8_t *packet_public_key, const uint8_t *data_public_key) { - Onion_Announce *onion_a = (Onion_Announce *)object; + if (index < 0) { + response[0] = 0; + memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); + return; + } - if (length != ANNOUNCE_REQUEST_SIZE_RECV) { - return 1; + if (pk_equal(onion_a->entries[index].public_key, packet_public_key)) { + if (!pk_equal(onion_a->entries[index].data_public_key, data_public_key)) { + response[0] = 0; + memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); + } else { + response[0] = 2; + memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); + } + } else { + response[0] = 1; + memcpy(response + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE); } +} +/** @brief Handle an onion announce request, possibly with extra data for group chats. + * + * @param onion_a The announce object. + * @param source Requester IP/Port. + * @param packet Encrypted incoming packet. + * @param length Length of incoming packet. + * @param response_packet_id Packet ID to use for the onion announce response. + * @param plain_size Expected size of the decrypted packet. This function returns an error if the + * actual decrypted size is not exactly equal to this number. + * @param want_node_count If true, the packed nodes in the response are preceded by the number of + * nodes sent in the packet. This is necessary if you want to send extra data after the nodes. + * @param max_extra_size Amount of memory to allocate in the outgoing packet to be filled by the + * extra data callback. + * @param pack_extra_data_callback Callback that may write extra data into the packet. + * + * @retval 1 on failure. + * @retval 0 on success. + */ +non_null(1, 2, 3) nullable(9) +static int handle_announce_request_common( + Onion_Announce *onion_a, const IP_Port *source, const uint8_t *packet, uint16_t length, + uint8_t response_packet_id, uint16_t plain_size, bool want_node_count, uint16_t max_extra_size, + pack_extra_data_cb *pack_extra_data_callback) +{ const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE; uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; get_shared_key(onion_a->mono_time, &onion_a->shared_keys_recv, shared_key, dht_get_self_secret_key(onion_a->dht), packet_public_key); - uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + - ONION_ANNOUNCE_SENDBACK_DATA_LENGTH]; - int len = decrypt_data_symmetric(shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, - ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + - CRYPTO_MAC_SIZE, plain); + uint8_t *plain = (uint8_t *)malloc(plain_size); - if ((uint32_t)len != sizeof(plain)) { + if (plain == nullptr) { return 1; } - uint8_t ping_id1[ONION_PING_ID_SIZE]; - generate_ping_id(onion_a, mono_time_get(onion_a->mono_time), packet_public_key, source, ping_id1); + const int decrypted_len = decrypt_data_symmetric(shared_key, packet + 1, + packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, plain_size + CRYPTO_MAC_SIZE, plain); - uint8_t ping_id2[ONION_PING_ID_SIZE]; - generate_ping_id(onion_a, mono_time_get(onion_a->mono_time) + PING_ID_TIMEOUT, packet_public_key, source, ping_id2); + if ((uint32_t)decrypted_len != plain_size) { + free(plain); + return 1; + } - int index; + const uint16_t ping_id_data_len = CRYPTO_PUBLIC_KEY_SIZE + sizeof(*source); + uint8_t ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + sizeof(*source)]; + memcpy(ping_id_data, packet_public_key, CRYPTO_PUBLIC_KEY_SIZE); + memcpy(ping_id_data + CRYPTO_PUBLIC_KEY_SIZE, source, sizeof(*source)); const uint8_t *data_public_key = plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE; - if (onion_ping_id_eq(ping_id1, plain) - || onion_ping_id_eq(ping_id2, plain)) { + int index; + + if (check_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, + ping_id_data, ping_id_data_len, plain)) { index = add_to_entries(onion_a, source, packet_public_key, data_public_key, - packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)); + packet + (length - ONION_RETURN_3)); } else { index = in_entries(onion_a, plain + ONION_PING_ID_SIZE); } - /*Respond with a announce response packet*/ + /* Respond with a announce response packet */ Node_format nodes_list[MAX_SENT_NODES]; - unsigned int num_nodes = - get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec, ip_is_lan(&source->ip)); + const unsigned int num_nodes = + get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec(), ip_is_lan(&source->ip), false); + + assert(num_nodes <= UINT8_MAX); + uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(onion_a->rng, nonce); - uint8_t pl[1 + ONION_PING_ID_SIZE + sizeof(nodes_list)]; + const uint16_t nodes_offset = 1 + ONION_PING_ID_SIZE + (want_node_count ? 1 : 0); + const uint16_t response_size = nodes_offset + + MAX_SENT_NODES * PACKED_NODE_SIZE_IP6 + + max_extra_size; + uint8_t *response = (uint8_t *)malloc(response_size); - if (index == -1) { - pl[0] = 0; - memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); - } else { - if (public_key_cmp(onion_a->entries[index].public_key, packet_public_key) == 0) { - if (public_key_cmp(onion_a->entries[index].data_public_key, data_public_key) != 0) { - pl[0] = 0; - memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); - } else { - pl[0] = 2; - memcpy(pl + 1, ping_id2, ONION_PING_ID_SIZE); - } - } else { - pl[0] = 1; - memcpy(pl + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE); - } + if (response == nullptr) { + free(plain); + return 1; } + uint8_t ping_id[TIMED_AUTH_SIZE]; + generate_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, + ping_id_data, ping_id_data_len, ping_id); + + make_announce_payload_helper(onion_a, ping_id, response, index, packet_public_key, data_public_key); + int nodes_length = 0; if (num_nodes != 0) { - nodes_length = pack_nodes(pl + 1 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes); + nodes_length = pack_nodes(onion_a->log, response + nodes_offset, sizeof(nodes_list), nodes_list, + (uint16_t)num_nodes); if (nodes_length <= 0) { + LOGGER_WARNING(onion_a->log, "Failed to pack nodes"); + free(response); + free(plain); return 1; } } + uint16_t offset = nodes_offset + nodes_length; + + if (want_node_count) { + response[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes; + } + + const int extra_size = pack_extra_data_callback == nullptr ? 0 + : pack_extra_data_callback(onion_a->extra_data_object, + onion_a->log, onion_a->mono_time, num_nodes, + plain + ONION_MINIMAL_SIZE, length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, + response, response_size, offset); + + if (extra_size == -1) { + free(response); + free(plain); + return 1; + } + + offset += extra_size; + uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE]; - len = encrypt_data_symmetric(shared_key, nonce, pl, 1 + ONION_PING_ID_SIZE + nodes_length, - data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); + const int len = encrypt_data_symmetric(shared_key, nonce, response, offset, + data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); - if (len != 1 + ONION_PING_ID_SIZE + nodes_length + CRYPTO_MAC_SIZE) { + if (len != offset + CRYPTO_MAC_SIZE) { + LOGGER_ERROR(onion_a->log, "Failed to encrypt announce response"); + free(response); + free(plain); return 1; } - data[0] = NET_PACKET_ANNOUNCE_RESPONSE; + data[0] = response_packet_id; memcpy(data + 1, plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, ONION_ANNOUNCE_SENDBACK_DATA_LENGTH); memcpy(data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, nonce, CRYPTO_NONCE_SIZE); if (send_onion_response(onion_a->net, source, data, 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len, - packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)) == -1) { + packet + (length - ONION_RETURN_3)) == -1) { + free(response); + free(plain); return 1; } + free(response); + free(plain); return 0; } +non_null() +static int handle_gca_announce_request(Onion_Announce *onion_a, const IP_Port *source, const uint8_t *packet, + uint16_t length) +{ + if (length > ANNOUNCE_REQUEST_MAX_SIZE_RECV || length <= ANNOUNCE_REQUEST_MIN_SIZE_RECV) { + return 1; + } + + if (onion_a->extra_data_callback == nullptr) { + return 1; + } + + return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, + ONION_MINIMAL_SIZE + length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, + true, onion_a->extra_data_max_size, onion_a->extra_data_callback); +} + +non_null(1, 2, 3) nullable(5) +static int handle_announce_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + Onion_Announce *onion_a = (Onion_Announce *)object; + + if (length != ANNOUNCE_REQUEST_MIN_SIZE_RECV) { + return handle_gca_announce_request(onion_a, source, packet, length); + } + + return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, + true, 0, nullptr); +} + +/* TODO(Jfreegman): DEPRECATE */ +non_null(1, 2, 3) nullable(5) +static int handle_announce_request_old(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, + void *userdata) +{ + Onion_Announce *onion_a = (Onion_Announce *)object; + + if (length != ANNOUNCE_REQUEST_SIZE_RECV) { + return 1; + } + + return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE_OLD, + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, + false, 0, nullptr); +} + +non_null() static int handle_data_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -474,7 +614,7 @@ static int handle_data_request(void *object, const IP_Port *source, const uint8_ return 1; } - int index = in_entries(onion_a, packet + 1); + const int index = in_entries(onion_a, packet + 1); if (index == -1) { return 1; @@ -492,7 +632,7 @@ static int handle_data_request(void *object, const IP_Port *source, const uint8_ return 0; } -Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT *dht) +Onion_Announce *new_onion_announce(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht) { if (dht == nullptr) { return nullptr; @@ -505,14 +645,22 @@ Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT } onion_a->log = log; + onion_a->rng = rng; onion_a->mono_time = mono_time; onion_a->dht = dht; onion_a->net = dht_get_net(dht); - new_symmetric_key(onion_a->secret_bytes); + onion_a->extra_data_max_size = 0; + onion_a->extra_data_callback = nullptr; + onion_a->extra_data_object = nullptr; + new_hmac_key(rng, onion_a->hmac_key); networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a); + networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, &handle_announce_request_old, onion_a); networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, &handle_data_request, onion_a); + // TODO(Jfreegman): Remove this when we merge the rest of new groupchats + onion_announce_extra_data_callback(onion_a, 0, nullptr, nullptr); + return onion_a; } @@ -523,6 +671,10 @@ void kill_onion_announce(Onion_Announce *onion_a) } networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, nullptr, nullptr); + networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, nullptr, nullptr); networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, nullptr, nullptr); + + crypto_memzero(onion_a->hmac_key, CRYPTO_HMAC_KEY_SIZE); + free(onion_a); } diff --git a/protocols/Tox/libtox/src/toxcore/onion_announce.h b/protocols/Tox/libtox/src/toxcore/onion_announce.h index c4159d2bf6..8cf1f49539 100644 --- a/protocols/Tox/libtox/src/toxcore/onion_announce.h +++ b/protocols/Tox/libtox/src/toxcore/onion_announce.h @@ -11,17 +11,23 @@ #include "logger.h" #include "onion.h" +#include "timed_auth.h" #define ONION_ANNOUNCE_MAX_ENTRIES 160 #define ONION_ANNOUNCE_TIMEOUT 300 -#define ONION_PING_ID_SIZE CRYPTO_SHA256_SIZE +#define ONION_PING_ID_SIZE TIMED_AUTH_SIZE +#define ONION_MAX_EXTRA_DATA_SIZE 136 #define ONION_ANNOUNCE_SENDBACK_DATA_LENGTH (sizeof(uint64_t)) -#define ONION_ANNOUNCE_REQUEST_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE) +#define ONION_ANNOUNCE_REQUEST_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE) +#define ONION_ANNOUNCE_REQUEST_MAX_SIZE (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_MAX_EXTRA_DATA_SIZE) + +#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE) +#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + ONION_MAX_EXTRA_DATA_SIZE * MAX_SENT_NODES) -#define ONION_ANNOUNCE_RESPONSE_MIN_SIZE (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + 1 + ONION_PING_ID_SIZE + CRYPTO_MAC_SIZE) -#define ONION_ANNOUNCE_RESPONSE_MAX_SIZE (ONION_ANNOUNCE_RESPONSE_MIN_SIZE + sizeof(Node_format)*MAX_SENT_NODES) +/* TODO: DEPRECATE */ +#define ONION_ANNOUNCE_REQUEST_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_MAC_SIZE) #define ONION_DATA_RESPONSE_MIN_SIZE (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) @@ -31,10 +37,14 @@ typedef struct Onion_Announce Onion_Announce; /** These two are not public; they are for tests only! */ +non_null() uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry); -void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t time); +non_null() +void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t announce_time); -/** Create an onion announce request packet in packet of max_packet_length (recommended size ONION_ANNOUNCE_REQUEST_SIZE). +/** @brief Create an onion announce request packet in packet of max_packet_length. + * + * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_MIN_SIZE. * * dest_client_id is the public key of the node the packet will be sent to. * public_key and secret_key is the kepair which will be used to encrypt the request. @@ -47,11 +57,14 @@ void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint * return -1 on failure. * return packet length on success. */ -int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, +non_null() +int create_announce_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, const uint8_t *data_public_key, uint64_t sendback_data); -/** Create an onion data request packet in packet of max_packet_length (recommended size ONION_MAX_PACKET_SIZE). +/** @brief Create an onion data request packet in packet of max_packet_length. + * + * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_SIZE. * * public_key is the real public key of the node which we want to send the data of length length to. * encrypt_public_key is the public key used to encrypt the data packet. @@ -61,10 +74,11 @@ int create_announce_request(uint8_t *packet, uint16_t max_packet_length, const u * return -1 on failure. * return 0 on success. */ -int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key, +non_null() +int create_data_request(const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length); -/** Create and send an onion announce request packet. +/** @brief Create and send an onion announce request packet. * * path is the path the request will take before it is sent to dest. * @@ -78,12 +92,14 @@ int create_data_request(uint8_t *packet, uint16_t max_packet_length, const uint8 * return -1 on failure. * return 0 on success. */ -int send_announce_request(const Networking_Core *net, const Onion_Path *path, Node_format dest, +non_null() +int send_announce_request(const Networking_Core *net, const Random *rng, + const Onion_Path *path, const Node_format *dest, const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, - const uint8_t *data_public_key, uint64_t sendback_data); + const uint8_t *data_public_key, uint64_t sendback_data); -/** Create and send an onion data request packet. +/** @brief Create and send an onion data request packet. * * path is the path the request will take before it is sent to dest. * (if dest knows the person with the public_key they should @@ -99,14 +115,24 @@ int send_announce_request(const Networking_Core *net, const Onion_Path *path, No * return -1 on failure. * return 0 on success. */ -int send_data_request(const Networking_Core *net, const Onion_Path *path, const IP_Port *dest, - const uint8_t *public_key, - const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length); +non_null() +int send_data_request(const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest, + const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce, + const uint8_t *data, uint16_t length); -Onion_Announce *new_onion_announce(const Logger *log, Mono_Time *mono_time, DHT *dht); +typedef int pack_extra_data_cb(void *object, const Logger *logger, const Mono_Time *mono_time, + uint8_t num_nodes, uint8_t *plain, uint16_t plain_size, + uint8_t *response, uint16_t response_size, uint16_t offset); -void kill_onion_announce(Onion_Announce *onion_a); +non_null(1) nullable(3, 4) +void onion_announce_extra_data_callback(Onion_Announce *onion_a, uint16_t extra_data_max_size, + pack_extra_data_cb *extra_data_callback, void *extra_data_object); +non_null() +Onion_Announce *new_onion_announce(const Logger *log, const Random *rng, const Mono_Time *mono_time, DHT *dht); + +nullable(1) +void kill_onion_announce(Onion_Announce *onion_a); #endif diff --git a/protocols/Tox/libtox/src/toxcore/onion_client.c b/protocols/Tox/libtox/src/toxcore/onion_client.c index 592cb4f57d..91168c661c 100644 --- a/protocols/Tox/libtox/src/toxcore/onion_client.c +++ b/protocols/Tox/libtox/src/toxcore/onion_client.c @@ -14,11 +14,11 @@ #include <string.h> #include "LAN_discovery.h" +#include "ccompat.h" #include "mono_time.h" #include "util.h" -/** defines for the array size and - * timeout for onion announce packets. */ +/** @brief defines for the array size and timeout for onion announce packets. */ #define ANNOUNCE_ARRAY_SIZE 256 #define ANNOUNCE_TIMEOUT 10 @@ -27,7 +27,7 @@ typedef struct Onion_Node { IP_Port ip_port; uint8_t ping_id[ONION_PING_ID_SIZE]; uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t is_stored; + uint8_t is_stored; // Tribool. uint64_t added_time; @@ -35,7 +35,7 @@ typedef struct Onion_Node { uint64_t last_pinged; - uint8_t unsuccessful_pings; + uint8_t pings_since_last_response; uint32_t path_used; } Onion_Node; @@ -55,10 +55,10 @@ typedef struct Last_Pinged { } Last_Pinged; typedef struct Onion_Friend { - uint8_t status; /* 0 if friend is not valid, 1 if friend is valid.*/ - uint8_t is_online; /* Set by the onion_set_friend_status function. */ + bool is_valid; + bool is_online; - uint8_t know_dht_public_key; /* 0 if we don't know the dht public key of the other, 1 if we do. */ + bool know_dht_public_key; uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; @@ -71,7 +71,11 @@ typedef struct Onion_Friend { uint64_t last_noreplay; - uint64_t last_seen; + uint64_t last_populated; // the last time we had a fully populated client nodes list + uint64_t time_last_pinged; // the last time we pinged this friend with any node + + uint32_t run_count; + uint32_t pings; // how many sucessful pings we've made for this friend Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; uint8_t last_pinged_index; @@ -83,18 +87,19 @@ typedef struct Onion_Friend { onion_dht_pk_cb *dht_pk_callback; void *dht_pk_callback_object; uint32_t dht_pk_callback_number; - - uint32_t run_count; } Onion_Friend; +static const Onion_Friend empty_onion_friend = {false}; + typedef struct Onion_Data_Handler { oniondata_handler_cb *function; void *object; } Onion_Data_Handler; struct Onion_Client { - Mono_Time *mono_time; + const Mono_Time *mono_time; const Logger *logger; + const Random *rng; DHT *dht; Net_Crypto *c; @@ -111,6 +116,7 @@ struct Onion_Client { uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; uint64_t last_run; uint64_t first_run; + uint64_t last_time_connected; uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; @@ -128,6 +134,7 @@ struct Onion_Client { Onion_Data_Handler onion_data_handlers[256]; uint64_t last_packet_recv; + uint64_t last_populated; // the last time we had a fully populated path nodes list unsigned int onion_connected; bool udp_connected; @@ -143,20 +150,28 @@ Net_Crypto *onion_get_net_crypto(const Onion_Client *onion_c) return onion_c->c; } -/** Add a node to the path_nodes bootstrap array. +/** @brief Add a node to the path_nodes bootstrap array. * - * return -1 on failure - * return 0 on success + * If a node with the given public key was already in the bootstrap array, this function has no + * effect and returns successfully. There is currently no way to update the IP/port for a bootstrap + * node, so if it changes, the Onion_Client must be recreated. + * + * @param onion_c The onion client object. + * @param ip_port IP/port for the bootstrap node. + * @param public_key DHT public key for the bootstrap node. + * + * @retval false on failure + * @retval true on success */ -int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key) +bool onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key) { if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { - return -1; + return false; } for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) { - if (public_key_cmp(public_key, onion_c->path_nodes_bs[i].public_key) == 0) { - return -1; + if (pk_equal(public_key, onion_c->path_nodes_bs[i].public_key)) { + return true; } } @@ -164,21 +179,22 @@ int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const memcpy(onion_c->path_nodes_bs[onion_c->path_nodes_index_bs % MAX_PATH_NODES].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - uint16_t last = onion_c->path_nodes_index_bs; + const uint16_t last = onion_c->path_nodes_index_bs; ++onion_c->path_nodes_index_bs; if (onion_c->path_nodes_index_bs < last) { onion_c->path_nodes_index_bs = MAX_PATH_NODES + 1; } - return 0; + return true; } -/** Add a node to the path_nodes array. +/** @brief Add a node to the path_nodes array. * * return -1 on failure * return 0 on success */ +non_null() static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key) { if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { @@ -186,7 +202,7 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co } for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) { - if (public_key_cmp(public_key, onion_c->path_nodes[i].public_key) == 0) { + if (pk_equal(public_key, onion_c->path_nodes[i].public_key)) { return -1; } } @@ -195,7 +211,7 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co memcpy(onion_c->path_nodes[onion_c->path_nodes_index % MAX_PATH_NODES].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - uint16_t last = onion_c->path_nodes_index; + const uint16_t last = onion_c->path_nodes_index; ++onion_c->path_nodes_index; if (onion_c->path_nodes_index < last) { @@ -205,13 +221,13 @@ static int onion_add_path_node(Onion_Client *onion_c, const IP_Port *ip_port, co return 0; } -/** Put up to max_num nodes in nodes. +/** @brief Put up to max_num nodes in nodes. * * return the number of nodes. */ uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num) { - if (!max_num) { + if (max_num == 0) { return 0; } @@ -227,7 +243,7 @@ uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uin bool already_saved = false; for (uint16_t k = 0; k < num_nodes; ++k) { - if (public_key_cmp(nodes[k].public_key, onion_c->path_nodes_bs[j].public_key) == 0) { + if (pk_equal(nodes[k].public_key, onion_c->path_nodes_bs[j].public_key)) { already_saved = true; break; } @@ -242,13 +258,14 @@ uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uin return i; } -/** Put up to max_num random nodes in nodes. +/** @brief Put up to max_num random nodes in nodes. * * return the number of nodes. */ +non_null() static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num) { - if (!max_num) { + if (max_num == 0) { return 0; } @@ -261,25 +278,22 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format } for (unsigned int i = 0; i < max_num; ++i) { - const uint32_t rand_idx = random_range_u32(num_nodes); + const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes); nodes[i] = onion_c->path_nodes[rand_idx]; } } else { - int random_tcp = get_random_tcp_con_number(onion_c->c); + const int random_tcp = get_random_tcp_con_number(onion_c->c); if (random_tcp == -1) { return 0; } if (num_nodes >= 2) { - nodes[0] = (Node_format) { - 0 - }; - nodes[0].ip_port.ip.family = net_family_tcp_family; - nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp; + nodes[0] = empty_node_format; + nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); for (unsigned int i = 1; i < max_num; ++i) { - const uint32_t rand_idx = random_range_u32(num_nodes); + const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes); nodes[i] = onion_c->path_nodes[rand_idx]; } } else { @@ -289,14 +303,11 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format return 0; } - nodes[0] = (Node_format) { - 0 - }; - nodes[0].ip_port.ip.family = net_family_tcp_family; - nodes[0].ip_port.ip.ip.v4.uint32 = random_tcp; + nodes[0] = empty_node_format; + nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); for (unsigned int i = 1; i < max_num; ++i) { - const uint32_t rand_idx = random_range_u32(num_nodes_bs); + const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes_bs); nodes[i] = onion_c->path_nodes_bs[rand_idx]; } } @@ -309,6 +320,7 @@ static uint16_t random_nodes_path_onion(const Onion_Client *onion_c, Node_format * return -1 if nodes are suitable for creating a new path. * return path number of already existing similar path if one already exists. */ +non_null() static int is_path_used(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, const Node_format *nodes) { for (unsigned int i = 0; i < NUMBER_ONION_PATHS; ++i) { @@ -330,27 +342,29 @@ static int is_path_used(const Mono_Time *mono_time, const Onion_Client_Paths *on } /** is path timed out */ +non_null() static bool path_timed_out(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, uint32_t pathnum) { pathnum = pathnum % NUMBER_ONION_PATHS; - bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum]; - uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT; + const bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum]; + const uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT; - return ((onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES + return (onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES && mono_time_is_timeout(mono_time, onion_paths->last_path_used[pathnum], timeout)) - || mono_time_is_timeout(mono_time, onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME)); + || mono_time_is_timeout(mono_time, onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME); } /** should node be considered to have timed out */ +non_null() static bool onion_node_timed_out(const Onion_Node *node, const Mono_Time *mono_time) { - return (node->timestamp == 0 - || (node->unsuccessful_pings >= ONION_NODE_MAX_PINGS - && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT))); + return node->timestamp == 0 + || (node->pings_since_last_response >= ONION_NODE_MAX_PINGS + && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT)); } -/** Create a new path or use an old suitable one (if pathnum is valid) +/** @brief Create a new path or use an old suitable one (if pathnum is valid) * or a random one from onion_paths. * * return -1 on failure @@ -359,10 +373,11 @@ static bool onion_node_timed_out(const Onion_Node *node, const Mono_Time *mono_t * TODO(irungentoo): Make this function better, it currently probably is * vulnerable to some attacks that could deanonimize us. */ +non_null() static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_paths, uint32_t pathnum, Onion_Path *path) { if (pathnum == UINT32_MAX) { - pathnum = random_range_u32(NUMBER_ONION_PATHS); + pathnum = random_range_u32(onion_c->rng, NUMBER_ONION_PATHS); } else { pathnum = pathnum % NUMBER_ONION_PATHS; } @@ -374,10 +389,10 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa return -1; } - int n = is_path_used(onion_c->mono_time, onion_paths, nodes); + const int n = is_path_used(onion_c->mono_time, onion_paths, nodes); if (n == -1) { - if (create_onion_path(onion_c->dht, &onion_paths->paths[pathnum], nodes) == -1) { + if (create_onion_path(onion_c->rng, onion_c->dht, &onion_paths->paths[pathnum], nodes) == -1) { return -1; } @@ -385,7 +400,7 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa onion_paths->last_path_success[pathnum] = onion_paths->path_creation_time[pathnum]; onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2; - uint32_t path_num = random_u32(); + uint32_t path_num = random_u32(onion_c->rng); path_num /= NUMBER_ONION_PATHS; path_num *= NUMBER_ONION_PATHS; path_num += pathnum; @@ -406,18 +421,18 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa } /** Does path with path_num exist. */ +non_null() static bool path_exists(const Mono_Time *mono_time, const Onion_Client_Paths *onion_paths, uint32_t path_num) { if (path_timed_out(mono_time, onion_paths, path_num)) { - return 0; + return false; } return onion_paths->paths[path_num % NUMBER_ONION_PATHS].path_num == path_num; } -/** Set path timeouts, return the path number. - * - */ +/** Set path timeouts, return the path number. */ +non_null() static uint32_t set_path_timeouts(Onion_Client *onion_c, uint32_t num, uint32_t path_num) { if (num > onion_c->num_friends) { @@ -450,17 +465,18 @@ static uint32_t set_path_timeouts(Onion_Client *onion_c, uint32_t num, uint32_t return -1; } -/** Function to send onion packet via TCP and UDP. +/** @brief Function to send onion packet via TCP and UDP. * * return -1 on failure. * return 0 on success. */ +non_null() static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Path *path, const IP_Port *dest, const uint8_t *data, uint16_t length) { if (net_family_is_ipv4(path->ip_port1.ip.family) || net_family_is_ipv6(path->ip_port1.ip.family)) { uint8_t packet[ONION_MAX_PACKET_SIZE]; - int len = create_onion_packet(packet, sizeof(packet), path, dest, data, length); + const int len = create_onion_packet(onion_c->rng, packet, sizeof(packet), path, dest, data, length); if (len == -1) { return -1; @@ -473,21 +489,23 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa return 0; } - if (net_family_is_tcp_family(path->ip_port1.ip.family)) { + unsigned int tcp_connections_number; + + if (ip_port_to_tcp_connections_number(&path->ip_port1, &tcp_connections_number)) { uint8_t packet[ONION_MAX_PACKET_SIZE]; - int len = create_onion_packet_tcp(packet, sizeof(packet), path, dest, data, length); + const int len = create_onion_packet_tcp(onion_c->rng, packet, sizeof(packet), path, dest, data, length); if (len == -1) { return -1; } - return send_tcp_onion_request(onion_c->c, path->ip_port1.ip.ip.v4.uint32, packet, len); + return send_tcp_onion_request(onion_c->c, tcp_connections_number, packet, len); } return -1; } -/** Creates a sendback for use in an announce request. +/** @brief Creates a sendback for use in an announce request. * * num is 0 if we used our secret public key for the announce * num is 1 + friendnum if we use a temporary one. @@ -502,6 +520,7 @@ static int send_onion_packet_tcp_udp(const Onion_Client *onion_c, const Onion_Pa * return 0 on success * */ +non_null() static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, const IP_Port *ip_port, uint32_t path_num, uint64_t *sendback) { @@ -510,7 +529,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ memcpy(data + sizeof(uint32_t), public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE, ip_port, sizeof(IP_Port)); memcpy(data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + sizeof(IP_Port), &path_num, sizeof(uint32_t)); - *sendback = ping_array_add(onion_c->announce_ping_array, onion_c->mono_time, data, sizeof(data)); + *sendback = ping_array_add(onion_c->announce_ping_array, onion_c->mono_time, onion_c->rng, data, sizeof(data)); if (*sendback == 0) { return -1; @@ -519,7 +538,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ return 0; } -/** Checks if the sendback is valid and returns the public key contained in it in ret_pubkey and the +/** @brief Checks if the sendback is valid and returns the public key contained in it in ret_pubkey and the * ip contained in it in ret_ip_port * * sendback is the sendback ONION_ANNOUNCE_SENDBACK_DATA_LENGTH big @@ -529,6 +548,7 @@ static int new_sendback(Onion_Client *onion_c, uint32_t num, const uint8_t *publ * return -1 on failure * return num (see new_sendback(...)) on success */ +non_null() static uint32_t check_sendback(Onion_Client *onion_c, const uint8_t *sendback, uint8_t *ret_pubkey, IP_Port *ret_ip_port, uint32_t *path_num) { @@ -549,6 +569,7 @@ static uint32_t check_sendback(Onion_Client *onion_c, const uint8_t *sendback, u return num; } +non_null(1, 3, 4) nullable(5) static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, const IP_Port *dest, const uint8_t *dest_pubkey, const uint8_t *ping_id, uint32_t pathnum) { @@ -583,13 +604,15 @@ static int client_send_announce_request(Onion_Client *onion_c, uint32_t num, con int len; if (num == 0) { - len = create_announce_request(request, sizeof(request), dest_pubkey, nc_get_self_public_key(onion_c->c), - nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c), - onion_c->temp_public_key, sendback); + len = create_announce_request( + onion_c->rng, request, sizeof(request), dest_pubkey, nc_get_self_public_key(onion_c->c), + nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c), + onion_c->temp_public_key, sendback); } else { - len = create_announce_request(request, sizeof(request), dest_pubkey, onion_c->friends_list[num - 1].temp_public_key, - onion_c->friends_list[num - 1].temp_secret_key, ping_id, - onion_c->friends_list[num - 1].real_public_key, zero_ping_id, sendback); + len = create_announce_request( + onion_c->rng, request, sizeof(request), dest_pubkey, onion_c->friends_list[num - 1].temp_public_key, + onion_c->friends_list[num - 1].temp_secret_key, ping_id, + onion_c->friends_list[num - 1].real_public_key, zero_ping_id, sendback); } if (len == -1) { @@ -605,6 +628,7 @@ typedef struct Onion_Client_Cmp_Data { Onion_Node entry; } Onion_Client_Cmp_Data; +non_null() static int onion_client_cmp_entry(const void *a, const void *b) { const Onion_Client_Cmp_Data *cmp1 = (const Onion_Client_Cmp_Data *)a; @@ -613,8 +637,8 @@ static int onion_client_cmp_entry(const void *a, const void *b) const Onion_Node entry2 = cmp2->entry; const uint8_t *cmp_public_key = cmp1->base_public_key; - const int t1 = onion_node_timed_out(&entry1, cmp1->mono_time); - const int t2 = onion_node_timed_out(&entry2, cmp2->mono_time); + const bool t1 = onion_node_timed_out(&entry1, cmp1->mono_time); + const bool t2 = onion_node_timed_out(&entry2, cmp2->mono_time); if (t1 && t2) { return 0; @@ -628,25 +652,30 @@ static int onion_client_cmp_entry(const void *a, const void *b) return 1; } - const int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); + const int closest = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); - if (close == 1) { + if (closest == 1) { return 1; } - if (close == 2) { + if (closest == 2) { return -1; } return 0; } +non_null() static void sort_onion_node_list(Onion_Node *list, unsigned int length, const Mono_Time *mono_time, const uint8_t *comp_public_key) { // Pass comp_public_key to qsort with each Client_data entry, so the // comparison function can use it as the base of comparison. - VLA(Onion_Client_Cmp_Data, cmp_list, length); + Onion_Client_Cmp_Data *cmp_list = (Onion_Client_Cmp_Data *)calloc(length, sizeof(Onion_Client_Cmp_Data)); + + if (cmp_list == nullptr) { + return; + } for (uint32_t i = 0; i < length; ++i) { cmp_list[i].mono_time = mono_time; @@ -659,8 +688,11 @@ static void sort_onion_node_list(Onion_Node *list, unsigned int length, const Mo for (uint32_t i = 0; i < length; ++i) { list[i] = cmp_list[i].entry; } + + free(cmp_list); } +non_null() static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, const IP_Port *ip_port, uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_used) { @@ -668,16 +700,16 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t return -1; } - Onion_Node *list_nodes = nullptr; + Onion_Node *node_list = nullptr; const uint8_t *reference_id = nullptr; unsigned int list_length; if (num == 0) { - list_nodes = onion_c->clients_announce_list; + node_list = onion_c->clients_announce_list; reference_id = nc_get_self_public_key(onion_c->c); list_length = MAX_ONION_CLIENTS_ANNOUNCE; - if (is_stored == 1 && public_key_cmp(pingid_or_key, onion_c->temp_public_key) != 0) { + if (is_stored == 1 && !pk_equal(pingid_or_key, onion_c->temp_public_key)) { is_stored = 0; } } else { @@ -685,29 +717,25 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t return -1; } - if (is_stored == 1) { - onion_c->friends_list[num - 1].last_seen = mono_time_get(onion_c->mono_time); - } - - list_nodes = onion_c->friends_list[num - 1].clients_list; + node_list = onion_c->friends_list[num - 1].clients_list; reference_id = onion_c->friends_list[num - 1].real_public_key; list_length = MAX_ONION_CLIENTS; } - sort_onion_node_list(list_nodes, list_length, onion_c->mono_time, reference_id); + sort_onion_node_list(node_list, list_length, onion_c->mono_time, reference_id); int index = -1; - int stored = 0; + bool stored = false; - if (onion_node_timed_out(&list_nodes[0], onion_c->mono_time) - || id_closest(reference_id, list_nodes[0].public_key, public_key) == 2) { + if (onion_node_timed_out(&node_list[0], onion_c->mono_time) + || id_closest(reference_id, node_list[0].public_key, public_key) == 2) { index = 0; } for (unsigned int i = 0; i < list_length; ++i) { - if (public_key_cmp(list_nodes[i].public_key, public_key) == 0) { + if (pk_equal(node_list[i].public_key, public_key)) { index = i; - stored = 1; + stored = true; break; } } @@ -716,38 +744,39 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t return 0; } - memcpy(list_nodes[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - list_nodes[index].ip_port = *ip_port; + memcpy(node_list[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); + node_list[index].ip_port = *ip_port; // TODO(irungentoo): remove this and find a better source of nodes to use for paths. onion_add_path_node(onion_c, ip_port, public_key); if (is_stored == 1) { - memcpy(list_nodes[index].data_public_key, pingid_or_key, CRYPTO_PUBLIC_KEY_SIZE); + memcpy(node_list[index].data_public_key, pingid_or_key, CRYPTO_PUBLIC_KEY_SIZE); } else { - memcpy(list_nodes[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE); + memcpy(node_list[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE); } - list_nodes[index].is_stored = is_stored; - list_nodes[index].timestamp = mono_time_get(onion_c->mono_time); - list_nodes[index].unsuccessful_pings = 0; + node_list[index].is_stored = is_stored; + node_list[index].timestamp = mono_time_get(onion_c->mono_time); + node_list[index].pings_since_last_response = 0; if (!stored) { - list_nodes[index].last_pinged = 0; - list_nodes[index].added_time = mono_time_get(onion_c->mono_time); + node_list[index].last_pinged = 0; + node_list[index].added_time = mono_time_get(onion_c->mono_time); } - list_nodes[index].path_used = path_used; + node_list[index].path_used = path_used; return 0; } -static int good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, uint8_t *last_pinged_index, - const uint8_t *public_key) +non_null() +static bool good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, uint8_t *last_pinged_index, + const uint8_t *public_key) { for (unsigned int i = 0; i < MAX_STORED_PINGED_NODES; ++i) { if (!mono_time_is_timeout(mono_time, last_pinged[i].timestamp, MIN_NODE_PING_TIME)) { - if (public_key_cmp(last_pinged[i].public_key, public_key) == 0) { - return 0; + if (pk_equal(last_pinged[i].public_key, public_key)) { + return false; } } } @@ -755,9 +784,10 @@ static int good_to_ping(const Mono_Time *mono_time, Last_Pinged *last_pinged, ui memcpy(last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].timestamp = mono_time_get(mono_time); ++*last_pinged_index; - return 1; + return true; } +non_null() static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_format *nodes, uint16_t num_nodes, const IP_Port *source) { @@ -769,7 +799,7 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for return 0; } - const Onion_Node *list_nodes = nullptr; + const Onion_Node *node_list = nullptr; const uint8_t *reference_id = nullptr; unsigned int list_length; @@ -777,13 +807,13 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for uint8_t *last_pinged_index = nullptr; if (num == 0) { - list_nodes = onion_c->clients_announce_list; + node_list = onion_c->clients_announce_list; reference_id = nc_get_self_public_key(onion_c->c); list_length = MAX_ONION_CLIENTS_ANNOUNCE; last_pinged = onion_c->last_pinged; last_pinged_index = &onion_c->last_pinged_index; } else { - list_nodes = onion_c->friends_list[num - 1].clients_list; + node_list = onion_c->friends_list[num - 1].clients_list; reference_id = onion_c->friends_list[num - 1].real_public_key; list_length = MAX_ONION_CLIENTS; last_pinged = onion_c->friends_list[num - 1].last_pinged; @@ -799,15 +829,15 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for } } - if (onion_node_timed_out(&list_nodes[0], onion_c->mono_time) - || id_closest(reference_id, list_nodes[0].public_key, nodes[i].public_key) == 2 - || onion_node_timed_out(&list_nodes[1], onion_c->mono_time) - || id_closest(reference_id, list_nodes[1].public_key, nodes[i].public_key) == 2) { + if (onion_node_timed_out(&node_list[0], onion_c->mono_time) + || id_closest(reference_id, node_list[0].public_key, nodes[i].public_key) == 2 + || onion_node_timed_out(&node_list[1], onion_c->mono_time) + || id_closest(reference_id, node_list[1].public_key, nodes[i].public_key) == 2) { uint32_t j; /* check if node is already in list. */ for (j = 0; j < list_length; ++j) { - if (public_key_cmp(list_nodes[j].public_key, nodes[i].public_key) == 0) { + if (pk_equal(node_list[j].public_key, nodes[i].public_key)) { break; } } @@ -821,6 +851,7 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for return 0; } +non_null() static int handle_announce_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -830,12 +861,12 @@ static int handle_announce_response(void *object, const IP_Port *source, const u return 1; } - uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; + const uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; IP_Port ip_port; uint32_t path_num; - uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); + const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); if (num > onion_c->num_friends) { return 1; @@ -850,7 +881,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE, length - (1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE), plain); } else { - if (onion_c->friends_list[num - 1].status == 0) { + if (!onion_c->friends_list[num - 1].is_valid) { return 1; } @@ -864,7 +895,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u return 1; } - uint32_t path_used = set_path_timeouts(onion_c, num, path_num); + const uint32_t path_used = set_path_timeouts(onion_c, num, path_num); if (client_add_to_list(onion_c, num, public_key, &ip_port, plain[0], plain + 1, path_used) == -1) { return 1; @@ -872,7 +903,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u if (len_nodes != 0) { Node_format nodes[MAX_SENT_NODES]; - int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, 0); + const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, false); if (num_nodes <= 0) { return 1; @@ -890,6 +921,7 @@ static int handle_announce_response(void *object, const IP_Port *source, const u #define DATA_IN_RESPONSE_MIN_SIZE ONION_DATA_IN_RESPONSE_MIN_SIZE +non_null() static int handle_data_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -921,7 +953,7 @@ static int handle_data_response(void *object, const IP_Port *source, const uint8 return 1; } - if (!onion_c->onion_data_handlers[plain[0]].function) { + if (onion_c->onion_data_handlers[plain[0]].function == nullptr) { return 1; } @@ -931,6 +963,7 @@ static int handle_data_response(void *object, const IP_Port *source, const uint8 #define DHTPK_DATA_MIN_LENGTH (1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE) #define DHTPK_DATA_MAX_LENGTH (DHTPK_DATA_MIN_LENGTH + sizeof(Node_format)*MAX_SENT_NODES) +non_null(1, 2, 3) nullable(5) static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t length, void *userdata) { @@ -944,7 +977,7 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con return 1; } - int friend_num = onion_friend_num(onion_c, source_pubkey); + const int friend_num = onion_friend_num(onion_c, source_pubkey); if (friend_num == -1) { return 1; @@ -959,20 +992,19 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con onion_c->friends_list[friend_num].last_noreplay = no_replay; - if (onion_c->friends_list[friend_num].dht_pk_callback) { + if (onion_c->friends_list[friend_num].dht_pk_callback != nullptr) { onion_c->friends_list[friend_num].dht_pk_callback(onion_c->friends_list[friend_num].dht_pk_callback_object, onion_c->friends_list[friend_num].dht_pk_callback_number, data + 1 + sizeof(uint64_t), userdata); } onion_set_friend_DHT_pubkey(onion_c, friend_num, data + 1 + sizeof(uint64_t)); - onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time); - uint16_t len_nodes = length - DHTPK_DATA_MIN_LENGTH; + const uint16_t len_nodes = length - DHTPK_DATA_MIN_LENGTH; if (len_nodes != 0) { Node_format nodes[MAX_SENT_NODES]; - int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, data + 1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE, - len_nodes, 1); + const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, data + 1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE, + len_nodes, true); if (num_nodes <= 0) { return 1; @@ -984,9 +1016,9 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con if (net_family_is_ipv4(family) || net_family_is_ipv6(family)) { dht_getnodes(onion_c->dht, &nodes[i].ip_port, nodes[i].public_key, onion_c->friends_list[friend_num].dht_public_key); } else if (net_family_is_tcp_ipv4(family) || net_family_is_tcp_ipv6(family)) { - if (onion_c->friends_list[friend_num].tcp_relay_node_callback) { + if (onion_c->friends_list[friend_num].tcp_relay_node_callback != nullptr) { void *obj = onion_c->friends_list[friend_num].tcp_relay_node_callback_object; - uint32_t number = onion_c->friends_list[friend_num].tcp_relay_node_callback_number; + const uint32_t number = onion_c->friends_list[friend_num].tcp_relay_node_callback_number; onion_c->friends_list[friend_num].tcp_relay_node_callback(obj, number, &nodes[i].ip_port, nodes[i].public_key); } } @@ -996,16 +1028,17 @@ static int handle_dhtpk_announce(void *object, const uint8_t *source_pubkey, con return 0; } +non_null() static int handle_tcp_onion(void *object, const uint8_t *data, uint16_t length, void *userdata) { if (length == 0) { return 1; } - IP_Port ip_port = {0}; - ip_port.ip.family = net_family_tcp_family; + IP_Port ip_port = {{{0}}}; + ip_port.ip.family = net_family_tcp_server(); - if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE) { + if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE_OLD) { return handle_announce_response(object, &ip_port, data, length, userdata); } @@ -1016,7 +1049,7 @@ static int handle_tcp_onion(void *object, const uint8_t *data, uint16_t length, return 1; } -/** Send data of length length to friendnum. +/** @brief Send data of length length to friendnum. * Maximum length of data is ONION_CLIENT_MAX_DATA_SIZE. * This data will be received by the friend using the Onion_Data_Handlers callbacks. * @@ -1042,16 +1075,16 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, unsigned int good_nodes[MAX_ONION_CLIENTS]; unsigned int num_good = 0; unsigned int num_nodes = 0; - const Onion_Node *list_nodes = onion_c->friends_list[friend_num].clients_list; + const Onion_Node *node_list = onion_c->friends_list[friend_num].clients_list; for (unsigned int i = 0; i < MAX_ONION_CLIENTS; ++i) { - if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) { + if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { continue; } ++num_nodes; - if (list_nodes[i].is_stored) { + if (node_list[i].is_stored != 0) { good_nodes[num_good] = i; ++num_good; } @@ -1062,7 +1095,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, } uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(onion_c->rng, nonce); VLA(uint8_t, packet, DATA_IN_RESPONSE_MIN_SIZE + length); memcpy(packet, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE); @@ -1084,14 +1117,15 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, } uint8_t o_packet[ONION_MAX_PACKET_SIZE]; - len = create_data_request(o_packet, sizeof(o_packet), onion_c->friends_list[friend_num].real_public_key, - list_nodes[good_nodes[i]].data_public_key, nonce, packet, SIZEOF_VLA(packet)); + len = create_data_request( + onion_c->rng, o_packet, sizeof(o_packet), onion_c->friends_list[friend_num].real_public_key, + node_list[good_nodes[i]].data_public_key, nonce, packet, SIZEOF_VLA(packet)); if (len == -1) { continue; } - if (send_onion_packet_tcp_udp(onion_c, &path, &list_nodes[good_nodes[i]].ip_port, o_packet, len) == 0) { + if (send_onion_packet_tcp_udp(onion_c, &path, &node_list[good_nodes[i]].ip_port, o_packet, len) == 0) { ++good; } } @@ -1099,13 +1133,14 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, return good; } -/** Try to send the dht public key via the DHT instead of onion +/** @brief Try to send the dht public key via the DHT instead of onion * * Even if this function succeeds, the friend might not receive any data. * * return the number of packets sent on success * return -1 on failure. */ +non_null() static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uint8_t *data, uint16_t length) { if ((uint32_t)friend_num >= onion_c->num_friends) { @@ -1117,7 +1152,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin } uint8_t nonce[CRYPTO_NONCE_SIZE]; - random_nonce(nonce); + random_nonce(onion_c->rng, nonce); VLA(uint8_t, temp, DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE + length); memcpy(temp, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE); @@ -1131,8 +1166,9 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin } uint8_t packet_data[MAX_CRYPTO_REQUEST_SIZE]; - len = create_request(dht_get_self_public_key(onion_c->dht), dht_get_self_secret_key(onion_c->dht), packet_data, - onion_c->friends_list[friend_num].dht_public_key, temp, SIZEOF_VLA(temp), CRYPTO_PACKET_DHTPK); + len = create_request( + onion_c->rng, dht_get_self_public_key(onion_c->dht), dht_get_self_secret_key(onion_c->dht), packet_data, + onion_c->friends_list[friend_num].dht_public_key, temp, SIZEOF_VLA(temp), CRYPTO_PACKET_DHTPK); assert(len <= UINT16_MAX); const Packet packet = {packet_data, (uint16_t)len}; @@ -1143,6 +1179,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin return route_to_friend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, &packet); } +non_null() static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t *source_pubkey, const uint8_t *packet, uint16_t length, void *userdata) { @@ -1157,22 +1194,22 @@ static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t * } uint8_t plain[DHTPK_DATA_MAX_LENGTH]; - int len = decrypt_data(packet, nc_get_self_secret_key(onion_c->c), - packet + CRYPTO_PUBLIC_KEY_SIZE, - packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, - length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain); + const int len = decrypt_data(packet, nc_get_self_secret_key(onion_c->c), + packet + CRYPTO_PUBLIC_KEY_SIZE, + packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, + length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain); if (len != length - (DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE)) { return 1; } - if (public_key_cmp(source_pubkey, plain + 1 + sizeof(uint64_t)) != 0) { + if (!pk_equal(source_pubkey, plain + 1 + sizeof(uint64_t))) { return 1; } return handle_dhtpk_announce(onion_c, packet, plain, len, userdata); } -/** Send the packets to tell our friends what our DHT public key is. +/** @brief Send the packets to tell our friends what our DHT public key is. * * if onion_dht_both is 0, use only the onion to send the packet. * if it is 1, use only the dht. @@ -1181,6 +1218,7 @@ static int handle_dht_dhtpk(void *object, const IP_Port *source, const uint8_t * * return the number of packets sent on success * return -1 on failure. */ +non_null() static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8_t onion_dht_both) { if (friend_num >= onion_c->num_friends) { @@ -1193,14 +1231,13 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8 net_pack_u64(data + 1, no_replay); memcpy(data + 1 + sizeof(uint64_t), dht_get_self_public_key(onion_c->dht), CRYPTO_PUBLIC_KEY_SIZE); Node_format nodes[MAX_SENT_NODES]; - uint16_t num_relays = copy_connected_tcp_relays(onion_c->c, nodes, MAX_SENT_NODES / 2); + const uint16_t num_relays = copy_connected_tcp_relays(onion_c->c, nodes, MAX_SENT_NODES / 2); uint16_t num_nodes = closelist_nodes(onion_c->dht, &nodes[num_relays], MAX_SENT_NODES - num_relays); num_nodes += num_relays; int nodes_len = 0; if (num_nodes != 0) { - nodes_len = pack_nodes(data + DHTPK_DATA_MIN_LENGTH, DHTPK_DATA_MAX_LENGTH - DHTPK_DATA_MIN_LENGTH, nodes, - num_nodes); + nodes_len = pack_nodes(onion_c->logger, data + DHTPK_DATA_MIN_LENGTH, DHTPK_DATA_MAX_LENGTH - DHTPK_DATA_MIN_LENGTH, nodes, num_nodes); if (nodes_len <= 0) { return -1; @@ -1229,7 +1266,7 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8 return num1 + num2; } -/** Get the friend_num of a friend. +/** @brief Get the friend_num of a friend. * * return -1 on failure. * return friend number on success. @@ -1237,11 +1274,11 @@ static int send_dhtpk_announce(Onion_Client *onion_c, uint16_t friend_num, uint8 int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key) { for (unsigned int i = 0; i < onion_c->num_friends; ++i) { - if (onion_c->friends_list[i].status == 0) { + if (!onion_c->friends_list[i].is_valid) { continue; } - if (public_key_cmp(public_key, onion_c->friends_list[i].real_public_key) == 0) { + if (pk_equal(public_key, onion_c->friends_list[i].real_public_key)) { return i; } } @@ -1249,11 +1286,12 @@ int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key) return -1; } -/** Set the size of the friend list to num. +/** @brief Set the size of the friend list to num. * - * return -1 if realloc fails. - * return 0 if it succeeds. + * @retval -1 if realloc fails. + * @retval 0 if it succeeds. */ +non_null() static int realloc_onion_friends(Onion_Client *onion_c, uint32_t num) { if (num == 0) { @@ -1272,14 +1310,14 @@ static int realloc_onion_friends(Onion_Client *onion_c, uint32_t num) return 0; } -/** Add a friend who we want to connect to. +/** @brief Add a friend who we want to connect to. * * return -1 on failure. * return the friend number on success or if the friend was already added. */ int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key) { - int num = onion_friend_num(onion_c, public_key); + const int num = onion_friend_num(onion_c, public_key); if (num != -1) { return num; @@ -1288,7 +1326,7 @@ int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key) unsigned int index = -1; for (unsigned int i = 0; i < onion_c->num_friends; ++i) { - if (onion_c->friends_list[i].status == 0) { + if (!onion_c->friends_list[i].is_valid) { index = i; break; } @@ -1300,17 +1338,17 @@ int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key) } index = onion_c->num_friends; - memset(&onion_c->friends_list[onion_c->num_friends], 0, sizeof(Onion_Friend)); + onion_c->friends_list[onion_c->num_friends] = empty_onion_friend; ++onion_c->num_friends; } - onion_c->friends_list[index].status = 1; + onion_c->friends_list[index].is_valid = true; memcpy(onion_c->friends_list[index].real_public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - crypto_new_keypair(onion_c->friends_list[index].temp_public_key, onion_c->friends_list[index].temp_secret_key); + crypto_new_keypair(onion_c->rng, onion_c->friends_list[index].temp_public_key, onion_c->friends_list[index].temp_secret_key); return index; } -/** Delete a friend. +/** @brief Delete a friend. * * return -1 on failure. * return the deleted friend number on success. @@ -1333,7 +1371,7 @@ int onion_delfriend(Onion_Client *onion_c, int friend_num) unsigned int i; for (i = onion_c->num_friends; i != 0; --i) { - if (onion_c->friends_list[i - 1].status != 0) { + if (onion_c->friends_list[i - 1].is_valid) { break; } } @@ -1346,8 +1384,8 @@ int onion_delfriend(Onion_Client *onion_c, int friend_num) return friend_num; } -/** Set the function for this friend that will be callbacked with object and number - * when that friends gives us one of the TCP relays he is connected to. +/** @brief Set the function for this friend that will be callbacked with object and number + * when that friend gives us one of the TCP relays they are connected to. * * object and number will be passed as argument to this function. * @@ -1367,8 +1405,8 @@ int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, return 0; } -/** Set the function for this friend that will be callbacked with object and number - * when that friend gives us his DHT temporary public key. +/** @brief Set the function for this friend that will be callbacked with object and number + * when that friend gives us their DHT temporary public key. * * object and number will be passed as argument to this function. * @@ -1388,7 +1426,7 @@ int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, return 0; } -/** Set a friend's DHT public key. +/** @brief Set a friend's DHT public key. * * return -1 on failure. * return 0 on success. @@ -1399,24 +1437,23 @@ int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uin return -1; } - if (onion_c->friends_list[friend_num].status == 0) { + if (!onion_c->friends_list[friend_num].is_valid) { return -1; } if (onion_c->friends_list[friend_num].know_dht_public_key) { - if (public_key_cmp(dht_key, onion_c->friends_list[friend_num].dht_public_key) == 0) { + if (pk_equal(dht_key, onion_c->friends_list[friend_num].dht_public_key)) { return -1; } } - onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time); - onion_c->friends_list[friend_num].know_dht_public_key = 1; + onion_c->friends_list[friend_num].know_dht_public_key = true; memcpy(onion_c->friends_list[friend_num].dht_public_key, dht_key, CRYPTO_PUBLIC_KEY_SIZE); return 0; } -/** Copy friends DHT public key into dht_key. +/** @brief Copy friends DHT public key into dht_key. * * return 0 on failure (no key copied). * return 1 on success (key copied). @@ -1427,7 +1464,7 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_ return 0; } - if (onion_c->friends_list[friend_num].status == 0) { + if (!onion_c->friends_list[friend_num].is_valid) { return 0; } @@ -1439,12 +1476,11 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_ return 1; } -/** Get the ip of friend friendnum and put it in ip_port - * - * return -1, -- if public_key does NOT refer to a friend - * return 0, -- if public_key refers to a friend and we failed to find the friend (yet) - * return 1, ip if public_key refers to a friend and we found him +/** @brief Get the ip of friend friendnum and put it in ip_port * + * @retval -1 if public_key does NOT refer to a friend + * @retval 0 if public_key refers to a friend and we failed to find the friend (yet) + * @retval 1 if public_key refers to a friend and we found them */ int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_port) { @@ -1458,25 +1494,20 @@ int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_p } -/** Set if friend is online or not. - * NOTE: This function is there and should be used so that we don't send useless packets to the friend if he is online. +/** @brief Set if friend is online or not. * - * is_online 1 means friend is online. - * is_online 0 means friend is offline + * NOTE: This function is there and should be used so that we don't send + * useless packets to the friend if they are online. * * return -1 on failure. * return 0 on success. */ -int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_online) +int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_online) { if ((uint32_t)friend_num >= onion_c->num_friends) { return -1; } - if (is_online == 0 && onion_c->friends_list[friend_num].is_online == 1) { - onion_c->friends_list[friend_num].last_seen = mono_time_get(onion_c->mono_time); - } - onion_c->friends_list[friend_num].is_online = is_online; /* This should prevent some clock related issues */ @@ -1488,148 +1519,149 @@ int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_on return 0; } +non_null() static void populate_path_nodes(Onion_Client *onion_c) { - Node_format nodes_list[MAX_FRIEND_CLIENTS]; + Node_format node_list[MAX_FRIEND_CLIENTS]; - unsigned int num_nodes = randfriends_nodes(onion_c->dht, nodes_list, MAX_FRIEND_CLIENTS); + const unsigned int num_nodes = randfriends_nodes(onion_c->dht, node_list, MAX_FRIEND_CLIENTS); for (unsigned int i = 0; i < num_nodes; ++i) { - onion_add_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key); + onion_add_path_node(onion_c, &node_list[i].ip_port, node_list[i].public_key); } } -static void populate_path_nodes_tcp(Onion_Client *onion_c) -{ - Node_format nodes_list[MAX_SENT_NODES]; - - unsigned int num_nodes = copy_connected_tcp_relays(onion_c->c, nodes_list, MAX_SENT_NODES); +/* How often we ping new friends per node */ +#define ANNOUNCE_FRIEND_NEW_INTERVAL 3 - for (unsigned int i = 0; i < num_nodes; ++i) { - onion_add_bs_path_node(onion_c, &nodes_list[i].ip_port, nodes_list[i].public_key); - } -} +/* How long we consider a friend new based on the value of their run_count */ +#define ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING 5 -#define ANNOUNCE_FRIEND (ONION_NODE_PING_INTERVAL * 6) -#define ANNOUNCE_FRIEND_BEGINNING 3 +/* How often we try to re-populate the nodes lists if we don't meet a minimum threshhold of nodes */ +#define ANNOUNCE_POPULATE_TIMEOUT (60 * 10) -#define RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING 17 +/* The max time between lookup requests for a friend per node */ +#define ANNOUNCE_FRIEND_MAX_INTERVAL (60 * 60) -#define ONION_FRIEND_BACKOFF_FACTOR 4 -#define ONION_FRIEND_MAX_PING_INTERVAL (5*60*MAX_ONION_CLIENTS) +/* Max exponent when calculating the announce request interval */ +#define MAX_RUN_COUNT_EXPONENT 12 +non_null() static void do_friend(Onion_Client *onion_c, uint16_t friendnum) { if (friendnum >= onion_c->num_friends) { return; } - if (onion_c->friends_list[friendnum].status == 0) { + Onion_Friend *o_friend = &onion_c->friends_list[friendnum]; + + if (!o_friend->is_valid) { return; } - unsigned int interval = ANNOUNCE_FRIEND; + uint32_t interval; + const uint64_t tm = mono_time_get(onion_c->mono_time); + const bool friend_is_new = o_friend->run_count <= ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING; - if (onion_c->friends_list[friendnum].run_count < RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING) { - interval = ANNOUNCE_FRIEND_BEGINNING; + if (!friend_is_new) { + // how often we ping a node for a friend depends on how many times we've already tried. + // the interval increases exponentially, as the longer a friend has been offline, the less + // likely the case is that they're online and failed to find us + const uint32_t c = 1 << min_u32(MAX_RUN_COUNT_EXPONENT, o_friend->run_count - 2); + interval = min_u32(c, ANNOUNCE_FRIEND_MAX_INTERVAL); } else { - if (onion_c->friends_list[friendnum].last_seen == 0) { - onion_c->friends_list[friendnum].last_seen = mono_time_get(onion_c->mono_time); - } + interval = ANNOUNCE_FRIEND_NEW_INTERVAL; + } + + if (o_friend->is_online) { + return; + } - uint64_t backoff_interval = (mono_time_get(onion_c->mono_time) - - onion_c->friends_list[friendnum].last_seen) - / ONION_FRIEND_BACKOFF_FACTOR; + assert(interval >= ANNOUNCE_FRIEND_NEW_INTERVAL); // an int overflow would be devastating - if (backoff_interval > ONION_FRIEND_MAX_PING_INTERVAL) { - backoff_interval = ONION_FRIEND_MAX_PING_INTERVAL; + /* send packets to friend telling them our DHT public key. */ + if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent, + ONION_DHTPK_SEND_INTERVAL)) { + if (send_dhtpk_announce(onion_c, friendnum, 0) >= 1) { + onion_c->friends_list[friendnum].last_dht_pk_onion_sent = tm; } + } - if (interval < backoff_interval) { - interval = backoff_interval; + if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_dht_sent, + DHT_DHTPK_SEND_INTERVAL)) { + if (send_dhtpk_announce(onion_c, friendnum, 1) >= 1) { + onion_c->friends_list[friendnum].last_dht_pk_dht_sent = tm; } } - if (!onion_c->friends_list[friendnum].is_online) { - unsigned int count = 0; - Onion_Node *list_nodes = onion_c->friends_list[friendnum].clients_list; + uint16_t count = 0; // number of alive path nodes - // ensure we get a response from some node roughly once per - // (interval / MAX_ONION_CLIENTS) - bool ping_random = true; + Onion_Node *node_list = o_friend->clients_list; - for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) { - if (!(mono_time_is_timeout(onion_c->mono_time, list_nodes[i].timestamp, interval / MAX_ONION_CLIENTS) - && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, ONION_NODE_PING_INTERVAL))) { - ping_random = false; - break; - } + for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) { + if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { + continue; } - for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) { - if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) { - continue; - } + ++count; + + // we don't want new nodes to be pinged immediately + if (node_list[i].last_pinged == 0) { + node_list[i].last_pinged = tm; + continue; + } - ++count; + // node hasn't responded in a while so we skip it + if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) { + continue; + } + // space requests out between nodes + if (!mono_time_is_timeout(onion_c->mono_time, o_friend->time_last_pinged, interval / (MAX_ONION_CLIENTS / 2))) { + continue; + } - if (list_nodes[i].last_pinged == 0) { - list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time); - continue; - } + if (!mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)) { + continue; + } - if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) { - continue; - } + if (client_send_announce_request(onion_c, friendnum + 1, &node_list[i].ip_port, + node_list[i].public_key, nullptr, -1) == 0) { + node_list[i].last_pinged = tm; + o_friend->time_last_pinged = tm; + ++node_list[i].pings_since_last_response; + ++o_friend->pings; - if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, interval) - || (ping_random && random_range_u32(MAX_ONION_CLIENTS - i) == 0)) { - if (client_send_announce_request(onion_c, friendnum + 1, &list_nodes[i].ip_port, - list_nodes[i].public_key, nullptr, -1) == 0) { - list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time); - ++list_nodes[i].unsuccessful_pings; - ping_random = false; - } + if (o_friend->pings % (MAX_ONION_CLIENTS / 2) == 0) { + ++o_friend->run_count; } } + } - if (count != MAX_ONION_CLIENTS) { - const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); - uint16_t n = num_nodes; + if (count == MAX_ONION_CLIENTS) { + if (!friend_is_new) { + o_friend->last_populated = tm; + } - if (num_nodes > (MAX_ONION_CLIENTS / 2)) { - n = (MAX_ONION_CLIENTS / 2); - } + return; + } - if (count <= random_range_u32(MAX_ONION_CLIENTS)) { - if (num_nodes != 0) { - for (unsigned int j = 0; j < n; ++j) { - const uint32_t num = random_range_u32(num_nodes); - client_send_announce_request(onion_c, friendnum + 1, &onion_c->path_nodes[num].ip_port, - onion_c->path_nodes[num].public_key, nullptr, -1); - } + // check if path nodes list for this friend needs to be repopulated + if (count <= MAX_ONION_CLIENTS / 2 + || mono_time_is_timeout(onion_c->mono_time, o_friend->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) { + const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); + const uint16_t n = min_u16(num_nodes, MAX_PATH_NODES / 4); - ++onion_c->friends_list[friendnum].run_count; - } - } - } else { - ++onion_c->friends_list[friendnum].run_count; + if (n == 0) { + return; } - /* send packets to friend telling them our DHT public key. */ - if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent, - ONION_DHTPK_SEND_INTERVAL)) { - if (send_dhtpk_announce(onion_c, friendnum, 0) >= 1) { - onion_c->friends_list[friendnum].last_dht_pk_onion_sent = mono_time_get(onion_c->mono_time); - } - } + o_friend->last_populated = tm; - if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_dht_sent, - DHT_DHTPK_SEND_INTERVAL)) { - if (send_dhtpk_announce(onion_c, friendnum, 1) >= 1) { - onion_c->friends_list[friendnum].last_dht_pk_dht_sent = mono_time_get(onion_c->mono_time); - } + for (uint16_t i = 0; i < n; ++i) { + const uint32_t num = random_range_u32(onion_c->rng, num_nodes); + client_send_announce_request(onion_c, friendnum + 1, &onion_c->path_nodes[num].ip_port, + onion_c->path_nodes[num].public_key, nullptr, -1); } } } @@ -1648,43 +1680,45 @@ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_ha #define TIME_TO_STABLE (ONION_NODE_PING_INTERVAL * 6) #define ANNOUNCE_INTERVAL_STABLE (ONION_NODE_PING_INTERVAL * 8) +non_null() static void do_announce(Onion_Client *onion_c) { unsigned int count = 0; - Onion_Node *list_nodes = onion_c->clients_announce_list; + Onion_Node *node_list = onion_c->clients_announce_list; for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { - if (onion_node_timed_out(&list_nodes[i], onion_c->mono_time)) { + if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { continue; } ++count; /* Don't announce ourselves the first time this is run to new peers */ - if (list_nodes[i].last_pinged == 0) { - list_nodes[i].last_pinged = 1; + if (node_list[i].last_pinged == 0) { + node_list[i].last_pinged = 1; continue; } - if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) { + if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) { continue; } unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED; - if (list_nodes[i].is_stored && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, list_nodes[i].path_used)) { + if (node_list[i].is_stored != 0 + && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, node_list[i].path_used)) { interval = ANNOUNCE_INTERVAL_ANNOUNCED; - uint32_t pathnum = list_nodes[i].path_used % NUMBER_ONION_PATHS; + const uint32_t pathnum = node_list[i].path_used % NUMBER_ONION_PATHS; /* A node/path is considered "stable", and can be pinged less * aggressively, if it has survived for at least TIME_TO_STABLE * and the latest packets sent to it are not timing out. */ - if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE) - && !(list_nodes[i].unsuccessful_pings > 0 - && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, ONION_NODE_TIMEOUT)) + if (mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE) + && !(node_list[i].pings_since_last_response > 0 + && mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, ONION_NODE_TIMEOUT)) && mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE) && !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0 && mono_time_is_timeout(onion_c->mono_time, onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) { @@ -1692,31 +1726,37 @@ static void do_announce(Onion_Client *onion_c) } } - if (mono_time_is_timeout(onion_c->mono_time, list_nodes[i].last_pinged, interval) - || (mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL) - && random_range_u32(MAX_ONION_CLIENTS_ANNOUNCE - i) == 0)) { - uint32_t path_to_use = list_nodes[i].path_used; + if (mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval) + || mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)) { + uint32_t path_to_use = node_list[i].path_used; - if (list_nodes[i].unsuccessful_pings == ONION_NODE_MAX_PINGS - 1 - && mono_time_is_timeout(onion_c->mono_time, list_nodes[i].added_time, TIME_TO_STABLE)) { + if (node_list[i].pings_since_last_response == ONION_NODE_MAX_PINGS - 1 + && mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)) { /* Last chance for a long-lived node - try a random path */ path_to_use = -1; } - if (client_send_announce_request(onion_c, 0, &list_nodes[i].ip_port, list_nodes[i].public_key, - list_nodes[i].ping_id, path_to_use) == 0) { - list_nodes[i].last_pinged = mono_time_get(onion_c->mono_time); - ++list_nodes[i].unsuccessful_pings; + if (client_send_announce_request(onion_c, 0, &node_list[i].ip_port, node_list[i].public_key, + node_list[i].ping_id, path_to_use) == 0) { + node_list[i].last_pinged = mono_time_get(onion_c->mono_time); + ++node_list[i].pings_since_last_response; onion_c->last_announce = mono_time_get(onion_c->mono_time); } } } - if (count != MAX_ONION_CLIENTS_ANNOUNCE) { + if (count == MAX_ONION_CLIENTS_ANNOUNCE) { + onion_c->last_populated = mono_time_get(onion_c->mono_time); + return; + } + + // check if list needs to be re-populated + if (count <= MAX_ONION_CLIENTS_ANNOUNCE / 2 + || mono_time_is_timeout(onion_c->mono_time, onion_c->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) { uint16_t num_nodes; const Node_format *path_nodes; - if (random_u08() % 2 == 0 || onion_c->path_nodes_index == 0) { + if (onion_c->path_nodes_index == 0) { num_nodes = min_u16(onion_c->path_nodes_index_bs, MAX_PATH_NODES); path_nodes = onion_c->path_nodes_bs; } else { @@ -1724,38 +1764,42 @@ static void do_announce(Onion_Client *onion_c) path_nodes = onion_c->path_nodes; } - if (count <= random_range_u32(MAX_ONION_CLIENTS_ANNOUNCE)) { - if (num_nodes != 0) { - for (unsigned int i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) { - const uint32_t num = random_range_u32(num_nodes); - client_send_announce_request(onion_c, 0, &path_nodes[num].ip_port, path_nodes[num].public_key, nullptr, -1); - } - } + if (num_nodes == 0) { + return; + } + + for (unsigned int i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) { + const uint32_t num = random_range_u32(onion_c->rng, num_nodes); + client_send_announce_request(onion_c, 0, &path_nodes[num].ip_port, path_nodes[num].public_key, nullptr, -1); } } } -/** return 0 if we are not connected to the network. - * return 1 if we are. +/** + * @retval false if we are not connected to the network. + * @retval true if we are. */ -static int onion_isconnected(const Onion_Client *onion_c) +non_null() +static bool onion_isconnected(Onion_Client *onion_c) { unsigned int num = 0; unsigned int announced = 0; if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_packet_recv, ONION_OFFLINE_TIMEOUT)) { - return 0; + onion_c->last_populated = 0; + return false; } if (onion_c->path_nodes_index == 0) { - return 0; + onion_c->last_populated = 0; + return false; } for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { if (!onion_node_timed_out(&onion_c->clients_announce_list[i], onion_c->mono_time)) { ++num; - if (onion_c->clients_announce_list[i].is_stored) { + if (onion_c->clients_announce_list[i].is_stored != 0) { ++announced; } } @@ -1769,32 +1813,43 @@ static int onion_isconnected(const Onion_Client *onion_c) /* Consider ourselves online if we are announced to half or more nodes * we are connected to */ - if (num && announced) { + if (num != 0 && announced != 0) { if ((num / 2) <= announced && (pnodes / 2) <= num) { - return 1; + return true; } } - return 0; + onion_c->last_populated = 0; + + return false; +} + +non_null() +static void reset_friend_run_counts(Onion_Client *onion_c) +{ + for (uint16_t i = 0; i < onion_c->num_friends; ++i) { + Onion_Friend *o_friend = &onion_c->friends_list[i]; + + if (o_friend->is_valid) { + o_friend->run_count = 0; + } + } } #define ONION_CONNECTION_SECONDS 3 +#define ONION_CONNECTED_TIMEOUT 10 -/** return 0 if we are not connected to the network. - * return 1 if we are connected with TCP only. - * return 2 if we are also connected with UDP. - */ -unsigned int onion_connection_status(const Onion_Client *onion_c) +Onion_Connection_Status onion_connection_status(const Onion_Client *onion_c) { if (onion_c->onion_connected >= ONION_CONNECTION_SECONDS) { if (onion_c->udp_connected) { - return 2; + return ONION_CONNECTION_STATUS_UDP; } - return 1; + return ONION_CONNECTION_STATUS_TCP; } - return 0; + return ONION_CONNECTION_STATUS_NONE; } void do_onion_client(Onion_Client *onion_c) @@ -1809,12 +1864,16 @@ void do_onion_client(Onion_Client *onion_c) } if (onion_isconnected(onion_c)) { + if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_time_connected, ONION_CONNECTED_TIMEOUT)) { + reset_friend_run_counts(onion_c); + } + + onion_c->last_time_connected = mono_time_get(onion_c->mono_time); + if (onion_c->onion_connected < ONION_CONNECTION_SECONDS * 2) { ++onion_c->onion_connected; } } else { - populate_path_nodes_tcp(onion_c); - if (onion_c->onion_connected != 0) { --onion_c->onion_connected; } @@ -1826,7 +1885,7 @@ void do_onion_client(Onion_Client *onion_c) set_tcp_onion_status(nc_get_tcp_c(onion_c->c), !onion_c->udp_connected); } - if (onion_connection_status(onion_c)) { + if (onion_connection_status(onion_c) != ONION_CONNECTION_STATUS_NONE) { for (unsigned i = 0; i < onion_c->num_friends; ++i) { do_friend(onion_c, i); } @@ -1839,7 +1898,7 @@ void do_onion_client(Onion_Client *onion_c) onion_c->last_run = mono_time_get(onion_c->mono_time); } -Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_Crypto *c) +Onion_Client *new_onion_client(const Logger *logger, const Random *rng, const Mono_Time *mono_time, Net_Crypto *c) { if (c == nullptr) { return nullptr; @@ -1860,12 +1919,13 @@ Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_C onion_c->mono_time = mono_time; onion_c->logger = logger; + onion_c->rng = rng; onion_c->dht = nc_get_dht(c); onion_c->net = dht_get_net(onion_c->dht); onion_c->c = c; - new_symmetric_key(onion_c->secret_symmetric_key); - crypto_new_keypair(onion_c->temp_public_key, onion_c->temp_secret_key); - networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c); + new_symmetric_key(rng, onion_c->secret_symmetric_key); + crypto_new_keypair(rng, onion_c->temp_public_key, onion_c->temp_secret_key); + networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, &handle_announce_response, onion_c); networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c); oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c); cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, &handle_dht_dhtpk, onion_c); @@ -1882,7 +1942,7 @@ void kill_onion_client(Onion_Client *onion_c) ping_array_kill(onion_c->announce_ping_array); realloc_onion_friends(onion_c, 0); - networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, nullptr, nullptr); + networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, nullptr, nullptr); networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, nullptr, nullptr); oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, nullptr, nullptr); cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, nullptr, nullptr); diff --git a/protocols/Tox/libtox/src/toxcore/onion_client.h b/protocols/Tox/libtox/src/toxcore/onion_client.h index c724e97344..2a30005c69 100644 --- a/protocols/Tox/libtox/src/toxcore/onion_client.h +++ b/protocols/Tox/libtox/src/toxcore/onion_client.h @@ -10,6 +10,8 @@ #ifndef C_TOXCORE_TOXCORE_ONION_CLIENT_H #define C_TOXCORE_TOXCORE_ONION_CLIENT_H +#include <stdbool.h> + #include "net_crypto.h" #include "onion_announce.h" #include "ping_array.h" @@ -25,8 +27,10 @@ #define NUMBER_ONION_PATHS 6 -/** The timeout the first time the path is added and - * then for all the next consecutive times */ +/** + * The timeout the first time the path is added and + * then for all the next consecutive times + */ #define ONION_PATH_FIRST_TIMEOUT 4 #define ONION_PATH_TIMEOUT 10 #define ONION_PATH_MAX_LIFETIME 1200 @@ -39,7 +43,8 @@ #define MAX_PATH_NODES 32 -/** If no announce response packets are received within this interval tox will +/** + * If no announce response packets are received within this interval tox will * be considered offline. We give time for a node to be pinged often enough * that it times out, which leads to the network being thoroughly tested as it * is replaced. @@ -52,107 +57,126 @@ typedef struct Onion_Client Onion_Client; +non_null() DHT *onion_get_dht(const Onion_Client *onion_c); +non_null() Net_Crypto *onion_get_net_crypto(const Onion_Client *onion_c); -/** Add a node to the path_nodes bootstrap array. +/** @brief Add a node to the path_nodes bootstrap array. + * + * If a node with the given public key was already in the bootstrap array, this function has no + * effect and returns successfully. There is currently no way to update the IP/port for a bootstrap + * node, so if it changes, the Onion_Client must be recreated. * - * return -1 on failure - * return 0 on success + * @param onion_c The onion client object. + * @param ip_port IP/port for the bootstrap node. + * @param public_key DHT public key for the bootstrap node. + * + * @retval false on failure + * @retval true on success */ -int onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key); +non_null() +bool onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key); -/** Put up to max_num nodes in nodes. +/** @brief Put up to max_num nodes in nodes. * * return the number of nodes. */ +non_null() uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num); -/** Get the friend_num of a friend. +/** @brief Get the friend_num of a friend. * * return -1 on failure. * return friend number on success. */ +non_null() int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key); -/** Add a friend who we want to connect to. +/** @brief Add a friend who we want to connect to. * * return -1 on failure. * return the friend number on success or if the friend was already added. */ +non_null() int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key); -/** Delete a friend. +/** @brief Delete a friend. * * return -1 on failure. * return the deleted friend number on success. */ +non_null() int onion_delfriend(Onion_Client *onion_c, int friend_num); -/** Set if friend is online or not. - * NOTE: This function is there and should be used so that we don't send useless packets to the friend if he is online. +/** @brief Set if friend is online or not. * - * is_online 1 means friend is online. - * is_online 0 means friend is offline + * NOTE: This function is there and should be used so that we don't send + * useless packets to the friend if they are online. * * return -1 on failure. * return 0 on success. */ -int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_online); +non_null() +int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_online); -/** Get the ip of friend friendnum and put it in ip_port - * - * return -1, -- if public_key does NOT refer to a friend - * return 0, -- if public_key refers to a friend and we failed to find the friend (yet) - * return 1, ip if public_key refers to a friend and we found him +/** @brief Get the ip of friend friendnum and put it in ip_port * + * @retval -1 if public_key does NOT refer to a friend + * @retval 0 if public_key refers to a friend and we failed to find the friend (yet) + * @retval 1 if public_key refers to a friend and we found them */ +non_null() int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_port); typedef int recv_tcp_relay_cb(void *object, uint32_t number, const IP_Port *ip_port, const uint8_t *public_key); -/** Set the function for this friend that will be callbacked with object and number - * when that friends gives us one of the TCP relays he is connected to. +/** @brief Set the function for this friend that will be callbacked with object and number + * when that friend gives us one of the TCP relays they are connected to. * * object and number will be passed as argument to this function. * * return -1 on failure. * return 0 on success. */ +non_null() int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, recv_tcp_relay_cb *callback, void *object, uint32_t number); typedef void onion_dht_pk_cb(void *data, int32_t number, const uint8_t *dht_public_key, void *userdata); -/** Set the function for this friend that will be callbacked with object and number - * when that friend gives us his DHT temporary public key. +/** @brief Set the function for this friend that will be callbacked with object and number + * when that friend gives us their DHT temporary public key. * * object and number will be passed as argument to this function. * * return -1 on failure. * return 0 on success. */ +non_null() int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, onion_dht_pk_cb *function, void *object, uint32_t number); -/** Set a friend's DHT public key. +/** @brief Set a friend's DHT public key. * * return -1 on failure. * return 0 on success. */ +non_null() int onion_set_friend_DHT_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key); -/** Copy friends DHT public key into dht_key. +/** @brief Copy friends DHT public key into dht_key. * * return 0 on failure (no key copied). * return 1 on success (key copied). */ +non_null() unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key); #define ONION_DATA_IN_RESPONSE_MIN_SIZE (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) #define ONION_CLIENT_MAX_DATA_SIZE (MAX_DATA_REQUEST_SIZE - ONION_DATA_IN_RESPONSE_MIN_SIZE) -/** Send data of length length to friendnum. +/** @brief Send data of length length to friendnum. * Maximum length of data is ONION_CLIENT_MAX_DATA_SIZE. * This data will be received by the friend using the Onion_Data_Handlers callbacks. * @@ -161,25 +185,36 @@ unsigned int onion_getfriend_DHT_pubkey(const Onion_Client *onion_c, int friend_ * return the number of packets sent on success * return -1 on failure. */ +non_null() int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, uint16_t length); typedef int oniondata_handler_cb(void *object, const uint8_t *source_pubkey, const uint8_t *data, uint16_t len, void *userdata); /** Function to call when onion data packet with contents beginning with byte is received. */ +non_null(1) nullable(3, 4) void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_cb *cb, void *object); +non_null() void do_onion_client(Onion_Client *onion_c); -Onion_Client *new_onion_client(const Logger *logger, Mono_Time *mono_time, Net_Crypto *c); +non_null() +Onion_Client *new_onion_client(const Logger *logger, const Random *rng, const Mono_Time *mono_time, Net_Crypto *c); +nullable(1) void kill_onion_client(Onion_Client *onion_c); -/** return 0 if we are not connected to the network. - * return 1 if we are connected with TCP only. - * return 2 if we are also connected with UDP. - */ -unsigned int onion_connection_status(const Onion_Client *onion_c); +typedef enum Onion_Connection_Status { + /** We are not connected to the network. */ + ONION_CONNECTION_STATUS_NONE = 0, + /** We are connected with TCP only. */ + ONION_CONNECTION_STATUS_TCP = 1, + /** We are also connected with UDP. */ + ONION_CONNECTION_STATUS_UDP = 2, +} Onion_Connection_Status; + +non_null() +Onion_Connection_Status onion_connection_status(const Onion_Client *onion_c); #endif diff --git a/protocols/Tox/libtox/src/toxcore/ping.c b/protocols/Tox/libtox/src/toxcore/ping.c index 249d4a0647..91a780b123 100644 --- a/protocols/Tox/libtox/src/toxcore/ping.c +++ b/protocols/Tox/libtox/src/toxcore/ping.c @@ -13,6 +13,7 @@ #include <string.h> #include "DHT.h" +#include "ccompat.h" #include "mono_time.h" #include "network.h" #include "ping_array.h" @@ -29,6 +30,7 @@ struct Ping { const Mono_Time *mono_time; + const Random *rng; DHT *dht; Ping_Array *ping_array; @@ -47,7 +49,7 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key int rc; uint64_t ping_id; - if (id_equal(public_key, dht_get_self_public_key(ping->dht))) { + if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) { return; } @@ -57,9 +59,9 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key dht_get_shared_key_sent(ping->dht, shared_key, public_key); // Generate random ping_id. uint8_t data[PING_DATA_SIZE]; - id_copy(data, public_key); + pk_copy(data, public_key); memcpy(data + CRYPTO_PUBLIC_KEY_SIZE, ipp, sizeof(IP_Port)); - ping_id = ping_array_add(ping->ping_array, ping->mono_time, data, sizeof(data)); + ping_id = ping_array_add(ping->ping_array, ping->mono_time, ping->rng, data, sizeof(data)); if (ping_id == 0) { crypto_memzero(shared_key, sizeof(shared_key)); @@ -71,8 +73,8 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key memcpy(ping_plain + 1, &ping_id, sizeof(ping_id)); pk[0] = NET_PACKET_PING_REQUEST; - id_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey - random_nonce(pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce + pk_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey + random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce rc = encrypt_data_symmetric(shared_key, @@ -90,12 +92,13 @@ void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk)); } +non_null() static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_t *public_key, uint64_t ping_id, const uint8_t *shared_encryption_key) { uint8_t pk[DHT_PING_SIZE]; - if (id_equal(public_key, dht_get_self_public_key(ping->dht))) { + if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) { return 1; } @@ -104,8 +107,8 @@ static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_ memcpy(ping_plain + 1, &ping_id, sizeof(ping_id)); pk[0] = NET_PACKET_PING_RESPONSE; - id_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey - random_nonce(pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce + pk_copy(pk + 1, dht_get_self_public_key(ping->dht)); // Our pubkey + random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce // Encrypt ping_id using recipient privkey const int rc = encrypt_data_symmetric(shared_encryption_key, @@ -120,6 +123,7 @@ static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_ return sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk)); } +non_null() static int handle_ping_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -131,7 +135,7 @@ static int handle_ping_request(void *object, const IP_Port *source, const uint8_ Ping *ping = dht_get_ping(dht); - if (id_equal(packet + 1, dht_get_self_public_key(ping->dht))) { + if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) { return 1; } @@ -167,6 +171,7 @@ static int handle_ping_request(void *object, const IP_Port *source, const uint8_ return 0; } +non_null() static int handle_ping_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length, void *userdata) { @@ -179,7 +184,7 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8 Ping *ping = dht_get_ping(dht); - if (id_equal(packet + 1, dht_get_self_public_key(ping->dht))) { + if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) { return 1; } @@ -214,7 +219,7 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8 return 1; } - if (!id_equal(packet + 1, data)) { + if (!pk_equal(packet + 1, data)) { return 1; } @@ -229,16 +234,17 @@ static int handle_ping_response(void *object, const IP_Port *source, const uint8 return 0; } -/** Check if public_key with ip_port is in the list. +/** @brief Check if public_key with ip_port is in the list. * - * return 1 if it is. - * return 0 if it isn't. + * return true if it is. + * return false if it isn't. */ -static int in_list(const Client_data *list, uint16_t length, const Mono_Time *mono_time, const uint8_t *public_key, - const IP_Port *ip_port) +non_null() +static bool in_list(const Client_data *list, uint16_t length, const Mono_Time *mono_time, const uint8_t *public_key, + const IP_Port *ip_port) { for (unsigned int i = 0; i < length; ++i) { - if (id_equal(list[i].public_key, public_key)) { + if (pk_equal(list[i].public_key, public_key)) { const IPPTsPng *ipptp; if (net_family_is_ipv4(ip_port->ip.family)) { @@ -249,23 +255,23 @@ static int in_list(const Client_data *list, uint16_t length, const Mono_Time *mo if (!mono_time_is_timeout(mono_time, ipptp->timestamp, BAD_NODE_TIMEOUT) && ipport_equal(&ipptp->ip_port, ip_port)) { - return 1; + return true; } } } - return 0; + return false; } -/** Add nodes to the to_ping list. +/** @brief Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. * If the list is full the nodes farthest from our public_key are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * - * return 0 if node was added. - * return -1 if node was not added. + * @retval 0 if node was added. + * @retval -1 if node was not added. */ int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port) { @@ -295,7 +301,7 @@ int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port) return 0; } - if (public_key_cmp(ping->to_ping[i].public_key, public_key) == 0) { + if (pk_equal(ping->to_ping[i].public_key, public_key)) { return -1; } } @@ -308,7 +314,7 @@ int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port) } -/** Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. +/** @brief Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. * This function must be run at least once every TIME_TO_PING seconds. */ void ping_iterate(Ping *ping) @@ -342,7 +348,7 @@ void ping_iterate(Ping *ping) } -Ping *ping_new(const Mono_Time *mono_time, DHT *dht) +Ping *ping_new(const Mono_Time *mono_time, const Random *rng, DHT *dht) { Ping *ping = (Ping *)calloc(1, sizeof(Ping)); @@ -358,6 +364,7 @@ Ping *ping_new(const Mono_Time *mono_time, DHT *dht) } ping->mono_time = mono_time; + ping->rng = rng; ping->dht = dht; networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, &handle_ping_request, dht); networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, &handle_ping_response, dht); diff --git a/protocols/Tox/libtox/src/toxcore/ping.h b/protocols/Tox/libtox/src/toxcore/ping.h index 4e8192323a..da8d7f003e 100644 --- a/protocols/Tox/libtox/src/toxcore/ping.h +++ b/protocols/Tox/libtox/src/toxcore/ping.h @@ -17,24 +17,32 @@ typedef struct Ping Ping; -Ping *ping_new(const struct Mono_Time *mono_time, DHT *dht); +non_null() +Ping *ping_new(const Mono_Time *mono_time, const Random *rng, DHT *dht); +nullable(1) void ping_kill(Ping *ping); -/** Add nodes to the to_ping list. +/** @brief Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. * If the list is full the nodes farthest from our public_key are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * - * return 0 if node was added. - * return -1 if node was not added. + * @retval 0 if node was added. + * @retval -1 if node was not added. */ +non_null() int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port); +/** @brief Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. + * This function must be run at least once every TIME_TO_PING seconds. + */ +non_null() void ping_iterate(Ping *ping); +non_null() void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key); #endif // C_TOXCORE_TOXCORE_PING_H diff --git a/protocols/Tox/libtox/src/toxcore/ping_array.c b/protocols/Tox/libtox/src/toxcore/ping_array.c index fe067d2b29..e22a2e6c1c 100644 --- a/protocols/Tox/libtox/src/toxcore/ping_array.c +++ b/protocols/Tox/libtox/src/toxcore/ping_array.c @@ -11,6 +11,7 @@ #include <stdlib.h> #include <string.h> +#include "ccompat.h" #include "crypto_core.h" #include "mono_time.h" #include "util.h" @@ -18,7 +19,7 @@ typedef struct Ping_Array_Entry { uint8_t *data; uint32_t length; - uint64_t time; + uint64_t ping_time; uint64_t ping_id; } Ping_Array_Entry; @@ -62,6 +63,7 @@ Ping_Array *ping_array_new(uint32_t size, uint32_t timeout) return empty_array; } +non_null() static void clear_entry(Ping_Array *array, uint32_t index) { const Ping_Array_Entry empty = {nullptr}; @@ -85,14 +87,14 @@ void ping_array_kill(Ping_Array *array) free(array); } -/** Clear timed out entries. - */ +/** Clear timed out entries. */ +non_null() static void ping_array_clear_timedout(Ping_Array *array, const Mono_Time *mono_time) { while (array->last_deleted != array->last_added) { const uint32_t index = array->last_deleted % array->total_size; - if (!mono_time_is_timeout(mono_time, array->entries[index].time, array->timeout)) { + if (!mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { break; } @@ -101,8 +103,8 @@ static void ping_array_clear_timedout(Ping_Array *array, const Mono_Time *mono_t } } -uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const uint8_t *data, - uint32_t length) +uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng, + const uint8_t *data, uint32_t length) { ping_array_clear_timedout(array, mono_time); const uint32_t index = array->last_added % array->total_size; @@ -120,9 +122,9 @@ uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const uin memcpy(array->entries[index].data, data, length); array->entries[index].length = length; - array->entries[index].time = mono_time_get(mono_time); + array->entries[index].ping_time = mono_time_get(mono_time); ++array->last_added; - uint64_t ping_id = random_u64(); + uint64_t ping_id = random_u64(rng); ping_id /= array->total_size; ping_id *= array->total_size; ping_id += index; @@ -148,7 +150,7 @@ int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t return -1; } - if (mono_time_is_timeout(mono_time, array->entries[index].time, array->timeout)) { + if (mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { return -1; } diff --git a/protocols/Tox/libtox/src/toxcore/ping_array.h b/protocols/Tox/libtox/src/toxcore/ping_array.h index 98dceaba0c..fda84cb110 100644 --- a/protocols/Tox/libtox/src/toxcore/ping_array.h +++ b/protocols/Tox/libtox/src/toxcore/ping_array.h @@ -12,6 +12,7 @@ #include <stddef.h> #include <stdint.h> +#include "crypto_core.h" #include "mono_time.h" #ifdef __cplusplus @@ -26,22 +27,24 @@ typedef struct Ping_Array Ping_Array; * @param size represents the total size of the array and should be a power of 2. * @param timeout represents the maximum timeout in seconds for the entry. * - * @return 0 on success, -1 on failure. + * @return pointer to allocated Ping_Array on success, nullptr on failure. */ struct Ping_Array *ping_array_new(uint32_t size, uint32_t timeout); /** * @brief Free all the allocated memory in a @ref Ping_Array. */ -void ping_array_kill(struct Ping_Array *array); +nullable(1) +void ping_array_kill(Ping_Array *array); /** * @brief Add a data with length to the @ref Ping_Array list and return a ping_id. * * @return ping_id on success, 0 on failure. */ -uint64_t ping_array_add(struct Ping_Array *array, const struct Mono_Time *mono_time, const uint8_t *data, - uint32_t length); +non_null() +uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng, + const uint8_t *data, uint32_t length); /** * @brief Check if @p ping_id is valid and not timed out. @@ -50,7 +53,8 @@ uint64_t ping_array_add(struct Ping_Array *array, const struct Mono_Time *mono_t * * @return length of data copied on success, -1 on failure. */ -int32_t ping_array_check(struct Ping_Array *array, const struct Mono_Time *mono_time, uint8_t *data, size_t length, +non_null() +int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t *data, size_t length, uint64_t ping_id); #ifdef __cplusplus diff --git a/protocols/Tox/libtox/src/toxcore/state.c b/protocols/Tox/libtox/src/toxcore/state.c index a31768131a..701cf4426b 100644 --- a/protocols/Tox/libtox/src/toxcore/state.c +++ b/protocols/Tox/libtox/src/toxcore/state.c @@ -6,6 +6,8 @@ #include <string.h> +#include "ccompat.h" + /** state load/save */ int state_load(const Logger *log, state_load_cb *state_load_callback, void *outer, const uint8_t *data, uint32_t length, uint16_t cookie_inner) diff --git a/protocols/Tox/libtox/src/toxcore/state.h b/protocols/Tox/libtox/src/toxcore/state.h index c539828b11..716286d6d1 100644 --- a/protocols/Tox/libtox/src/toxcore/state.h +++ b/protocols/Tox/libtox/src/toxcore/state.h @@ -53,9 +53,11 @@ typedef enum State_Load_Status { typedef State_Load_Status state_load_cb(void *outer, const uint8_t *data, uint32_t len, uint16_t type); /** state load/save */ +non_null() int state_load(const Logger *log, state_load_cb *state_load_callback, void *outer, const uint8_t *data, uint32_t length, uint16_t cookie_inner); +non_null() uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_t len, uint32_t section_type); // Utilities for state data serialisation. @@ -63,13 +65,19 @@ uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_ uint16_t lendian_to_host16(uint16_t lendian); uint16_t host_to_lendian16(uint16_t host); +non_null() void host_to_lendian_bytes64(uint8_t *dest, uint64_t num); +non_null() void lendian_bytes_to_host64(uint64_t *dest, const uint8_t *lendian); +non_null() void host_to_lendian_bytes32(uint8_t *dest, uint32_t num); +non_null() void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian); +non_null() void host_to_lendian_bytes16(uint8_t *dest, uint16_t num); +non_null() void lendian_bytes_to_host16(uint16_t *dest, const uint8_t *lendian); #ifdef __cplusplus diff --git a/protocols/Tox/libtox/src/toxcore/timed_auth.c b/protocols/Tox/libtox/src/toxcore/timed_auth.c new file mode 100644 index 0000000000..ebd5100286 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/timed_auth.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2019-2021 The TokTok team. + */ +#include "timed_auth.h" + +#include <string.h> + +#include "ccompat.h" + +non_null(1,6) nullable(4) +static void create_timed_auth_to_hash(const Mono_Time *mono_time, uint16_t timeout, bool previous, const uint8_t *data, + uint16_t length, uint8_t *to_hash) +{ + const uint64_t t = (mono_time_get(mono_time) / timeout) - (previous ? 1 : 0); + memcpy(to_hash, &t, sizeof(t)); + + if (data != nullptr) { + memcpy(to_hash + sizeof(t), data, length); + } +} + +void generate_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key, + const uint8_t *data, uint16_t length, uint8_t *timed_auth) +{ + VLA(uint8_t, to_hash, sizeof(uint64_t) + length); + create_timed_auth_to_hash(mono_time, timeout, false, data, length, to_hash); + crypto_hmac(timed_auth, key, to_hash, SIZEOF_VLA(to_hash)); +} + +bool check_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key, const uint8_t *data, + uint16_t length, const uint8_t *timed_auth) +{ + VLA(uint8_t, to_hash, sizeof(uint64_t) + length); + + for (uint8_t i = 0; i < 2; ++i) { + create_timed_auth_to_hash(mono_time, timeout, i != 0, data, length, to_hash); + + if (crypto_hmac_verify(timed_auth, key, to_hash, SIZEOF_VLA(to_hash))) { + return true; + } + } + + return false; +} diff --git a/protocols/Tox/libtox/src/toxcore/timed_auth.h b/protocols/Tox/libtox/src/toxcore/timed_auth.h new file mode 100644 index 0000000000..691b04d0df --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/timed_auth.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2019-2021 The TokTok team. + */ +#ifndef C_TOXCORE_TOXCORE_TIMED_AUTH_H +#define C_TOXCORE_TOXCORE_TIMED_AUTH_H + +#include "crypto_core.h" +#include "mono_time.h" + +#define TIMED_AUTH_SIZE CRYPTO_HMAC_SIZE + +/** + * @brief Write timed authentication code of data to timed_auth. + * + * @param timed_auth Must be of size TIMED_AUTH_SIZE. + */ + +non_null(1, 3, 6) nullable(4) +void generate_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key, + const uint8_t *data, uint16_t length, uint8_t *timed_auth); + +/** + * @brief Check timed_auth. This succeeds if `timed_auth` was generated by + * `generate_timed_auth` at most `timeout` seconds ago, and fails if at least + * `2*timeout` seconds ago. More precisely, it succeeds iff + * `current_time / timeout` is equal to or one more than + * `creation_time / timeout`. + * + * @param timed_auth Must be of size TIMED_AUTH_SIZE. + * @return true on success, false otherwise. + */ +non_null(1, 3, 6) nullable(4) +bool check_timed_auth(const Mono_Time *mono_time, uint16_t timeout, const uint8_t *key, + const uint8_t *data, uint16_t length, const uint8_t *timed_auth); +#endif diff --git a/protocols/Tox/libtox/src/toxcore/tox.c b/protocols/Tox/libtox/src/toxcore/tox.c index fce8347c5e..079f404079 100644 --- a/protocols/Tox/libtox/src/toxcore/tox.c +++ b/protocols/Tox/libtox/src/toxcore/tox.c @@ -11,22 +11,25 @@ #endif #include "tox.h" -#include "tox_private.h" #include <assert.h> #include <stdlib.h> #include <string.h> #include "Messenger.h" +#include "ccompat.h" #include "group.h" #include "logger.h" #include "mono_time.h" +#include "network.h" +#include "tox_private.h" +#include "tox_struct.h" #include "../toxencryptsave/defines.h" #define SET_ERROR_PARAMETER(param, x) \ do { \ - if (param) { \ + if (param != nullptr) { \ *param = x; \ } \ } while (0) @@ -35,6 +38,10 @@ static_assert(TOX_HASH_LENGTH == CRYPTO_SHA256_SIZE, "TOX_HASH_LENGTH is assumed to be equal to CRYPTO_SHA256_SIZE"); static_assert(FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE, "FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE"); +static_assert(TOX_DHT_NODE_IP_STRING_SIZE == IP_NTOA_LEN, + "TOX_DHT_NODE_IP_STRING_SIZE is assumed to be equal to IP_NTOA_LEN"); +static_assert(TOX_DHT_NODE_PUBLIC_KEY_SIZE == CRYPTO_PUBLIC_KEY_SIZE, + "TOX_DHT_NODE_PUBLIC_KEY_SIZE is assumed to be equal to CRYPTO_PUBLIC_KEY_SIZE"); static_assert(TOX_FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE, "TOX_FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE"); static_assert(TOX_FILE_ID_LENGTH == TOX_HASH_LENGTH, @@ -48,58 +55,27 @@ static_assert(TOX_MAX_NAME_LENGTH == MAX_NAME_LENGTH, static_assert(TOX_MAX_STATUS_MESSAGE_LENGTH == MAX_STATUSMESSAGE_LENGTH, "TOX_MAX_STATUS_MESSAGE_LENGTH is assumed to be equal to MAX_STATUSMESSAGE_LENGTH"); -struct Tox { - // XXX: Messenger *must* be the first member, because toxav casts its - // `Tox *` to `Messenger **`. - Messenger *m; - Mono_Time *mono_time; - pthread_mutex_t *mutex; - - tox_self_connection_status_cb *self_connection_status_callback; - tox_friend_name_cb *friend_name_callback; - tox_friend_status_message_cb *friend_status_message_callback; - tox_friend_status_cb *friend_status_callback; - tox_friend_connection_status_cb *friend_connection_status_callback; - tox_friend_typing_cb *friend_typing_callback; - tox_friend_read_receipt_cb *friend_read_receipt_callback; - tox_friend_request_cb *friend_request_callback; - tox_friend_message_cb *friend_message_callback; - tox_file_recv_control_cb *file_recv_control_callback; - tox_file_chunk_request_cb *file_chunk_request_callback; - tox_file_recv_cb *file_recv_callback; - tox_file_recv_chunk_cb *file_recv_chunk_callback; - tox_conference_invite_cb *conference_invite_callback; - tox_conference_connected_cb *conference_connected_callback; - tox_conference_message_cb *conference_message_callback; - tox_conference_title_cb *conference_title_callback; - tox_conference_peer_name_cb *conference_peer_name_callback; - tox_conference_peer_list_changed_cb *conference_peer_list_changed_callback; - tox_friend_lossy_packet_cb *friend_lossy_packet_callback_per_pktid[UINT8_MAX + 1]; - tox_friend_lossless_packet_cb *friend_lossless_packet_callback_per_pktid[UINT8_MAX + 1]; - - void *toxav_object; // workaround to store a ToxAV object (setter and getter functions are available) +struct Tox_Userdata { + Tox *tox; + void *user_data; }; -static void lock(const Tox *tox) +static logger_cb tox_log_handler; +non_null(1, 3, 5, 6) nullable(7) +static void tox_log_handler(void *context, Logger_Level level, const char *file, int line, const char *func, + const char *message, void *userdata) { - if (tox->mutex != nullptr) { - pthread_mutex_lock(tox->mutex); - } -} + Tox *tox = (Tox *)context; + assert(tox != nullptr); -static void unlock(const Tox *tox) -{ - if (tox->mutex != nullptr) { - pthread_mutex_unlock(tox->mutex); + if (tox->log_callback != nullptr) { + tox->log_callback(tox, (Tox_Log_Level)level, file, line, func, message, userdata); } } -struct Tox_Userdata { - Tox *tox; - void *user_data; -}; - -static void tox_self_connection_status_handler(Messenger *m, unsigned int connection_status, void *user_data) +static m_self_connection_status_cb tox_self_connection_status_handler; +non_null(1) nullable(3) +static void tox_self_connection_status_handler(Messenger *m, Onion_Connection_Status connection_status, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -108,6 +84,8 @@ static void tox_self_connection_status_handler(Messenger *m, unsigned int connec } } +static m_friend_name_cb tox_friend_name_handler; +non_null(1, 3) nullable(5) static void tox_friend_name_handler(Messenger *m, uint32_t friend_number, const uint8_t *name, size_t length, void *user_data) { @@ -118,6 +96,8 @@ static void tox_friend_name_handler(Messenger *m, uint32_t friend_number, const } } +static m_friend_status_message_cb tox_friend_status_message_handler; +non_null(1, 3) nullable(5) static void tox_friend_status_message_handler(Messenger *m, uint32_t friend_number, const uint8_t *message, size_t length, void *user_data) { @@ -128,6 +108,8 @@ static void tox_friend_status_message_handler(Messenger *m, uint32_t friend_numb } } +static m_friend_status_cb tox_friend_status_handler; +non_null(1) nullable(4) static void tox_friend_status_handler(Messenger *m, uint32_t friend_number, unsigned int status, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -137,6 +119,8 @@ static void tox_friend_status_handler(Messenger *m, uint32_t friend_number, unsi } } +static m_friend_connection_status_cb tox_friend_connection_status_handler; +non_null(1) nullable(4) static void tox_friend_connection_status_handler(Messenger *m, uint32_t friend_number, unsigned int connection_status, void *user_data) { @@ -148,6 +132,8 @@ static void tox_friend_connection_status_handler(Messenger *m, uint32_t friend_n } } +static m_friend_typing_cb tox_friend_typing_handler; +non_null(1) nullable(4) static void tox_friend_typing_handler(Messenger *m, uint32_t friend_number, bool is_typing, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -157,6 +143,8 @@ static void tox_friend_typing_handler(Messenger *m, uint32_t friend_number, bool } } +static m_friend_read_receipt_cb tox_friend_read_receipt_handler; +non_null(1) nullable(4) static void tox_friend_read_receipt_handler(Messenger *m, uint32_t friend_number, uint32_t message_id, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -166,6 +154,8 @@ static void tox_friend_read_receipt_handler(Messenger *m, uint32_t friend_number } } +static m_friend_request_cb tox_friend_request_handler; +non_null(1, 2, 3) nullable(5) static void tox_friend_request_handler(Messenger *m, const uint8_t *public_key, const uint8_t *message, size_t length, void *user_data) { @@ -176,17 +166,21 @@ static void tox_friend_request_handler(Messenger *m, const uint8_t *public_key, } } -static void tox_friend_message_handler(Messenger *m, uint32_t friend_number, unsigned int type, const uint8_t *message, - size_t length, void *user_data) +static m_friend_message_cb tox_friend_message_handler; +non_null(1, 4) nullable(6) +static void tox_friend_message_handler(Messenger *m, uint32_t friend_number, unsigned int message_type, + const uint8_t *message, size_t length, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; if (tox_data->tox->friend_message_callback != nullptr) { - tox_data->tox->friend_message_callback(tox_data->tox, friend_number, (Tox_Message_Type)type, message, length, + tox_data->tox->friend_message_callback(tox_data->tox, friend_number, (Tox_Message_Type)message_type, message, length, tox_data->user_data); } } +static m_file_recv_control_cb tox_file_recv_control_handler; +non_null(1) nullable(5) static void tox_file_recv_control_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, unsigned int control, void *user_data) { @@ -198,6 +192,8 @@ static void tox_file_recv_control_handler(Messenger *m, uint32_t friend_number, } } +static m_file_chunk_request_cb tox_file_chunk_request_handler; +non_null(1) nullable(6) static void tox_file_chunk_request_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length, void *user_data) { @@ -209,6 +205,8 @@ static void tox_file_chunk_request_handler(Messenger *m, uint32_t friend_number, } } +static m_file_recv_cb tox_file_recv_handler; +non_null(1, 6) nullable(8) static void tox_file_recv_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint32_t kind, uint64_t file_size, const uint8_t *filename, size_t filename_length, void *user_data) { @@ -220,6 +218,8 @@ static void tox_file_recv_handler(Messenger *m, uint32_t friend_number, uint32_t } } +static m_file_recv_chunk_cb tox_file_recv_chunk_handler; +non_null(1, 5) nullable(7) static void tox_file_recv_chunk_handler(Messenger *m, uint32_t friend_number, uint32_t file_number, uint64_t position, const uint8_t *data, size_t length, void *user_data) { @@ -231,6 +231,8 @@ static void tox_file_recv_chunk_handler(Messenger *m, uint32_t friend_number, ui } } +static g_conference_invite_cb tox_conference_invite_handler; +non_null(1, 4) nullable(6) static void tox_conference_invite_handler(Messenger *m, uint32_t friend_number, int type, const uint8_t *cookie, size_t length, void *user_data) { @@ -242,6 +244,8 @@ static void tox_conference_invite_handler(Messenger *m, uint32_t friend_number, } } +static g_conference_connected_cb tox_conference_connected_handler; +non_null(1) nullable(3) static void tox_conference_connected_handler(Messenger *m, uint32_t conference_number, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -251,6 +255,8 @@ static void tox_conference_connected_handler(Messenger *m, uint32_t conference_n } } +static g_conference_message_cb tox_conference_message_handler; +non_null(1, 5) nullable(7) static void tox_conference_message_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number, int type, const uint8_t *message, size_t length, void *user_data) { @@ -262,6 +268,8 @@ static void tox_conference_message_handler(Messenger *m, uint32_t conference_num } } +static title_cb tox_conference_title_handler; +non_null(1, 4) nullable(6) static void tox_conference_title_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number, const uint8_t *title, size_t length, void *user_data) { @@ -273,6 +281,8 @@ static void tox_conference_title_handler(Messenger *m, uint32_t conference_numbe } } +static peer_name_cb tox_conference_peer_name_handler; +non_null(1, 4) nullable(6) static void tox_conference_peer_name_handler(Messenger *m, uint32_t conference_number, uint32_t peer_number, const uint8_t *name, size_t length, void *user_data) { @@ -284,6 +294,8 @@ static void tox_conference_peer_name_handler(Messenger *m, uint32_t conference_n } } +static peer_list_changed_cb tox_conference_peer_list_changed_handler; +non_null(1) nullable(3) static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conference_number, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -293,6 +305,24 @@ static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conf } } +static dht_get_nodes_response_cb tox_dht_get_nodes_response_handler; +non_null(1, 2) nullable(3) +static void tox_dht_get_nodes_response_handler(const DHT *dht, const Node_format *node, void *user_data) +{ + struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; + + if (tox_data->tox->dht_get_nodes_response_callback == nullptr) { + return; + } + + Ip_Ntoa ip_str; + tox_data->tox->dht_get_nodes_response_callback( + tox_data->tox, node->public_key, net_ip_ntoa(&node->ip_port.ip, &ip_str), net_ntohs(node->ip_port.port), + tox_data->user_data); +} + +static m_friend_lossy_packet_cb tox_friend_lossy_packet_handler; +non_null(1, 4) nullable(6) static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id, const uint8_t *data, size_t length, void *user_data) { @@ -307,6 +337,8 @@ static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number } } +static m_friend_lossless_packet_cb tox_friend_lossless_packet_handler; +non_null(1, 4) nullable(6) static void tox_friend_lossless_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id, const uint8_t *data, size_t length, void *user_data) { @@ -327,6 +359,7 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch) return TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch); } +non_null() static State_Load_Status state_load_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type) { const Tox *tox = (const Tox *)outer; @@ -352,6 +385,7 @@ static State_Load_Status state_load_callback(void *outer, const uint8_t *data, u } /** Load tox from data of size length. */ +non_null() static int tox_load(Tox *tox, const uint8_t *data, uint32_t length) { uint32_t data32[2]; @@ -451,8 +485,14 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) m_options.tcp_server_port = tox_options_get_tcp_port(opts); m_options.hole_punching_enabled = tox_options_get_hole_punching_enabled(opts); m_options.local_discovery_enabled = tox_options_get_local_discovery_enabled(opts); + m_options.dht_announcements_enabled = tox_options_get_dht_announcements_enabled(opts); + + if (m_options.udp_disabled) { + m_options.local_discovery_enabled = false; + } - m_options.log_callback = (logger_cb *)tox_options_get_log_callback(opts); + tox->log_callback = tox_options_get_log_callback(opts); + m_options.log_callback = tox_log_handler; m_options.log_context = tox; m_options.log_user_data = tox_options_get_log_user_data(opts); @@ -480,6 +520,23 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) } } + const Tox_System *sys = tox_options_get_operating_system(opts); + const Tox_System default_system = tox_default_system(); + if (sys == nullptr) { + sys = &default_system; + } + + if (sys->rng == nullptr || sys->ns == nullptr) { + // TODO(iphydf): Not quite right, but similar. + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + tox_options_free(default_options); + free(tox); + return nullptr; + } + + tox->rng = *sys->rng; + tox->ns = *sys->ns; + if (m_options.proxy_info.proxy_type != TCP_PROXY_NONE) { if (tox_options_get_proxy_port(opts) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PORT); @@ -491,10 +548,12 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled); if (m_options.ipv6enabled) { - m_options.proxy_info.ip_port.ip.family = net_family_unspec; + m_options.proxy_info.ip_port.ip.family = net_family_unspec(); } - if (addr_resolve_or_parse_ip(tox_options_get_proxy_host(opts), &m_options.proxy_info.ip_port.ip, nullptr) == 0) { + const char *const proxy_host = tox_options_get_proxy_host(opts); + + if (proxy_host == nullptr || !addr_resolve_or_parse_ip(&tox->ns, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr)) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST); // TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain. tox_options_free(default_options); @@ -505,7 +564,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) m_options.proxy_info.ip_port.port = net_htons(tox_options_get_proxy_port(opts)); } - tox->mono_time = mono_time_new(); + tox->mono_time = mono_time_new(sys->mono_time_callback, sys->mono_time_user_data); if (tox->mono_time == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); @@ -534,16 +593,12 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) tox->mutex = nullptr; } - lock(tox); + tox_lock(tox); - unsigned int m_error; - tox->m = new_messenger(tox->mono_time, &m_options, &m_error); - - // TODO(iphydf): Clarify this code, check for NULL before new_groupchats, so - // new_groupchats can assume m is non-NULL. - if (!new_groupchats(tox->mono_time, tox->m)) { - kill_messenger(tox->m); + Messenger_Error m_error; + tox->m = new_messenger(tox->mono_time, &tox->rng, &tox->ns, &m_options, &m_error); + if (tox->m == nullptr) { if (m_error == MESSENGER_ERROR_PORT) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PORT_ALLOC); } else if (m_error == MESSENGER_ERROR_TCP_SERVER) { @@ -554,7 +609,23 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) mono_time_free(tox->mono_time); tox_options_free(default_options); - unlock(tox); + tox_unlock(tox); + + if (tox->mutex != nullptr) { + pthread_mutex_destroy(tox->mutex); + } + + free(tox->mutex); + free(tox); + return nullptr; + } + + if (new_groupchats(tox->mono_time, tox->m) == nullptr) { + kill_messenger(tox->m); + + mono_time_free(tox->mono_time); + tox_options_free(default_options); + tox_unlock(tox); if (tox->mutex != nullptr) { pthread_mutex_destroy(tox->mutex); @@ -562,6 +633,8 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) free(tox->mutex); free(tox); + + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); return nullptr; } @@ -588,6 +661,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) callback_file_reqchunk(tox->m, tox_file_chunk_request_handler); callback_file_sendrequest(tox->m, tox_file_recv_handler); callback_file_data(tox->m, tox_file_recv_chunk_handler); + dht_callback_get_nodes_response(tox->m->dht, tox_dht_get_nodes_response_handler); g_callback_group_invite(tox->m->conferences_object, tox_conference_invite_handler); g_callback_group_connected(tox->m->conferences_object, tox_conference_connected_handler); g_callback_group_message(tox->m->conferences_object, tox_conference_message_handler); @@ -599,7 +673,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error) tox_options_free(default_options); - unlock(tox); + tox_unlock(tox); return tox; } @@ -609,12 +683,12 @@ void tox_kill(Tox *tox) return; } - lock(tox); + tox_lock(tox); LOGGER_ASSERT(tox->m->log, tox->m->msi_packet == nullptr, "Attempted to kill tox while toxav is still alive"); kill_groupchats(tox->m->conferences_object); kill_messenger(tox->m); mono_time_free(tox->mono_time); - unlock(tox); + tox_unlock(tox); if (tox->mutex != nullptr) { pthread_mutex_destroy(tox->mutex); @@ -629,6 +703,7 @@ static uint32_t end_size(void) return 2 * sizeof(uint32_t); } +non_null() static void end_save(uint8_t *data) { state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_END); @@ -637,12 +712,12 @@ static void end_save(uint8_t *data) size_t tox_get_savedata_size(const Tox *tox) { assert(tox != nullptr); - lock(tox); - size_t ret = 2 * sizeof(uint32_t) + tox_lock(tox); + const size_t ret = 2 * sizeof(uint32_t) + messenger_size(tox->m) + conferences_size(tox->m->conferences_object) + end_size(); - unlock(tox); + tox_unlock(tox); return ret; } @@ -656,7 +731,7 @@ void tox_get_savedata(const Tox *tox, uint8_t *savedata) memset(savedata, 0, tox_get_savedata_size(tox)); - lock(tox); + tox_lock(tox); const uint32_t size32 = sizeof(uint32_t); @@ -670,86 +745,101 @@ void tox_get_savedata(const Tox *tox, uint8_t *savedata) savedata = conferences_save(tox->m->conferences_object, savedata); end_save(savedata); - unlock(tox); + tox_unlock(tox); } -bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error) +non_null(5) nullable(1, 2, 4, 6) +static int32_t resolve_bootstrap_node(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, IP_Port **root, Tox_Err_Bootstrap *error) { assert(tox != nullptr); + assert(root != nullptr); - if (!host || !public_key) { + if (host == nullptr || public_key == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL); - return 0; + return -1; } if (port == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT); - return 0; + return -1; } - IP_Port *root; - - const int32_t count = net_getipport(host, &root, TOX_SOCK_DGRAM); + const int32_t count = net_getipport(host, root, TOX_SOCK_DGRAM); if (count == -1) { LOGGER_DEBUG(tox->m->log, "could not resolve bootstrap node '%s'", host); - net_freeipport(root); + net_freeipport(*root); SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); - return 0; + return -1; + } + + assert(*root != nullptr); + return count; +} + +bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error) +{ + IP_Port *root; + const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error); + + if (count == -1) { + return false; } - lock(tox); + tox_lock(tox); + assert(count >= 0); + bool onion_success = false; + // UDP bootstrap is default success if it's disabled (because we don't even try). + bool udp_success = tox->m->options.udp_disabled; for (int32_t i = 0; i < count; ++i) { root[i].port = net_htons(port); - onion_add_bs_path_node(tox->m->onion_c, &root[i], public_key); + if (onion_add_bs_path_node(tox->m->onion_c, &root[i], public_key)) { + // If UDP is enabled, the caller cares about whether any of the + // bootstrap calls below will succeed. In TCP-only mode, adding + // onion path nodes successfully is sufficient. + onion_success = true; + } if (!tox->m->options.udp_disabled) { - dht_bootstrap(tox->m->dht, &root[i], public_key); + if (dht_bootstrap(tox->m->dht, &root[i], public_key)) { + // If any of the bootstrap calls worked, we call it success. + udp_success = true; + } } } - unlock(tox); + tox_unlock(tox); net_freeipport(root); - if (count) { - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); - return 1; + if (count == 0 || !onion_success || !udp_success) { + LOGGER_DEBUG(tox->m->log, "bootstrap node '%s' resolved to %d IP_Ports%s (onion: %s, UDP: %s)", + host, count, + count > 0 ? ", but failed to bootstrap with any of them" : "", + onion_success ? "success" : "FAILURE", + tox->m->options.udp_disabled ? "disabled" : (udp_success ? "success" : "FAILURE")); + SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); + return false; } - LOGGER_DEBUG(tox->m->log, "bootstrap node '%s' resolved to 0 IP_Ports", host); - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); - return 0; + SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); + return true; } bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error) { - assert(tox != nullptr); - - if (!host || !public_key) { - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL); - return 0; - } - - if (port == 0) { - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT); - return 0; - } - IP_Port *root; - - const int32_t count = net_getipport(host, &root, TOX_SOCK_STREAM); + const int32_t count = resolve_bootstrap_node(tox, host, port, public_key, &root, error); if (count == -1) { - net_freeipport(root); - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); - return 0; + return false; } - lock(tox); + tox_lock(tox); + assert(count >= 0); for (int32_t i = 0; i < count; ++i) { root[i].port = net_htons(port); @@ -757,35 +847,38 @@ bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t add_tcp_relay(tox->m->net_crypto, &root[i], public_key); } - unlock(tox); + tox_unlock(tox); net_freeipport(root); - if (count) { - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); - return 1; + if (count == 0) { + SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); + return false; } - SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); - return 0; + SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); + return true; } Tox_Connection tox_self_get_connection_status(const Tox *tox) { assert(tox != nullptr); - lock(tox); - const unsigned int ret = onion_connection_status(tox->m->onion_c); - unlock(tox); + tox_lock(tox); + const Onion_Connection_Status ret = onion_connection_status(tox->m->onion_c); + tox_unlock(tox); - if (ret == 2) { - return TOX_CONNECTION_UDP; - } + switch (ret) { + case ONION_CONNECTION_STATUS_NONE: + return TOX_CONNECTION_NONE; + + case ONION_CONNECTION_STATUS_TCP: + return TOX_CONNECTION_TCP; - if (ret == 1) { - return TOX_CONNECTION_TCP; + case ONION_CONNECTION_STATUS_UDP: + return TOX_CONNECTION_UDP; } - return TOX_CONNECTION_NONE; + LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret); } @@ -798,16 +891,21 @@ void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb uint32_t tox_iteration_interval(const Tox *tox) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); uint32_t ret = messenger_run_interval(tox->m); - unlock(tox); + + if (m_is_receiving_file(tox->m)) { + ret = 1; + } + + tox_unlock(tox); return ret; } void tox_iterate(Tox *tox, void *user_data) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); mono_time_update(tox->mono_time); @@ -815,34 +913,34 @@ void tox_iterate(Tox *tox, void *user_data) do_messenger(tox->m, &tox_data); do_groupchats(tox->m->conferences_object, &tox_data); - unlock(tox); + tox_unlock(tox); } void tox_self_get_address(const Tox *tox, uint8_t *address) { assert(tox != nullptr); - if (address) { - lock(tox); + if (address != nullptr) { + tox_lock(tox); getaddress(tox->m, address); - unlock(tox); + tox_unlock(tox); } } void tox_self_set_nospam(Tox *tox, uint32_t nospam) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); set_nospam(tox->m->fr, net_htonl(nospam)); - unlock(tox); + tox_unlock(tox); } uint32_t tox_self_get_nospam(const Tox *tox) { assert(tox != nullptr); - lock(tox); - uint32_t ret = net_ntohl(get_nospam(tox->m->fr)); - unlock(tox); + tox_lock(tox); + const uint32_t ret = net_ntohl(get_nospam(tox->m->fr)); + tox_unlock(tox); return ret; } @@ -850,10 +948,10 @@ void tox_self_get_public_key(const Tox *tox, uint8_t *public_key) { assert(tox != nullptr); - if (public_key) { - lock(tox); + if (public_key != nullptr) { + tox_lock(tox); memcpy(public_key, nc_get_self_public_key(tox->m->net_crypto), CRYPTO_PUBLIC_KEY_SIZE); - unlock(tox); + tox_unlock(tox); } } @@ -861,10 +959,10 @@ void tox_self_get_secret_key(const Tox *tox, uint8_t *secret_key) { assert(tox != nullptr); - if (secret_key) { - lock(tox); + if (secret_key != nullptr) { + tox_lock(tox); memcpy(secret_key, nc_get_self_secret_key(tox->m->net_crypto), CRYPTO_SECRET_KEY_SIZE); - unlock(tox); + tox_unlock(tox); } } @@ -872,32 +970,32 @@ bool tox_self_set_name(Tox *tox, const uint8_t *name, size_t length, Tox_Err_Set { assert(tox != nullptr); - if (!name && length != 0) { + if (name == nullptr && length != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL); - return 0; + return false; } - lock(tox); + tox_lock(tox); if (setname(tox->m, name, length) == 0) { // TODO(irungentoo): function to set different per group names? send_name_all_groups(tox->m->conferences_object); SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK); - unlock(tox); - return 1; + tox_unlock(tox); + return true; } SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG); - unlock(tox); - return 0; + tox_unlock(tox); + return false; } size_t tox_self_get_name_size(const Tox *tox) { assert(tox != nullptr); - lock(tox); - size_t ret = m_get_self_name_size(tox->m); - unlock(tox); + tox_lock(tox); + const size_t ret = m_get_self_name_size(tox->m); + tox_unlock(tox); return ret; } @@ -905,10 +1003,10 @@ void tox_self_get_name(const Tox *tox, uint8_t *name) { assert(tox != nullptr); - if (name) { - lock(tox); + if (name != nullptr) { + tox_lock(tox); getself_name(tox->m, name); - unlock(tox); + tox_unlock(tox); } } @@ -916,30 +1014,30 @@ bool tox_self_set_status_message(Tox *tox, const uint8_t *status_message, size_t { assert(tox != nullptr); - if (!status_message && length != 0) { + if (status_message == nullptr && length != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL); - return 0; + return false; } - lock(tox); + tox_lock(tox); if (m_set_statusmessage(tox->m, status_message, length) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK); - unlock(tox); - return 1; + tox_unlock(tox); + return true; } SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG); - unlock(tox); - return 0; + tox_unlock(tox); + return false; } size_t tox_self_get_status_message_size(const Tox *tox) { assert(tox != nullptr); - lock(tox); - size_t ret = m_get_self_statusmessage_size(tox->m); - unlock(tox); + tox_lock(tox); + const size_t ret = m_get_self_statusmessage_size(tox->m); + tox_unlock(tox); return ret; } @@ -947,30 +1045,31 @@ void tox_self_get_status_message(const Tox *tox, uint8_t *status_message) { assert(tox != nullptr); - if (status_message) { - lock(tox); + if (status_message != nullptr) { + tox_lock(tox); m_copy_self_statusmessage(tox->m, status_message); - unlock(tox); + tox_unlock(tox); } } void tox_self_set_status(Tox *tox, Tox_User_Status status) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); m_set_userstatus(tox->m, status); - unlock(tox); + tox_unlock(tox); } Tox_User_Status tox_self_get_status(const Tox *tox) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const uint8_t status = m_get_self_userstatus(tox->m); - unlock(tox); + tox_unlock(tox); return (Tox_User_Status)status; } +non_null(1) nullable(3) static void set_friend_error(const Logger *log, int32_t ret, Tox_Err_Friend_Add *error) { switch (ret) { @@ -1022,22 +1121,22 @@ uint32_t tox_friend_add(Tox *tox, const uint8_t *address, const uint8_t *message { assert(tox != nullptr); - if (!address || !message) { + if (address == nullptr || message == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL); return UINT32_MAX; } - lock(tox); + tox_lock(tox); const int32_t ret = m_addfriend(tox->m, address, message, length); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK); - unlock(tox); - return ret; + tox_unlock(tox); + return (uint32_t)ret; } set_friend_error(tox->m->log, ret, error); - unlock(tox); + tox_unlock(tox); return UINT32_MAX; } @@ -1045,54 +1144,54 @@ uint32_t tox_friend_add_norequest(Tox *tox, const uint8_t *public_key, Tox_Err_F { assert(tox != nullptr); - if (!public_key) { + if (public_key == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL); return UINT32_MAX; } - lock(tox); + tox_lock(tox); const int32_t ret = m_addfriend_norequest(tox->m, public_key); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK); - unlock(tox); - return ret; + tox_unlock(tox); + return (uint32_t)ret; } set_friend_error(tox->m->log, ret, error); - unlock(tox); + tox_unlock(tox); return UINT32_MAX; } bool tox_friend_delete(Tox *tox, uint32_t friend_number, Tox_Err_Friend_Delete *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_delfriend(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); // TODO(irungentoo): handle if realloc fails? if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_FRIEND_NOT_FOUND); - return 0; + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_OK); - return 1; + return true; } uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t *public_key, Tox_Err_Friend_By_Public_Key *error) { assert(tox != nullptr); - if (!public_key) { + if (public_key == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NULL); return UINT32_MAX; } - lock(tox); + tox_lock(tox); const int32_t ret = getfriend_id(tox->m, public_key); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NOT_FOUND); @@ -1100,7 +1199,8 @@ uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t *public_key, Tox } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_OK); - return ret; + assert(ret >= 0); + return (uint32_t)ret; } bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t *public_key, @@ -1108,38 +1208,38 @@ bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t * { assert(tox != nullptr); - if (!public_key) { - return 0; + if (public_key == nullptr) { + return false; } - lock(tox); + tox_lock(tox); if (get_real_pk(tox->m, friend_number, public_key) == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_FRIEND_NOT_FOUND); - unlock(tox); - return 0; + tox_unlock(tox); + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK); - unlock(tox); - return 1; + tox_unlock(tox); + return true; } bool tox_friend_exists(const Tox *tox, uint32_t friend_number) { assert(tox != nullptr); - lock(tox); - bool ret = m_friend_exists(tox->m, friend_number); - unlock(tox); + tox_lock(tox); + const bool ret = m_friend_exists(tox->m, friend_number); + tox_unlock(tox); return ret; } uint64_t tox_friend_get_last_online(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Get_Last_Online *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const uint64_t timestamp = m_get_last_online(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (timestamp == UINT64_MAX) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_LAST_ONLINE_FRIEND_NOT_FOUND); @@ -1153,9 +1253,9 @@ uint64_t tox_friend_get_last_online(const Tox *tox, uint32_t friend_number, Tox_ size_t tox_self_get_friend_list_size(const Tox *tox) { assert(tox != nullptr); - lock(tox); - size_t ret = count_friendlist(tox->m); - unlock(tox); + tox_lock(tox); + const size_t ret = count_friendlist(tox->m); + tox_unlock(tox); return ret; } @@ -1163,20 +1263,20 @@ void tox_self_get_friend_list(const Tox *tox, uint32_t *friend_list) { assert(tox != nullptr); - if (friend_list) { - lock(tox); + if (friend_list != nullptr) { + tox_lock(tox); // TODO(irungentoo): size parameter? copy_friendlist(tox->m, friend_list, count_friendlist(tox->m)); - unlock(tox); + tox_unlock(tox); } } size_t tox_friend_get_name_size(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_get_name_size(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); @@ -1191,22 +1291,22 @@ bool tox_friend_get_name(const Tox *tox, uint32_t friend_number, uint8_t *name, { assert(tox != nullptr); - if (!name) { + if (name == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL); - return 0; + return false; } - lock(tox); + tox_lock(tox); const int ret = getname(tox->m, friend_number, name); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); - return 0; + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); - return 1; + return true; } void tox_callback_friend_name(Tox *tox, tox_friend_name_cb *callback) @@ -1218,9 +1318,9 @@ void tox_callback_friend_name(Tox *tox, tox_friend_name_cb *callback) size_t tox_friend_get_status_message_size(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_get_statusmessage_size(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); @@ -1236,17 +1336,17 @@ bool tox_friend_get_status_message(const Tox *tox, uint32_t friend_number, uint8 { assert(tox != nullptr); - if (!status_message) { + if (status_message == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL); return false; } - lock(tox); + tox_lock(tox); const int size = m_get_statusmessage_size(tox->m, friend_number); if (size == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); - unlock(tox); + tox_unlock(tox); return false; } @@ -1254,7 +1354,7 @@ bool tox_friend_get_status_message(const Tox *tox, uint32_t friend_number, uint8 LOGGER_ASSERT(tox->m->log, ret == size, "concurrency problem: friend status message changed"); SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); - unlock(tox); + tox_unlock(tox); return ret == size; } @@ -1267,13 +1367,13 @@ void tox_callback_friend_status_message(Tox *tox, tox_friend_status_message_cb * Tox_User_Status tox_friend_get_status(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_get_userstatus(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (ret == USERSTATUS_INVALID) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); - return (Tox_User_Status)(TOX_USER_STATUS_BUSY + 1); + return TOX_USER_STATUS_NONE; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); @@ -1289,9 +1389,9 @@ void tox_callback_friend_status(Tox *tox, tox_friend_status_cb *callback) Tox_Connection tox_friend_get_connection_status(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_get_friend_connectionstatus(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); @@ -1311,17 +1411,17 @@ void tox_callback_friend_connection_status(Tox *tox, tox_friend_connection_statu bool tox_friend_get_typing(const Tox *tox, uint32_t friend_number, Tox_Err_Friend_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = m_get_istyping(tox->m, friend_number); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); - return 0; + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); - return !!ret; + return ret != 0; } void tox_callback_friend_typing(Tox *tox, tox_friend_typing_cb *callback) @@ -1333,19 +1433,20 @@ void tox_callback_friend_typing(Tox *tox, tox_friend_typing_cb *callback) bool tox_self_set_typing(Tox *tox, uint32_t friend_number, bool typing, Tox_Err_Set_Typing *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); if (m_set_usertyping(tox->m, friend_number, typing) == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_FRIEND_NOT_FOUND); - unlock(tox); - return 0; + tox_unlock(tox); + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_OK); - unlock(tox); - return 1; + tox_unlock(tox); + return true; } +non_null(1) nullable(3) static void set_message_error(const Logger *log, int ret, Tox_Err_Friend_Send_Message *error) { switch (ret) { @@ -1392,21 +1493,21 @@ uint32_t tox_friend_send_message(Tox *tox, uint32_t friend_number, Tox_Message_T { assert(tox != nullptr); - if (!message) { + if (message == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_NULL); return 0; } - if (!length) { + if (length == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_SEND_MESSAGE_EMPTY); return 0; } uint32_t message_id = 0; - lock(tox); + tox_lock(tox); set_message_error(tox->m->log, m_send_message_generic(tox->m, friend_number, type, message, length, &message_id), error); - unlock(tox); + tox_unlock(tox); return message_id; } @@ -1430,126 +1531,126 @@ void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback) bool tox_hash(uint8_t *hash, const uint8_t *data, size_t length) { - if (!hash || (length && !data)) { - return 0; + if (hash == nullptr || (data == nullptr && length != 0)) { + return false; } crypto_sha256(hash, data, length); - return 1; + return true; } bool tox_file_control(Tox *tox, uint32_t friend_number, uint32_t file_number, Tox_File_Control control, Tox_Err_File_Control *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = file_control(tox->m, friend_number, file_number, control); - unlock(tox); + tox_unlock(tox); if (ret == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_OK); - return 1; + return true; } switch (ret) { case -1: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND); - return 0; + return false; } case -2: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED); - return 0; + return false; } case -3: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_FOUND); - return 0; + return false; } case -4: { /* can't happen (this code is returned if `control` is invalid type) */ LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret); - return 0; + return false; } case -5: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_ALREADY_PAUSED); - return 0; + return false; } case -6: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_DENIED); - return 0; + return false; } case -7: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_PAUSED); - return 0; + return false; } case -8: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_SENDQ); - return 0; + return false; } } /* can't happen */ LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret); - return 0; + return false; } bool tox_file_seek(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, Tox_Err_File_Seek *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = file_seek(tox->m, friend_number, file_number, position); - unlock(tox); + tox_unlock(tox); if (ret == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_OK); - return 1; + return true; } switch (ret) { case -1: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_FOUND); - return 0; + return false; } case -2: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_FRIEND_NOT_CONNECTED); - return 0; + return false; } case -3: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_NOT_FOUND); - return 0; + return false; } case -4: // fall-through case -5: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_DENIED); - return 0; + return false; } case -6: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_INVALID_POSITION); - return 0; + return false; } case -8: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEEK_SENDQ); - return 0; + return false; } } /* can't happen */ LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret); - return 0; + return false; } void tox_callback_file_recv_control(Tox *tox, tox_file_recv_control_cb *callback) @@ -1563,18 +1664,18 @@ bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_ { assert(tox != nullptr); - if (!file_id) { + if (file_id == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NULL); - return 0; + return false; } - lock(tox); + tox_lock(tox); const int ret = file_get_id(tox->m, friend_number, file_number, file_id); - unlock(tox); + tox_unlock(tox); if (ret == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_OK); - return 1; + return true; } if (ret == -1) { @@ -1583,7 +1684,7 @@ bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_ SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NOT_FOUND); } - return 0; + return false; } uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *file_id, @@ -1591,22 +1692,22 @@ uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t { assert(tox != nullptr); - if (filename_length && !filename) { + if (filename == nullptr && filename_length != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NULL); return UINT32_MAX; } uint8_t f_id[FILE_ID_LENGTH]; - if (!file_id) { + if (file_id == nullptr) { /* Tox keys are 32 bytes like FILE_ID_LENGTH. */ - new_symmetric_key(f_id); + new_symmetric_key(&tox->rng, f_id); file_id = f_id; } - lock(tox); + tox_lock(tox); const long int file_num = new_filesender(tox->m, friend_number, kind, file_size, file_id, filename, filename_length); - unlock(tox); + tox_unlock(tox); if (file_num >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_OK); @@ -1645,56 +1746,56 @@ bool tox_file_send_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, size_t length, Tox_Err_File_Send_Chunk *error) { assert(tox != nullptr); - lock(tox); - const int ret = file_data(tox->m, friend_number, file_number, position, data, length); - unlock(tox); + tox_lock(tox); + const int ret = send_file_data(tox->m, friend_number, file_number, position, data, length); + tox_unlock(tox); if (ret == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_OK); - return 1; + return true; } switch (ret) { case -1: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_FOUND); - return 0; + return false; } case -2: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_CONNECTED); - return 0; + return false; } case -3: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_FOUND); - return 0; + return false; } case -4: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_TRANSFERRING); - return 0; + return false; } case -5: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_INVALID_LENGTH); - return 0; + return false; } case -6: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_SENDQ); - return 0; + return false; } case -7: { SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_WRONG_POSITION); - return 0; + return false; } } /* can't happen */ LOGGER_FATAL(tox->m->log, "impossible return value: %d", ret); - return 0; + return false; } void tox_callback_file_chunk_request(Tox *tox, tox_file_chunk_request_cb *callback) @@ -1754,9 +1855,9 @@ void tox_callback_conference_peer_list_changed(Tox *tox, tox_conference_peer_lis uint32_t tox_conference_new(Tox *tox, Tox_Err_Conference_New *error) { assert(tox != nullptr); - lock(tox); - const int ret = add_groupchat(tox->m->conferences_object, GROUPCHAT_TYPE_TEXT); - unlock(tox); + tox_lock(tox); + const int ret = add_groupchat(tox->m->conferences_object, &tox->rng, GROUPCHAT_TYPE_TEXT); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_NEW_INIT); @@ -1770,11 +1871,11 @@ uint32_t tox_conference_new(Tox *tox, Tox_Err_Conference_New *error) bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Delete *error) { assert(tox != nullptr); - lock(tox); - const int ret = del_groupchat(tox->m->conferences_object, conference_number, true); - unlock(tox); + tox_lock(tox); + const bool ret = del_groupchat(tox->m->conferences_object, conference_number, true); + tox_unlock(tox); - if (ret == -1) { + if (!ret) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_DELETE_CONFERENCE_NOT_FOUND); return false; } @@ -1786,9 +1887,9 @@ bool tox_conference_delete(Tox *tox, uint32_t conference_number, Tox_Err_Confere uint32_t tox_conference_peer_count(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_number_peers(tox->m->conferences_object, conference_number, false); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND); @@ -1803,9 +1904,9 @@ size_t tox_conference_peer_get_name_size(const Tox *tox, uint32_t conference_num Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peername_size(tox->m->conferences_object, conference_number, peer_number, false); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1827,9 +1928,9 @@ bool tox_conference_peer_get_name(const Tox *tox, uint32_t conference_number, ui Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peername(tox->m->conferences_object, conference_number, peer_number, name, false); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1851,9 +1952,9 @@ bool tox_conference_peer_get_public_key(const Tox *tox, uint32_t conference_numb uint8_t *public_key, Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, peer_number, public_key, false); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1875,9 +1976,9 @@ bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_numb Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peernumber_is_ours(tox->m->conferences_object, conference_number, peer_number); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1897,16 +1998,16 @@ bool tox_conference_peer_number_is_ours(const Tox *tox, uint32_t conference_numb } SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_OK); - return ret; + return ret != 0; } uint32_t tox_conference_offline_peer_count(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_number_peers(tox->m->conferences_object, conference_number, true); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_PEER_QUERY_CONFERENCE_NOT_FOUND); @@ -1922,9 +2023,9 @@ size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t confer Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peername_size(tox->m->conferences_object, conference_number, offline_peer_number, true); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1947,9 +2048,9 @@ bool tox_conference_offline_peer_get_name(const Tox *tox, uint32_t conference_nu Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peername(tox->m->conferences_object, conference_number, offline_peer_number, name, true); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1972,9 +2073,9 @@ bool tox_conference_offline_peer_get_public_key(const Tox *tox, uint32_t confere uint8_t *public_key, Tox_Err_Conference_Peer_Query *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_peer_pubkey(tox->m->conferences_object, conference_number, offline_peer_number, public_key, true); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -1998,10 +2099,10 @@ uint64_t tox_conference_offline_peer_get_last_active(const Tox *tox, uint32_t co { assert(tox != nullptr); uint64_t last_active = UINT64_MAX; - lock(tox); + tox_lock(tox); const int ret = group_frozen_last_active(tox->m->conferences_object, conference_number, offline_peer_number, &last_active); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2024,9 +2125,9 @@ bool tox_conference_set_max_offline(Tox *tox, uint32_t conference_number, Tox_Err_Conference_Set_Max_Offline *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_set_max_frozen(tox->m->conferences_object, conference_number, max_offline_peers); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_CONFERENCE_NOT_FOUND); @@ -2041,9 +2142,9 @@ bool tox_conference_invite(Tox *tox, uint32_t friend_number, uint32_t conference Tox_Err_Conference_Invite *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = invite_friend(tox->m->conferences_object, friend_number, conference_number); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2070,9 +2171,9 @@ uint32_t tox_conference_join(Tox *tox, uint32_t friend_number, const uint8_t *co Tox_Err_Conference_Join *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = join_groupchat(tox->m->conferences_object, friend_number, GROUPCHAT_TYPE_TEXT, cookie, length); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2114,7 +2215,7 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa size_t length, Tox_Err_Conference_Send_Message *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); int ret = 0; if (type == TOX_MESSAGE_TYPE_NORMAL) { @@ -2123,7 +2224,7 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa ret = group_action_send(tox->m->conferences_object, conference_number, message, length); } - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2154,9 +2255,9 @@ bool tox_conference_send_message(Tox *tox, uint32_t conference_number, Tox_Messa size_t tox_conference_get_title_size(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Title *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_title_get_size(tox->m->conferences_object, conference_number); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2178,9 +2279,9 @@ bool tox_conference_get_title(const Tox *tox, uint32_t conference_number, uint8_ Tox_Err_Conference_Title *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_title_get(tox->m->conferences_object, conference_number, title); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2202,9 +2303,9 @@ bool tox_conference_set_title(Tox *tox, uint32_t conference_number, const uint8_ Tox_Err_Conference_Title *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_title_send(tox->m->conferences_object, conference_number, title, length); - unlock(tox); + tox_unlock(tox); switch (ret) { case -1: { @@ -2230,28 +2331,28 @@ bool tox_conference_set_title(Tox *tox, uint32_t conference_number, const uint8_ size_t tox_conference_get_chatlist_size(const Tox *tox) { assert(tox != nullptr); - lock(tox); - size_t ret = count_chatlist(tox->m->conferences_object); - unlock(tox); + tox_lock(tox); + const size_t ret = count_chatlist(tox->m->conferences_object); + tox_unlock(tox); return ret; } void tox_conference_get_chatlist(const Tox *tox, uint32_t *chatlist) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const size_t list_size = count_chatlist(tox->m->conferences_object); copy_chatlist(tox->m->conferences_object, chatlist, list_size); - unlock(tox); + tox_unlock(tox); } Tox_Conference_Type tox_conference_get_type(const Tox *tox, uint32_t conference_number, Tox_Err_Conference_Get_Type *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const int ret = group_get_type(tox->m->conferences_object, conference_number); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_GET_TYPE_CONFERENCE_NOT_FOUND); @@ -2265,9 +2366,9 @@ Tox_Conference_Type tox_conference_get_type(const Tox *tox, uint32_t conference_ bool tox_conference_get_id(const Tox *tox, uint32_t conference_number, uint8_t *id) { assert(tox != nullptr); - lock(tox); - bool ret = conference_get_id(tox->m->conferences_object, conference_number, id); - unlock(tox); + tox_lock(tox); + const bool ret = conference_get_id(tox->m->conferences_object, conference_number, id); + tox_unlock(tox); return ret; } @@ -2282,14 +2383,14 @@ uint32_t tox_conference_by_id(const Tox *tox, const uint8_t *id, Tox_Err_Confere { assert(tox != nullptr); - if (!id) { + if (id == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NULL); return UINT32_MAX; } - lock(tox); + tox_lock(tox); const int32_t ret = conference_by_id(tox->m->conferences_object, id); - unlock(tox); + tox_unlock(tox); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_NOT_FOUND); @@ -2297,7 +2398,8 @@ uint32_t tox_conference_by_id(const Tox *tox, const uint8_t *id, Tox_Err_Confere } SET_ERROR_PARAMETER(error, TOX_ERR_CONFERENCE_BY_ID_OK); - return ret; + assert(ret >= 0); + return (uint32_t)ret; } // TODO(iphydf): Delete in 0.3.0. @@ -2327,6 +2429,7 @@ uint32_t tox_conference_by_uid(const Tox *tox, const uint8_t *uid, Tox_Err_Confe return res; } +nullable(2) static void set_custom_packet_error(int ret, Tox_Err_Friend_Custom_Packet *error) { switch (ret) { @@ -2367,32 +2470,28 @@ bool tox_friend_send_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_ { assert(tox != nullptr); - if (!data) { + if (data == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL); - return 0; + return false; } if (length == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY); - return 0; + return false; } if (data[0] < PACKET_ID_RANGE_LOSSY_START || data[0] > PACKET_ID_RANGE_LOSSY_END) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_INVALID); - return 0; + return false; } - lock(tox); + tox_lock(tox); const int ret = m_send_custom_lossy_packet(tox->m, friend_number, data, length); - unlock(tox); + tox_unlock(tox); set_custom_packet_error(ret, error); - if (ret == 0) { - return 1; - } - - return 0; + return ret == 0; } void tox_callback_friend_lossy_packet(Tox *tox, tox_friend_lossy_packet_cb *callback) @@ -2405,41 +2504,28 @@ void tox_callback_friend_lossy_packet(Tox *tox, tox_friend_lossy_packet_cb *call } } -void tox_callback_friend_lossy_packet_per_pktid(Tox *tox, tox_friend_lossy_packet_cb *callback, uint8_t pktid) -{ - assert(tox != nullptr); - - if (pktid >= PACKET_ID_RANGE_LOSSY_START && pktid <= PACKET_ID_RANGE_LOSSY_END) { - tox->friend_lossy_packet_callback_per_pktid[pktid] = callback; - } -} - bool tox_friend_send_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, Tox_Err_Friend_Custom_Packet *error) { assert(tox != nullptr); - if (!data) { + if (data == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_NULL); - return 0; + return false; } if (length == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_CUSTOM_PACKET_EMPTY); - return 0; + return false; } - lock(tox); + tox_lock(tox); const int ret = send_custom_lossless_packet(tox->m, friend_number, data, length); - unlock(tox); + tox_unlock(tox); set_custom_packet_error(ret, error); - if (ret == 0) { - return 1; - } - - return 0; + return ret == 0; } void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb *callback) @@ -2451,73 +2537,46 @@ void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb } } -void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless_packet_cb *callback, uint8_t pktid) -{ - assert(tox != nullptr); - - if ((pktid >= PACKET_ID_RANGE_LOSSLESS_CUSTOM_START && pktid <= PACKET_ID_RANGE_LOSSLESS_CUSTOM_END) - || pktid == PACKET_ID_MSI) { - tox->friend_lossless_packet_callback_per_pktid[pktid] = callback; - } -} - void tox_self_get_dht_id(const Tox *tox, uint8_t *dht_id) { assert(tox != nullptr); - if (dht_id) { - lock(tox); + if (dht_id != nullptr) { + tox_lock(tox); memcpy(dht_id, dht_get_self_public_key(tox->m->dht), CRYPTO_PUBLIC_KEY_SIZE); - unlock(tox); + tox_unlock(tox); } } -void tox_set_av_object(Tox *tox, void *object) -{ - assert(tox != nullptr); - lock(tox); - tox->toxav_object = object; - unlock(tox); -} - -void *tox_get_av_object(const Tox *tox) -{ - assert(tox != nullptr); - lock(tox); - void *object = tox->toxav_object; - unlock(tox); - return object; -} - uint16_t tox_self_get_udp_port(const Tox *tox, Tox_Err_Get_Port *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); const uint16_t port = net_htons(net_port(tox->m->net)); - unlock(tox); + tox_unlock(tox); - if (port) { - SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK); - } else { + if (port == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND); + return 0; } + SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK); return port; } uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error) { assert(tox != nullptr); - lock(tox); + tox_lock(tox); - if (tox->m->tcp_server) { + if (tox->m->tcp_server != nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_OK); - uint16_t ret = tox->m->options.tcp_server_port; - unlock(tox); + const uint16_t ret = tox->m->options.tcp_server_port; + tox_unlock(tox); return ret; } SET_ERROR_PARAMETER(error, TOX_ERR_GET_PORT_NOT_BOUND); - unlock(tox); + tox_unlock(tox); return 0; } diff --git a/protocols/Tox/libtox/src/toxcore/tox.h b/protocols/Tox/libtox/src/toxcore/tox.h index 38ec9f4b5a..e8d55c6689 100644 --- a/protocols/Tox/libtox/src/toxcore/tox.h +++ b/protocols/Tox/libtox/src/toxcore/tox.h @@ -108,6 +108,8 @@ extern "C" { #endif +/** @{ @namespace tox */ + #ifndef TOX_DEFINED #define TOX_DEFINED /** @@ -156,14 +158,14 @@ uint32_t tox_version_minor(void); * Incremented when bugfixes are applied without changing any functionality or * API or ABI. */ -#define TOX_VERSION_PATCH 15 +#define TOX_VERSION_PATCH 18 uint32_t tox_version_patch(void); //!TOKSTYLE- /** * @brief A macro to check at preprocessing time whether the client code is - * compatible with the installed version of Tox. + * compatible with the installed version of Tox. * * Leading zeros in the version number are ignored. E.g. 0.1.5 is to 0.1.4 * what 1.5 is to 1.4, that is: it can add new features, but can't break the @@ -187,13 +189,13 @@ uint32_t tox_version_patch(void); /** * @brief Return whether the compiled library version is compatible with the - * passed version numbers. + * passed version numbers. */ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); /** * @brief A convenience macro to call tox_version_is_compatible with the - * currently compiling API version. + * currently compiling API version. */ #define TOX_VERSION_IS_ABI_COMPATIBLE() \ tox_version_is_compatible(TOX_VERSION_MAJOR, TOX_VERSION_MINOR, TOX_VERSION_PATCH) @@ -374,7 +376,7 @@ typedef enum Tox_User_Status { /** * @brief Represents message types for tox_friend_send_message and conference - * messages. + * messages. */ typedef enum Tox_Message_Type { @@ -465,12 +467,12 @@ typedef enum Tox_Log_Level { TOX_LOG_LEVEL_INFO, /** - * Warnings about internal inconsistency or logic errors. + * Warnings about events_alloc inconsistency or logic errors. */ TOX_LOG_LEVEL_WARNING, /** - * Severe unexpected errors caused by external or internal inconsistency. + * Severe unexpected errors caused by external or events_alloc inconsistency. */ TOX_LOG_LEVEL_ERROR, @@ -478,7 +480,7 @@ typedef enum Tox_Log_Level { /** - * @brief This event is triggered when the toxcore library logs an internal message. + * @brief This event is triggered when the toxcore library logs an events_alloc message. * * This is mostly useful for debugging. This callback can be called from any * function, not just tox_iterate. This means the user data lifetime must at @@ -500,6 +502,16 @@ typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_ /** + * @brief Operating system functions used by Tox. + * + * This struct is opaque and generally shouldn't be used in clients, but in + * combination with tox_private.h, it allows tests to inject non-IO (hermetic) + * versions of low level network, RNG, and time keeping functions. + */ +typedef struct Tox_System Tox_System; + + +/** * @brief This struct contains all the startup options for Tox. * * You must tox_options_new to allocate an object of this type. @@ -509,9 +521,10 @@ typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_ * directly, as it *will* break binary compatibility frequently. * * @deprecated The memory layout of this struct (size, alignment, and field - * order) is not part of the ABI. To remain compatible, prefer to use tox_options_new to - * allocate the object and accessor functions to set the members. The struct - * will become opaque (i.e. the definition will become private) in v0.3.0. + * order) is not part of the ABI. To remain compatible, prefer to use + * tox_options_new to allocate the object and accessor functions to set the + * members. The struct will become opaque (i.e. the definition will become + * private) in v0.3.0. */ struct Tox_Options { @@ -547,6 +560,13 @@ struct Tox_Options { /** + * Enable storing DHT announcements and forwarding corresponding requests. + * + * Disabling this will cause Tox to ignore the relevant packets. + */ + bool dht_announcements_enabled; + + /** * Pass communications through a proxy. */ Tox_Proxy_Type proxy_type; @@ -663,6 +683,12 @@ struct Tox_Options { */ bool experimental_thread_safety; + /** + * Low level operating system functionality such as send/recv and random + * number generation. + */ + const Tox_System *operating_system; + }; @@ -678,6 +704,10 @@ bool tox_options_get_local_discovery_enabled(const struct Tox_Options *options); void tox_options_set_local_discovery_enabled(struct Tox_Options *options, bool local_discovery_enabled); +bool tox_options_get_dht_announcements_enabled(const struct Tox_Options *options); + +void tox_options_set_dht_announcements_enabled(struct Tox_Options *options, bool dht_announcements_enabled); + Tox_Proxy_Type tox_options_get_proxy_type(const struct Tox_Options *options); void tox_options_set_proxy_type(struct Tox_Options *options, Tox_Proxy_Type type); @@ -728,7 +758,11 @@ void tox_options_set_log_user_data(struct Tox_Options *options, void *user_data) bool tox_options_get_experimental_thread_safety(const struct Tox_Options *options); -void tox_options_set_experimental_thread_safety(struct Tox_Options *options, bool thread_safety); +void tox_options_set_experimental_thread_safety(struct Tox_Options *options, bool experimental_thread_safety); + +const Tox_System *tox_options_get_operating_system(const struct Tox_Options *options); + +void tox_options_set_operating_system(struct Tox_Options *options, const Tox_System *operating_system); /** * @brief Initialises a Tox_Options object with the default options. @@ -760,7 +794,7 @@ typedef enum Tox_Err_Options_New { /** * @brief Allocates a new Tox_Options object and initialises it with the default - * options. + * options. * * This function can be used to preserve long term ABI compatibility by * giving the responsibility of allocation and deallocation to the Tox library. @@ -800,7 +834,7 @@ typedef enum Tox_Err_New { TOX_ERR_NEW_NULL, /** - * The function was unable to allocate enough memory to store the internal + * The function was unable to allocate enough memory to store the events_alloc * structures for the Tox object. */ TOX_ERR_NEW_MALLOC, @@ -870,7 +904,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error); /** * @brief Releases all resources associated with the Tox instance and - * disconnects from the network. + * disconnects from the network. * * After calling this function, the Tox pointer becomes invalid. No other * functions can be called, and the pointer value can no longer be read. @@ -879,7 +913,7 @@ void tox_kill(Tox *tox); /** * @brief Calculates the number of bytes required to store the tox instance with - * tox_get_savedata. + * tox_get_savedata. * * This function cannot fail. The result is always greater than 0. * @@ -916,8 +950,9 @@ typedef enum Tox_Err_Bootstrap { TOX_ERR_BOOTSTRAP_NULL, /** - * The hostname could not be resolved to an IP address, or the IP address - * passed was invalid. + * The hostname could not be resolved to an IP address, the IP address + * passed was invalid, or the function failed to send the initial request + * packet to the bootstrap node or TCP relay. */ TOX_ERR_BOOTSTRAP_BAD_HOST, @@ -930,8 +965,8 @@ typedef enum Tox_Err_Bootstrap { /** - * @brief Sends a "get nodes" request to the given bootstrap node with IP, port, and - * public key to setup connections. + * @brief Sends a "get nodes" request to the given bootstrap node with IP, port, + * and public key to setup connections. * * This function will attempt to connect to the node using UDP. You must use * this function even if Tox_Options.udp_enabled was set to false. @@ -968,29 +1003,33 @@ bool tox_add_tcp_relay(Tox *tox, const char *host, uint16_t port, const uint8_t typedef enum Tox_Connection { /** - * There is no connection. This instance, or the friend the state change is - * about, is now offline. + * @brief There is no connection. + * + * This instance, or the friend the state change is about, is now offline. */ TOX_CONNECTION_NONE, /** - * A TCP connection has been established. For the own instance, this means it - * is connected through a TCP relay, only. For a friend, this means that the - * connection to that particular friend goes through a TCP relay. + * @brief A TCP connection has been established. + * + * For the own instance, this means it is connected through a TCP relay, + * only. For a friend, this means that the connection to that particular + * friend goes through a TCP relay. */ TOX_CONNECTION_TCP, /** - * A UDP connection has been established. For the own instance, this means it - * is able to send UDP packets to DHT nodes, but may still be connected to - * a TCP relay. For a friend, this means that the connection to that - * particular friend was built using direct UDP packets. + * @brief A UDP connection has been established. + * + * For the own instance, this means it is able to send UDP packets to DHT + * nodes, but may still be connected to a TCP relay. For a friend, this + * means that the connection to that particular friend was built using + * direct UDP packets. */ TOX_CONNECTION_UDP, } Tox_Connection; - /** * @brief Return whether we are connected to the DHT. * @@ -998,7 +1037,7 @@ typedef enum Tox_Connection { * `self_connection_status` callback. * * @deprecated This getter is deprecated. Use the event and store the status - * in the client state. + * in the client state. */ Tox_Connection tox_self_get_connection_status(const Tox *tox); @@ -1024,14 +1063,14 @@ typedef void tox_self_connection_status_cb(Tox *tox, Tox_Connection connection_s void tox_callback_self_connection_status(Tox *tox, tox_self_connection_status_cb *callback); /** - * @brief Return the time in milliseconds before tox_iterate() should be called again - * for optimal performance. + * @brief Return the time in milliseconds before `tox_iterate()` should be called again + * for optimal performance. */ uint32_t tox_iteration_interval(const Tox *tox); /** - * @brief The main loop that needs to be run in intervals of tox_iteration_interval() - * milliseconds. + * @brief The main loop that needs to be run in intervals of `tox_iteration_interval()` + * milliseconds. */ void tox_iterate(Tox *tox, void *user_data); @@ -1096,7 +1135,7 @@ void tox_self_get_secret_key(const Tox *tox, uint8_t *secret_key); /** * @brief Common error codes for all functions that set a piece of user-visible - * client information. + * client information. */ typedef enum Tox_Err_Set_Info { @@ -1652,10 +1691,10 @@ bool tox_friend_get_typing(const Tox *tox, uint32_t friend_number, Tox_Err_Frien /** * @param friend_number The friend number of the friend who started or stopped * typing. - * @param is_typing The result of calling tox_friend_get_typing on the passed + * @param typing The result of calling tox_friend_get_typing on the passed * friend_number. */ -typedef void tox_friend_typing_cb(Tox *tox, uint32_t friend_number, bool is_typing, void *user_data); +typedef void tox_friend_typing_cb(Tox *tox, uint32_t friend_number, bool typing, void *user_data); /** @@ -1845,7 +1884,7 @@ void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *callback); * If hash is NULL or data is NULL while length is not 0 the function returns false, * otherwise it returns true. * - * This function is a wrapper to internal message-digest functions. + * This function is a wrapper to events_alloc message-digest functions. * * @param hash A valid memory location the hash data. It must be at least * TOX_HASH_LENGTH bytes in size. @@ -2147,8 +2186,8 @@ typedef enum Tox_Err_File_Send { * should generally just be a file name, not a path with directory names. * * If a non-UINT64_MAX file size is provided, it can be used by both sides to - * determine the sending progress. File size can be set to UINT64_MAX for streaming - * data of unknown size. + * determine the sending progress. File size can be set to UINT64_MAX for + * streaming data of unknown size. * * File transmission occurs in chunks, which are requested through the * `file_chunk_request` event. @@ -2163,12 +2202,11 @@ typedef enum Tox_Err_File_Send { * - If the file size was increased * - and sending mode was streaming (file_size = UINT64_MAX), the behaviour * will be as expected. - * - and sending mode was file (file_size != UINT64_MAX), the - * file_chunk_request callback will receive length = 0 when Core thinks - * the file transfer has finished. If the client remembers the file size as - * it was when sending the request, it will terminate the transfer normally. - * If the client re-reads the size, it will think the friend cancelled the - * transfer. + * - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request + * callback will receive length = 0 when Core thinks the file transfer has + * finished. If the client remembers the file size as it was when sending the + * request, it will terminate the transfer normally. If the client re-reads the + * size, it will think the friend cancelled the transfer. * - If the file size was decreased * - and sending mode was streaming, the behaviour is as expected. * - and sending mode was file, the callback will return 0 at the new @@ -2189,7 +2227,7 @@ typedef enum Tox_Err_File_Send { * unknown or streaming. * @param file_id A file identifier of length TOX_FILE_ID_LENGTH that can be used to * uniquely identify file transfers across core restarts. If NULL, a random one will - * be generated by core. It can then be obtained by using tox_file_get_file_id(). + * be generated by core. It can then be obtained by using `tox_file_get_file_id()`. * @param filename Name of the file. Does not need to be the actual name. This * name will be sent along with the file send request. * @param filename_length Size in bytes of the filename. @@ -2442,7 +2480,8 @@ typedef void tox_conference_connected_cb(Tox *tox, uint32_t conference_number, v void tox_callback_conference_connected(Tox *tox, tox_conference_connected_cb *callback); /** - * @param conference_number The conference number of the conference the message is intended for. + * @param conference_number The conference number of the conference the message + * is intended for. * @param peer_number The ID of the peer who sent the message. * @param type The type of message (normal, action, ...). * @param message The message data. @@ -2462,7 +2501,8 @@ typedef void tox_conference_message_cb(Tox *tox, uint32_t conference_number, uin void tox_callback_conference_message(Tox *tox, tox_conference_message_cb *callback); /** - * @param conference_number The conference number of the conference the title change is intended for. + * @param conference_number The conference number of the conference the title + * change is intended for. * @param peer_number The ID of the peer who changed the title. * @param title The title data. * @param length The title length. @@ -2663,7 +2703,8 @@ size_t tox_conference_offline_peer_get_name_size(const Tox *tox, uint32_t confer /** * @brief Copy the name of offline_peer_number who is in conference_number to name. * - * Call tox_conference_offline_peer_get_name_size to determine the allocation size for the `name` parameter. + * Call tox_conference_offline_peer_get_name_size to determine the allocation + * size for the `name` parameter. * * @param name A valid memory region large enough to store the peer's name. * @@ -2845,7 +2886,8 @@ typedef enum Tox_Err_Conference_Send_Message { * must be split by the client and sent as separate messages. Other clients can * then reassemble the fragments. * - * @param conference_number The conference number of the conference the message is intended for. + * @param conference_number The conference number of the conference the message + * is intended for. * @param type Message type (normal, action, ...). * @param message A non-NULL pointer to the first element of a byte array * containing the message text. @@ -3225,6 +3267,8 @@ uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error); /** @} */ +/** @} */ + #ifdef __cplusplus } #endif diff --git a/protocols/Tox/libtox/src/toxcore/tox_api.c b/protocols/Tox/libtox/src/toxcore/tox_api.c index 89524ae4e2..00051e497b 100644 --- a/protocols/Tox/libtox/src/toxcore/tox_api.c +++ b/protocols/Tox/libtox/src/toxcore/tox_api.c @@ -7,41 +7,97 @@ #include <string.h> #include "ccompat.h" +#include "tox_private.h" #define SET_ERROR_PARAMETER(param, x) \ do { \ - if (param) { \ + if (param != nullptr) { \ *param = x; \ } \ } while (0) -//!TOKSTYLE- - -#define CONST_FUNCTION(lowercase, uppercase) \ -uint32_t tox_##lowercase(void) \ -{ \ - return TOX_##uppercase; \ +uint32_t tox_version_major(void) +{ + return TOX_VERSION_MAJOR; +} +uint32_t tox_version_minor(void) +{ + return TOX_VERSION_MINOR; +} +uint32_t tox_version_patch(void) +{ + return TOX_VERSION_PATCH; +} +uint32_t tox_public_key_size(void) +{ + return TOX_PUBLIC_KEY_SIZE; +} +uint32_t tox_secret_key_size(void) +{ + return TOX_SECRET_KEY_SIZE; +} +uint32_t tox_conference_uid_size(void) +{ + return TOX_CONFERENCE_UID_SIZE; +} +uint32_t tox_conference_id_size(void) +{ + return TOX_CONFERENCE_ID_SIZE; +} +uint32_t tox_nospam_size(void) +{ + return TOX_NOSPAM_SIZE; +} +uint32_t tox_address_size(void) +{ + return TOX_ADDRESS_SIZE; +} +uint32_t tox_max_name_length(void) +{ + return TOX_MAX_NAME_LENGTH; +} +uint32_t tox_max_status_message_length(void) +{ + return TOX_MAX_STATUS_MESSAGE_LENGTH; +} +uint32_t tox_max_friend_request_length(void) +{ + return TOX_MAX_FRIEND_REQUEST_LENGTH; +} +uint32_t tox_max_message_length(void) +{ + return TOX_MAX_MESSAGE_LENGTH; +} +uint32_t tox_max_custom_packet_size(void) +{ + return TOX_MAX_CUSTOM_PACKET_SIZE; +} +uint32_t tox_hash_length(void) +{ + return TOX_HASH_LENGTH; +} +uint32_t tox_file_id_length(void) +{ + return TOX_FILE_ID_LENGTH; +} +uint32_t tox_max_filename_length(void) +{ + return TOX_MAX_FILENAME_LENGTH; +} +uint32_t tox_max_hostname_length(void) +{ + return TOX_MAX_HOSTNAME_LENGTH; +} +uint32_t tox_dht_node_ip_string_size(void) +{ + return TOX_DHT_NODE_IP_STRING_SIZE; +} +uint32_t tox_dht_node_public_key_size(void) +{ + return TOX_DHT_NODE_PUBLIC_KEY_SIZE; } -CONST_FUNCTION(version_major, VERSION_MAJOR) -CONST_FUNCTION(version_minor, VERSION_MINOR) -CONST_FUNCTION(version_patch, VERSION_PATCH) -CONST_FUNCTION(public_key_size, PUBLIC_KEY_SIZE) -CONST_FUNCTION(secret_key_size, SECRET_KEY_SIZE) -CONST_FUNCTION(conference_uid_size, CONFERENCE_UID_SIZE) -CONST_FUNCTION(conference_id_size, CONFERENCE_ID_SIZE) -CONST_FUNCTION(nospam_size, NOSPAM_SIZE) -CONST_FUNCTION(address_size, ADDRESS_SIZE) -CONST_FUNCTION(max_name_length, MAX_NAME_LENGTH) -CONST_FUNCTION(max_status_message_length, MAX_STATUS_MESSAGE_LENGTH) -CONST_FUNCTION(max_friend_request_length, MAX_FRIEND_REQUEST_LENGTH) -CONST_FUNCTION(max_message_length, MAX_MESSAGE_LENGTH) -CONST_FUNCTION(max_custom_packet_size, MAX_CUSTOM_PACKET_SIZE) -CONST_FUNCTION(hash_length, HASH_LENGTH) -CONST_FUNCTION(file_id_length, FILE_ID_LENGTH) -CONST_FUNCTION(max_filename_length, MAX_FILENAME_LENGTH) -CONST_FUNCTION(max_hostname_length, MAX_HOSTNAME_LENGTH) - +//!TOKSTYLE- #define ACCESSORS(type, ns, name) \ type tox_options_get_##ns##name(const struct Tox_Options *options) \ @@ -67,7 +123,9 @@ ACCESSORS(size_t, savedata_, length) ACCESSORS(tox_log_cb *, log_, callback) ACCESSORS(void *, log_, user_data) ACCESSORS(bool,, local_discovery_enabled) +ACCESSORS(bool,, dht_announcements_enabled) ACCESSORS(bool,, experimental_thread_safety) +ACCESSORS(const Tox_System *,, operating_system) //!TOKSTYLE+ @@ -84,14 +142,15 @@ void tox_options_set_savedata_data(struct Tox_Options *options, const uint8_t *d void tox_options_default(struct Tox_Options *options) { - if (options) { - struct Tox_Options default_options = { 0 }; + if (options != nullptr) { + const struct Tox_Options default_options = {0}; *options = default_options; tox_options_set_ipv6_enabled(options, true); tox_options_set_udp_enabled(options, true); tox_options_set_proxy_type(options, TOX_PROXY_TYPE_NONE); tox_options_set_hole_punching_enabled(options, true); tox_options_set_local_discovery_enabled(options, true); + tox_options_set_dht_announcements_enabled(options, true); tox_options_set_experimental_thread_safety(options, false); } } @@ -100,7 +159,7 @@ struct Tox_Options *tox_options_new(Tox_Err_Options_New *error) { struct Tox_Options *options = (struct Tox_Options *)calloc(1, sizeof(struct Tox_Options)); - if (options) { + if (options != nullptr) { tox_options_default(options); SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_OK); return options; diff --git a/protocols/Tox/libtox/src/toxcore/tox_dispatch.c b/protocols/Tox/libtox/src/toxcore/tox_dispatch.c new file mode 100644 index 0000000000..4b4546e4a4 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_dispatch.c @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "tox_dispatch.h" + +#include <stdlib.h> + +#include "ccompat.h" + +struct Tox_Dispatch { + tox_events_conference_connected_cb *conference_connected_callback; + tox_events_conference_invite_cb *conference_invite_callback; + tox_events_conference_message_cb *conference_message_callback; + tox_events_conference_peer_list_changed_cb *conference_peer_list_changed_callback; + tox_events_conference_peer_name_cb *conference_peer_name_callback; + tox_events_conference_title_cb *conference_title_callback; + tox_events_file_chunk_request_cb *file_chunk_request_callback; + tox_events_file_recv_cb *file_recv_callback; + tox_events_file_recv_chunk_cb *file_recv_chunk_callback; + tox_events_file_recv_control_cb *file_recv_control_callback; + tox_events_friend_connection_status_cb *friend_connection_status_callback; + tox_events_friend_lossless_packet_cb *friend_lossless_packet_callback; + tox_events_friend_lossy_packet_cb *friend_lossy_packet_callback; + tox_events_friend_message_cb *friend_message_callback; + tox_events_friend_name_cb *friend_name_callback; + tox_events_friend_read_receipt_cb *friend_read_receipt_callback; + tox_events_friend_request_cb *friend_request_callback; + tox_events_friend_status_cb *friend_status_callback; + tox_events_friend_status_message_cb *friend_status_message_callback; + tox_events_friend_typing_cb *friend_typing_callback; + tox_events_self_connection_status_cb *self_connection_status_callback; +}; + +Tox_Dispatch *tox_dispatch_new(Tox_Err_Dispatch_New *error) +{ + Tox_Dispatch *dispatch = (Tox_Dispatch *)calloc(1, sizeof(Tox_Dispatch)); + + if (dispatch == nullptr) { + if (error != nullptr) { + *error = TOX_ERR_DISPATCH_NEW_MALLOC; + } + + return nullptr; + } + + *dispatch = (Tox_Dispatch) { + nullptr + }; + if (error != nullptr) { + *error = TOX_ERR_DISPATCH_NEW_OK; + } + return dispatch; +} + +void tox_dispatch_free(Tox_Dispatch *dispatch) +{ + free(dispatch); +} + +void tox_events_callback_conference_connected( + Tox_Dispatch *dispatch, tox_events_conference_connected_cb *callback) +{ + dispatch->conference_connected_callback = callback; +} +void tox_events_callback_conference_invite( + Tox_Dispatch *dispatch, tox_events_conference_invite_cb *callback) +{ + dispatch->conference_invite_callback = callback; +} +void tox_events_callback_conference_message( + Tox_Dispatch *dispatch, tox_events_conference_message_cb *callback) +{ + dispatch->conference_message_callback = callback; +} +void tox_events_callback_conference_peer_list_changed( + Tox_Dispatch *dispatch, tox_events_conference_peer_list_changed_cb *callback) +{ + dispatch->conference_peer_list_changed_callback = callback; +} +void tox_events_callback_conference_peer_name( + Tox_Dispatch *dispatch, tox_events_conference_peer_name_cb *callback) +{ + dispatch->conference_peer_name_callback = callback; +} +void tox_events_callback_conference_title( + Tox_Dispatch *dispatch, tox_events_conference_title_cb *callback) +{ + dispatch->conference_title_callback = callback; +} +void tox_events_callback_file_chunk_request( + Tox_Dispatch *dispatch, tox_events_file_chunk_request_cb *callback) +{ + dispatch->file_chunk_request_callback = callback; +} +void tox_events_callback_file_recv( + Tox_Dispatch *dispatch, tox_events_file_recv_cb *callback) +{ + dispatch->file_recv_callback = callback; +} +void tox_events_callback_file_recv_chunk( + Tox_Dispatch *dispatch, tox_events_file_recv_chunk_cb *callback) +{ + dispatch->file_recv_chunk_callback = callback; +} +void tox_events_callback_file_recv_control( + Tox_Dispatch *dispatch, tox_events_file_recv_control_cb *callback) +{ + dispatch->file_recv_control_callback = callback; +} +void tox_events_callback_friend_connection_status( + Tox_Dispatch *dispatch, tox_events_friend_connection_status_cb *callback) +{ + dispatch->friend_connection_status_callback = callback; +} +void tox_events_callback_friend_lossless_packet( + Tox_Dispatch *dispatch, tox_events_friend_lossless_packet_cb *callback) +{ + dispatch->friend_lossless_packet_callback = callback; +} +void tox_events_callback_friend_lossy_packet( + Tox_Dispatch *dispatch, tox_events_friend_lossy_packet_cb *callback) +{ + dispatch->friend_lossy_packet_callback = callback; +} +void tox_events_callback_friend_message( + Tox_Dispatch *dispatch, tox_events_friend_message_cb *callback) +{ + dispatch->friend_message_callback = callback; +} +void tox_events_callback_friend_name( + Tox_Dispatch *dispatch, tox_events_friend_name_cb *callback) +{ + dispatch->friend_name_callback = callback; +} +void tox_events_callback_friend_read_receipt( + Tox_Dispatch *dispatch, tox_events_friend_read_receipt_cb *callback) +{ + dispatch->friend_read_receipt_callback = callback; +} +void tox_events_callback_friend_request( + Tox_Dispatch *dispatch, tox_events_friend_request_cb *callback) +{ + dispatch->friend_request_callback = callback; +} +void tox_events_callback_friend_status( + Tox_Dispatch *dispatch, tox_events_friend_status_cb *callback) +{ + dispatch->friend_status_callback = callback; +} +void tox_events_callback_friend_status_message( + Tox_Dispatch *dispatch, tox_events_friend_status_message_cb *callback) +{ + dispatch->friend_status_message_callback = callback; +} +void tox_events_callback_friend_typing( + Tox_Dispatch *dispatch, tox_events_friend_typing_cb *callback) +{ + dispatch->friend_typing_callback = callback; +} +void tox_events_callback_self_connection_status( + Tox_Dispatch *dispatch, tox_events_self_connection_status_cb *callback) +{ + dispatch->self_connection_status_callback = callback; +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_connected( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_connected_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_connected_callback != nullptr) { + dispatch->conference_connected_callback( + tox, tox_events_get_conference_connected(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_invite( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_invite_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_invite_callback != nullptr) { + dispatch->conference_invite_callback( + tox, tox_events_get_conference_invite(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_message( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_message_callback != nullptr) { + dispatch->conference_message_callback( + tox, tox_events_get_conference_message(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_peer_list_changed( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_peer_list_changed_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_peer_list_changed_callback != nullptr) { + dispatch->conference_peer_list_changed_callback( + tox, tox_events_get_conference_peer_list_changed(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_peer_name( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_peer_name_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_peer_name_callback != nullptr) { + dispatch->conference_peer_name_callback( + tox, tox_events_get_conference_peer_name(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_conference_title( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_conference_title_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->conference_title_callback != nullptr) { + dispatch->conference_title_callback( + tox, tox_events_get_conference_title(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_file_chunk_request( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_file_chunk_request_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->file_chunk_request_callback != nullptr) { + dispatch->file_chunk_request_callback( + tox, tox_events_get_file_chunk_request(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_file_recv( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_file_recv_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->file_recv_callback != nullptr) { + dispatch->file_recv_callback( + tox, tox_events_get_file_recv(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_file_recv_chunk( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_file_recv_chunk_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->file_recv_chunk_callback != nullptr) { + dispatch->file_recv_chunk_callback( + tox, tox_events_get_file_recv_chunk(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_file_recv_control( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_file_recv_control_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->file_recv_control_callback != nullptr) { + dispatch->file_recv_control_callback( + tox, tox_events_get_file_recv_control(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_connection_status( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_connection_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_connection_status_callback != nullptr) { + dispatch->friend_connection_status_callback( + tox, tox_events_get_friend_connection_status(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_lossless_packet( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_lossless_packet_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_lossless_packet_callback != nullptr) { + dispatch->friend_lossless_packet_callback( + tox, tox_events_get_friend_lossless_packet(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_lossy_packet( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_lossy_packet_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_lossy_packet_callback != nullptr) { + dispatch->friend_lossy_packet_callback( + tox, tox_events_get_friend_lossy_packet(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_message( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_message_callback != nullptr) { + dispatch->friend_message_callback( + tox, tox_events_get_friend_message(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_name( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_name_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_name_callback != nullptr) { + dispatch->friend_name_callback( + tox, tox_events_get_friend_name(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_read_receipt( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_read_receipt_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_read_receipt_callback != nullptr) { + dispatch->friend_read_receipt_callback( + tox, tox_events_get_friend_read_receipt(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_request( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_request_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_request_callback != nullptr) { + dispatch->friend_request_callback( + tox, tox_events_get_friend_request(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_status( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_status_callback != nullptr) { + dispatch->friend_status_callback( + tox, tox_events_get_friend_status(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_status_message( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_status_message_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_status_message_callback != nullptr) { + dispatch->friend_status_message_callback( + tox, tox_events_get_friend_status_message(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_friend_typing( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_friend_typing_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->friend_typing_callback != nullptr) { + dispatch->friend_typing_callback( + tox, tox_events_get_friend_typing(events, i), user_data); + } + } +} + +non_null(1, 3) nullable(2, 4) +static void tox_dispatch_invoke_self_connection_status( + const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + const uint32_t size = tox_events_get_self_connection_status_size(events); + + for (uint32_t i = 0; i < size; ++i) { + if (dispatch->self_connection_status_callback != nullptr) { + dispatch->self_connection_status_callback( + tox, tox_events_get_self_connection_status(events, i), user_data); + } + } +} + +void tox_dispatch_invoke(const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data) +{ + tox_dispatch_invoke_conference_connected(dispatch, events, tox, user_data); + tox_dispatch_invoke_conference_invite(dispatch, events, tox, user_data); + tox_dispatch_invoke_conference_message(dispatch, events, tox, user_data); + tox_dispatch_invoke_conference_peer_list_changed(dispatch, events, tox, user_data); + tox_dispatch_invoke_conference_peer_name(dispatch, events, tox, user_data); + tox_dispatch_invoke_conference_title(dispatch, events, tox, user_data); + tox_dispatch_invoke_file_chunk_request(dispatch, events, tox, user_data); + tox_dispatch_invoke_file_recv(dispatch, events, tox, user_data); + tox_dispatch_invoke_file_recv_chunk(dispatch, events, tox, user_data); + tox_dispatch_invoke_file_recv_control(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_connection_status(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_lossless_packet(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_lossy_packet(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_message(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_name(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_read_receipt(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_request(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_status(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_status_message(dispatch, events, tox, user_data); + tox_dispatch_invoke_friend_typing(dispatch, events, tox, user_data); + tox_dispatch_invoke_self_connection_status(dispatch, events, tox, user_data); +} diff --git a/protocols/Tox/libtox/src/toxcore/tox_dispatch.h b/protocols/Tox/libtox/src/toxcore/tox_dispatch.h new file mode 100644 index 0000000000..7e8fbba376 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_dispatch.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_TOX_DISPATCH_H +#define C_TOXCORE_TOXCORE_TOX_DISPATCH_H + +#include "tox_events.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The events dispatch table. + * + * This holds all the callbacks registered with `tox_events_callback_*` + * functions below. + */ +typedef struct Tox_Dispatch Tox_Dispatch; + +typedef enum Tox_Err_Dispatch_New { + /** + * The function returned successfully. + */ + TOX_ERR_DISPATCH_NEW_OK, + + /** + * The function failed to allocate memory for the dispatch table. + */ + TOX_ERR_DISPATCH_NEW_MALLOC, +} Tox_Err_Dispatch_New; + +/** + * @brief Creates a new empty event dispatch table. + */ +Tox_Dispatch *tox_dispatch_new(Tox_Err_Dispatch_New *error); + +/** + * @brief Deallocate an event dispatch table. + */ +void tox_dispatch_free(Tox_Dispatch *dispatch); + +/** + * @brief Invoke registered callbacks for each of the events. + * + * @param dispatch The events dispatch table. + * @param events The events object received from @ref tox_events_iterate. + * @param tox The tox object to pass down to the callbacks. + * @param user_data User data pointer to pass down to the callbacks. + */ +void tox_dispatch_invoke(const Tox_Dispatch *dispatch, const Tox_Events *events, Tox *tox, void *user_data); + +typedef void tox_events_conference_connected_cb( + Tox *tox, const Tox_Event_Conference_Connected *event, void *user_data); +typedef void tox_events_conference_invite_cb( + Tox *tox, const Tox_Event_Conference_Invite *event, void *user_data); +typedef void tox_events_conference_message_cb( + Tox *tox, const Tox_Event_Conference_Message *event, void *user_data); +typedef void tox_events_conference_peer_list_changed_cb( + Tox *tox, const Tox_Event_Conference_Peer_List_Changed *event, void *user_data); +typedef void tox_events_conference_peer_name_cb( + Tox *tox, const Tox_Event_Conference_Peer_Name *event, void *user_data); +typedef void tox_events_conference_title_cb( + Tox *tox, const Tox_Event_Conference_Title *event, void *user_data); +typedef void tox_events_file_chunk_request_cb( + Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data); +typedef void tox_events_file_recv_cb( + Tox *tox, const Tox_Event_File_Recv *event, void *user_data); +typedef void tox_events_file_recv_chunk_cb( + Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data); +typedef void tox_events_file_recv_control_cb( + Tox *tox, const Tox_Event_File_Recv_Control *event, void *user_data); +typedef void tox_events_friend_connection_status_cb( + Tox *tox, const Tox_Event_Friend_Connection_Status *event, void *user_data); +typedef void tox_events_friend_lossless_packet_cb( + Tox *tox, const Tox_Event_Friend_Lossless_Packet *event, void *user_data); +typedef void tox_events_friend_lossy_packet_cb( + Tox *tox, const Tox_Event_Friend_Lossy_Packet *event, void *user_data); +typedef void tox_events_friend_message_cb( + Tox *tox, const Tox_Event_Friend_Message *event, void *user_data); +typedef void tox_events_friend_name_cb( + Tox *tox, const Tox_Event_Friend_Name *event, void *user_data); +typedef void tox_events_friend_read_receipt_cb( + Tox *tox, const Tox_Event_Friend_Read_Receipt *event, void *user_data); +typedef void tox_events_friend_request_cb( + Tox *tox, const Tox_Event_Friend_Request *event, void *user_data); +typedef void tox_events_friend_status_cb( + Tox *tox, const Tox_Event_Friend_Status *event, void *user_data); +typedef void tox_events_friend_status_message_cb( + Tox *tox, const Tox_Event_Friend_Status_Message *event, void *user_data); +typedef void tox_events_friend_typing_cb( + Tox *tox, const Tox_Event_Friend_Typing *event, void *user_data); +typedef void tox_events_self_connection_status_cb( + Tox *tox, const Tox_Event_Self_Connection_Status *event, void *user_data); + +void tox_events_callback_conference_connected( + Tox_Dispatch *dispatch, tox_events_conference_connected_cb *callback); +void tox_events_callback_conference_invite( + Tox_Dispatch *dispatch, tox_events_conference_invite_cb *callback); +void tox_events_callback_conference_message( + Tox_Dispatch *dispatch, tox_events_conference_message_cb *callback); +void tox_events_callback_conference_peer_list_changed( + Tox_Dispatch *dispatch, tox_events_conference_peer_list_changed_cb *callback); +void tox_events_callback_conference_peer_name( + Tox_Dispatch *dispatch, tox_events_conference_peer_name_cb *callback); +void tox_events_callback_conference_title( + Tox_Dispatch *dispatch, tox_events_conference_title_cb *callback); +void tox_events_callback_file_chunk_request( + Tox_Dispatch *dispatch, tox_events_file_chunk_request_cb *callback); +void tox_events_callback_file_recv( + Tox_Dispatch *dispatch, tox_events_file_recv_cb *callback); +void tox_events_callback_file_recv_chunk( + Tox_Dispatch *dispatch, tox_events_file_recv_chunk_cb *callback); +void tox_events_callback_file_recv_control( + Tox_Dispatch *dispatch, tox_events_file_recv_control_cb *callback); +void tox_events_callback_friend_connection_status( + Tox_Dispatch *dispatch, tox_events_friend_connection_status_cb *callback); +void tox_events_callback_friend_lossless_packet( + Tox_Dispatch *dispatch, tox_events_friend_lossless_packet_cb *callback); +void tox_events_callback_friend_lossy_packet( + Tox_Dispatch *dispatch, tox_events_friend_lossy_packet_cb *callback); +void tox_events_callback_friend_message( + Tox_Dispatch *dispatch, tox_events_friend_message_cb *callback); +void tox_events_callback_friend_name( + Tox_Dispatch *dispatch, tox_events_friend_name_cb *callback); +void tox_events_callback_friend_read_receipt( + Tox_Dispatch *dispatch, tox_events_friend_read_receipt_cb *callback); +void tox_events_callback_friend_request( + Tox_Dispatch *dispatch, tox_events_friend_request_cb *callback); +void tox_events_callback_friend_status( + Tox_Dispatch *dispatch, tox_events_friend_status_cb *callback); +void tox_events_callback_friend_status_message( + Tox_Dispatch *dispatch, tox_events_friend_status_message_cb *callback); +void tox_events_callback_friend_typing( + Tox_Dispatch *dispatch, tox_events_friend_typing_cb *callback); +void tox_events_callback_self_connection_status( + Tox_Dispatch *dispatch, tox_events_self_connection_status_cb *callback); + +#ifdef __cplusplus +} +#endif + +#endif // C_TOXCORE_TOXCORE_TOX_DISPATCH_H diff --git a/protocols/Tox/libtox/src/toxcore/tox_events.c b/protocols/Tox/libtox/src/toxcore/tox_events.c new file mode 100644 index 0000000000..91a7767f7b --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_events.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "tox_events.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bin_unpack.h" +#include "ccompat.h" +#include "events/events_alloc.h" +#include "tox.h" + + +/***************************************************** + * + * :: Set up event handlers. + * + *****************************************************/ + + +void tox_events_init(Tox *tox) +{ + tox_callback_conference_connected(tox, tox_events_handle_conference_connected); + tox_callback_conference_invite(tox, tox_events_handle_conference_invite); + tox_callback_conference_message(tox, tox_events_handle_conference_message); + tox_callback_conference_peer_list_changed(tox, tox_events_handle_conference_peer_list_changed); + tox_callback_conference_peer_name(tox, tox_events_handle_conference_peer_name); + tox_callback_conference_title(tox, tox_events_handle_conference_title); + tox_callback_file_chunk_request(tox, tox_events_handle_file_chunk_request); + tox_callback_file_recv_chunk(tox, tox_events_handle_file_recv_chunk); + tox_callback_file_recv_control(tox, tox_events_handle_file_recv_control); + tox_callback_file_recv(tox, tox_events_handle_file_recv); + tox_callback_friend_connection_status(tox, tox_events_handle_friend_connection_status); + tox_callback_friend_lossless_packet(tox, tox_events_handle_friend_lossless_packet); + tox_callback_friend_lossy_packet(tox, tox_events_handle_friend_lossy_packet); + tox_callback_friend_message(tox, tox_events_handle_friend_message); + tox_callback_friend_name(tox, tox_events_handle_friend_name); + tox_callback_friend_read_receipt(tox, tox_events_handle_friend_read_receipt); + tox_callback_friend_request(tox, tox_events_handle_friend_request); + tox_callback_friend_status_message(tox, tox_events_handle_friend_status_message); + tox_callback_friend_status(tox, tox_events_handle_friend_status); + tox_callback_friend_typing(tox, tox_events_handle_friend_typing); + tox_callback_self_connection_status(tox, tox_events_handle_self_connection_status); +} + +Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate *error) +{ + Tox_Events_State state = {TOX_ERR_EVENTS_ITERATE_OK}; + tox_iterate(tox, &state); + + if (error != nullptr) { + *error = state.error; + } + + if (fail_hard && state.error != TOX_ERR_EVENTS_ITERATE_OK) { + tox_events_free(state.events); + return nullptr; + } + + return state.events; +} + +bool tox_events_pack(const Tox_Events *events, Bin_Pack *bp) +{ + const uint32_t count = tox_events_get_conference_connected_size(events) + + tox_events_get_conference_invite_size(events) + + tox_events_get_conference_message_size(events) + + tox_events_get_conference_peer_list_changed_size(events) + + tox_events_get_conference_peer_name_size(events) + + tox_events_get_conference_title_size(events) + + tox_events_get_file_chunk_request_size(events) + + tox_events_get_file_recv_chunk_size(events) + + tox_events_get_file_recv_control_size(events) + + tox_events_get_file_recv_size(events) + + tox_events_get_friend_connection_status_size(events) + + tox_events_get_friend_lossless_packet_size(events) + + tox_events_get_friend_lossy_packet_size(events) + + tox_events_get_friend_message_size(events) + + tox_events_get_friend_name_size(events) + + tox_events_get_friend_read_receipt_size(events) + + tox_events_get_friend_request_size(events) + + tox_events_get_friend_status_message_size(events) + + tox_events_get_friend_status_size(events) + + tox_events_get_friend_typing_size(events) + + tox_events_get_self_connection_status_size(events); + + return bin_pack_array(bp, count) + && tox_events_pack_conference_connected(events, bp) + && tox_events_pack_conference_invite(events, bp) + && tox_events_pack_conference_message(events, bp) + && tox_events_pack_conference_peer_list_changed(events, bp) + && tox_events_pack_conference_peer_name(events, bp) + && tox_events_pack_conference_title(events, bp) + && tox_events_pack_file_chunk_request(events, bp) + && tox_events_pack_file_recv_chunk(events, bp) + && tox_events_pack_file_recv_control(events, bp) + && tox_events_pack_file_recv(events, bp) + && tox_events_pack_friend_connection_status(events, bp) + && tox_events_pack_friend_lossless_packet(events, bp) + && tox_events_pack_friend_lossy_packet(events, bp) + && tox_events_pack_friend_message(events, bp) + && tox_events_pack_friend_name(events, bp) + && tox_events_pack_friend_read_receipt(events, bp) + && tox_events_pack_friend_request(events, bp) + && tox_events_pack_friend_status_message(events, bp) + && tox_events_pack_friend_status(events, bp) + && tox_events_pack_friend_typing(events, bp) + && tox_events_pack_self_connection_status(events, bp); +} + +non_null() +static bool tox_event_unpack(Tox_Events *events, Bin_Unpack *bu) +{ + uint32_t size; + if (!bin_unpack_array(bu, &size)) { + return false; + } + + if (size != 2) { + return false; + } + + uint8_t type; + if (!bin_unpack_u08(bu, &type)) { + return false; + } + + switch (type) { + case TOX_EVENT_CONFERENCE_CONNECTED: + return tox_events_unpack_conference_connected(events, bu); + + case TOX_EVENT_CONFERENCE_INVITE: + return tox_events_unpack_conference_invite(events, bu); + + case TOX_EVENT_CONFERENCE_MESSAGE: + return tox_events_unpack_conference_message(events, bu); + + case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: + return tox_events_unpack_conference_peer_list_changed(events, bu); + + case TOX_EVENT_CONFERENCE_PEER_NAME: + return tox_events_unpack_conference_peer_name(events, bu); + + case TOX_EVENT_CONFERENCE_TITLE: + return tox_events_unpack_conference_title(events, bu); + + case TOX_EVENT_FILE_CHUNK_REQUEST: + return tox_events_unpack_file_chunk_request(events, bu); + + case TOX_EVENT_FILE_RECV_CHUNK: + return tox_events_unpack_file_recv_chunk(events, bu); + + case TOX_EVENT_FILE_RECV_CONTROL: + return tox_events_unpack_file_recv_control(events, bu); + + case TOX_EVENT_FILE_RECV: + return tox_events_unpack_file_recv(events, bu); + + case TOX_EVENT_FRIEND_CONNECTION_STATUS: + return tox_events_unpack_friend_connection_status(events, bu); + + case TOX_EVENT_FRIEND_LOSSLESS_PACKET: + return tox_events_unpack_friend_lossless_packet(events, bu); + + case TOX_EVENT_FRIEND_LOSSY_PACKET: + return tox_events_unpack_friend_lossy_packet(events, bu); + + case TOX_EVENT_FRIEND_MESSAGE: + return tox_events_unpack_friend_message(events, bu); + + case TOX_EVENT_FRIEND_NAME: + return tox_events_unpack_friend_name(events, bu); + + case TOX_EVENT_FRIEND_READ_RECEIPT: + return tox_events_unpack_friend_read_receipt(events, bu); + + case TOX_EVENT_FRIEND_REQUEST: + return tox_events_unpack_friend_request(events, bu); + + case TOX_EVENT_FRIEND_STATUS_MESSAGE: + return tox_events_unpack_friend_status_message(events, bu); + + case TOX_EVENT_FRIEND_STATUS: + return tox_events_unpack_friend_status(events, bu); + + case TOX_EVENT_FRIEND_TYPING: + return tox_events_unpack_friend_typing(events, bu); + + case TOX_EVENT_SELF_CONNECTION_STATUS: + return tox_events_unpack_self_connection_status(events, bu); + + default: + return false; + } + + return true; +} + +bool tox_events_unpack(Tox_Events *events, Bin_Unpack *bu) +{ + uint32_t size; + if (!bin_unpack_array(bu, &size)) { + return false; + } + + for (uint32_t i = 0; i < size; ++i) { + if (!tox_event_unpack(events, bu)) { + return false; + } + } + + return true; +} + +non_null(1) nullable(2) +static bool tox_events_bin_pack_handler(Bin_Pack *bp, const void *obj) +{ + return tox_events_pack((const Tox_Events *)obj, bp); +} + +uint32_t tox_events_bytes_size(const Tox_Events *events) +{ + return bin_pack_obj_size(tox_events_bin_pack_handler, events); +} + +void tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes) +{ + bin_pack_obj(tox_events_bin_pack_handler, events, bytes, UINT32_MAX); +} + +Tox_Events *tox_events_load(const uint8_t *bytes, uint32_t bytes_size) +{ + Bin_Unpack *bu = bin_unpack_new(bytes, bytes_size); + + if (bu == nullptr) { + return nullptr; + } + + Tox_Events *events = (Tox_Events *)calloc(1, sizeof(Tox_Events)); + + if (events == nullptr) { + bin_unpack_free(bu); + return nullptr; + } + + *events = (Tox_Events) { + nullptr + }; + + if (!tox_events_unpack(events, bu)) { + tox_events_free(events); + bin_unpack_free(bu); + return nullptr; + } + + bin_unpack_free(bu); + return events; +} + +bool tox_events_equal(const Tox_Events *a, const Tox_Events *b) +{ + const uint32_t a_size = tox_events_bytes_size(a); + const uint32_t b_size = tox_events_bytes_size(b); + + if (a_size != b_size) { + return false; + } + + uint8_t *a_bytes = (uint8_t *)malloc(a_size); + uint8_t *b_bytes = (uint8_t *)malloc(b_size); + + if (a_bytes == nullptr || b_bytes == nullptr) { + free(b_bytes); + free(a_bytes); + return false; + } + + tox_events_get_bytes(a, a_bytes); + tox_events_get_bytes(b, b_bytes); + + const bool ret = memcmp(a_bytes, b_bytes, a_size) == 0; + + free(b_bytes); + free(a_bytes); + + return ret; +} diff --git a/protocols/Tox/libtox/src/toxcore/tox_events.h b/protocols/Tox/libtox/src/toxcore/tox_events.h new file mode 100644 index 0000000000..f33a71b29c --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_events.h @@ -0,0 +1,354 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_TOX_EVENTS_H +#define C_TOXCORE_TOXCORE_TOX_EVENTS_H + +#include "tox.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Tox_Event_Conference_Connected Tox_Event_Conference_Connected; +uint32_t tox_event_conference_connected_get_conference_number( + const Tox_Event_Conference_Connected *conference_connected); + +typedef struct Tox_Event_Conference_Invite Tox_Event_Conference_Invite; +const uint8_t *tox_event_conference_invite_get_cookie( + const Tox_Event_Conference_Invite *conference_invite); +uint32_t tox_event_conference_invite_get_cookie_length( + const Tox_Event_Conference_Invite *conference_invite); +Tox_Conference_Type tox_event_conference_invite_get_type( + const Tox_Event_Conference_Invite *conference_invite); +uint32_t tox_event_conference_invite_get_friend_number( + const Tox_Event_Conference_Invite *conference_invite); + +typedef struct Tox_Event_Conference_Message Tox_Event_Conference_Message; +const uint8_t *tox_event_conference_message_get_message( + const Tox_Event_Conference_Message *conference_message); +uint32_t tox_event_conference_message_get_message_length( + const Tox_Event_Conference_Message *conference_message); +Tox_Message_Type tox_event_conference_message_get_type( + const Tox_Event_Conference_Message *conference_message); +uint32_t tox_event_conference_message_get_conference_number( + const Tox_Event_Conference_Message *conference_message); +uint32_t tox_event_conference_message_get_peer_number( + const Tox_Event_Conference_Message *conference_message); + +typedef struct Tox_Event_Conference_Peer_List_Changed Tox_Event_Conference_Peer_List_Changed; +uint32_t tox_event_conference_peer_list_changed_get_conference_number( + const Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed); + +typedef struct Tox_Event_Conference_Peer_Name Tox_Event_Conference_Peer_Name; +const uint8_t *tox_event_conference_peer_name_get_name( + const Tox_Event_Conference_Peer_Name *conference_peer_name); +uint32_t tox_event_conference_peer_name_get_name_length( + const Tox_Event_Conference_Peer_Name *conference_peer_name); +uint32_t tox_event_conference_peer_name_get_conference_number( + const Tox_Event_Conference_Peer_Name *conference_peer_name); +uint32_t tox_event_conference_peer_name_get_peer_number( + const Tox_Event_Conference_Peer_Name *conference_peer_name); + +typedef struct Tox_Event_Conference_Title Tox_Event_Conference_Title; +const uint8_t *tox_event_conference_title_get_title( + const Tox_Event_Conference_Title *conference_title); +uint32_t tox_event_conference_title_get_title_length( + const Tox_Event_Conference_Title *conference_title); +uint32_t tox_event_conference_title_get_conference_number( + const Tox_Event_Conference_Title *conference_title); +uint32_t tox_event_conference_title_get_peer_number( + const Tox_Event_Conference_Title *conference_title); + +typedef struct Tox_Event_File_Chunk_Request Tox_Event_File_Chunk_Request; +uint16_t tox_event_file_chunk_request_get_length( + const Tox_Event_File_Chunk_Request *file_chunk_request); +uint32_t tox_event_file_chunk_request_get_file_number( + const Tox_Event_File_Chunk_Request *file_chunk_request); +uint32_t tox_event_file_chunk_request_get_friend_number( + const Tox_Event_File_Chunk_Request *file_chunk_request); +uint64_t tox_event_file_chunk_request_get_position( + const Tox_Event_File_Chunk_Request *file_chunk_request); + +typedef struct Tox_Event_File_Recv Tox_Event_File_Recv; +const uint8_t *tox_event_file_recv_get_filename( + const Tox_Event_File_Recv *file_recv); +uint32_t tox_event_file_recv_get_filename_length( + const Tox_Event_File_Recv *file_recv); +uint32_t tox_event_file_recv_get_file_number( + const Tox_Event_File_Recv *file_recv); +uint64_t tox_event_file_recv_get_file_size( + const Tox_Event_File_Recv *file_recv); +uint32_t tox_event_file_recv_get_friend_number( + const Tox_Event_File_Recv *file_recv); +uint32_t tox_event_file_recv_get_kind( + const Tox_Event_File_Recv *file_recv); + +typedef struct Tox_Event_File_Recv_Chunk Tox_Event_File_Recv_Chunk; +const uint8_t *tox_event_file_recv_chunk_get_data( + const Tox_Event_File_Recv_Chunk *file_recv_chunk); +uint32_t tox_event_file_recv_chunk_get_length( + const Tox_Event_File_Recv_Chunk *file_recv_chunk); +uint32_t tox_event_file_recv_chunk_get_file_number( + const Tox_Event_File_Recv_Chunk *file_recv_chunk); +uint32_t tox_event_file_recv_chunk_get_friend_number( + const Tox_Event_File_Recv_Chunk *file_recv_chunk); +uint64_t tox_event_file_recv_chunk_get_position( + const Tox_Event_File_Recv_Chunk *file_recv_chunk); + +typedef struct Tox_Event_File_Recv_Control Tox_Event_File_Recv_Control; +Tox_File_Control tox_event_file_recv_control_get_control( + const Tox_Event_File_Recv_Control *file_recv_control); +uint32_t tox_event_file_recv_control_get_file_number( + const Tox_Event_File_Recv_Control *file_recv_control); +uint32_t tox_event_file_recv_control_get_friend_number( + const Tox_Event_File_Recv_Control *file_recv_control); + +typedef struct Tox_Event_Friend_Connection_Status Tox_Event_Friend_Connection_Status; +Tox_Connection tox_event_friend_connection_status_get_connection_status( + const Tox_Event_Friend_Connection_Status *friend_connection_status); +uint32_t tox_event_friend_connection_status_get_friend_number( + const Tox_Event_Friend_Connection_Status *friend_connection_status); + +typedef struct Tox_Event_Friend_Lossless_Packet Tox_Event_Friend_Lossless_Packet; +const uint8_t *tox_event_friend_lossless_packet_get_data( + const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet); +uint32_t tox_event_friend_lossless_packet_get_data_length( + const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet); +uint32_t tox_event_friend_lossless_packet_get_friend_number( + const Tox_Event_Friend_Lossless_Packet *friend_lossless_packet); + +typedef struct Tox_Event_Friend_Lossy_Packet Tox_Event_Friend_Lossy_Packet; +const uint8_t *tox_event_friend_lossy_packet_get_data( + const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet); +uint32_t tox_event_friend_lossy_packet_get_data_length( + const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet); +uint32_t tox_event_friend_lossy_packet_get_friend_number( + const Tox_Event_Friend_Lossy_Packet *friend_lossy_packet); + +typedef struct Tox_Event_Friend_Message Tox_Event_Friend_Message; +uint32_t tox_event_friend_message_get_friend_number( + const Tox_Event_Friend_Message *friend_message); +Tox_Message_Type tox_event_friend_message_get_type( + const Tox_Event_Friend_Message *friend_message); +uint32_t tox_event_friend_message_get_message_length( + const Tox_Event_Friend_Message *friend_message); +const uint8_t *tox_event_friend_message_get_message( + const Tox_Event_Friend_Message *friend_message); + +typedef struct Tox_Event_Friend_Name Tox_Event_Friend_Name; +const uint8_t *tox_event_friend_name_get_name( + const Tox_Event_Friend_Name *friend_name); +uint32_t tox_event_friend_name_get_name_length( + const Tox_Event_Friend_Name *friend_name); +uint32_t tox_event_friend_name_get_friend_number( + const Tox_Event_Friend_Name *friend_name); + +typedef struct Tox_Event_Friend_Read_Receipt Tox_Event_Friend_Read_Receipt; +uint32_t tox_event_friend_read_receipt_get_friend_number( + const Tox_Event_Friend_Read_Receipt *friend_read_receipt); +uint32_t tox_event_friend_read_receipt_get_message_id( + const Tox_Event_Friend_Read_Receipt *friend_read_receipt); + +typedef struct Tox_Event_Friend_Request Tox_Event_Friend_Request; +const uint8_t *tox_event_friend_request_get_message( + const Tox_Event_Friend_Request *friend_request); +const uint8_t *tox_event_friend_request_get_public_key( + const Tox_Event_Friend_Request *friend_request); +uint32_t tox_event_friend_request_get_message_length( + const Tox_Event_Friend_Request *friend_request); + +typedef struct Tox_Event_Friend_Status Tox_Event_Friend_Status; +Tox_User_Status tox_event_friend_status_get_status( + const Tox_Event_Friend_Status *friend_status); +uint32_t tox_event_friend_status_get_friend_number( + const Tox_Event_Friend_Status *friend_status); + +typedef struct Tox_Event_Friend_Status_Message Tox_Event_Friend_Status_Message; +const uint8_t *tox_event_friend_status_message_get_message( + const Tox_Event_Friend_Status_Message *friend_status_message); +uint32_t tox_event_friend_status_message_get_message_length( + const Tox_Event_Friend_Status_Message *friend_status_message); +uint32_t tox_event_friend_status_message_get_friend_number( + const Tox_Event_Friend_Status_Message *friend_status_message); + +typedef struct Tox_Event_Friend_Typing Tox_Event_Friend_Typing; +bool tox_event_friend_typing_get_typing( + const Tox_Event_Friend_Typing *friend_typing); +uint32_t tox_event_friend_typing_get_friend_number( + const Tox_Event_Friend_Typing *friend_typing); + +typedef struct Tox_Event_Self_Connection_Status Tox_Event_Self_Connection_Status; +Tox_Connection tox_event_self_connection_status_get_connection_status( + const Tox_Event_Self_Connection_Status *self_connection_status); + + +typedef enum Tox_Event { + TOX_EVENT_SELF_CONNECTION_STATUS = 0, + + TOX_EVENT_FRIEND_REQUEST = 1, + TOX_EVENT_FRIEND_CONNECTION_STATUS = 2, + TOX_EVENT_FRIEND_LOSSY_PACKET = 3, + TOX_EVENT_FRIEND_LOSSLESS_PACKET = 4, + + TOX_EVENT_FRIEND_NAME = 5, + TOX_EVENT_FRIEND_STATUS = 6, + TOX_EVENT_FRIEND_STATUS_MESSAGE = 7, + + TOX_EVENT_FRIEND_MESSAGE = 8, + TOX_EVENT_FRIEND_READ_RECEIPT = 9, + TOX_EVENT_FRIEND_TYPING = 10, + + TOX_EVENT_FILE_CHUNK_REQUEST = 11, + TOX_EVENT_FILE_RECV = 12, + TOX_EVENT_FILE_RECV_CHUNK = 13, + TOX_EVENT_FILE_RECV_CONTROL = 14, + + TOX_EVENT_CONFERENCE_INVITE = 15, + TOX_EVENT_CONFERENCE_CONNECTED = 16, + TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED = 17, + TOX_EVENT_CONFERENCE_PEER_NAME = 18, + TOX_EVENT_CONFERENCE_TITLE = 19, + + TOX_EVENT_CONFERENCE_MESSAGE = 20, +} Tox_Event; + +/** + * Container object for all Tox core events. + * + * This is an immutable object once created. + */ +typedef struct Tox_Events Tox_Events; + +uint32_t tox_events_get_conference_connected_size(const Tox_Events *events); +uint32_t tox_events_get_conference_invite_size(const Tox_Events *events); +uint32_t tox_events_get_conference_message_size(const Tox_Events *events); +uint32_t tox_events_get_conference_peer_list_changed_size(const Tox_Events *events); +uint32_t tox_events_get_conference_peer_name_size(const Tox_Events *events); +uint32_t tox_events_get_conference_title_size(const Tox_Events *events); +uint32_t tox_events_get_file_chunk_request_size(const Tox_Events *events); +uint32_t tox_events_get_file_recv_chunk_size(const Tox_Events *events); +uint32_t tox_events_get_file_recv_control_size(const Tox_Events *events); +uint32_t tox_events_get_file_recv_size(const Tox_Events *events); +uint32_t tox_events_get_friend_connection_status_size(const Tox_Events *events); +uint32_t tox_events_get_friend_lossless_packet_size(const Tox_Events *events); +uint32_t tox_events_get_friend_lossy_packet_size(const Tox_Events *events); +uint32_t tox_events_get_friend_message_size(const Tox_Events *events); +uint32_t tox_events_get_friend_name_size(const Tox_Events *events); +uint32_t tox_events_get_friend_read_receipt_size(const Tox_Events *events); +uint32_t tox_events_get_friend_request_size(const Tox_Events *events); +uint32_t tox_events_get_friend_status_message_size(const Tox_Events *events); +uint32_t tox_events_get_friend_status_size(const Tox_Events *events); +uint32_t tox_events_get_friend_typing_size(const Tox_Events *events); +uint32_t tox_events_get_self_connection_status_size(const Tox_Events *events); + +const Tox_Event_Conference_Connected *tox_events_get_conference_connected( + const Tox_Events *events, uint32_t index); +const Tox_Event_Conference_Invite *tox_events_get_conference_invite( + const Tox_Events *events, uint32_t index); +const Tox_Event_Conference_Message *tox_events_get_conference_message( + const Tox_Events *events, uint32_t index); +const Tox_Event_Conference_Peer_List_Changed *tox_events_get_conference_peer_list_changed( + const Tox_Events *events, uint32_t index); +const Tox_Event_Conference_Peer_Name *tox_events_get_conference_peer_name( + const Tox_Events *events, uint32_t index); +const Tox_Event_Conference_Title *tox_events_get_conference_title( + const Tox_Events *events, uint32_t index); +const Tox_Event_File_Chunk_Request *tox_events_get_file_chunk_request( + const Tox_Events *events, uint32_t index); +const Tox_Event_File_Recv_Chunk *tox_events_get_file_recv_chunk( + const Tox_Events *events, uint32_t index); +const Tox_Event_File_Recv_Control *tox_events_get_file_recv_control( + const Tox_Events *events, uint32_t index); +const Tox_Event_File_Recv *tox_events_get_file_recv( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Connection_Status *tox_events_get_friend_connection_status( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Lossless_Packet *tox_events_get_friend_lossless_packet( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Lossy_Packet *tox_events_get_friend_lossy_packet( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Message *tox_events_get_friend_message( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Name *tox_events_get_friend_name( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Read_Receipt *tox_events_get_friend_read_receipt( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Request *tox_events_get_friend_request( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Status_Message *tox_events_get_friend_status_message( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Status *tox_events_get_friend_status( + const Tox_Events *events, uint32_t index); +const Tox_Event_Friend_Typing *tox_events_get_friend_typing( + const Tox_Events *events, uint32_t index); +const Tox_Event_Self_Connection_Status *tox_events_get_self_connection_status( + const Tox_Events *events, uint32_t index); + +/** + * Initialise the events recording system. + * + * All callbacks will be set to handlers inside the events recording system. + * After this function returns, no user-defined event handlers will be + * invoked. If the client sets their own handlers after calling this function, + * the events associated with that handler will not be recorded. + */ +void tox_events_init(Tox *tox); + +typedef enum Tox_Err_Events_Iterate { + /** + * The function returned successfully. + */ + TOX_ERR_EVENTS_ITERATE_OK, + + /** + * The function failed to allocate enough memory to store the events. + * + * Some events may still be stored if the return value is NULL. The events + * object will always be valid (or NULL) but if this error code is set, + * the function may have missed some events. + */ + TOX_ERR_EVENTS_ITERATE_MALLOC, +} Tox_Err_Events_Iterate; + +/** + * Run a single `tox_iterate` iteration and record all the events. + * + * If allocation of the top level events object fails, this returns NULL. + * Otherwise it returns an object with the recorded events in it. If an + * allocation fails while recording events, some events may be dropped. + * + * If @p fail_hard is `true`, any failure will result in NULL, so all recorded + * events will be dropped. + * + * The result must be freed using `tox_events_free`. + * + * @param tox The Tox instance to iterate on. + * @param fail_hard Drop all events when any allocation fails. + * @param error An error code. Will be set to OK on success. + * + * @return the recorded events structure. + */ +Tox_Events *tox_events_iterate(Tox *tox, bool fail_hard, Tox_Err_Events_Iterate *error); + +/** + * Frees all memory associated with the events structure. + * + * All pointers into this object and its sub-objects, including byte buffers, + * will be invalid once this function returns. + */ +void tox_events_free(Tox_Events *events); + +uint32_t tox_events_bytes_size(const Tox_Events *events); +void tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes); + +Tox_Events *tox_events_load(const uint8_t *bytes, uint32_t bytes_size); + +bool tox_events_equal(const Tox_Events *a, const Tox_Events *b); + +#ifdef __cplusplus +} +#endif + +#endif // C_TOXCORE_TOXCORE_TOX_EVENTS_H diff --git a/protocols/Tox/libtox/src/toxcore/tox_private.c b/protocols/Tox/libtox/src/toxcore/tox_private.c new file mode 100644 index 0000000000..847e96d426 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_private.c @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2022 The TokTok team. + * Copyright © 2013 Tox project. + */ + +/** + * The Tox private API (for tests). + */ +#include "tox_private.h" + +#include <assert.h> + +#include "ccompat.h" +#include "network.h" +#include "tox_struct.h" + +#define SET_ERROR_PARAMETER(param, x) \ + do { \ + if (param != nullptr) { \ + *param = x; \ + } \ + } while (0) + +Tox_System tox_default_system(void) +{ + const Tox_System sys = { + nullptr, // mono_time_callback + nullptr, // mono_time_user_data + system_random(), + system_network(), + }; + return sys; +} + +void tox_lock(const Tox *tox) +{ + if (tox->mutex != nullptr) { + pthread_mutex_lock(tox->mutex); + } +} + +void tox_unlock(const Tox *tox) +{ + if (tox->mutex != nullptr) { + pthread_mutex_unlock(tox->mutex); + } +} + +void tox_callback_friend_lossy_packet_per_pktid(Tox *tox, tox_friend_lossy_packet_cb *callback, uint8_t pktid) +{ + assert(tox != nullptr); + + if (pktid >= PACKET_ID_RANGE_LOSSY_START && pktid <= PACKET_ID_RANGE_LOSSY_END) { + tox->friend_lossy_packet_callback_per_pktid[pktid] = callback; + } +} + +void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless_packet_cb *callback, uint8_t pktid) +{ + assert(tox != nullptr); + + if ((pktid >= PACKET_ID_RANGE_LOSSLESS_CUSTOM_START && pktid <= PACKET_ID_RANGE_LOSSLESS_CUSTOM_END) + || pktid == PACKET_ID_MSI) { + tox->friend_lossless_packet_callback_per_pktid[pktid] = callback; + } +} + +void tox_set_av_object(Tox *tox, void *object) +{ + assert(tox != nullptr); + tox_lock(tox); + tox->toxav_object = object; + tox_unlock(tox); +} + +void *tox_get_av_object(const Tox *tox) +{ + assert(tox != nullptr); + tox_lock(tox); + void *object = tox->toxav_object; + tox_unlock(tox); + return object; +} + +void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback) +{ + assert(tox != nullptr); + tox->dht_get_nodes_response_callback = callback; +} + +bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port, + const uint8_t *target_public_key, Tox_Err_Dht_Get_Nodes *error) +{ + assert(tox != nullptr); + + tox_lock(tox); + + if (tox->m->options.udp_disabled) { + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_UDP_DISABLED); + tox_unlock(tox); + return false; + } + + if (public_key == nullptr || ip == nullptr || target_public_key == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_NULL); + tox_unlock(tox); + return false; + } + + if (port == 0) { + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_PORT); + tox_unlock(tox); + return false; + } + + IP_Port *root; + + const int32_t count = net_getipport(ip, &root, TOX_SOCK_DGRAM); + + if (count < 1) { + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_IP); + net_freeipport(root); + tox_unlock(tox); + return false; + } + + bool success = false; + + for (int32_t i = 0; i < count; ++i) { + root[i].port = net_htons(port); + + if (dht_getnodes(tox->m->dht, &root[i], public_key, target_public_key)) { + success = true; + } + } + + tox_unlock(tox); + + net_freeipport(root); + + if (!success) { + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_FAIL); + return false; + } + + SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_OK); + + return true; +} diff --git a/protocols/Tox/libtox/src/toxcore/tox_private.h b/protocols/Tox/libtox/src/toxcore/tox_private.h index 593282f7c8..71d7a97601 100644 --- a/protocols/Tox/libtox/src/toxcore/tox_private.h +++ b/protocols/Tox/libtox/src/toxcore/tox_private.h @@ -10,10 +10,26 @@ #include <stddef.h> #include <stdint.h> +#include "tox.h" + #ifdef __cplusplus extern "C" { #endif +typedef uint64_t tox_mono_time_cb(void *user_data); + +struct Tox_System { + tox_mono_time_cb *mono_time_callback; + void *mono_time_user_data; + const struct Random *rng; + const struct Network *ns; +}; + +Tox_System tox_default_system(void); + +void tox_lock(const Tox *tox); +void tox_unlock(const Tox *tox); + /** * Set the callback for the `friend_lossy_packet` event for a specific packet ID. * Pass NULL to unset. @@ -37,6 +53,92 @@ void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless void tox_set_av_object(Tox *tox, void *object); void *tox_get_av_object(const Tox *tox); + +/******************************************************************************* + * + * :: DHT network queries. + * + ******************************************************************************/ + +/** + * The minimum size of an IP string buffer in bytes. + */ +#define TOX_DHT_NODE_IP_STRING_SIZE 96 + +uint32_t tox_dht_node_ip_string_size(void); + +/** + * The size of a DHT node public key in bytes. + */ +#define TOX_DHT_NODE_PUBLIC_KEY_SIZE 32 + +uint32_t tox_dht_node_public_key_size(void); + +/** + * @param public_key The node's public key. + * @param ip The node's IP address, represented as a null terminated string. + * @param port The node's port. + */ +typedef void tox_dht_get_nodes_response_cb(Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port, + void *user_data); + + +/** + * Set the callback for the `dht_get_nodes_response` event. Pass NULL to unset. + * + * This event is triggered when a getnodes response is received from a DHT peer. + */ +void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback); + + +typedef enum Tox_Err_Dht_Get_Nodes { + /** + * The function returned successfully. + */ + TOX_ERR_DHT_GET_NODES_OK, + + /** + * UDP is disabled in tox options; the DHT can only be queried when UDP is enabled. + */ + TOX_ERR_DHT_GET_NODES_UDP_DISABLED, + + /** + * One of the arguments to the function was NULL when it was not expected. + */ + TOX_ERR_DHT_GET_NODES_NULL, + + /** + * The supplied port is invalid. + */ + TOX_ERR_DHT_GET_NODES_BAD_PORT, + + /** + * The supplied IP address is invalid. + */ + TOX_ERR_DHT_GET_NODES_BAD_IP, + + /** + * The getnodes request failed. This usually means the packet failed to send. + */ + TOX_ERR_DHT_GET_NODES_FAIL, +} Tox_Err_Dht_Get_Nodes; + +/** + * This function sends a getnodes request to a DHT node for its peers that + * are "close" to the passed target public key according to the distance metric used + * by the DHT implementation. + * + * @param public_key The public key of the node that we wish to query. This key must be + * at least `TOX_DHT_NODE_PUBLIC_KEY_SIZE` bytes in length. + * @param ip A NULL terminated string representing the IP address of the node we wish to query. + * @param port The port of the node we wish to query. + * @param target_public_key The public key for which we want to find close nodes. + * + * @return true on success. + */ +bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port, + const uint8_t *target_public_key, Tox_Err_Dht_Get_Nodes *error); + #ifdef __cplusplus } #endif diff --git a/protocols/Tox/libtox/src/toxcore/tox_struct.h b/protocols/Tox/libtox/src/toxcore/tox_struct.h new file mode 100644 index 0000000000..22d1c54a27 --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_struct.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2016-2022 The TokTok team. + * Copyright © 2013 Tox project. + */ + +#ifndef C_TOXCORE_TOXCORE_TOX_STRUCT_H +#define C_TOXCORE_TOXCORE_TOX_STRUCT_H + +#include "Messenger.h" +#include "tox.h" +#include "tox_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Tox { + Messenger *m; + Mono_Time *mono_time; + Random rng; + Network ns; + pthread_mutex_t *mutex; + + tox_log_cb *log_callback; + tox_self_connection_status_cb *self_connection_status_callback; + tox_friend_name_cb *friend_name_callback; + tox_friend_status_message_cb *friend_status_message_callback; + tox_friend_status_cb *friend_status_callback; + tox_friend_connection_status_cb *friend_connection_status_callback; + tox_friend_typing_cb *friend_typing_callback; + tox_friend_read_receipt_cb *friend_read_receipt_callback; + tox_friend_request_cb *friend_request_callback; + tox_friend_message_cb *friend_message_callback; + tox_file_recv_control_cb *file_recv_control_callback; + tox_file_chunk_request_cb *file_chunk_request_callback; + tox_file_recv_cb *file_recv_callback; + tox_file_recv_chunk_cb *file_recv_chunk_callback; + tox_conference_invite_cb *conference_invite_callback; + tox_conference_connected_cb *conference_connected_callback; + tox_conference_message_cb *conference_message_callback; + tox_conference_title_cb *conference_title_callback; + tox_conference_peer_name_cb *conference_peer_name_callback; + tox_conference_peer_list_changed_cb *conference_peer_list_changed_callback; + tox_dht_get_nodes_response_cb *dht_get_nodes_response_callback; + tox_friend_lossy_packet_cb *friend_lossy_packet_callback_per_pktid[UINT8_MAX + 1]; + tox_friend_lossless_packet_cb *friend_lossless_packet_callback_per_pktid[UINT8_MAX + 1]; + + void *toxav_object; // workaround to store a ToxAV object (setter and getter functions are available) +}; + +#ifdef __cplusplus +} +#endif + +#endif // C_TOXCORE_TOXCORE_TOX_STRUCT_H diff --git a/protocols/Tox/libtox/src/toxcore/tox_unpack.c b/protocols/Tox/libtox/src/toxcore/tox_unpack.c new file mode 100644 index 0000000000..6508399e7e --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_unpack.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#include "tox_unpack.h" + +#include <stdint.h> + +#include "bin_unpack.h" +#include "ccompat.h" + +bool tox_unpack_conference_type(Bin_Unpack *bu, Tox_Conference_Type *val) +{ + uint32_t u32; + + if (!bin_unpack_u32(bu, &u32)) { + return false; + } + + *val = (Tox_Conference_Type)u32; + return true; +} + +bool tox_unpack_connection(Bin_Unpack *bu, Tox_Connection *val) +{ + uint32_t u32; + + if (!bin_unpack_u32(bu, &u32)) { + return false; + } + + *val = (Tox_Connection)u32; + return true; +} + +bool tox_unpack_file_control(Bin_Unpack *bu, Tox_File_Control *val) +{ + uint32_t u32; + + if (!bin_unpack_u32(bu, &u32)) { + return false; + } + + *val = (Tox_File_Control)u32; + return true; +} + +bool tox_unpack_message_type(Bin_Unpack *bu, Tox_Message_Type *val) +{ + uint32_t u32; + + if (!bin_unpack_u32(bu, &u32)) { + return false; + } + + *val = (Tox_Message_Type)u32; + return true; +} + +bool tox_unpack_user_status(Bin_Unpack *bu, Tox_User_Status *val) +{ + uint32_t u32; + + if (!bin_unpack_u32(bu, &u32)) { + return false; + } + + *val = (Tox_User_Status)u32; + return true; +} diff --git a/protocols/Tox/libtox/src/toxcore/tox_unpack.h b/protocols/Tox/libtox/src/toxcore/tox_unpack.h new file mode 100644 index 0000000000..5f0b18ad1b --- /dev/null +++ b/protocols/Tox/libtox/src/toxcore/tox_unpack.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2022 The TokTok team. + */ + +#ifndef C_TOXCORE_TOXCORE_TOX_UNPACK_H +#define C_TOXCORE_TOXCORE_TOX_UNPACK_H + +#include "attributes.h" +#include "bin_unpack.h" +#include "tox.h" + +non_null() bool tox_unpack_conference_type(Bin_Unpack *bu, Tox_Conference_Type *val); +non_null() bool tox_unpack_connection(Bin_Unpack *bu, Tox_Connection *val); +non_null() bool tox_unpack_file_control(Bin_Unpack *bu, Tox_File_Control *val); +non_null() bool tox_unpack_message_type(Bin_Unpack *bu, Tox_Message_Type *val); +non_null() bool tox_unpack_user_status(Bin_Unpack *bu, Tox_User_Status *val); + +#endif // C_TOXCORE_TOXCORE_TOX_UNPACK_H diff --git a/protocols/Tox/libtox/src/toxcore/util.c b/protocols/Tox/libtox/src/toxcore/util.c index 8174ef3fa2..c70c3a76f5 100644 --- a/protocols/Tox/libtox/src/toxcore/util.c +++ b/protocols/Tox/libtox/src/toxcore/util.c @@ -17,19 +17,39 @@ #include <string.h> #include <time.h> -#include "crypto_core.h" /* for CRYPTO_PUBLIC_KEY_SIZE */ +#include "ccompat.h" +bool is_power_of_2(uint64_t x) +{ + return x != 0 && (x & (~x + 1)) == x; +} -/** id functions */ -bool id_equal(const uint8_t *dest, const uint8_t *src) +void free_uint8_t_pointer_array(uint8_t **ary, size_t n_items) { - return public_key_cmp(dest, src) == 0; + if (ary == nullptr) { + return; + } + + for (size_t i = 0; i < n_items; ++i) { + if (ary[i] != nullptr) { + free(ary[i]); + } + } + + free(ary); } -uint32_t id_copy(uint8_t *dest, const uint8_t *src) +uint16_t data_checksum(const uint8_t *data, uint32_t length) { - memcpy(dest, src, CRYPTO_PUBLIC_KEY_SIZE); - return CRYPTO_PUBLIC_KEY_SIZE; + uint8_t checksum[2] = {0}; + uint16_t check; + + for (uint32_t i = 0; i < length; ++i) { + checksum[i % 2] ^= data[i]; + } + + memcpy(&check, checksum, sizeof(check)); + return check; } int create_recursive_mutex(pthread_mutex_t *mutex) @@ -56,6 +76,11 @@ int create_recursive_mutex(pthread_mutex_t *mutex) return 0; } +bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size) +{ + return a_size == b_size && memcmp(a, b, a_size) == 0; +} + int16_t max_s16(int16_t a, int16_t b) { return a > b ? a : b; @@ -107,3 +132,19 @@ uint64_t min_u64(uint64_t a, uint64_t b) { return a < b ? a : b; } + +uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len) +{ + uint32_t hash = 0; + + for (uint32_t i = 0; i < len; ++i) { + hash += key[i]; + hash += hash << 10; + hash ^= hash >> 6; + } + + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; +} diff --git a/protocols/Tox/libtox/src/toxcore/util.h b/protocols/Tox/libtox/src/toxcore/util.h index bcae4f4a67..588feb5694 100644 --- a/protocols/Tox/libtox/src/toxcore/util.h +++ b/protocols/Tox/libtox/src/toxcore/util.h @@ -12,20 +12,33 @@ #include <pthread.h> #include <stdbool.h> +#include <stddef.h> #include <stdint.h> -#include "logger.h" +#include "attributes.h" #ifdef __cplusplus extern "C" { #endif -/** id functions */ -bool id_equal(const uint8_t *dest, const uint8_t *src); -uint32_t id_copy(uint8_t *dest, const uint8_t *src); /* return value is CLIENT_ID_SIZE */ +bool is_power_of_2(uint64_t x); + +/** @brief Frees all pointers in a uint8_t pointer array, as well as the array itself. */ +nullable(1) +void free_uint8_t_pointer_array(uint8_t **ary, size_t n_items); /** Returns -1 if failed or 0 if success */ -int create_recursive_mutex(pthread_mutex_t *mutex); +non_null() int create_recursive_mutex(pthread_mutex_t *mutex); + +/** + * @brief Checks whether two buffers are the same length and contents. + * + * Calls `memcmp` after checking the sizes are equal. + * + * @retval true if sizes and contents are equal. + * @retval false otherwise. + */ +non_null() bool memeq(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size); // Safe min/max functions with specific types. This forces the conversion to the // desired type before the comparison expression, giving the choice of @@ -47,6 +60,20 @@ uint16_t min_u16(uint16_t a, uint16_t b); uint32_t min_u32(uint32_t a, uint32_t b); uint64_t min_u64(uint64_t a, uint64_t b); +/** @brief Returns a 32-bit hash of key of size len */ +non_null() +uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len); + +/** @brief Computes a checksum of a byte array. + * + * @param data The byte array used to compute the checksum. + * @param length The length in bytes of the passed data. + * + * @retval The resulting checksum. + */ +non_null() +uint16_t data_checksum(const uint8_t *data, uint32_t length); + #ifdef __cplusplus } // extern "C" #endif diff --git a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c index 1d464534d0..45003055f4 100644 --- a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c +++ b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.c @@ -27,7 +27,7 @@ static_assert(TOX_PASS_ENCRYPTION_EXTRA_LENGTH == (crypto_box_MACBYTES + crypto_ #define SET_ERROR_PARAMETER(param, x) \ do { \ - if (param) { \ + if (param != nullptr) { \ *param = x; \ } \ } while (0) @@ -50,9 +50,9 @@ struct Tox_Pass_Key { uint8_t key[TOX_PASS_KEY_LENGTH]; }; -void tox_pass_key_free(Tox_Pass_Key *pass_key) +void tox_pass_key_free(Tox_Pass_Key *key) { - free(pass_key); + free(key); } /* Clients should consider alerting their users that, unlike plain data, if even one bit @@ -60,64 +60,92 @@ void tox_pass_key_free(Tox_Pass_Key *pass_key) * Ditto if they forget their password, there is no way to recover the data. */ -/* This retrieves the salt used to encrypt the given data, which can then be passed to - * tox_pass_key_derive_with_salt to produce the same key as was previously used. Any encrpyted - * data with this module can be used as input. +/** + * Retrieves the salt used to encrypt the given data. + * + * The retrieved salt can then be passed to tox_pass_key_derive_with_salt to + * produce the same key as was previously used. Any data encrypted with this + * module can be used as input. + * + * The cipher text must be at least TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes in length. + * The salt must be TOX_PASS_SALT_LENGTH bytes in length. + * If the passed byte arrays are smaller than required, the behaviour is + * undefined. * - * returns true if magic number matches - * success does not say anything about the validity of the data, only that data of - * the appropriate size was copied + * If the cipher text pointer or the salt is NULL, this function returns false. + * + * Success does not say anything about the validity of the data, only that + * data of the appropriate size was copied. + * + * @return true on success. */ -bool tox_get_salt(const uint8_t *data, uint8_t *salt, Tox_Err_Get_Salt *error) +bool tox_get_salt(const uint8_t *ciphertext, uint8_t *salt, Tox_Err_Get_Salt *error) { - if (!data || !salt) { + if (ciphertext == nullptr || salt == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_NULL); return false; } - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { + if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_BAD_FORMAT); return false; } - data += TOX_ENC_SAVE_MAGIC_LENGTH; - memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); + ciphertext += TOX_ENC_SAVE_MAGIC_LENGTH; + memcpy(salt, ciphertext, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); SET_ERROR_PARAMETER(error, TOX_ERR_GET_SALT_OK); return true; } -/* Generates a secret symmetric key from the given passphrase. out_key must be at least - * TOX_PASS_KEY_LENGTH bytes long. - * Be sure to not compromise the key! Only keep it in memory, do not write to disk. - * The password is zeroed after key derivation. - * The key should only be used with the other functions in this module, as it - * includes a salt. - * Note that this function is not deterministic; to derive the same key from a - * password, you also must know the random salt that was used. See below. +/** + * Generates a secret symmetric key from the given passphrase. + * + * Be sure to not compromise the key! Only keep it in memory, do not write + * it to disk. + * + * Note that this function is not deterministic; to derive the same key from + * a password, you also must know the random salt that was used. A + * deterministic version of this function is `tox_pass_key_derive_with_salt`. + * + * @param passphrase The user-provided password. Can be empty. + * @param passphrase_len The length of the password. * - * returns true on success + * @return new symmetric key on success, NULL on failure. */ -Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t pplength, +Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len, Tox_Err_Key_Derivation *error) { + const Random *rng = system_random(); + + if (rng == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED); + return nullptr; + } + uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; - random_bytes(salt, sizeof(salt)); - return tox_pass_key_derive_with_salt(passphrase, pplength, salt, error); + random_bytes(rng, salt, sizeof(salt)); + return tox_pass_key_derive_with_salt(passphrase, passphrase_len, salt, error); } -/* Same as above, except with use the given salt for deterministic key derivation. - * The salt must be TOX_PASS_SALT_LENGTH bytes in length. +/** + * Same as above, except use the given salt for deterministic key derivation. + * + * @param passphrase The user-provided password. Can be empty. + * @param passphrase_len The length of the password. + * @param salt An array of at least TOX_PASS_SALT_LENGTH bytes. + * + * @return new symmetric key on success, NULL on failure. */ -Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pplength, +Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len, const uint8_t *salt, Tox_Err_Key_Derivation *error) { - if (!salt || (!passphrase && pplength != 0)) { + if (salt == nullptr || (passphrase == nullptr && passphrase_len != 0)) { SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL); return nullptr; } uint8_t passkey[crypto_hash_sha256_BYTES]; - crypto_hash_sha256(passkey, passphrase, pplength); + crypto_hash_sha256(passkey, passphrase, passphrase_len); uint8_t key[CRYPTO_SHARED_KEY_SIZE]; @@ -138,7 +166,7 @@ Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pp Tox_Pass_Key *out_key = (Tox_Pass_Key *)calloc(1, sizeof(Tox_Pass_Key)); - if (!out_key) { + if (out_key == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED); return nullptr; } @@ -164,9 +192,16 @@ Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t pp bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, Tox_Err_Encryption *error) { - if (plaintext_len == 0 || !plaintext || !key || !ciphertext) { + const Random *rng = system_random(); + + if (rng == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED); + return false; + } + + if (plaintext_len == 0 || plaintext == nullptr || key == nullptr || ciphertext == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL); - return 0; + return false; } // the output data consists of, in order: @@ -185,7 +220,7 @@ bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, siz ciphertext += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; uint8_t nonce[crypto_box_NONCEBYTES]; - random_nonce(nonce); + random_nonce(rng, nonce); memcpy(ciphertext, nonce, crypto_box_NONCEBYTES); ciphertext += crypto_box_NONCEBYTES; @@ -193,135 +228,161 @@ bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, siz if (encrypt_data_symmetric(key->key, nonce, plaintext, plaintext_len, ciphertext) != plaintext_len + crypto_box_MACBYTES) { SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED); - return 0; + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_OK); - return 1; + return true; } -/* Encrypts the given data with the given passphrase. The output array must be - * at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates - * to tox_derive_key and tox_pass_key_encrypt. +/** + * Encrypts the given data with the given passphrase. * - * returns true on success + * The output array must be at least `plaintext_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH` + * bytes long. This delegates to tox_pass_key_derive and + * tox_pass_key_encrypt. + * + * @param plaintext A byte array of length `plaintext_len`. + * @param plaintext_len The length of the plain text array. Bigger than 0. + * @param passphrase The user-provided password. Can be empty. + * @param passphrase_len The length of the password. + * @param ciphertext The cipher text array to write the encrypted data to. + * + * @return true on success. */ -bool tox_pass_encrypt(const uint8_t *data, size_t data_len, const uint8_t *passphrase, size_t pplength, uint8_t *out, - Tox_Err_Encryption *error) +bool tox_pass_encrypt(const uint8_t *plaintext, size_t plaintext_len, const uint8_t *passphrase, size_t passphrase_len, + uint8_t *ciphertext, Tox_Err_Encryption *error) { Tox_Err_Key_Derivation err; - Tox_Pass_Key *key = tox_pass_key_derive(passphrase, pplength, &err); + Tox_Pass_Key *key = tox_pass_key_derive(passphrase, passphrase_len, &err); - if (!key) { + if (key == nullptr) { if (err == TOX_ERR_KEY_DERIVATION_NULL) { SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL); } else if (err == TOX_ERR_KEY_DERIVATION_FAILED) { SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED); } - return 0; + return false; } - bool result = tox_pass_key_encrypt(key, data, data_len, out, error); + const bool result = tox_pass_key_encrypt(key, plaintext, plaintext_len, ciphertext, error); tox_pass_key_free(key); return result; } -/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by - * tox_derive_key. +/** + * This is the inverse of tox_pass_key_encrypt, also using only keys produced by + * tox_pass_key_derive or tox_pass_key_derive_with_salt. * - * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH + * @param ciphertext A byte array of length `ciphertext_len`. + * @param ciphertext_len The length of the cipher text array. At least TOX_PASS_ENCRYPTION_EXTRA_LENGTH. + * @param plaintext The plain text array to write the decrypted data to. * - * returns true on success + * @return true on success. */ -bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *data, size_t length, uint8_t *out, - Tox_Err_Decryption *error) +bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len, + uint8_t *plaintext, Tox_Err_Decryption *error) { - if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) { + if (ciphertext_len <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH); - return 0; + return false; } - if (!data || !key || !out) { + if (ciphertext == nullptr || key == nullptr || plaintext == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL); - return 0; + return false; } - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { + if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT); - return 0; + return false; } - data += TOX_ENC_SAVE_MAGIC_LENGTH; - data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation + ciphertext += TOX_ENC_SAVE_MAGIC_LENGTH; + ciphertext += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation - size_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; + const size_t decrypt_length = ciphertext_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; uint8_t nonce[crypto_box_NONCEBYTES]; - memcpy(nonce, data, crypto_box_NONCEBYTES); - data += crypto_box_NONCEBYTES; + memcpy(nonce, ciphertext, crypto_box_NONCEBYTES); + ciphertext += crypto_box_NONCEBYTES; - /* decrypt the data */ - if (decrypt_data_symmetric(key->key, nonce, data, decrypt_length + crypto_box_MACBYTES, out) + /* decrypt the ciphertext */ + if (decrypt_data_symmetric(key->key, nonce, ciphertext, decrypt_length + crypto_box_MACBYTES, plaintext) != decrypt_length) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED); - return 0; + return false; } SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_OK); - return 1; + return true; } -/* Decrypts the given data with the given passphrase. The output array must be - * at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates - * to tox_pass_key_decrypt. +/** + * Decrypts the given data with the given passphrase. * - * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH + * The output array must be at least `ciphertext_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH` + * bytes long. This delegates to tox_pass_key_decrypt. * - * returns true on success + * @param ciphertext A byte array of length `ciphertext_len`. + * @param ciphertext_len The length of the cipher text array. At least TOX_PASS_ENCRYPTION_EXTRA_LENGTH. + * @param passphrase The user-provided password. Can be empty. + * @param passphrase_len The length of the password. + * @param plaintext The plain text array to write the decrypted data to. + * + * @return true on success. */ -bool tox_pass_decrypt(const uint8_t *data, size_t length, const uint8_t *passphrase, size_t pplength, uint8_t *out, - Tox_Err_Decryption *error) +bool tox_pass_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t *passphrase, + size_t passphrase_len, uint8_t *plaintext, Tox_Err_Decryption *error) { - if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) { + if (ciphertext_len <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH); - return 0; + return false; } - if (!data || !passphrase || !out) { + if (ciphertext == nullptr || passphrase == nullptr || plaintext == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL); - return 0; + return false; } - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { + if (memcmp(ciphertext, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT); - return 0; + return false; } uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; - memcpy(salt, data + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); + memcpy(salt, ciphertext + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); /* derive the key */ - Tox_Pass_Key *key = tox_pass_key_derive_with_salt(passphrase, pplength, salt, nullptr); + Tox_Pass_Key *key = tox_pass_key_derive_with_salt(passphrase, passphrase_len, salt, nullptr); - if (!key) { + if (key == nullptr) { /* out of memory most likely */ SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED); - return 0; + return false; } - bool result = tox_pass_key_decrypt(key, data, length, out, error); + const bool result = tox_pass_key_decrypt(key, ciphertext, ciphertext_len, plaintext, error); tox_pass_key_free(key); return result; } -/* Determines whether or not the given data is encrypted (by checking the magic number) +/** + * Determines whether or not the given data is encrypted by this module. + * + * It does this check by verifying that the magic number is the one put in + * place by the encryption functions. + * + * The data must be at least TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes in length. + * If the passed byte array is smaller than required, the behaviour is + * undefined. + * + * If the data pointer is NULL, the behaviour is undefined + * + * @return true if the data is encrypted by this module. */ bool tox_is_data_encrypted(const uint8_t *data) { - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0) { - return 1; - } - - return 0; + return memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0; } diff --git a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h index 3f22b75884..90d8255eec 100644 --- a/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h +++ b/protocols/Tox/libtox/src/toxencryptsave/toxencryptsave.h @@ -223,8 +223,8 @@ bool tox_pass_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, const ui * * A pass-key and a password are two different concepts: a password is given * by the user in plain text. A pass-key is the generated symmetric key used - * for encryption and decryption. It is derived from a salt and the user- - * provided password. + * for encryption and decryption. It is derived from a salt and the + * user-provided password. * * The Tox_Pass_Key structure is hidden in the implementation. It can be created * using tox_pass_key_derive or tox_pass_key_derive_with_salt and must be deallocated using tox_pass_key_free. @@ -235,10 +235,10 @@ typedef struct Tox_Pass_Key Tox_Pass_Key; #endif /* TOX_PASS_KEY_DEFINED */ /** - * Deallocate a Tox_Pass_Key. This function behaves like free(), so NULL is an + * Deallocate a Tox_Pass_Key. This function behaves like `free()`, so NULL is an * acceptable argument value. */ -void tox_pass_key_free(struct Tox_Pass_Key *key); +void tox_pass_key_free(Tox_Pass_Key *key); /** * Generates a secret symmetric key from the given passphrase. @@ -253,9 +253,9 @@ void tox_pass_key_free(struct Tox_Pass_Key *key); * @param passphrase The user-provided password. Can be empty. * @param passphrase_len The length of the password. * - * @return true on success. + * @return new symmetric key on success, NULL on failure. */ -struct Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len, +Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passphrase_len, Tox_Err_Key_Derivation *error); /** @@ -265,9 +265,9 @@ struct Tox_Pass_Key *tox_pass_key_derive(const uint8_t *passphrase, size_t passp * @param passphrase_len The length of the password. * @param salt An array of at least TOX_PASS_SALT_LENGTH bytes. * - * @return true on success. + * @return new symmetric key on success, NULL on failure. */ -struct Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len, +Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, size_t passphrase_len, const uint8_t *salt, Tox_Err_Key_Derivation *error); /** @@ -282,7 +282,7 @@ struct Tox_Pass_Key *tox_pass_key_derive_with_salt(const uint8_t *passphrase, si * * @return true on success. */ -bool tox_pass_key_encrypt(const struct Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len, +bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, Tox_Err_Encryption *error); /** @@ -295,7 +295,7 @@ bool tox_pass_key_encrypt(const struct Tox_Pass_Key *key, const uint8_t *plainte * * @return true on success. */ -bool tox_pass_key_decrypt(const struct Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len, +bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, Tox_Err_Decryption *error); typedef enum Tox_Err_Get_Salt { |