From ac812871a2ec42c37dd7f73bf0f7a6734dfc0bf2 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Fri, 11 Oct 2024 12:11:05 +0200 Subject: [PATCH] feat: implement the last 2 missing network struct functions and make use of them --- auto_tests/network_test.c | 11 ++- toxcore/BUILD.bazel | 1 + toxcore/DHT.c | 2 +- toxcore/network.c | 185 +++++++++++++++++++++++++---------- toxcore/network.h | 12 ++- toxcore/network_test_util.cc | 10 +- toxcore/network_test_util.hh | 6 +- toxcore/tox.c | 4 +- toxcore/tox_private.c | 2 +- 9 files changed, 164 insertions(+), 69 deletions(-) diff --git a/auto_tests/network_test.c b/auto_tests/network_test.c index ebf3982708..a2bc301a2f 100644 --- a/auto_tests/network_test.c +++ b/auto_tests/network_test.c @@ -23,12 +23,15 @@ static void test_addr_resolv_localhost(void) const Network *ns = os_network(); ck_assert(ns != nullptr); + const Memory *mem = os_memory(); + ck_assert(mem != nullptr); + const char localhost[] = "localhost"; IP ip; ip_init(&ip, 0); // ipv6enabled = 0 - bool res = addr_resolve_or_parse_ip(ns, localhost, &ip, nullptr, true); + bool res = addr_resolve_or_parse_ip(ns, mem, localhost, &ip, nullptr, true); int error = net_error(); char *strerror = net_new_strerror(error); @@ -42,14 +45,14 @@ static void test_addr_resolv_localhost(void) net_ip_ntoa(&ip, &ip_str)); ip_init(&ip, 1); // ipv6enabled = 1 - res = addr_resolve_or_parse_ip(ns, localhost, &ip, nullptr, true); + res = addr_resolve_or_parse_ip(ns, mem, localhost, &ip, nullptr, true); #if USE_IPV6 int localhost_split = 0; if (!net_family_is_ipv6(ip.family)) { - res = addr_resolve_or_parse_ip(ns, "ip6-localhost", &ip, nullptr, true); + res = addr_resolve_or_parse_ip(ns, mem, "ip6-localhost", &ip, nullptr, true); localhost_split = 1; } @@ -75,7 +78,7 @@ static void test_addr_resolv_localhost(void) ip.family = net_family_unspec(); IP extra; ip_reset(&extra); - res = addr_resolve_or_parse_ip(ns, localhost, &ip, &extra, true); + res = addr_resolve_or_parse_ip(ns, mem, localhost, &ip, &extra, true); error = net_error(); strerror = net_new_strerror(error); ck_assert_msg(res, "Resolver failed: %d, %s", error, strerror); diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 1534d7a674..7af45d0427 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -333,6 +333,7 @@ cc_library( hdrs = ["network_test_util.hh"], deps = [ ":crypto_core", + ":mem", ":network", ":test_util", ], diff --git a/toxcore/DHT.c b/toxcore/DHT.c index a9bbca4b3f..ec51286246 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -1855,7 +1855,7 @@ bool dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled, ip_extra = &ip_port_v4.ip; } - if (addr_resolve_or_parse_ip(dht->ns, address, &ip_port_v64.ip, ip_extra, dns_enabled)) { + if (addr_resolve_or_parse_ip(dht->ns, dht->mem, address, &ip_port_v64.ip, ip_extra, dns_enabled)) { ip_port_v64.port = port; dht_bootstrap(dht, &ip_port_v64, public_key); diff --git a/toxcore/network.c b/toxcore/network.c index 74d0dcba71..03bea7e3e4 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -592,6 +592,92 @@ static int sys_setsockopt(void *obj, Socket sock, int level, int optname, const return setsockopt(net_socket_to_native(sock), level, optname, (const char *)optval, optlen); } +// sets and fills an array of addrs for address +// returns the number of entries in addrs +non_null() +static int sys_getaddrinfo(void *obj, const Memory *mem, const char *address, int family, int sock_type, Network_Addr **addrs) +{ + assert(addrs != nullptr); + + struct addrinfo hints = {0}; + hints.ai_family = family; + + + // different platforms favour a different field + // hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. + hints.ai_socktype = sock_type; + // hints.ai_protocol = protocol; + + struct addrinfo *infos = nullptr; + + const int rc = getaddrinfo(address, nullptr, &hints, &infos); + + // Lookup failed. + if (rc != 0) { + // TODO(Green-Sky): log error + return 0; + } + + const int32_t max_count = INT32_MAX / sizeof(Network_Addr); + + // we count number of "valid" results + int result = 0; + for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) { + if (walker->ai_family == family || family == AF_UNSPEC) { + ++result; + } + + // do we need to check socktype/protocol? + } + + assert(max_count >= result); + + Network_Addr *tmp_addrs = (Network_Addr *)mem_valloc(mem, result, sizeof(Network_Addr)); + if (tmp_addrs == nullptr) { + freeaddrinfo(infos); + return 0; + } + + // now we fill in + int i = 0; + for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) { + if (walker->ai_family == family || family == AF_UNSPEC) { + tmp_addrs[i].size = sizeof(struct sockaddr_storage); + tmp_addrs[i].addr.ss_family = walker->ai_family; + + // according to spec, storage is supposed to be large enough (and source shows they are) + // storage is 128 bytes + assert(walker->ai_addrlen <= tmp_addrs[i].size); + + memcpy(&tmp_addrs[i].addr, walker->ai_addr, walker->ai_addrlen); + tmp_addrs[i].size = walker->ai_addrlen; + + ++i; + } + } + + assert(i == result); + + freeaddrinfo(infos); + + *addrs = tmp_addrs; + + // number of entries in addrs + return result; +} + +non_null() +static int sys_freeaddrinfo(void *obj, const Memory *mem, Network_Addr *addrs) +{ + if (addrs == nullptr) { + return 0; + } + + mem_delete(mem, addrs); + + return 0; +} + static const Network_Funcs os_network_funcs = { sys_close, sys_accept, @@ -607,8 +693,10 @@ static const Network_Funcs os_network_funcs = { sys_socket_nonblock, sys_getsockopt, sys_setsockopt, + sys_getaddrinfo, + sys_freeaddrinfo, }; -static const Network os_network_obj = {&os_network_funcs}; +static const Network os_network_obj = {&os_network_funcs, nullptr}; const Network *os_network(void) { @@ -1828,8 +1916,8 @@ bool addr_parse_ip(const char *address, IP *to) * * @return false on failure, true on success. */ -non_null(1, 2, 3) nullable(4) -static bool addr_resolve(const Network *ns, const char *address, IP *to, IP *extra) +non_null(1, 2, 3, 4) nullable(5) +static bool addr_resolve(const Network *ns, const Memory *mem, const char *address, IP *to, IP *extra) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if ((true)) { @@ -1844,19 +1932,16 @@ static bool addr_resolve(const Network *ns, const char *address, IP *to, IP *ext const Family tox_family = to->family; const int family = make_family(tox_family); - struct addrinfo hints = {0}; - hints.ai_family = family; - hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. - - struct addrinfo *server = nullptr; + Network_Addr *addrs = nullptr; + const int rc = ns->funcs->getaddrinfo(ns->obj, mem, address, family, 0, &addrs); - const int rc = getaddrinfo(address, nullptr, &hints, &server); - - // Lookup failed. - if (rc != 0) { + // Lookup failed / empty. + if (rc <= 0) { return false; } + assert(addrs != nullptr); + IP ip4; ip_init(&ip4, false); // ipv6enabled = false IP ip6; @@ -1865,16 +1950,16 @@ static bool addr_resolve(const Network *ns, const char *address, IP *to, IP *ext int result = 0; bool done = false; - for (struct addrinfo *walker = server; walker != nullptr && !done; walker = walker->ai_next) { - switch (walker->ai_family) { + for (int i = 0; i < rc && !done; ++i) { + switch (addrs[i].addr.ss_family) { case AF_INET: { - if (walker->ai_family == family) { /* AF_INET requested, done */ - const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)walker->ai_addr; + if (addrs[i].addr.ss_family == family) { /* AF_INET requested, done */ + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr; get_ip4(&to->ip.v4, &addr->sin_addr); result = TOX_ADDR_RESOLVE_INET; done = true; } 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; + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr; get_ip4(&ip4.ip.v4, &addr->sin_addr); result |= TOX_ADDR_RESOLVE_INET; } @@ -1883,16 +1968,16 @@ static bool addr_resolve(const Network *ns, const char *address, IP *to, IP *ext } case AF_INET6: { - if (walker->ai_family == family) { /* AF_INET6 requested, done */ - if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { - const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)walker->ai_addr; + if (addrs[i].addr.ss_family == family) { /* AF_INET6 requested, done */ + if (addrs[i].size == sizeof(struct sockaddr_in6)) { + const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)&addrs[i].addr; get_ip6(&to->ip.v6, &addr->sin6_addr); result = TOX_ADDR_RESOLVE_INET6; done = true; } } 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; + if (addrs[i].size == sizeof(struct sockaddr_in6)) { + const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)&addrs[i].addr; get_ip6(&ip6.ip.v6, &addr->sin6_addr); result |= TOX_ADDR_RESOLVE_INET6; } @@ -1917,13 +2002,13 @@ static bool addr_resolve(const Network *ns, const char *address, IP *to, IP *ext } } - freeaddrinfo(server); + ns->funcs->freeaddrinfo(ns->obj, mem, addrs); return result != 0; } -bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra, bool dns_enabled) +bool addr_resolve_or_parse_ip(const Network *ns, const Memory *mem, const char *address, IP *to, IP *extra, bool dns_enabled) { - if (dns_enabled && addr_resolve(ns, address, to, extra)) { + if (dns_enabled && addr_resolve(ns, mem, address, to, extra)) { return true; } @@ -1982,7 +2067,7 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket return true; } -int32_t net_getipport(const Memory *mem, const char *node, IP_Port **res, int tox_type, bool dns_enabled) +int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP_Port **res, int tox_type, bool dns_enabled) { assert(node != nullptr); @@ -2022,25 +2107,29 @@ int32_t net_getipport(const Memory *mem, const char *node, IP_Port **res, int to } #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ + int type = make_socktype(tox_type); + // ugly + if (tox_type == -1) { + type = 0; + } + // It's not an IP address, so now we try doing a DNS lookup. - struct addrinfo *infos; - const int ret = getaddrinfo(node, nullptr, nullptr, &infos); + Network_Addr *addrs = nullptr; + const int rc = ns->funcs->getaddrinfo(ns->obj, mem, node, AF_UNSPEC, type, &addrs); - if (ret != 0) { + // Lookup failed / empty. + if (rc <= 0) { return -1; } + assert(addrs != nullptr); + // Used to avoid calloc parameter overflow const size_t max_count = min_u64(SIZE_MAX, INT32_MAX) / sizeof(IP_Port); - const int type = make_socktype(tox_type); size_t count = 0; - for (struct addrinfo *cur = infos; count < max_count && cur != nullptr; cur = cur->ai_next) { - if (cur->ai_socktype != 0 && type > 0 && cur->ai_socktype != type) { - continue; - } - - if (cur->ai_family != AF_INET && cur->ai_family != AF_INET6) { + for (int i = 0; i < rc && count < max_count; ++i) { + if (addrs[i].addr.ss_family != AF_INET && addrs[i].addr.ss_family != AF_INET6) { continue; } @@ -2050,40 +2139,36 @@ int32_t net_getipport(const Memory *mem, const char *node, IP_Port **res, int to assert(count <= max_count); if (count == 0) { - freeaddrinfo(infos); + ns->funcs->freeaddrinfo(ns->obj, mem, addrs); return 0; } IP_Port *ip_port = (IP_Port *)mem_valloc(mem, count, sizeof(IP_Port)); if (ip_port == nullptr) { - freeaddrinfo(infos); + ns->funcs->freeaddrinfo(ns->obj, mem, addrs); *res = nullptr; return -1; } *res = ip_port; - for (struct addrinfo *cur = infos; cur != nullptr; cur = cur->ai_next) { - if (cur->ai_socktype != 0 && type > 0 && cur->ai_socktype != type) { - continue; - } - - if (cur->ai_family == AF_INET) { - const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)cur->ai_addr; + for (int i = 0; i < rc && count < max_count; ++i) { + if (addrs[i].addr.ss_family == AF_INET) { + const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr; ip_port->ip.ip.v4.uint32 = addr->sin_addr.s_addr; - } else if (cur->ai_family == AF_INET6) { - const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(const void *)cur->ai_addr; + } else if (addrs[i].addr.ss_family == AF_INET6) { + const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(const void *)&addrs[i].addr; memcpy(ip_port->ip.ip.v6.uint8, addr->sin6_addr.s6_addr, sizeof(IP6)); } else { continue; } - const Family *const family = make_tox_family(cur->ai_family); + const Family *const family = make_tox_family(addrs[i].addr.ss_family); assert(family != nullptr); if (family == nullptr) { - freeaddrinfo(infos); + ns->funcs->freeaddrinfo(ns->obj, mem, addrs); return -1; } @@ -2092,7 +2177,7 @@ int32_t net_getipport(const Memory *mem, const char *node, IP_Port **res, int to ++ip_port; } - freeaddrinfo(infos); + ns->funcs->freeaddrinfo(ns->obj, mem, addrs); return count; } diff --git a/toxcore/network.h b/toxcore/network.h index 3e191e13a8..84c2433274 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -49,8 +49,8 @@ typedef Socket net_socket_cb(void *obj, int domain, int type, int proto); typedef int net_socket_nonblock_cb(void *obj, Socket sock, bool nonblock); typedef int net_getsockopt_cb(void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen); typedef int net_setsockopt_cb(void *obj, Socket 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); +typedef int net_getaddrinfo_cb(void *obj, const Memory *mem, const char *address, int family, int protocol, Network_Addr **addrs); +typedef int net_freeaddrinfo_cb(void *obj, const Memory *mem, Network_Addr *addrs); /** @brief Functions wrapping POSIX network functions. * @@ -400,6 +400,7 @@ void ipport_copy(IP_Port *target, const IP_Port *source); /** * @brief Resolves string into an IP address. * + * @param[in,out] ns Network object. * @param[in] address a hostname (or something parseable to an IP address). * @param[in,out] 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 @@ -412,8 +413,8 @@ void ipport_copy(IP_Port *target, const IP_Port *source); * * @return true on success, false on failure */ -non_null(1, 2, 3) nullable(4) -bool addr_resolve_or_parse_ip(const Network *ns, const char *address, IP *to, IP *extra, bool dns_enabled); +non_null(1, 2, 3, 4) nullable(5) +bool addr_resolve_or_parse_ip(const Network *ns, const Memory *mem, const char *address, IP *to, IP *extra, bool dns_enabled); /** @brief Function to receive data, ip and port of sender is put into ip_port. * Packet data is put into data. @@ -515,6 +516,7 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket * Skip all addresses with socktype != type (use type = -1 to get all addresses) * To correctly deallocate array memory use `net_freeipport()`. * + * @param ns Network object. * @param mem Memory allocator. * @param node The node parameter identifies the host or service on which to connect. * @param[out] res An array of IP_Port structures will be allocated into this pointer. @@ -526,7 +528,7 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket * @retval -1 on error. */ non_null() -int32_t net_getipport(const Memory *mem, const char *node, IP_Port **res, int tox_type, bool dns_enabled); +int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP_Port **res, int tox_type, bool dns_enabled); /** Deallocates memory allocated by net_getipport */ non_null(1) nullable(2) diff --git a/toxcore/network_test_util.cc b/toxcore/network_test_util.cc index bdb42ae1d9..c8d2ff3e8a 100644 --- a/toxcore/network_test_util.cc +++ b/toxcore/network_test_util.cc @@ -3,6 +3,7 @@ #include #include "crypto_core.h" +#include "mem.h" #include "network.h" #include "test_util.hh" @@ -75,13 +76,14 @@ int Test_Network::setsockopt( { return net->funcs->setsockopt(net->obj, sock, level, optname, optval, optlen); } -int Test_Network::getaddrinfo(void *obj, int family, Network_Addr **addrs) +int Test_Network::getaddrinfo(void *obj, const Memory *mem, const char *address, int family, + int protocol, Network_Addr **addrs) { - return net->funcs->getaddrinfo(net->obj, family, addrs); + return net->funcs->getaddrinfo(net->obj, mem, address, family, protocol, addrs); } -int Test_Network::freeaddrinfo(void *obj, Network_Addr *addrs) +int Test_Network::freeaddrinfo(void *obj, const Memory *mem, Network_Addr *addrs) { - return net->funcs->freeaddrinfo(net->obj, addrs); + return net->funcs->freeaddrinfo(net->obj, mem, addrs); } Network_Class::~Network_Class() = default; diff --git a/toxcore/network_test_util.hh b/toxcore/network_test_util.hh index 88084b1fb9..be967d3d4f 100644 --- a/toxcore/network_test_util.hh +++ b/toxcore/network_test_util.hh @@ -4,6 +4,7 @@ #include #include "crypto_core.h" +#include "mem.h" #include "network.h" #include "test_util.hh" @@ -62,8 +63,9 @@ class Test_Network : public Network_Class { void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen) override; int setsockopt( void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen) override; - int getaddrinfo(void *obj, int family, Network_Addr **addrs) override; - int freeaddrinfo(void *obj, Network_Addr *addrs) override; + int getaddrinfo(void *obj, const Memory *mem, const char *address, int family, int protocol, + Network_Addr **addrs) override; + int freeaddrinfo(void *obj, const Memory *mem, Network_Addr *addrs) override; }; template <> diff --git a/toxcore/tox.c b/toxcore/tox.c index eb12d07181..4918e7ba9c 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -860,7 +860,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error const bool dns_enabled = !tox_options_get_experimental_disable_dns(opts); if (proxy_host == nullptr - || !addr_resolve_or_parse_ip(tox->sys.ns, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr, dns_enabled)) { + || !addr_resolve_or_parse_ip(tox->sys.ns, tox->sys.mem, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr, dns_enabled)) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST); // TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain. mem_delete(sys->mem, tox); @@ -1142,7 +1142,7 @@ static int32_t resolve_bootstrap_node(Tox *tox, const char *host, uint16_t port, return -1; } - const int32_t count = net_getipport(tox->sys.mem, host, root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled); + const int32_t count = net_getipport(tox->sys.ns, tox->sys.mem, host, root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled); if (count < 1) { LOGGER_DEBUG(tox->m->log, "could not resolve bootstrap node '%s'", host); diff --git a/toxcore/tox_private.c b/toxcore/tox_private.c index 8c141ff9d2..ff6aa6e19d 100644 --- a/toxcore/tox_private.c +++ b/toxcore/tox_private.c @@ -124,7 +124,7 @@ bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip IP_Port *root; - const int32_t count = net_getipport(tox->sys.mem, ip, &root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled); + const int32_t count = net_getipport(tox->sys.ns, tox->sys.mem, ip, &root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled); if (count < 1) { SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_IP);