From 04a28e220c7500f712d0c0cbf837bae0c4fa8200 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Tue, 20 Aug 2024 13:54:52 +0200 Subject: [PATCH] Added name resolution code for linux. --- SModule | 1 + source/mijin/net/ip.cpp | 175 +++++++++++++++++++++++++++++++++++- source/mijin/net/ip.hpp | 14 +++ source/mijin/net/socket.cpp | 82 ++--------------- 4 files changed, 197 insertions(+), 75 deletions(-) diff --git a/SModule b/SModule index 48b8777..57ab3f2 100644 --- a/SModule +++ b/SModule @@ -11,6 +11,7 @@ mijin_sources = Split(""" source/mijin/io/process.cpp source/mijin/io/stream.cpp source/mijin/net/http.cpp + source/mijin/net/ip.cpp source/mijin/net/socket.cpp source/mijin/util/os.cpp source/mijin/types/name.cpp diff --git a/source/mijin/net/ip.cpp b/source/mijin/net/ip.cpp index 5e54d72..5bfb027 100644 --- a/source/mijin/net/ip.cpp +++ b/source/mijin/net/ip.cpp @@ -1,14 +1,105 @@ #include "./ip.hpp" +#include + #include "../detect.hpp" +#include "../util/string.hpp" #include "./detail/net_common.hpp" +#if MIJIN_TARGET_OS == MIJIN_OS_LINUX +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#include +#endif + namespace mijin { namespace { -#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS +#if MIJIN_TARGET_OS == MIJIN_OS_LINUX +struct AddrInfoContext +{ + gaicb item; + gaicb* list = &item; +}; +using os_resolve_handle_t = AddrInfoContext; + +StreamError translateGAIError(int error) +{ + (void) error; // TODO + return StreamError::UNKNOWN_ERROR; +} + +StreamError osBeginResolve(const std::string& hostname, os_resolve_handle_t& handle) noexcept +{ + handle.item = {.ar_name = hostname.c_str()}; + + const int result = getaddrinfo_a(GAI_NOWAIT, &handle.list, 1, nullptr); + if (result != 0) + { + return StreamError::UNKNOWN_ERROR; + } + return StreamError::SUCCESS; +} + +bool osResolveDone(os_resolve_handle_t& handle) noexcept +{ + return gai_error(&handle.item) != EAI_INPROGRESS; +} + +StreamResult> osResolveResult(os_resolve_handle_t& handle) noexcept +{ + if (const int error = gai_error(&handle.item); error != 0) + { + if (handle.item.ar_result != nullptr) + { + freeaddrinfo(handle.item.ar_result); + } + return translateGAIError(error); + } + if (handle.item.ar_result == nullptr) + { + return StreamError::UNKNOWN_ERROR; + } + std::vector resultAddresses; + for (addrinfo* result = handle.item.ar_result; result != nullptr; result = result->ai_next) + { + if (result->ai_protocol != IPPROTO_TCP) + { + // we actually just care about TCP, right? + continue; + } + switch (result->ai_family) + { +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error "TODO: swap byte order of the address" +#endif + case AF_INET: + { + sockaddr_in& addr = *reinterpret_cast(result->ai_addr); + resultAddresses.emplace_back(std::bit_cast(addr.sin_addr)); + break; + } + case AF_INET6: + { + sockaddr_in6& addr = *reinterpret_cast(result->ai_addr); + IPv6Address addr6 = std::bit_cast(addr.sin6_addr); + for (std::uint16_t& hextet : addr6.hextets) + { + hextet = ntohs(hextet); + } + resultAddresses.emplace_back(addr6); + break; + } + default: break; + } + } + freeaddrinfo(handle.item.ar_result); + return resultAddresses; +} +#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS struct WSAQueryContext { // WSA stuff @@ -100,6 +191,88 @@ StreamResult> osResolveResult(os_resolve_handle_t& que #endif // MIJIN_TARGET_OS } +std::string IPv4Address::toString() const +{ + return std::format("{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]); +} + +std::string IPv6Address::toString() const +{ + return std::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", hextets[0], hextets[1], hextets[2], hextets[3], hextets[4], + hextets[5], hextets[6], hextets[7]); +} + +Optional IPv4Address::fromString(std::string_view stringView) noexcept +{ + std::vector parts = split(stringView, ".", {.limitParts = 4}); + if (parts.size() != 4) { + return NULL_OPTIONAL; + } + IPv4Address address; + for (int idx = 0; idx < 4; ++idx) + { + if (!toNumber(parts[idx], address.octets[idx])) + { + return NULL_OPTIONAL; + } + } + return address; +} + +Optional IPv6Address::fromString(std::string_view stringView) noexcept +{ + // very specific edge case + if (stringView.contains(":::")) + { + return NULL_OPTIONAL; + } + + std::vector parts = split(stringView, "::", {.ignoreEmpty = false}); + if (parts.size() > 2) + { + return NULL_OPTIONAL; + } + if (parts.size() == 1) + { + parts.emplace_back(""); + } + + std::vector partsLeft = split(parts[0], ":"); + std::vector partsRight = split(parts[1], ":"); + + std::erase_if(partsLeft, std::mem_fn(&std::string_view::empty)); + std::erase_if(partsRight, std::mem_fn(&std::string_view::empty)); + + if (partsLeft.size() + partsRight.size() > 8) + { + return NULL_OPTIONAL; + } + + IPv6Address address = {}; + unsigned hextet = 0; + for (std::string_view part : partsLeft) + { + if (!toNumber(part, address.hextets[hextet], /* base = */ 16)) + { + return NULL_OPTIONAL; + } + ++hextet; + } + for (; hextet < (8 - partsRight.size()); ++hextet) + { + address.hextets[hextet] = 0; + } + for (std::string_view part : partsRight) + { + if (!toNumber(part, address.hextets[hextet], /* base = */ 16)) + { + return NULL_OPTIONAL; + } + ++hextet; + } + return address; +} + Task>> c_resolveHostname(std::string hostname) noexcept { os_resolve_handle_t resolveHandle; diff --git a/source/mijin/net/ip.hpp b/source/mijin/net/ip.hpp index 8166328..c4faa12 100644 --- a/source/mijin/net/ip.hpp +++ b/source/mijin/net/ip.hpp @@ -17,6 +17,8 @@ struct IPv4Address auto operator<=>(const IPv4Address&) const noexcept = default; + [[nodiscard]] std::string toString() const; + [[nodiscard]] static Optional fromString(std::string_view stringView) noexcept; }; @@ -27,11 +29,23 @@ struct IPv6Address auto operator<=>(const IPv6Address&) const noexcept = default; + [[nodiscard]] std::string toString() const; + [[nodiscard]] static Optional fromString(std::string_view stringView) noexcept; }; using ip_address_t = std::variant; +[[nodiscard]] +inline std::string ipAddressToString(const ip_address_t& address) noexcept +{ + if (address.valueless_by_exception()) + { + return ""; + } + return std::visit([](const auto& addr) { return addr.toString(); }, address); +} + [[nodiscard]] inline Optional ipAddressFromString(std::string_view stringView) noexcept { diff --git a/source/mijin/net/socket.cpp b/source/mijin/net/socket.cpp index 10e1d23..e1e3531 100644 --- a/source/mijin/net/socket.cpp +++ b/source/mijin/net/socket.cpp @@ -3,7 +3,6 @@ #include "./detail/net_common.hpp" #include "../detect.hpp" -#include "../util/string.hpp" #include "../util/variant.hpp" namespace mijin @@ -181,77 +180,6 @@ StreamError translateWinError() noexcept #endif // MIJIN_TARGET_OS == MIJIN_OS_WINDOWS }// namespace impl -Optional IPv4Address::fromString(std::string_view stringView) noexcept -{ - std::vector parts = split(stringView, ".", {.limitParts = 4}); - if (parts.size() != 4) { - return NULL_OPTIONAL; - } - IPv4Address address; - for (int idx = 0; idx < 4; ++idx) - { - if (!toNumber(parts[idx], address.octets[idx])) - { - return NULL_OPTIONAL; - } - } - return address; -} - -Optional IPv6Address::fromString(std::string_view stringView) noexcept -{ - // very specific edge case - if (stringView.contains(":::")) - { - return NULL_OPTIONAL; - } - - std::vector parts = split(stringView, "::", {.ignoreEmpty = false}); - if (parts.size() > 2) - { - return NULL_OPTIONAL; - } - if (parts.size() == 1) - { - parts.emplace_back(""); - } - - std::vector partsLeft = split(parts[0], ":"); - std::vector partsRight = split(parts[1], ":"); - - std::erase_if(partsLeft, std::mem_fn(&std::string_view::empty)); - std::erase_if(partsRight, std::mem_fn(&std::string_view::empty)); - - if (partsLeft.size() + partsRight.size() > 8) - { - return NULL_OPTIONAL; - } - - IPv6Address address = {}; - unsigned hextet = 0; - for (std::string_view part : partsLeft) - { - if (!toNumber(part, address.hextets[hextet], /* base = */ 16)) - { - return NULL_OPTIONAL; - } - ++hextet; - } - for (; hextet < (8 - partsRight.size()); ++hextet) - { - address.hextets[hextet] = 0; - } - for (std::string_view part : partsRight) - { - if (!toNumber(part, address.hextets[hextet], /* base = */ 16)) - { - return NULL_OPTIONAL; - } - ++hextet; - } - return address; -} - StreamError TCPStream::readRaw(std::span buffer, const ReadOptions& options, std::size_t* outBytesRead) { MIJIN_ASSERT(isOpen(), "Socket is not open."); @@ -412,8 +340,11 @@ StreamError TCPStream::open(ip_address_t address, std::uint16_t port) noexcept }; return connect(handle_, reinterpret_cast(&connectAddress), sizeof(sockaddr_in)) == 0; }, - [&](const IPv6Address& address6) + [&](IPv6Address address6) { + for (std::uint16_t& hextet : address6.hextets) { + hextet = htons(hextet); + } sockaddr_in6 connectAddress = { .sin6_family = AF_INET6, @@ -474,8 +405,11 @@ StreamError TCPServerSocket::setup(ip_address_t address, std::uint16_t port) noe }; return bind(handle_, reinterpret_cast(&bindAddress), sizeof(sockaddr_in)) == 0; }, - [&](const IPv6Address& address6) + [&](IPv6Address address6) { + for (std::uint16_t& hextet : address6.hextets) { + hextet = htons(hextet); + } sockaddr_in6 bindAddress = { .sin6_family = AF_INET6,