blob: bec91bfa29990a33d906e6146250e0151a1bc5be [file] [log] [blame]
#pragma once
#include <fmt/core.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <stdplus/net/addr/ip.hpp>
#include <stdplus/numeric/endian.hpp>
#include <stdplus/numeric/str.hpp>
#include <stdplus/str/conv.hpp>
#include <stdplus/variant.hpp>
#include <array>
#include <cstdint>
#include <format>
#include <string_view>
namespace stdplus
{
namespace detail
{
struct SockAddrUnsafe
{};
} // namespace detail
struct SockAddrBuf
{
union
{
sa_family_t fam;
sockaddr_storage ss_;
};
std::uint8_t len;
static inline constexpr std::size_t maxLen = sizeof(ss_);
static_assert(maxLen <= std::numeric_limits<decltype(len)>::max());
inline operator const sockaddr*() const
{
return reinterpret_cast<const sockaddr*>(&ss_);
}
inline operator sockaddr*()
{
return reinterpret_cast<sockaddr*>(&ss_);
}
};
static_assert(std::is_standard_layout_v<SockAddrBuf>);
static_assert(offsetof(SockAddrBuf, ss_) == 0);
static_assert(offsetof(SockAddrBuf, fam) ==
offsetof(sockaddr_storage, ss_family));
static_assert(SockAddrBuf::maxLen <= offsetof(SockAddrBuf, len));
static_assert(alignof(SockAddrBuf) % alignof(sockaddr) == 0);
static_assert(alignof(SockAddrBuf) % alignof(sockaddr_in) == 0);
static_assert(alignof(SockAddrBuf) % alignof(sockaddr_in6) == 0);
static_assert(alignof(SockAddrBuf) % alignof(sockaddr_un) == 0);
template <typename T>
struct IsSockAddr : std::false_type
{};
template <typename T>
concept SockAddr = IsSockAddr<T>::value;
struct Sock4Addr
{
In4Addr addr;
std::uint16_t port;
static constexpr Sock4Addr fromBuf(const SockAddrBuf& buf)
{
if (buf.fam != AF_INET)
{
throw std::invalid_argument("Sock4Addr fromBuf");
}
return fromBuf(detail::SockAddrUnsafe(), buf);
}
static constexpr Sock4Addr fromBuf(detail::SockAddrUnsafe,
const SockAddrBuf& buf)
{
if (buf.len != sizeof(sockaddr_in))
{
throw std::invalid_argument("Sock4Addr fromBuf");
}
const auto& sin = reinterpret_cast<const sockaddr_in&>(buf);
return Sock4Addr{.addr = sin.sin_addr,
.port = stdplus::ntoh(sin.sin_port)};
}
constexpr bool operator==(Sock4Addr rhs) const noexcept
{
return addr == rhs.addr && port == rhs.port;
}
constexpr sockaddr_in sockaddr() const noexcept
{
return {.sin_family = AF_INET,
.sin_port = stdplus::hton(port),
.sin_addr = addr,
.sin_zero = {}};
}
constexpr operator sockaddr_in() const noexcept
{
return sockaddr();
}
static constexpr std::size_t sockaddrLen() noexcept
{
return sizeof(sockaddr_in);
}
constexpr SockAddrBuf buf() const noexcept
{
SockAddrBuf ret;
reinterpret_cast<sockaddr_in&>(ret) = sockaddr();
ret.len = sockaddrLen();
return ret;
}
constexpr operator SockAddrBuf() const noexcept
{
return buf();
}
};
template <>
struct IsSockAddr<Sock4Addr> : std::true_type
{};
struct Sock6Addr
{
In6Addr addr;
std::uint16_t port;
std::uint32_t scope;
static constexpr Sock6Addr fromBuf(const SockAddrBuf& buf)
{
if (buf.fam != AF_INET6)
{
throw std::invalid_argument("Sock6Addr fromBuf");
}
return fromBuf(detail::SockAddrUnsafe(), buf);
}
static constexpr Sock6Addr fromBuf(detail::SockAddrUnsafe,
const SockAddrBuf& buf)
{
if (buf.len != sizeof(sockaddr_in6))
{
throw std::invalid_argument("Sock6Addr fromBuf");
}
const auto& sin6 = reinterpret_cast<const sockaddr_in6&>(buf);
return Sock6Addr{.addr = sin6.sin6_addr,
.port = stdplus::ntoh(sin6.sin6_port),
.scope = sin6.sin6_scope_id};
}
constexpr bool operator==(Sock6Addr rhs) const noexcept
{
return addr == rhs.addr && port == rhs.port && scope == rhs.scope;
}
constexpr sockaddr_in6 sockaddr() const noexcept
{
return {.sin6_family = AF_INET6,
.sin6_port = stdplus::hton(port),
.sin6_flowinfo = 0,
.sin6_addr = addr,
.sin6_scope_id = scope};
}
constexpr operator sockaddr_in6() const noexcept
{
return sockaddr();
}
static constexpr std::size_t sockaddrLen() noexcept
{
return sizeof(sockaddr_in6);
}
constexpr SockAddrBuf buf() const noexcept
{
SockAddrBuf ret;
reinterpret_cast<sockaddr_in6&>(ret) = sockaddr();
ret.len = sockaddrLen();
return ret;
}
constexpr operator SockAddrBuf() const noexcept
{
return buf();
}
};
template <>
struct IsSockAddr<Sock6Addr> : std::true_type
{};
struct SockUAddr
{
static inline constexpr std::size_t maxLen =
std::size(sockaddr_un{}.sun_path);
constexpr explicit SockUAddr(std::string_view path) : len_(path.size())
{
if (path.empty())
{
return;
}
bool abstract = path[0] == '@' || path[0] == '\0';
// Abstract sockets are not null terminated but path sockets are
if (path.size() >= maxLen + (abstract ? 1 : 0))
{
throw std::invalid_argument("Socket path too long");
}
if (!abstract && path.find('\0') != path.npos)
{
throw std::invalid_argument("Null bytes in non-abtract path");
}
buf_[0] = abstract ? '@' : path[0];
std::copy(path.begin() + 1, path.end(), buf_.begin() + 1);
}
constexpr SockUAddr(detail::SockAddrUnsafe, std::string_view path) noexcept
{
if (path.empty())
{
len_ = 0;
return;
}
bool abstract = path[0] == '\0';
// Abstract sockets are not null terminated but path sockets are
len_ = path.size() - (abstract ? 0 : 1);
buf_[0] = abstract ? '@' : path[0];
std::copy_n(path.begin() + 1, len_ - 1, buf_.begin() + 1);
}
static constexpr SockUAddr fromBuf(const SockAddrBuf& buf)
{
if (buf.fam != AF_UNIX)
{
throw std::invalid_argument("SockUAddr fromBuf");
}
return fromBuf(detail::SockAddrUnsafe(), buf);
}
static constexpr SockUAddr fromBuf(detail::SockAddrUnsafe,
const SockAddrBuf& buf)
{
if (buf.len < sizeof(sockaddr_un{}.sun_family))
{
throw std::invalid_argument("SockUAddr fromBuf");
}
const auto& sun = reinterpret_cast<const sockaddr_un&>(buf);
return SockUAddr{detail::SockAddrUnsafe(),
{sun.sun_path,
buf.len - offsetof(sockaddr_un, sun_path)}};
}
constexpr bool operator==(const SockUAddr& rhs) const noexcept
{
return path() == rhs.path();
}
constexpr std::string_view path() const noexcept
{
return {buf_.data(), len_};
}
constexpr sockaddr_un sockaddr() const noexcept
{
sockaddr_un ret;
fill(ret);
return ret;
}
constexpr operator sockaddr_un() const noexcept
{
return sockaddr();
}
constexpr std::size_t sockaddrLen() const noexcept
{
return sizeof(sockaddr_un{}.sun_family) + len_ +
(len_ > 0 && buf_[0] != '@' ? 1 : 0);
}
constexpr SockAddrBuf buf() const noexcept
{
SockAddrBuf ret;
fill(reinterpret_cast<sockaddr_un&>(ret));
ret.len = sockaddrLen();
return ret;
}
constexpr operator SockAddrBuf() const noexcept
{
return buf();
}
private:
std::array<char, maxLen> buf_ = {};
std::uint8_t len_;
static_assert(maxLen <= std::numeric_limits<decltype(len_)>::max());
constexpr void fill(sockaddr_un& v) const noexcept
{
v.sun_family = AF_UNIX;
bool abstract = buf_[0] == '@';
v.sun_path[0] = abstract ? '\0' : buf_[0];
std::copy(buf_.begin() + 1, buf_.begin() + len_, v.sun_path + 1);
if (abstract)
{
v.sun_path[len_] = '\0';
}
};
};
template <>
struct IsSockAddr<SockUAddr> : std::true_type
{};
namespace detail
{
using SockInAddrV = std::variant<Sock4Addr, Sock6Addr>;
using SockAnyAddrV = std::variant<Sock4Addr, Sock6Addr, SockUAddr>;
template <typename V>
struct SockAnyAddr : V
{
constexpr SockAnyAddr(auto&&... args) :
V(std::forward<decltype(args)>(args)...)
{}
template <SockAddr T>
constexpr bool operator==(const T& rhs) const noexcept
{
return variantEqFuzzy(*this, rhs);
}
constexpr SockAddrBuf sockaddr() const noexcept
{
return std::visit([](const auto& t) { return t.buf(); }, *this);
}
constexpr std::size_t sockaddrLen() const noexcept
{
return std::visit([](const auto& t) { return t.sockaddrLen(); }, *this);
}
constexpr SockAddrBuf buf() const noexcept
{
return sockaddr();
}
constexpr operator SockAddrBuf() const noexcept
{
return sockaddr();
}
};
} // namespace detail
struct SockInAddr : detail::SockAnyAddr<detail::SockInAddrV>
{
constexpr SockInAddr(In4Addr a, std::uint16_t p) noexcept :
detail::SockAnyAddr<detail::SockInAddrV>(std::in_place_type<Sock4Addr>,
a, p)
{}
constexpr SockInAddr(Sock4Addr a) noexcept :
detail::SockAnyAddr<detail::SockInAddrV>(a)
{}
constexpr SockInAddr(In6Addr a, std::uint16_t p) noexcept :
detail::SockAnyAddr<detail::SockInAddrV>(std::in_place_type<Sock6Addr>,
a, p)
{}
constexpr SockInAddr(Sock6Addr a) noexcept :
detail::SockAnyAddr<detail::SockInAddrV>(a)
{}
static constexpr SockInAddr fromBuf(const SockAddrBuf& buf)
{
if (buf.fam == AF_INET)
{
return Sock4Addr::fromBuf(detail::SockAddrUnsafe(), buf);
}
else if (buf.fam == AF_INET6)
{
return Sock6Addr::fromBuf(detail::SockAddrUnsafe(), buf);
}
throw std::invalid_argument("Unknown SockInAddr");
}
};
template <>
struct IsSockAddr<SockInAddr> : std::true_type
{};
struct SockAnyAddr : detail::SockAnyAddr<detail::SockAnyAddrV>
{
constexpr SockAnyAddr(In4Addr a, std::uint16_t p) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<Sock4Addr>,
a, p)
{}
constexpr SockAnyAddr(Sock4Addr a) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(a)
{}
constexpr SockAnyAddr(In6Addr a, std::uint16_t p) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<Sock6Addr>,
a, p)
{}
constexpr SockAnyAddr(Sock6Addr a) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(a)
{}
constexpr SockAnyAddr(std::string_view p) :
detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<SockUAddr>,
p)
{}
constexpr SockAnyAddr(SockUAddr a) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(a)
{}
constexpr SockAnyAddr(SockInAddr a) noexcept :
detail::SockAnyAddr<detail::SockAnyAddrV>(
std::visit([](auto v) { return detail::SockAnyAddrV(v); }, a))
{}
static constexpr SockAnyAddr fromBuf(const SockAddrBuf& buf)
{
if (buf.fam == AF_INET)
{
return Sock4Addr::fromBuf(detail::SockAddrUnsafe(), buf);
}
else if (buf.fam == AF_INET6)
{
return Sock6Addr::fromBuf(detail::SockAddrUnsafe(), buf);
}
else if (buf.fam == AF_UNIX)
{
return SockUAddr::fromBuf(detail::SockAddrUnsafe(), buf);
}
throw std::invalid_argument("Unknown SockInAddr");
}
};
template <>
struct IsSockAddr<SockAnyAddr> : std::true_type
{};
template <>
struct FromStr<Sock4Addr>
{
template <typename CharT>
constexpr Sock4Addr operator()(std::basic_string_view<CharT> sv) const
{
const auto pos = sv.rfind(':');
if (pos == sv.npos)
{
throw std::invalid_argument("Invalid string for Sock4Addr");
}
return {FromStr<In4Addr>{}(sv.substr(0, pos)),
StrToInt<10, std::uint16_t>{}(sv.substr(pos + 1))};
}
};
template <>
struct ToStr<Sock4Addr>
{
using type = Sock4Addr;
using FromAddr = ToStr<In4Addr>;
using FromDec = IntToStr<10, std::uint16_t>;
// Addr + sep + port chars
static inline constexpr std::size_t buf_size =
FromAddr::buf_size + 1 + FromDec::buf_size;
template <typename CharT>
constexpr CharT* operator()(CharT* buf, Sock4Addr v) const noexcept
{
buf = FromAddr{}(buf, v.addr);
(buf++)[0] = ':';
return FromDec{}(buf, v.port);
}
};
template <>
struct FromStr<Sock6Addr>
{
template <typename CharT>
constexpr Sock6Addr operator()(std::basic_string_view<CharT> sv) const
{
const auto pos = sv.rfind(':');
if (pos == sv.npos)
{
throw std::invalid_argument("Invalid string for Sock6Addr");
}
const auto v6seg = sv.substr(0, pos);
if (!v6seg.starts_with('[') || !v6seg.ends_with(']'))
{
throw std::invalid_argument("Invalid string for Sock6Addr");
}
return {FromStr<In6Addr>{}(v6seg.substr(1, v6seg.size() - 2)),
StrToInt<10, std::uint16_t>{}(sv.substr(pos + 1)), 0};
}
};
template <>
struct ToStr<Sock6Addr>
{
using type = Sock6Addr;
using FromAddr = ToStr<In6Addr>;
using FromDec = IntToStr<10, std::uint16_t>;
// Addr + sep + port chars
static inline constexpr std::size_t buf_size =
FromAddr::buf_size + 1 + FromDec::buf_size;
template <typename CharT>
constexpr CharT* operator()(CharT* buf, Sock6Addr v) const noexcept
{
(buf++)[0] = '[';
buf = FromAddr{}(buf, v.addr);
(buf++)[0] = ']';
(buf++)[0] = ':';
return FromDec{}(buf, v.port);
}
};
namespace detail
{
static inline constexpr std::string_view upfx = "unix:";
}
template <>
struct FromStr<SockUAddr>
{
template <typename CharT>
constexpr SockUAddr operator()(std::basic_string_view<CharT> sv) const
{
if (sv.starts_with(detail::upfx))
{
sv = sv.substr(detail::upfx.size());
}
return SockUAddr{sv};
}
};
template <>
struct ToStr<SockUAddr>
{
using type = SockUAddr;
static inline constexpr std::size_t buf_size =
detail::upfx.size() + SockUAddr::maxLen;
template <typename CharT>
constexpr CharT* operator()(CharT* buf, const SockUAddr& v) const noexcept
{
buf = std::copy(detail::upfx.begin(), detail::upfx.end(), buf);
auto p = v.path();
return std::copy(p.begin(), p.end(), buf);
}
};
template <>
struct FromStr<SockInAddr>
{
template <typename CharT>
constexpr SockInAddr operator()(std::basic_string_view<CharT> sv) const
{
if (sv.starts_with('['))
{
return FromStr<Sock6Addr>{}(sv);
}
return FromStr<Sock4Addr>{}(sv);
}
};
template <>
struct ToStr<SockInAddr>
{
using type = SockInAddr;
static inline constexpr std::size_t buf_size =
std::max({ToStr<Sock4Addr>::buf_size, ToStr<Sock6Addr>::buf_size});
template <typename CharT>
constexpr CharT* operator()(CharT* buf, const SockInAddr& v) const noexcept
{
return std::visit(
[buf]<typename T>(const T& t) { return ToStr<T>{}(buf, t); }, v);
}
};
template <>
struct FromStr<SockAnyAddr>
{
template <typename CharT>
constexpr SockAnyAddr operator()(std::basic_string_view<CharT> sv) const
{
if (sv.starts_with('['))
{
return FromStr<Sock6Addr>{}(sv);
}
else if (sv.starts_with(detail::upfx))
{
return FromStr<SockUAddr>{}(sv);
}
return FromStr<Sock4Addr>{}(sv);
}
};
template <>
struct ToStr<SockAnyAddr>
{
using type = SockAnyAddr;
static inline constexpr std::size_t buf_size =
std::max({ToStr<Sock4Addr>::buf_size, ToStr<Sock6Addr>::buf_size,
ToStr<SockUAddr>::buf_size});
template <typename CharT>
constexpr CharT* operator()(CharT* buf, const SockAnyAddr& v) const noexcept
{
return std::visit(
[buf]<typename T>(const T& t) { return ToStr<T>{}(buf, t); }, v);
}
};
} // namespace stdplus
template <stdplus::SockAddr T, typename CharT>
struct fmt::formatter<T, CharT> : stdplus::Format<stdplus::ToStr<T>, CharT>
{};
template <stdplus::SockAddr T, typename CharT>
struct std::formatter<T, CharT> : stdplus::Format<stdplus::ToStr<T>, CharT>
{};