Squashed 'external/toxcore/c-toxcore/' changes from c9cdae001..9ed2fa80d

9ed2fa80d fix(toxav): remove extra copy of video frame on encode
de30cf3ad docs: Add new file kinds, that should be useful to all clients.
d5b5e879d fix(DHT): Correct node skipping logic timed out nodes.
30e71fe97 refactor: Generate event dispatch functions and add tox_events_dispatch.
8fdbb0b50 style: Format parameter lists in event handlers.
d00dee12b refactor: Add warning logs when losing chat invites.
b144e8db1 feat: Add a way to look up a file number by ID.
849281ea0 feat: Add a way to fetch groups by chat ID.
a2c177396 refactor: Harden event system and improve type safety.
8f5caa656 refactor: Add MessagePack string support to bin_pack.
34e8d5ad5 chore: Add GitHub CodeQL workflow and local Docker runner.
f7b068010 refactor: Add nullability annotations to event headers.
788abe651 refactor(toxav): Use system allocator for mutexes.
2e4b423eb refactor: Use specific typedefs for public API arrays.
2baf34775 docs(toxav): update idle iteration interval see 679444751876fa3882a717772918ebdc8f083354
2f87ac67b feat: Add Event Loop abstraction (Ev).
f8dfc38d8 test: Fix data race in ToxScenario virtual_clock.
38313921e test(TCP): Add regression test for TCP priority queue integrity.
f94a50d9a refactor(toxav): Replace mutable_mutex with dynamically allocated mutex.
ad054511e refactor: Internalize DHT structs and add debug helpers.
8b467cc96 fix: Prevent potential integer overflow in group chat handshake.
4962bdbb8 test: Improve TCP simulation and add tests
5f0227093 refactor: Allow nullable data in group chat handlers.
e97b18ea9 chore: Improve Windows Docker support.
b14943bbd refactor: Move Logger out of Messenger into Tox.
dd3136250 cleanup: Apply nullability qualifiers to C++ codebase.
1849f70fc refactor: Extract low-level networking code to net and os_network.
8fec75421 refactor: Delete tox_random, align on rng and os_random.
a03ae8051 refactor: Delete tox_memory, align on mem and os_memory.
4c88fed2c refactor: Use `std::` prefixes more consistently in C++ code.
72452f2ae test: Add some more tests for onion and shared key cache.
d5a51b09a cleanup: Use tox_attributes.h in tox_private.h and install it.
b6f5b9fc5 test: Add some benchmarks for various high level things.
8a8d02785 test(support): Introduce threaded Tox runner and simulation barrier
d68d1d095 perf(toxav): optimize audio and video intermediate buffers by keeping them around
REVERT: c9cdae001 fix(toxav): remove extra copy of video frame on encode

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 9ed2fa80d582c714d6bdde6a7648220a92cddff8
This commit is contained in:
Green Sky
2026-02-01 14:26:52 +01:00
parent 565efa4f39
commit 9b36dd9d99
274 changed files with 11891 additions and 4292 deletions

View File

