Files
tomato/toxcore/os_network.c
Green Sky 9b36dd9d99 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
2026-02-01 14:26:52 +01:00

787 lines
20 KiB
C

/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2026 The TokTok team.
* Copyright © 2013 Tox project.
*/
#ifdef __APPLE__
#define _DARWIN_C_SOURCE
#endif /* __APPLE__ */
// For Solaris.
#ifdef __sun
#define __EXTENSIONS__ 1
#endif /* __sun */
// For Linux (and some BSDs).
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#endif /* _XOPEN_SOURCE */
#include "os_network.h"
#if defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#endif /* defined(_WIN32) && defined(_WIN32_WINNT) && defined(_WIN32_WINNT_WINXP) && _WIN32_WINNT >= _WIN32_WINNT_WINXP */
#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32))
#define OS_WIN32
#endif /* !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) */
#if defined(OS_WIN32) && !defined(WINVER)
// Windows XP
#define WINVER 0x0501
#endif /* defined(OS_WIN32) && !defined(WINVER) */
#ifdef OS_WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#endif /* OS_WIN32 */
#if !defined(OS_WIN32)
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __sun
#include <stropts.h>
#include <sys/filio.h>
#endif /* __sun */
#else
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 27
#endif /* IPV6_V6ONLY */
#endif /* !defined(OS_WIN32) */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attributes.h"
#include "ccompat.h"
#include "mem.h"
#include "net.h"
// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif /* !defined(MSG_NOSIGNAL) */
static int make_proto(int proto)
{
switch (proto) {
case TOX_PROTO_TCP:
return IPPROTO_TCP;
case TOX_PROTO_UDP:
return IPPROTO_UDP;
default:
return proto;
}
}
static int make_socktype(int type)
{
switch (type) {
case TOX_SOCK_STREAM:
return SOCK_STREAM;
case TOX_SOCK_DGRAM:
return SOCK_DGRAM;
default:
return type;
}
}
static int make_family(int family_value)
{
switch (family_value) {
case TOX_AF_INET:
case TCP_INET:
return AF_INET;
case TOX_AF_INET6:
case TCP_INET6:
return AF_INET6;
case TOX_AF_UNSPEC:
return AF_UNSPEC;
default:
return family_value;
}
}
static void get_ip4(IP4 *_Nonnull result, const struct in_addr *_Nonnull addr)
{
static_assert(sizeof(result->uint32) == sizeof(addr->s_addr),
"Tox and operating system don't agree on size of IPv4 addresses");
result->uint32 = addr->s_addr;
}
static void get_ip6(IP6 *_Nonnull result, const struct in6_addr *_Nonnull addr)
{
static_assert(sizeof(result->uint8) == sizeof(addr->s6_addr),
"Tox and operating system don't agree on size of IPv6 addresses");
memcpy(result->uint8, addr->s6_addr, sizeof(result->uint8));
}
static void fill_addr4(const IP4 *_Nonnull ip, struct in_addr *_Nonnull addr)
{
addr->s_addr = ip->uint32;
}
static void fill_addr6(const IP6 *_Nonnull ip, struct in6_addr *_Nonnull addr)
{
memcpy(addr->s6_addr, ip->uint8, sizeof(ip->uint8));
}
#ifndef OS_WIN32
#define INVALID_SOCKET (-1)
#endif /* OS_WIN32 */
typedef struct Network_Addr {
struct sockaddr_storage addr;
size_t size;
} Network_Addr;
static void ip_port_to_network_addr(const IP_Port *ip_port, Network_Addr *addr)
{
addr->size = 0;
if (net_family_is_ipv4(ip_port->ip.family)) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr;
addr->size = sizeof(struct sockaddr_in);
addr4->sin_family = AF_INET;
fill_addr4(&ip_port->ip.ip.v4, &addr4->sin_addr);
addr4->sin_port = ip_port->port;
} else if (net_family_is_ipv6(ip_port->ip.family)) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr->addr;
addr->size = sizeof(struct sockaddr_in6);
addr6->sin6_family = AF_INET6;
fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr);
addr6->sin6_port = ip_port->port;
addr6->sin6_flowinfo = 0;
addr6->sin6_scope_id = 0;
}
}
static bool network_addr_to_ip_port(const Network_Addr *addr, IP_Port *ip_port)
{
if (addr->addr.ss_family == AF_INET) {
const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr->addr;
ip_port->ip.family = net_family_ipv4();
get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr);
ip_port->port = addr_in->sin_port;
return true;
} else if (addr->addr.ss_family == AF_INET6) {
const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr->addr;
ip_port->ip.family = net_family_ipv6();
get_ip6(&ip_port->ip.ip.v6, &addr_in6->sin6_addr);
ip_port->port = addr_in6->sin6_port;
return true;
}
return false;
}
#if !defined(OS_WIN32)
static bool should_ignore_recv_error(int err)
{
return err == EWOULDBLOCK;
}
static bool should_ignore_connect_error(int err)
{
return err == EWOULDBLOCK || err == EINPROGRESS;
}
static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize)
{
return inet_ntop(AF_INET, addr, buf, bufsize);
}
static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize)
{
return inet_ntop(AF_INET6, addr, buf, bufsize);
}
static int inet_pton4(const char *_Nonnull addr_string, struct in_addr *_Nonnull addrbuf)
{
return inet_pton(AF_INET, addr_string, addrbuf);
}
static int inet_pton6(const char *_Nonnull addr_string, struct in6_addr *_Nonnull addrbuf)
{
return inet_pton(AF_INET6, addr_string, addrbuf);
}
#else
static bool should_ignore_recv_error(int err)
{
// We ignore WSAECONNRESET as Windows helpfully* sends that error if a
// previously sent UDP packet wasn't delivered.
return err == WSAEWOULDBLOCK || err == WSAECONNRESET;
}
static bool should_ignore_connect_error(int err)
{
return err == WSAEWOULDBLOCK || err == WSAEINPROGRESS;
}
static const char *inet_ntop4(const struct in_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize)
{
struct sockaddr_in saddr = {0};
saddr.sin_family = AF_INET;
saddr.sin_addr = *addr;
DWORD len = (DWORD)bufsize;
if (WSAAddressToStringA((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) {
return nullptr;
}
return buf;
}
static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnull buf, size_t bufsize)
{
struct sockaddr_in6 saddr = {0};
saddr.sin6_family = AF_INET6;
saddr.sin6_addr = *addr;
DWORD len = (DWORD)bufsize;
if (WSAAddressToStringA((LPSOCKADDR)&saddr, sizeof(saddr), nullptr, buf, &len)) {
return nullptr;
}
return buf;
}
static int inet_pton4(const char *_Nonnull addrString, struct in_addr *_Nonnull addrbuf)
{
struct sockaddr_in saddr = {0};
INT len = sizeof(saddr);
if (WSAStringToAddressA((LPSTR)addrString, AF_INET, nullptr, (LPSOCKADDR)&saddr, &len)) {
return 0;
}
*addrbuf = saddr.sin_addr;
return 1;
}
static int inet_pton6(const char *_Nonnull addrString, struct in6_addr *_Nonnull addrbuf)
{
struct sockaddr_in6 saddr = {0};
INT len = sizeof(saddr);
if (WSAStringToAddressA((LPSTR)addrString, AF_INET6, nullptr, (LPSOCKADDR)&saddr, &len)) {
return 0;
}
*addrbuf = saddr.sin6_addr;
return 1;
}
#endif /* !defined(OS_WIN32) */
const char *net_inet_ntop4(const IP4 *addr, char *buf, size_t bufsize)
{
struct in_addr a;
fill_addr4(addr, &a);
return inet_ntop4(&a, buf, bufsize);
}
const char *net_inet_ntop6(const IP6 *addr, char *buf, size_t bufsize)
{
struct in6_addr a;
fill_addr6(addr, &a);
return inet_ntop6(&a, buf, bufsize);
}
int net_inet_pton4(const char *addr_string, IP4 *addrbuf)
{
struct in_addr a;
const int ret = inet_pton4(addr_string, &a);
if (ret == 1) {
get_ip4(addrbuf, &a);
}
return ret;
}
int net_inet_pton6(const char *addr_string, IP6 *addrbuf)
{
struct in6_addr a;
const int ret = inet_pton6(addr_string, &a);
if (ret == 1) {
get_ip6(addrbuf, &a);
}
return ret;
}
bool net_should_ignore_recv_error(int err)
{
return should_ignore_recv_error(err);
}
bool net_should_ignore_connect_error(int err)
{
return should_ignore_connect_error(err);
}
static int sys_close(void *_Nullable obj, Socket sock)
{
#if defined(OS_WIN32)
return closesocket(net_socket_to_native(sock));
#else // !OS_WIN32
return close(net_socket_to_native(sock));
#endif /* OS_WIN32 */
}
static Socket sys_accept(void *_Nullable obj, Socket sock)
{
return net_socket_from_native(accept(net_socket_to_native(sock), nullptr, nullptr));
}
static int sys_bind(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr)
{
Network_Addr naddr;
ip_port_to_network_addr(addr, &naddr);
if (naddr.size == 0) {
return -1;
}
return bind(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size);
}
static int sys_listen(void *_Nullable obj, Socket sock, int backlog)
{
return listen(net_socket_to_native(sock), backlog);
}
static int sys_connect(void *_Nullable obj, Socket sock, const IP_Port *_Nonnull addr)
{
Network_Addr naddr;
ip_port_to_network_addr(addr, &naddr);
if (naddr.size == 0) {
return -1;
}
return connect(net_socket_to_native(sock), (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size);
}
static int sys_recvbuf(void *_Nullable obj, Socket sock)
{
#ifdef OS_WIN32
u_long count = 0;
ioctlsocket(net_socket_to_native(sock), FIONREAD, &count);
#else
int count = 0;
ioctl(net_socket_to_native(sock), FIONREAD, &count);
#endif /* OS_WIN32 */
return count;
}
static int sys_recv(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len)
{
return recv(net_socket_to_native(sock), (char *)buf, len, MSG_NOSIGNAL);
}
static int sys_send(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len)
{
return send(net_socket_to_native(sock), (const char *)buf, len, MSG_NOSIGNAL);
}
static int sys_sendto(void *_Nullable obj, Socket sock, const uint8_t *_Nonnull buf, size_t len, const IP_Port *_Nonnull addr)
{
Network_Addr naddr;
ip_port_to_network_addr(addr, &naddr);
if (naddr.size == 0) {
return -1;
}
return sendto(net_socket_to_native(sock), (const char *)buf, len, 0, (const struct sockaddr *)&naddr.addr, (socklen_t)naddr.size);
}
static int sys_recvfrom(void *_Nullable obj, Socket sock, uint8_t *_Nonnull buf, size_t len, IP_Port *_Nonnull addr)
{
Network_Addr naddr = {{0}};
socklen_t addrlen = sizeof(naddr.addr);
const int ret = recvfrom(net_socket_to_native(sock), (char *)buf, len, 0, (struct sockaddr *)&naddr.addr, &addrlen);
naddr.size = addrlen;
if (ret >= 0) {
if (!network_addr_to_ip_port(&naddr, addr)) {
// Ignore packets from unknown families
return -1;
}
}
return ret;
}
static Socket sys_socket(void *_Nullable obj, int domain, int type, int proto)
{
const int platform_domain = make_family(domain);
const int platform_type = make_socktype(type);
const int platform_prot = make_proto(proto);
return net_socket_from_native(socket(platform_domain, platform_type, platform_prot));
}
static int sys_socket_nonblock(void *_Nullable obj, Socket sock, bool nonblock)
{
#ifdef OS_WIN32
u_long mode = nonblock ? 1 : 0;
return ioctlsocket(net_socket_to_native(sock), FIONBIO, &mode);
#else
const int fd = net_socket_to_native(sock);
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
return -1;
}
if (nonblock) {
flags |= O_NONBLOCK;
} else {
flags &= ~O_NONBLOCK;
}
return fcntl(fd, F_SETFL, flags);
#endif /* OS_WIN32 */
}
static int sys_getsockopt(void *_Nullable obj, Socket sock, int level, int optname, void *_Nonnull optval, size_t *_Nonnull optlen)
{
socklen_t len = (socklen_t) * optlen;
const int ret = getsockopt(net_socket_to_native(sock), level, optname, (char *)optval, &len);
*optlen = len;
return ret;
}
static int sys_setsockopt(void *_Nullable obj, Socket sock, int level, int optname, const void *_Nonnull optval, size_t optlen)
{
#ifdef EMSCRIPTEN
return 0;
#else
return setsockopt(net_socket_to_native(sock), level, optname, (const char *)optval, (socklen_t)optlen);
#endif /* EMSCRIPTEN */
}
static int sys_getaddrinfo(void *_Nullable obj, const Memory *_Nonnull mem, const char *_Nonnull address, int family, int sock_type, IP_Port *_Nullable *_Nonnull addrs)
{
assert(addrs != nullptr);
struct addrinfo hints = {0};
hints.ai_family = make_family(family);
hints.ai_socktype = make_socktype(sock_type);
struct addrinfo *infos = nullptr;
const int rc = getaddrinfo(address, nullptr, &hints, &infos);
if (rc != 0) {
return 0;
}
const int32_t max_count = INT32_MAX / sizeof(IP_Port);
int result = 0;
for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) {
if (walker->ai_family == hints.ai_family || hints.ai_family == AF_UNSPEC) {
++result;
}
}
assert(max_count >= result);
IP_Port *tmp_addrs = (IP_Port *)mem_valloc(mem, result, sizeof(IP_Port));
if (tmp_addrs == nullptr) {
freeaddrinfo(infos);
return 0;
}
int i = 0;
for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) {
if (walker->ai_family == hints.ai_family || hints.ai_family == AF_UNSPEC) {
Network_Addr naddr;
naddr.size = walker->ai_addrlen;
memcpy(&naddr.addr, walker->ai_addr, walker->ai_addrlen);
if (network_addr_to_ip_port(&naddr, &tmp_addrs[i])) {
++i;
}
}
}
result = i;
freeaddrinfo(infos);
*addrs = tmp_addrs;
return result;
}
static int sys_freeaddrinfo(void *_Nullable obj, const Memory *_Nonnull mem, IP_Port *_Nullable addrs)
{
if (addrs == nullptr) {
return 0;
}
mem_delete(mem, addrs);
return 0;
}
static const Network_Funcs os_network_funcs = {
sys_close,
sys_accept,
sys_bind,
sys_listen,
sys_connect,
sys_recvbuf,
sys_recv,
sys_recvfrom,
sys_send,
sys_sendto,
sys_socket,
sys_socket_nonblock,
sys_getsockopt,
sys_setsockopt,
sys_getaddrinfo,
sys_freeaddrinfo,
};
const Network os_network_obj = {&os_network_funcs, nullptr};
const Network *os_network(void)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if ((true)) {
return nullptr;
}
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
#ifdef OS_WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
return nullptr;
}
#endif /* OS_WIN32 */
return &os_network_obj;
}
Socket net_invalid_socket(void)
{
return net_socket_from_native(INVALID_SOCKET);
}
uint32_t net_htonl(uint32_t hostlong)
{
return htonl(hostlong);
}
uint16_t net_htons(uint16_t hostshort)
{
return htons(hostshort);
}
uint32_t net_ntohl(uint32_t hostlong)
{
return ntohl(hostlong);
}
uint16_t net_ntohs(uint16_t hostshort)
{
return ntohs(hostshort);
}
#if !defined(INADDR_LOOPBACK)
#define INADDR_LOOPBACK 0x7f000001
#endif /* !defined(INADDR_LOOPBACK) */
IP4 net_get_ip4_broadcast(void)
{
const IP4 ip4_broadcast = {INADDR_BROADCAST};
return ip4_broadcast;
}
IP6 net_get_ip6_broadcast(void)
{
const IP6 ip6_broadcast = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
};
return ip6_broadcast;
}
IP4 net_get_ip4_loopback(void)
{
IP4 loopback;
loopback.uint32 = htonl(INADDR_LOOPBACK);
return loopback;
}
IP6 net_get_ip6_loopback(void)
{
/* in6addr_loopback isn't available everywhere, so we do it ourselves. */
IP6 loopback = {{0}};
loopback.uint8[15] = 1;
return loopback;
}
#ifndef IPV6_ADD_MEMBERSHIP
#ifdef IPV6_JOIN_GROUP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif /* IPV6_JOIN_GROUP */
#endif /* IPV6_ADD_MEMBERSHIP */
bool net_join_multicast(const Network *ns, Socket sock, Family family)
{
#ifndef ESP_PLATFORM
if (net_family_is_ipv6(family)) {
/* multicast local nodes */
struct ipv6_mreq mreq = {{{{0}}}};
mreq.ipv6mr_multiaddr.s6_addr[0] = 0xFF;
mreq.ipv6mr_multiaddr.s6_addr[1] = 0x02;
mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
mreq.ipv6mr_interface = 0;
return ns_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == 0;
}
#endif /* ESP_PLATFORM */
return false;
}
bool net_set_socket_nonblock(const Network *ns, Socket sock)
{
return ns_socket_nonblock(ns, sock, true) == 0;
}
bool net_set_socket_nosigpipe(const Network *ns, Socket sock)
{
#if defined(__APPLE__)
int set = 1;
return ns_setsockopt(ns, sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(int)) == 0;
#else
return true;
#endif /* __APPLE__ */
}
bool net_set_socket_reuseaddr(const Network *ns, Socket sock)
{
int set = 1;
#if defined(OS_WIN32)
return ns_setsockopt(ns, sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &set, sizeof(set)) == 0;
#else
return ns_setsockopt(ns, sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)) == 0;
#endif /* OS_WIN32 */
}
bool net_set_socket_dualstack(const Network *ns, Socket sock)
{
int ipv6only = 0;
size_t optsize = sizeof(ipv6only);
const int res = ns_getsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize);
if ((res == 0) && (ipv6only == 0)) {
return true;
}
ipv6only = 0;
return ns_setsockopt(ns, sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0;
}
bool net_set_socket_buffer_size(const Network *ns, Socket sock, int size)
{
bool ok = true;
if (ns_setsockopt(ns, sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) {
ok = false;
}
if (ns_setsockopt(ns, sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) {
ok = false;
}
return ok;
}
bool net_set_socket_broadcast(const Network *ns, Socket sock)
{
int broadcast = 1;
return ns_setsockopt(ns, sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == 0;
}
int net_error(void)
{
#ifdef OS_WIN32
return WSAGetLastError();
#else
return errno;
#endif /* OS_WIN32 */
}
void net_clear_error(void)
{
#ifdef OS_WIN32
WSASetLastError(0);
#else
errno = 0;
#endif /* OS_WIN32 */
}
#ifdef OS_WIN32
char *net_strerror(int error, Net_Strerror *buf)
{
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error, 0, buf->data, NET_STRERROR_SIZE, nullptr);
return buf->data;
}
#else
#if defined(_GNU_SOURCE) && defined(__GLIBC__)
static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size)
{
const char *retstr = strerror_r(error, tmp, tmp_size);
if (errno != 0) {
snprintf(tmp, tmp_size, "error %d (strerror_r failed with errno %d)", error, errno);
}
return retstr;
}
#else
static const char *net_strerror_r(int error, char *_Nonnull tmp, size_t tmp_size)
{
const int fmt_error = strerror_r(error, tmp, tmp_size);
if (fmt_error != 0) {
snprintf(tmp, tmp_size, "error %d (strerror_r failed with error %d, errno %d)", error, fmt_error, errno);
}
return tmp;
}
#endif /* GNU */
char *net_strerror(int error, Net_Strerror *buf)
{
errno = 0;
const char *retstr = net_strerror_r(error, buf->data, NET_STRERROR_SIZE);
const size_t retstr_len = strlen(retstr);
assert(retstr_len < NET_STRERROR_SIZE);
buf->size = (uint16_t)retstr_len;
return buf->data;
}
#endif /* OS_WIN32 */