Added name resolution code for linux.

This commit is contained in:
Patrick 2024-08-20 13:54:52 +02:00
parent a755de5c5f
commit 04a28e220c
4 changed files with 197 additions and 75 deletions

View File

@ -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

View File

@ -1,14 +1,105 @@
#include "./ip.hpp"
#include <format>
#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 <netdb.h>
#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<std::vector<ip_address_t>> 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<ip_address_t> 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<sockaddr_in*>(result->ai_addr);
resultAddresses.emplace_back(std::bit_cast<IPv4Address>(addr.sin_addr));
break;
}
case AF_INET6:
{
sockaddr_in6& addr = *reinterpret_cast<sockaddr_in6*>(result->ai_addr);
IPv6Address addr6 = std::bit_cast<IPv6Address>(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<std::vector<ip_address_t>> 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> IPv4Address::fromString(std::string_view stringView) noexcept
{
std::vector<std::string_view> 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> IPv6Address::fromString(std::string_view stringView) noexcept
{
// very specific edge case
if (stringView.contains(":::"))
{
return NULL_OPTIONAL;
}
std::vector<std::string_view> parts = split(stringView, "::", {.ignoreEmpty = false});
if (parts.size() > 2)
{
return NULL_OPTIONAL;
}
if (parts.size() == 1)
{
parts.emplace_back("");
}
std::vector<std::string_view> partsLeft = split(parts[0], ":");
std::vector<std::string_view> 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<StreamResult<std::vector<ip_address_t>>> c_resolveHostname(std::string hostname) noexcept
{
os_resolve_handle_t resolveHandle;

View File

@ -17,6 +17,8 @@ struct IPv4Address
auto operator<=>(const IPv4Address&) const noexcept = default;
[[nodiscard]] std::string toString() const;
[[nodiscard]]
static Optional<IPv4Address> 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<IPv6Address> fromString(std::string_view stringView) noexcept;
};
using ip_address_t = std::variant<IPv4Address, IPv6Address>;
[[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<ip_address_t> ipAddressFromString(std::string_view stringView) noexcept
{

View File

@ -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> IPv4Address::fromString(std::string_view stringView) noexcept
{
std::vector<std::string_view> 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> IPv6Address::fromString(std::string_view stringView) noexcept
{
// very specific edge case
if (stringView.contains(":::"))
{
return NULL_OPTIONAL;
}
std::vector<std::string_view> parts = split(stringView, "::", {.ignoreEmpty = false});
if (parts.size() > 2)
{
return NULL_OPTIONAL;
}
if (parts.size() == 1)
{
parts.emplace_back("");
}
std::vector<std::string_view> partsLeft = split(parts[0], ":");
std::vector<std::string_view> 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<std::uint8_t> 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<sockaddr*>(&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<sockaddr*>(&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,