@@ -3,7 +3,11 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <mutex>
#include <vector>
#include "network_universe.hh"
@@ -27,8 +31,14 @@ int FakeSocket::close()
return 0;
}
int FakeSocket::getsockopt(int level, int optname, void *optval, size_t *optlen) { return 0; }
int FakeSocket::setsockopt(int level, int optname, const void *optval, size_t optlen) { return 0; }
int FakeSocket::getsockopt(int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen)
{
return 0;
}
int FakeSocket::setsockopt(int level, int optname, const void *_Nonnull optval, size_t optlen)
{
return 0;
}
int FakeSocket::socket_nonblock(bool nonblock)
{
nonblocking_ = nonblock;
@@ -59,7 +69,7 @@ void FakeUdpSocket::close_impl()
}
}
int FakeUdpSocket::bind(const IP_Port *addr)
int FakeUdpSocket::bind(const IP_Port *_Nonnull addr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (local_port_ != 0)
@@ -80,7 +90,7 @@ int FakeUdpSocket::bind(const IP_Port *addr)
return -1;
}
int FakeUdpSocket::connect(const IP_Port *addr)
int FakeUdpSocket::connect(const IP_Port *_Nonnull addr)
{
// UDP connect just sets default dest.
// Not strictly needed for toxcore UDP but good for completeness.
@@ -92,23 +102,29 @@ int FakeUdpSocket::listen(int backlog)
errno = EOPNOTSUPP;
return -1;
}
std::unique_ptr<FakeSocket> FakeUdpSocket::accept(IP_Port *addr)
std::unique_ptr<FakeSocket> FakeUdpSocket::accept(IP_Port *_Nullable addr)
{
errno = EOPNOTSUPP;
return nullptr;
}
int FakeUdpSocket::send(const uint8_t *buf, size_t len)
int FakeUdpSocket::send(const uint8_t *_Nonnull buf, size_t len)
{
errno = EDESTADDRREQ;
return -1;
}
int FakeUdpSocket::recv(uint8_t *buf, size_t len)
int FakeUdpSocket::recv(uint8_t *_Nonnull buf, size_t len)
{
errno = EOPNOTSUPP;
return -1;
}
int FakeUdpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr)
size_t FakeUdpSocket::recv_buffer_size()
{
std::lock_guard<std::mutex> lock(mutex_);
return recv_queue_.size();
}
int FakeUdpSocket::sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (local_port_ == 0) {
@@ -132,16 +148,15 @@ int FakeUdpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr)
universe_.send_packet(p);
if (universe_.is_verbose()) {
uint32_t tip4 = net_ntohl(addr->ip.ip.v4.uint32);
Ip_Ntoa ip_str;
net_ip_ntoa(&addr->ip, &ip_str);
std::cerr << "[FakeUdpSocket] sent " << len << " bytes from port " << local_port_ << " to "
<< ((tip4 >> 24) & 0xFF) << "." << ((tip4 >> 16) & 0xFF) << "."
<< ((tip4 >> 8) & 0xFF) << "." << (tip4 & 0xFF) << ":" << net_ntohs(addr->port)
<< std::endl;
<< ip_str.buf << ":" << net_ntohs(addr->port) << std::endl;
}
return len;
}
int FakeUdpSocket::recvfrom(uint8_t *buf, size_t len, IP_Port *addr)
int FakeUdpSocket::recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr)
{
RecvObserver observer_copy;
std::vector<uint8_t> data_copy;
@@ -196,15 +211,13 @@ void FakeUdpSocket::push_packet(std::vector<uint8_t> data, IP_Port from)
{
std::lock_guard<std::mutex> lock(mutex_);
if (universe_.is_verbose()) {
uint32_t fip4 = net_ntohl(from.ip.ip.v4.uint32);
Ip_Ntoa local_ip_str, from_ip_str;
net_ip_ntoa(&ip_, &local_ip_str);
net_ip_ntoa(&from.ip, &from_ip_str);
std::cerr << "[FakeUdpSocket] push " << data.size() << " bytes into queue for "
<< ((ip_.ip.v4.uint32 >> 24) & 0xFF)
<< "." // ip_ is in network order from net_htonl
<< ((ip_.ip.v4.uint32 >> 16) & 0xFF) << "." << ((ip_.ip.v4.uint32 >> 8) & 0xFF)
<< "." << (ip_.ip.v4.uint32 & 0xFF) << ":" << local_port_ << " from "
<< ((fip4 >> 24) & 0xFF) << "." << ((fip4 >> 16) & 0xFF) << "."
<< ((fip4 >> 8) & 0xFF) << "." << (fip4 & 0xFF) << ":" << net_ntohs(from.port)
<< std::endl;
<< local_ip_str.buf << ":" << local_port_ << " from " << from_ip_str.buf << ":"
<< net_ntohs(from.port) << std::endl;
}
recv_queue_.push_back({std::move(data), from});
}
@@ -225,8 +238,8 @@ void FakeUdpSocket::set_recv_observer(RecvObserver observer)
FakeTcpSocket::FakeTcpSocket(NetworkUniverse &universe)
: FakeSocket(universe, SOCK_STREAM)
, remote_addr_{}
{
ipport_reset(&remote_addr_);
}
FakeTcpSocket::~FakeTcpSocket() { close_impl(); }
@@ -234,6 +247,17 @@ FakeTcpSocket::~FakeTcpSocket() { close_impl(); }
int FakeTcpSocket::close()
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ == ESTABLISHED || state_ == SYN_SENT || state_ == SYN_RECEIVED
|| state_ == CLOSE_WAIT) {
// Send RST to peer
Packet p{};
p.from.ip = ip_;
p.from.port = net_htons(local_port_);
p.to = remote_addr_;
p.is_tcp = true;
p.tcp_flags = 0x04; // RST
universe_.send_packet(p);
}
close_impl();
return 0;
}
@@ -247,7 +271,7 @@ void FakeTcpSocket::close_impl()
state_ = CLOSED;
}
int FakeTcpSocket::bind(const IP_Port *addr)
int FakeTcpSocket::bind(const IP_Port *_Nonnull addr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (local_port_ != 0)
@@ -276,14 +300,25 @@ int FakeTcpSocket::listen(int backlog)
return 0;
}
int FakeTcpSocket::connect(const IP_Port *addr)
int FakeTcpSocket::connect(const IP_Port *_Nonnull addr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (universe_.is_verbose()) {
Ip_Ntoa ip_str, dest_str;
net_ip_ntoa(&ip_, &ip_str);
net_ip_ntoa(&addr->ip, &dest_str);
std::cerr << "[FakeTcpSocket] connect from " << ip_str.buf << " to " << dest_str.buf << ":"
<< net_ntohs(addr->port) << std::endl;
}
if (local_port_ == 0) {
// Implicit bind
uint16_t p = universe_.find_free_port(ip_);
if (universe_.bind_tcp(ip_, p, this)) {
local_port_ = p;
if (universe_.is_verbose()) {
std::cerr << "[FakeTcpSocket] implicit bind to port " << local_port_ << std::endl;
}
} else {
errno = EADDRINUSE;
return -1;
@@ -310,7 +345,7 @@ int FakeTcpSocket::connect(const IP_Port *addr)
return -1;
}
std::unique_ptr<FakeSocket> FakeTcpSocket::accept(IP_Port *addr)
std::unique_ptr<FakeSocket> FakeTcpSocket::accept(IP_Port *_Nullable addr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ != LISTEN) {
@@ -318,13 +353,16 @@ std::unique_ptr<FakeSocket> FakeTcpSocket::accept(IP_Port *addr)
return nullptr;
}
if (pending_connections_.empty()) {
auto it = std::find_if(pending_connections_.begin(), pending_connections_.end(),
[](const std::unique_ptr<FakeTcpSocket> &s) { return s->state() == ESTABLISHED; });
if (it == pending_connections_.end()) {
errno = EWOULDBLOCK;
return nullptr;
}
auto client = std::move(pending_connections_.front());
pending_connections_.pop_front();
auto client = std::move(*it);
pending_connections_.erase(it);
if (addr) {
*addr = client->remote_addr();
@@ -332,11 +370,19 @@ std::unique_ptr<FakeSocket> FakeTcpSocket::accept(IP_Port *addr)
return client;
}
int FakeTcpSocket::send(const uint8_t *buf, size_t len)
int FakeTcpSocket::send(const uint8_t *_Nonnull buf, size_t len)
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ != ESTABLISHED) {
errno = ENOTCONN;
if (universe_.is_verbose()) {
std::cerr << "[FakeTcpSocket] send failed: state " << state_ << " port " << local_port_
<< std::endl;
}
if (state_ == SYN_SENT || state_ == SYN_RECEIVED) {
errno = EWOULDBLOCK;
} else {
errno = ENOTCONN;
}
return -1;
}
@@ -357,7 +403,7 @@ int FakeTcpSocket::send(const uint8_t *buf, size_t len)
return len;
}
int FakeTcpSocket::recv(uint8_t *buf, size_t len)
int FakeTcpSocket::recv(uint8_t *_Nonnull buf, size_t len)
{
std::lock_guard<std::mutex> lock(mutex_);
if (recv_buffer_.empty()) {
@@ -368,6 +414,13 @@ int FakeTcpSocket::recv(uint8_t *buf, size_t len)
}
size_t actual = std::min(len, recv_buffer_.size());
if (universe_.is_verbose() && actual > 0) {
char remote_ip_str[TOX_INET_ADDRSTRLEN];
ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str));
std::cerr << "[FakeTcpSocket] Port " << local_port_ << " (Peer: " << remote_ip_str << ":"
<< net_ntohs(remote_addr_.port) << ") recv requested " << len << " got " << actual
<< " (remaining " << recv_buffer_.size() - actual << ")" << std::endl;
}
for (size_t i = 0; i < actual; ++i) {
buf[i] = recv_buffer_.front();
recv_buffer_.pop_front();
@@ -381,37 +434,109 @@ size_t FakeTcpSocket::recv_buffer_size()
return recv_buffer_.size();
}
int FakeTcpSocket::sendto(const uint8_t *buf, size_t len, const IP_Port *addr)
bool FakeTcpSocket::is_readable()
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ == LISTEN) {
return std::any_of(pending_connections_.begin(), pending_connections_.end(),
[](const std::unique_ptr<FakeTcpSocket> &s) { return s->state() == ESTABLISHED; });
}
return !recv_buffer_.empty() || state_ == CLOSED || state_ == CLOSE_WAIT;
}
bool FakeTcpSocket::is_writable()
{
std::lock_guard<std::mutex> lock(mutex_);
return state_ == ESTABLISHED;
}
int FakeTcpSocket::sendto(const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr)
{
errno = EOPNOTSUPP;
return -1;
}
int FakeTcpSocket::recvfrom(uint8_t *buf, size_t len, IP_Port *addr)
int FakeTcpSocket::recvfrom(uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr)
{
errno = EOPNOTSUPP;
return -1;
}
void FakeTcpSocket::handle_packet(const Packet &p)
int FakeTcpSocket::getsockopt(
int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen)
{
if (universe_.is_verbose()) {
std::cerr << "[FakeTcpSocket] getsockopt level=" << level << " optname=" << optname
<< " state=" << state_ << std::endl;
}
if (level == SOL_SOCKET && optname == SO_ERROR) {
int error = 0;
if (state_ == SYN_SENT || state_ == SYN_RECEIVED) {
error = EINPROGRESS;
} else if (state_ == CLOSED) {
error = ECONNREFUSED;
}
if (*optlen >= sizeof(int)) {
*static_cast<int *>(optval) = error;
*optlen = sizeof(int);
}
if (universe_.is_verbose()) {
std::cerr << "[FakeTcpSocket] getsockopt SO_ERROR returning error=" << error
<< std::endl;
}
return 0;
}
return 0;
}
bool FakeTcpSocket::handle_packet(const Packet &p)
{
std::lock_guard<std::mutex> lock(mutex_);
if (universe_.is_verbose()) {
std::cerr << "Handle Packet: Port " << local_port_ << " Flags "
<< static_cast<int>(p.tcp_flags) << " State " << state_ << std::endl;
char remote_ip_str[TOX_INET_ADDRSTRLEN];
ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str));
std::cerr << "Handle Packet: Port " << local_port_ << " (Peer: " << remote_ip_str << ":"
<< net_ntohs(remote_addr_.port) << ") Flags " << TcpFlags{p.tcp_flags}
<< " State " << state_ << " From " << net_ntohs(p.from.port) << std::endl;
}
if (state_ != LISTEN) {
// Filter packets not from our peer
bool port_match = net_ntohs(p.from.port) == net_ntohs(remote_addr_.port);
bool ip_match = ip_equal(&p.from.ip, &remote_addr_.ip)
|| (is_loopback(p.from.ip) && ip_equal(&remote_addr_.ip, &ip_))
|| (is_loopback(remote_addr_.ip) && ip_equal(&p.from.ip, &ip_));
if (!port_match || !ip_match) {
return false;
}
if (p.tcp_flags & 0x04) { // RST
state_ = CLOSED;
if (local_port_ != 0) {
universe_.unbind_tcp(ip_, local_port_, this);
local_port_ = 0;
}
return true;
}
}
if (state_ == LISTEN) {
if (p.tcp_flags & 0x02) { // SYN
// Check for duplicate SYN from same peer
for (const auto &pending : pending_connections_) {
if (ipport_equal(&p.from, &pending->remote_addr_)) {
return true;
}
}
// Create new socket for connection
auto new_sock = std::make_unique<FakeTcpSocket>(universe_);
// Bind to ephemeral? No, it's accepted on the same port but distinct 4-tuple.
// In our simplified model, the new socket is not bound to the global map
// until accepted? Or effectively bound to the 4-tuple.
// For now, let's just create it and queue it.
new_sock->state_ = SYN_RECEIVED;
new_sock->remote_addr_ = p.from;
new_sock->local_port_ = local_port_;
new_sock->set_ip(ip_); // Inherit IP from listening socket
new_sock->last_ack_ = p.seq + 1;
new_sock->next_seq_ = 1000; // Random ISN
@@ -428,13 +553,9 @@ void FakeTcpSocket::handle_packet(const Packet &p)
universe_.send_packet(resp);
// In real TCP, we wait for ACK to move to ESTABLISHED and accept queue.
// Here we cheat and move to ESTABLISHED immediately or wait for ACK?
// Let's wait for ACK.
// But where do we store this half-open socket?
// For simplicity: auto-transition to ESTABLISHED and queue it.
new_sock->state_ = ESTABLISHED;
// Add to pending, but it's still SYN_RECEIVED
pending_connections_.push_back(std::move(new_sock));
return true;
}
} else if (state_ == SYN_SENT) {
if ((p.tcp_flags & 0x12) == 0x12) { // SYN | ACK
@@ -451,8 +572,31 @@ void FakeTcpSocket::handle_packet(const Packet &p)
ack.seq = next_seq_;
ack.ack = last_ack_;
universe_.send_packet(ack);
return true;
} else if (p.tcp_flags & 0x02) { // SYN (Simultaneous Open)
state_ = SYN_RECEIVED;
last_ack_ = p.seq + 1;
// Send SYN-ACK
Packet resp{};
resp.from = p.to;
resp.to = p.from;
resp.is_tcp = true;
resp.tcp_flags = 0x12; // SYN | ACK
resp.seq = next_seq_++;
resp.ack = last_ack_;
universe_.send_packet(resp);
return true;
}
} else if (state_ == ESTABLISHED) {
} else if (state_ == SYN_RECEIVED) {
if (p.tcp_flags & 0x10) { // ACK
state_ = ESTABLISHED;
} else {
return false;
}
}
if (state_ == ESTABLISHED) {
if (p.tcp_flags & 0x01) { // FIN
state_ = CLOSE_WAIT;
// Send ACK
@@ -464,12 +608,23 @@ void FakeTcpSocket::handle_packet(const Packet &p)
ack.seq = next_seq_;
ack.ack = p.seq + 1; // Consume FIN
universe_.send_packet(ack);
} else if (!p.data.empty()) {
recv_buffer_.insert(recv_buffer_.end(), p.data.begin(), p.data.end());
last_ack_ += p.data.size();
// Should send ACK?
return true;
} else {
if (!p.data.empty()) {
if (universe_.is_verbose()) {
char remote_ip_str[TOX_INET_ADDRSTRLEN];
ip_parse_addr(&remote_addr_.ip, remote_ip_str, sizeof(remote_ip_str));
std::cerr << "[FakeTcpSocket] Port " << local_port_
<< " (Peer: " << remote_ip_str << ":" << net_ntohs(remote_addr_.port)
<< ") adding " << p.data.size() << " bytes to buffer (currently "
<< recv_buffer_.size() << ")" << std::endl;
}
recv_buffer_.insert(recv_buffer_.end(), p.data.begin(), p.data.end());
}
return true;
}
}
return false;
}
std::unique_ptr<FakeTcpSocket> FakeTcpSocket::create_connected(
@@ -482,4 +637,23 @@ std::unique_ptr<FakeTcpSocket> FakeTcpSocket::create_connected(
return s;
}
std::ostream &operator<<(std::ostream &os, FakeTcpSocket::State state)
{
switch (state) {
case FakeTcpSocket::CLOSED:
return os << "CLOSED";
case FakeTcpSocket::LISTEN:
return os << "LISTEN";
case FakeTcpSocket::SYN_SENT:
return os << "SYN_SENT";
case FakeTcpSocket::SYN_RECEIVED:
return os << "SYN_RECEIVED";
case FakeTcpSocket::ESTABLISHED:
return os << "ESTABLISHED";
case FakeTcpSocket::CLOSE_WAIT:
return os << "CLOSE_WAIT";
}
return os << "UNKNOWN(" << static_cast<int>(state) << ")";
}
} // namespace tox::test