#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 #if MIJIN_COMPILER == MIJIN_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif // MIJIN_COMPILER == MIJIN_COMPILER_CLANG namespace mijin { namespace { #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) MIJIN_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) MIJIN_NOEXCEPT { return gai_error(&handle.item) != EAI_INPROGRESS; } StreamResult> osResolveResult(os_resolve_handle_t& handle) MIJIN_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 OVERLAPPED overlapped = {}; PADDRINFOEX results; HANDLE cancelHandle = nullptr; // my stuff StreamResult> result; }; using os_resolve_handle_t = WSAQueryContext; void WINAPI getAddrComplete(DWORD error, DWORD bytes, LPOVERLAPPED overlapped) MIJIN_NOEXCEPT { (void) bytes; WSAQueryContext& queryContext = *CONTAINING_RECORD(overlapped, WSAQueryContext, overlapped); if (error != ERROR_SUCCESS) { queryContext.result = detail::translateWinError(error); } std::vector resultAddresses; for (PADDRINFOEX result = queryContext.results; result != nullptr; result = result->ai_next) { switch (result->ai_family) { 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; } } if (queryContext.results != nullptr) { FreeAddrInfoEx(queryContext.results); } queryContext.result = std::move(resultAddresses); } StreamError osBeginResolve(const std::string& hostname, os_resolve_handle_t& queryContext) MIJIN_NOEXCEPT { if (!detail::initWSA()) { return detail::translateWSAError(); } ADDRINFOEX hints = {.ai_family = AF_UNSPEC}; std::wstring hostnameW(hostname.begin(), hostname.end()); const int error = GetAddrInfoEx( /* pName = */ hostnameW.c_str(), /* pServiceName = */ nullptr, /* dwNameSpace = */ NS_DNS, /* lpNspId = */ nullptr, /* hints = */ &hints, /* ppResult = */ &queryContext.results, /* timeout = */ nullptr, /* lpOverlapped = */ &queryContext.overlapped, /* lpCompletionRoutine = */ &getAddrComplete, /* lpNameHandle = */ nullptr ); if (error != WSA_IO_PENDING) { getAddrComplete(error, 0, &queryContext.overlapped); } return StreamError::SUCCESS; } bool osResolveDone(os_resolve_handle_t& queryContext) MIJIN_NOEXCEPT { return !queryContext.result.isEmpty(); } StreamResult> osResolveResult(os_resolve_handle_t& queryContext) MIJIN_NOEXCEPT { return queryContext.result; } #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) MIJIN_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) MIJIN_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) MIJIN_NOEXCEPT { os_resolve_handle_t resolveHandle; if (StreamError error = osBeginResolve(hostname, resolveHandle); error != StreamError::SUCCESS) { co_return error; } while (!osResolveDone(resolveHandle)) { co_await c_suspend(); } co_return osResolveResult(resolveHandle); } } #if MIJIN_COMPILER == MIJIN_COMPILER_CLANG #pragma clang diagnostic pop #endif // MIJIN_COMPILER == MIJIN_COMPILER_CLANG