aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/ipv6_address
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/ipv6_address
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/ipv6_address')
-rw-r--r--library/cpp/ipv6_address/ipv6_address.cpp437
-rw-r--r--library/cpp/ipv6_address/ipv6_address.h238
-rw-r--r--library/cpp/ipv6_address/ipv6_address_p.h7
-rw-r--r--library/cpp/ipv6_address/ut/ipv6_address_ut.cpp112
-rw-r--r--library/cpp/ipv6_address/ut/ya.make9
-rw-r--r--library/cpp/ipv6_address/ya.make15
6 files changed, 818 insertions, 0 deletions
diff --git a/library/cpp/ipv6_address/ipv6_address.cpp b/library/cpp/ipv6_address/ipv6_address.cpp
new file mode 100644
index 0000000000..be8fcbae13
--- /dev/null
+++ b/library/cpp/ipv6_address/ipv6_address.cpp
@@ -0,0 +1,437 @@
+#include "ipv6_address.h"
+#include "ipv6_address_p.h"
+
+#ifdef _unix_
+#include <netinet/in.h>
+#endif
+
+#include <util/network/address.h>
+#include <util/network/init.h>
+#include <util/string/cast.h>
+#include <util/string/split.h>
+#include <util/system/byteorder.h>
+#include <util/ysaveload.h>
+
+#include <array>
+
+namespace {
+ // reinterpret_cast from memory, where most significant bit is first
+ inline ui128 FromMemMSF(const char* memPtr) {
+ Y_VERIFY(memPtr, " ");
+ return ui128{
+ *reinterpret_cast<const ui64*>(memPtr),
+ *(reinterpret_cast<const ui64*>(memPtr) + 1)
+ };
+ }
+
+ // zero-terminated copy of address string
+ template <size_t N>
+ inline auto AddrBuf(TStringBuf str) noexcept {
+ std::array<char, N+1> res;
+ auto len = Min(str.size(), N);
+ CopyN(str.begin(), len, res.begin());
+ res[len] = '\0';
+ return res;
+ }
+}
+
+void TIpv6Address::InitFrom(const in6_addr& addr) {
+ const ui64* const ui64Ptr = reinterpret_cast<const ui64*>(&addr);
+ const ui64 raw[2] = {SwapBytes(*ui64Ptr), SwapBytes(*(ui64Ptr + 1))};
+ Ip = FromMemMSF(reinterpret_cast<const char*>(raw));
+ Type_ = Ipv6;
+}
+void TIpv6Address::InitFrom(const in_addr& addr) {
+ unsigned long swapped = SwapBytes(addr.s_addr);
+ Ip = ui128{0, swapped};
+ Type_ = Ipv4;
+}
+
+void TIpv6Address::InitFrom(const sockaddr_in6& Addr) {
+ InitFrom(Addr.sin6_addr);
+ ScopeId_ = Addr.sin6_scope_id;
+}
+void TIpv6Address::InitFrom(const sockaddr_in& Addr) {
+ InitFrom(Addr.sin_addr);
+}
+
+TIpv6Address::TIpv6Address(const NAddr::IRemoteAddr& addr) {
+ if (addr.Addr()->sa_family == AF_INET) { // IPv4
+ const sockaddr_in* Tmp = reinterpret_cast<const sockaddr_in*>(addr.Addr());
+ InitFrom(*Tmp);
+ } else { // IPv6
+ const sockaddr_in6* Tmp = reinterpret_cast<const sockaddr_in6*>(addr.Addr());
+ InitFrom(*Tmp);
+ }
+}
+TIpv6Address::TIpv6Address(const sockaddr_in6& Addr) {
+ InitFrom(Addr);
+}
+TIpv6Address::TIpv6Address(const sockaddr_in& Addr) {
+ InitFrom(Addr);
+}
+TIpv6Address::TIpv6Address(const in6_addr& addr, ui32 Scope) {
+ InitFrom(addr);
+ ScopeId_ = Scope;
+}
+TIpv6Address::TIpv6Address(const in_addr& addr) {
+ InitFrom(addr);
+}
+
+TIpv6Address TIpv6Address::FromString(TStringBuf str, bool& ok) noexcept {
+ const TIpType ipType = FigureOutType(str);
+
+ if (ipType == Ipv6) {
+ ui32 scopeId = 0;
+ if (size_t pos = str.find('%'); pos != TStringBuf::npos) {
+ ::TryFromString(str.substr(pos + 1), scopeId);
+ str.Trunc(pos);
+ }
+
+ const auto buf = AddrBuf<INET6_ADDRSTRLEN>(str);
+ in6_addr addr;
+ if (inet_pton(AF_INET6, buf.data(), &addr) != 1) {
+ ok = false;
+ return TIpv6Address();
+ }
+
+ ok = true;
+ return TIpv6Address(addr, scopeId);
+ } else { // if (ipType == Ipv4) {
+ const auto buf = AddrBuf<INET_ADDRSTRLEN>(str);
+ in_addr addr;
+ if (inet_pton(AF_INET, buf.data(), &addr) != 1) {
+ ok = false;
+ return TIpv6Address();
+ }
+
+ ok = true;
+ return TIpv6Address(addr);
+ }
+}
+
+TString TIpv6Address::ToString(bool* ok) const noexcept {
+ return ToString(true, ok);
+}
+TString TIpv6Address::ToString(bool PrintScopeId, bool* ok) const noexcept {
+ TString result;
+ bool isOk = true;
+
+ if (Type_ == TIpv6Address::Ipv4) {
+ result.resize(INET_ADDRSTRLEN + 2);
+ in_addr addr;
+ ToInAddr(addr);
+ isOk = inet_ntop(AF_INET, &addr, result.begin(), INET_ADDRSTRLEN);
+ result.resize(result.find('\0'));
+ } else if (Type_ == TIpv6Address::Ipv6) {
+ result.resize(INET6_ADDRSTRLEN + 2);
+ in6_addr addr;
+ ToIn6Addr(addr);
+ isOk = inet_ntop(AF_INET6, &addr, result.begin(), INET6_ADDRSTRLEN);
+ result.resize(result.find('\0'));
+ if (PrintScopeId)
+ result += "%" + ::ToString(ScopeId_);
+ } else {
+ result = "null";
+ isOk = true;
+ }
+
+ if (ok) {
+ *ok = isOk;
+ }
+
+ return result;
+}
+
+void TIpv6Address::ToSockaddrAndSocklen(sockaddr_in& sockAddrIPv4,
+ sockaddr_in6& sockAddrIPv6, // in
+ const sockaddr*& sockAddrPtr,
+ socklen_t& sockAddrSize,
+ ui16 Port) const { // out
+
+ if (Type_ == Ipv4) {
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(Port);
+ ToInAddr(sockAddrIPv4.sin_addr);
+
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = reinterpret_cast<sockaddr*>(&sockAddrIPv4);
+
+ } else if (Type_ == Ipv6) {
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(Port);
+ ToIn6Addr(sockAddrIPv6.sin6_addr);
+ sockAddrIPv6.sin6_scope_id = ScopeId_;
+ sockAddrIPv6.sin6_flowinfo = 0;
+
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = reinterpret_cast<sockaddr*>(&sockAddrIPv6);
+ } else
+ Y_VERIFY(false);
+}
+
+void TIpv6Address::ToInAddr(in_addr& Addr4) const {
+ Y_VERIFY(Type_ == TIpv6Address::Ipv4);
+
+ Zero(Addr4);
+ ui32 Value = GetLow(Ip);
+ Y_VERIFY(Value == GetLow(Ip), " ");
+ Y_VERIFY(GetHigh(Ip) == 0, " ");
+ Addr4.s_addr = SwapBytes(Value);
+}
+void TIpv6Address::ToIn6Addr(in6_addr& Addr6) const {
+ Y_VERIFY(Type_ == TIpv6Address::Ipv6);
+
+ Zero(Addr6);
+ ui64 Raw[2] = {GetHigh(Ip), GetLow(Ip)};
+ *Raw = SwapBytes(*Raw);
+ Raw[1] = SwapBytes(1 [Raw]);
+ memcpy(&Addr6, Raw, sizeof(Raw));
+}
+
+void TIpv6Address::Save(IOutputStream* out) const {
+ ::Save(out, Ip);
+ ::Save(out, static_cast<ui8>(Type_));
+ ::Save(out, ScopeId_);
+}
+void TIpv6Address::Load(IInputStream* in) {
+ ::Load(in, Ip);
+ ui8 num;
+ ::Load(in, num);
+ Type_ = static_cast<TIpType>(num);
+ ::Load(in, ScopeId_);
+}
+
+bool TIpv6Address::Isv4MappedTov6() const noexcept {
+ /// http://en.wikipedia.org/wiki/IPv6
+ /// Hybrid dual-stack IPv6/IPv4 implementations recognize a special class of addresses,
+ /// the IPv4-mapped IPv6 addresses. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ /// and the remaining 32 bits are the IPv4 address.
+
+ if (Type_ != Ipv6)
+ return false;
+
+ if (GetHigh(Ip) != 0)
+ return false; // First 64 bit are not zero -> it is not ipv4-mapped-ipv6 address
+
+ const ui64 Low = GetLow(Ip) >> 32;
+ if (Low != 0x0000ffff)
+ return false;
+
+ return true;
+}
+
+TIpv6Address TIpv6Address::TryToExtractIpv4From6() const noexcept {
+ if (Isv4MappedTov6() == false)
+ return TIpv6Address();
+
+ const ui64 NewLow = GetLow(Ip) & 0x00000000ffffffff;
+ TIpv6Address Result(ui128(0, NewLow), Ipv4);
+ return Result;
+}
+
+TIpv6Address TIpv6Address::Normalized() const noexcept {
+ if (Isv4MappedTov6() == false)
+ return *this;
+
+ TIpv6Address Result = TryToExtractIpv4From6();
+ Y_VERIFY(Result.IsNull() == false);
+ return Result;
+}
+
+IOutputStream& operator<<(IOutputStream& Out, const TIpv6Address::TIpType Type) noexcept {
+ switch (Type) {
+ case TIpv6Address::Ipv4:
+ Out << "Ipv4";
+ return Out;
+ case TIpv6Address::Ipv6:
+ Out << "Ipv6";
+ return Out;
+ default:
+ Out << "UnknownType";
+ return Out;
+ }
+}
+
+IOutputStream& operator<<(IOutputStream& out, const TIpv6Address& ipv6Address) noexcept {
+ bool ok;
+ const TString& strIp = ipv6Address.ToString(&ok);
+ if (!ok) {
+ return out << "Can not convert ip to string";
+ } else {
+ return out << strIp;
+ }
+}
+
+TString THostAddressAndPort::ToString() const noexcept {
+ TStringStream Str;
+ Str << *this;
+ return Str.Str();
+}
+
+IOutputStream& operator<<(IOutputStream& Out, const THostAddressAndPort& HostAddressAndPort) noexcept {
+ Out << HostAddressAndPort.Ip << ":" << HostAddressAndPort.Port;
+ return Out;
+}
+
+namespace {
+ class TRemoteAddr: public NAddr::IRemoteAddr {
+ public:
+ TRemoteAddr(const TIpv6Address& Address, TIpPort Port);
+ const sockaddr* Addr() const override;
+ socklen_t Len() const override;
+
+ private:
+ sockaddr_in SockAddrIPv4;
+ sockaddr_in6 SockAddrIPv6;
+ const sockaddr* SockAddrPtr = nullptr;
+ socklen_t SockAddrSize = 0;
+ };
+
+ TRemoteAddr::TRemoteAddr(const TIpv6Address& Address, TIpPort Port) {
+ Address.ToSockaddrAndSocklen(SockAddrIPv4, SockAddrIPv6, SockAddrPtr, SockAddrSize, Port);
+ }
+ const sockaddr* TRemoteAddr::Addr() const {
+ return SockAddrPtr;
+ }
+ socklen_t TRemoteAddr::Len() const {
+ return SockAddrSize;
+ }
+}
+
+NAddr::IRemoteAddr* ToIRemoteAddr(const TIpv6Address& Address, TIpPort Port) {
+ return new TRemoteAddr(Address, Port);
+}
+
+std::tuple<THostAddressAndPort, TString, TIpPort> ParseHostAndMayBePortFromString(const TString& RawStr,
+ TIpPort DefaultPort,
+ bool& Ok) noexcept {
+ // Cout << "ParseHostAndMayBePortFromString: " << RawStr << ", Port: " << DefaultPort << Endl;
+
+ using TRes = std::tuple<THostAddressAndPort, TString, TIpPort>;
+
+ // ---------------------------------------------------------------------
+
+ const size_t BracketColPos = RawStr.find("]:");
+ if (BracketColPos != TString::npos) {
+ // [ipv6]:port
+ if (!RawStr.StartsWith('[')) {
+ Ok = false;
+ return {};
+ }
+ const TStringBuf StrIpv6(RawStr.begin() + 1, RawStr.begin() + BracketColPos);
+ const TStringBuf StrPort(RawStr.begin() + BracketColPos + 2, RawStr.end());
+
+ bool IpConverted;
+ const TIpv6Address Ip = TIpv6Address::FromString(StrIpv6, IpConverted);
+ if (!IpConverted) {
+ Ok = false;
+ return {};
+ }
+ if (Ip.Type() != TIpv6Address::Ipv6) {
+ Ok = false;
+ return {};
+ }
+ TIpPort Port {};
+ if (!::TryFromString(StrPort, Port)) {
+ Ok = false;
+ return {};
+ }
+
+ Ok = true;
+ TRes Res{{Ip, Port}, {}, {}};
+ return Res;
+ }
+
+ // ---------------------------------------------------------------------
+
+ if (RawStr.StartsWith('[')) {
+ // [ipv6]
+ if (!RawStr.EndsWith(']')) {
+ Ok = false;
+ return {};
+ }
+ const TStringBuf StrIpv6(RawStr.begin() + 1, RawStr.end() - 1);
+
+ bool IpConverted;
+ const TIpv6Address Ip = TIpv6Address::FromString(StrIpv6, IpConverted);
+ if (!IpConverted || Ip.Type() != TIpv6Address::Ipv6) {
+ Ok = false;
+ return {};
+ }
+
+ Ok = true;
+ TRes Res{{Ip, DefaultPort}, {}, {}};
+ return Res;
+ }
+
+ // ---------------------------------------------------------------------
+
+ const size_t ColPos = RawStr.find(':');
+ if (ColPos != TString::npos) {
+ // host:port
+ // ipv4:port
+ // ipv6
+
+ {
+ bool IpConverted;
+ const TIpv6Address Ipv6 = TIpv6Address::FromString(RawStr, IpConverted);
+ if (IpConverted && Ipv6.Type() == TIpv6Address::Ipv6) {
+ // ipv6
+ Ok = true;
+ TRes Res{{Ipv6, DefaultPort}, {}, {}};
+ return Res;
+ }
+ }
+
+ const TStringBuf StrPort(RawStr.begin() + ColPos + 1, RawStr.end());
+ TIpPort Port {};
+ if (!::TryFromString(StrPort, Port)) {
+ Ok = false;
+ return {};
+ }
+
+ const TStringBuf StrIpv4OrHost(RawStr.begin(), RawStr.begin() + ColPos);
+ {
+ bool IpConverted;
+ const TIpv6Address Ipv4 = TIpv6Address::FromString(StrIpv4OrHost, IpConverted);
+ if (IpConverted && Ipv4.Type() == TIpv6Address::Ipv4) {
+ // ipv4:port
+ Ok = true;
+ TRes Res{{Ipv4, Port}, {}, {}};
+ return Res;
+ }
+ }
+
+ {
+ // host:port
+ Ok = true;
+ TRes Res{THostAddressAndPort{}, TString(StrIpv4OrHost), Port};
+ return Res;
+ }
+ }
+
+ // ---------------------------------------------------------------------
+
+ {
+ // ipv4
+ bool IpConverted;
+ const TIpv6Address Ipv4 = TIpv6Address::FromString(RawStr, IpConverted);
+ if (IpConverted && Ipv4.Type() == TIpv6Address::Ipv4) {
+ Ok = true;
+ TRes Res{{Ipv4, DefaultPort}, {}, {}};
+ return Res;
+ }
+ }
+
+ // ---------------------------------------------------------------------
+
+ {
+ // host
+ Ok = true;
+ TRes Res{THostAddressAndPort{}, TString(RawStr), DefaultPort};
+ return Res;
+ }
+}
diff --git a/library/cpp/ipv6_address/ipv6_address.h b/library/cpp/ipv6_address/ipv6_address.h
new file mode 100644
index 0000000000..1d7eb0b65f
--- /dev/null
+++ b/library/cpp/ipv6_address/ipv6_address.h
@@ -0,0 +1,238 @@
+#pragma once
+
+#include <util/generic/hash_set.h>
+#include <util/network/ip.h>
+#include <util/stream/input.h>
+
+#include <library/cpp/int128/int128.h>
+
+#if defined(_freebsd_)
+// #include required to avoid problem with undefined 'socklen_t' on FreeBSD
+#include <sys/socket.h>
+#endif
+
+#if defined(_win_)
+// #include required to avoid problem with undefined 'socklen_t' on Windows
+#include <util/network/socket.h>
+#endif
+
+namespace NAddr {
+ class IRemoteAddr;
+}
+struct in6_addr;
+struct in_addr;
+struct sockaddr;
+struct sockaddr_in;
+struct sockaddr_in6;
+
+// TODO (dimanne): rename to something like TIntInetAddress or THostAddress
+class TIpv6Address {
+public:
+ enum TIpType { Ipv6,
+ Ipv4,
+ LAST };
+
+ constexpr TIpv6Address() noexcept = default;
+ constexpr TIpv6Address(const TIpv6Address&) noexcept = default;
+ constexpr TIpv6Address& operator=(const TIpv6Address&) noexcept = default;
+
+ constexpr TIpv6Address(const ui128& ip, TIpType Type) noexcept
+ : Ip(ip), Type_(Type)
+ {}
+
+ constexpr TIpv6Address(ui8 a, ui8 b, ui8 c, ui8 d) noexcept
+ : Ip((ui32(a) << 24) | (ui32(b) << 16) | (ui32(c) << 8) | ui32(d))
+ , Type_(TIpv6Address::Ipv4)
+ {}
+
+ constexpr TIpv6Address(ui16 a, ui16 b, ui16 c, ui16 d, ui16 e, ui16 f, ui16 g, ui16 h, ui32 scope = 0) noexcept
+ : Type_(TIpv6Address::Ipv6)
+ , ScopeId_(scope)
+ {
+ auto hi = (ui64(a) << 48) | (ui64(b) << 32) | (ui64(c) << 16) | ui64(d);
+ auto lo = (ui64(e) << 48) | (ui64(f) << 32) | (ui64(g) << 16) | ui64(h);
+ Ip = {hi, lo};
+ }
+
+ explicit TIpv6Address(const NAddr::IRemoteAddr& addr);
+ explicit TIpv6Address(const sockaddr_in6& Addr);
+ explicit TIpv6Address(const sockaddr_in& Addr);
+ explicit TIpv6Address(const in6_addr& addr, ui32 Scope);
+ explicit TIpv6Address(const in_addr& addr);
+
+ static TIpv6Address FromString(TStringBuf srcStr, bool& ok) noexcept;
+
+ constexpr bool IsNull() const noexcept {
+ return Ip == 0;
+ }
+
+ constexpr bool IsValid() const noexcept {
+ return Ip != 0 && (Type_ == Ipv6 || Type_ == Ipv4);
+ }
+
+ explicit constexpr operator bool() const noexcept {
+ return IsValid();
+ }
+
+ constexpr bool operator ! () const noexcept {
+ return !IsValid();
+ }
+
+ bool Isv4MappedTov6() const noexcept;
+ TIpv6Address TryToExtractIpv4From6() const noexcept;
+ TIpv6Address Normalized() const noexcept;
+
+ TString ToString(bool* ok = nullptr) const noexcept;
+ TString ToString(bool PrintScopeId, bool* ok = nullptr) const noexcept;
+
+ void ToSockaddrAndSocklen(sockaddr_in& sockAddrIPv4,
+ sockaddr_in6& sockAddrIPv6, // in
+ const sockaddr*& sockAddrPtr,
+ socklen_t& sockAddrSize,
+ ui16 Port) const; // out
+
+ void ToInAddr(in_addr& Addr4) const;
+ void ToIn6Addr(in6_addr& Addr6) const;
+ // int SocketFamily() const;
+
+ constexpr bool operator<(const TIpv6Address& other) const noexcept {
+ if (Type_ != other.Type_)
+ return Type_ > other.Type_;
+ else
+ return Ip < other.Ip;
+ }
+ constexpr bool operator>(const TIpv6Address& other) const noexcept {
+ if (Type_ != other.Type_)
+ return Type_ < other.Type_;
+ else
+ return Ip > other.Ip;
+ }
+ constexpr bool operator==(const TIpv6Address& other) const noexcept {
+ return Type_ == other.Type_ && Ip == other.Ip;
+ }
+ constexpr bool operator!=(const TIpv6Address& other) const noexcept {
+ return Type_ != other.Type_ || Ip != other.Ip;
+ }
+
+ constexpr bool operator<=(const TIpv6Address& other) const noexcept {
+ return !(*this > other);
+ }
+
+ constexpr bool operator>=(const TIpv6Address& other) const noexcept {
+ return !(*this < other);
+ }
+
+ constexpr operator ui128() const noexcept {
+ return Ip;
+ }
+
+ constexpr TIpType Type() const noexcept {
+ return Type_;
+ }
+
+ void SetScopeId(ui32 New) noexcept {
+ ScopeId_ = New;
+ }
+ constexpr ui32 ScopeId() const noexcept {
+ return ScopeId_;
+ }
+
+ void Save(IOutputStream* out) const;
+ void Load(IInputStream* in);
+
+private:
+ void InitFrom(const in6_addr& addr);
+ void InitFrom(const in_addr& addr);
+
+ void InitFrom(const sockaddr_in6& Addr);
+ void InitFrom(const sockaddr_in& Addr);
+
+ ui128 Ip{};
+ TIpType Type_ = LAST;
+ ui32 ScopeId_ = 0;
+};
+IOutputStream& operator<<(IOutputStream& Out, const TIpv6Address::TIpType Type) noexcept;
+IOutputStream& operator<<(IOutputStream& Out, const TIpv6Address& ipv6Address) noexcept;
+
+constexpr TIpv6Address Get127001() noexcept {
+ return {127, 0, 0, 1};
+}
+
+constexpr TIpv6Address Get1() noexcept {
+ return {1, TIpv6Address::Ipv6};
+}
+
+struct THostAddressAndPort {
+ constexpr THostAddressAndPort() noexcept = default;
+ constexpr THostAddressAndPort(const TIpv6Address& i, TIpPort p) noexcept {
+ Ip = i;
+ Port = p;
+ }
+
+ constexpr bool operator==(const THostAddressAndPort& Other) const noexcept {
+ return Ip == Other.Ip && Port == Other.Port;
+ }
+ constexpr bool operator!=(const THostAddressAndPort& Other) const noexcept {
+ return !(*this == Other);
+ }
+ constexpr bool IsValid() const noexcept {
+ return Ip.IsValid() && Port != 0;
+ }
+
+ TString ToString() const noexcept;
+
+ TIpv6Address Ip {};
+ TIpPort Port {0};
+};
+IOutputStream& operator<<(IOutputStream& Out, const THostAddressAndPort& HostAddressAndPort) noexcept;
+
+///
+/// Returns
+/// 1. either valid THostAddressAndPort
+/// 2. or TString with hostname (which you should resolve) and TIpPort with port
+/// 3. or error, if Ok == false
+///
+/// Supported RawStrs are
+/// 1.2.3.4 // port wil be equal to DefaultPort
+/// 1.2.3.4:80
+/// [2001::7348] // port wil be equal to DefaultPort
+/// 2001::7348 // port wil be equal to DefaultPort
+/// [2001::7348]:80
+///
+std::tuple<THostAddressAndPort, TString, TIpPort> ParseHostAndMayBePortFromString(const TString& RawStr,
+ TIpPort DefaultPort,
+ bool& Ok) noexcept;
+
+using TIpv6AddressesSet = THashSet<TIpv6Address>;
+
+template <>
+struct THash<TIpv6Address> {
+ inline size_t operator()(const TIpv6Address& ip) const {
+ const ui128& Tmp = static_cast<ui128>(ip);
+ return CombineHashes(THash<ui128>()(Tmp), THash<ui8>()(static_cast<ui8>(ip.Type())));
+ }
+};
+template <>
+struct THash<THostAddressAndPort> {
+ inline size_t operator()(const THostAddressAndPort& IpAndPort) const {
+ return CombineHashes(THash<TIpv6Address>()(IpAndPort.Ip), THash<TIpPort>()(IpAndPort.Port));
+ }
+};
+
+namespace std {
+ template <>
+ struct hash<TIpv6Address> {
+ std::size_t operator()(const TIpv6Address& Ip) const noexcept {
+ return THash<TIpv6Address>()(Ip);
+ }
+ };
+}
+
+NAddr::IRemoteAddr* ToIRemoteAddr(const TIpv6Address& Address, TIpPort Port);
+
+// template <>
+// class TSerializer<TIpv6Address> {
+// public:
+// static void Save(IOutputStream *out, const TIpv6Address &ip);
+// static void Load(IInputStream *in, TIpv6Address &ip);
+//};
diff --git a/library/cpp/ipv6_address/ipv6_address_p.h b/library/cpp/ipv6_address/ipv6_address_p.h
new file mode 100644
index 0000000000..44bec74579
--- /dev/null
+++ b/library/cpp/ipv6_address/ipv6_address_p.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "ipv6_address.h"
+
+inline TIpv6Address::TIpType FigureOutType(TStringBuf srcStr) noexcept {
+ return srcStr.Contains(':') ? TIpv6Address::Ipv6 : TIpv6Address::Ipv4;
+}
diff --git a/library/cpp/ipv6_address/ut/ipv6_address_ut.cpp b/library/cpp/ipv6_address/ut/ipv6_address_ut.cpp
new file mode 100644
index 0000000000..73bb7cffdd
--- /dev/null
+++ b/library/cpp/ipv6_address/ut/ipv6_address_ut.cpp
@@ -0,0 +1,112 @@
+#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/ipv6_address/ipv6_address.h>
+#include <unordered_set>
+
+class TIpv6AddressTest: public TTestBase {
+ UNIT_TEST_SUITE(TIpv6AddressTest);
+ UNIT_TEST(ParseHostAndMayBePortFromString_data);
+ UNIT_TEST(CheckAddressValidity)
+ UNIT_TEST_SUITE_END();
+
+private:
+ void ParseHostAndMayBePortFromString_data();
+ void CheckAddressValidity();
+ void HashCompileTest();
+};
+
+UNIT_TEST_SUITE_REGISTRATION(TIpv6AddressTest);
+
+using TResult = std::tuple<THostAddressAndPort, TString, TIpPort>;
+
+TResult IpRes(TString Ip, TIpPort Port) {
+ bool Ok;
+ THostAddressAndPort HostAddressAndPort;
+ HostAddressAndPort.Ip = TIpv6Address::FromString(Ip, Ok);
+ Y_VERIFY(Ok);
+ HostAddressAndPort.Port = Port;
+ return TResult(HostAddressAndPort, {}, {});
+}
+TResult HostRes(TString HostName, TIpPort Port) {
+ return TResult({}, HostName, Port);
+}
+
+void ParseHostAndMayBePortFromString(TString RawStr,
+ TIpPort DefaultPort,
+ const TResult ExpectedResult,
+ const bool ExpectedOk) {
+ bool Ok = false;
+ const TResult ActualResult = ParseHostAndMayBePortFromString(RawStr, DefaultPort, Ok);
+
+ UNIT_ASSERT(Ok == ExpectedOk);
+ if (Ok == false)
+ return;
+
+ UNIT_ASSERT(ActualResult == ExpectedResult);
+}
+
+void CheckIpDefPortAgainstIpPortDefPort_v4OrHost(TString Ip,
+ TIpPort Port,
+ const TResult ExpectedResult,
+ const bool ExpectedOk) {
+ ParseHostAndMayBePortFromString(Ip, Port, ExpectedResult, ExpectedOk);
+
+ TString New = Ip + ":" + ToString(Port);
+ ParseHostAndMayBePortFromString(New, Port + 12, ExpectedResult, ExpectedOk);
+}
+
+void CheckIpDefPortAgainstIpPortDefPort_v6(TString Ip, TIpPort Port, const TResult ExpectedResult, const bool ExpectedOk) {
+ ParseHostAndMayBePortFromString(Ip, Port, ExpectedResult, ExpectedOk);
+
+ TString New = "[" + Ip + "]" + ":" + ToString(Port);
+ ParseHostAndMayBePortFromString(New, Port + 12, ExpectedResult, ExpectedOk);
+}
+
+void CheckIpDefPortAgainstIpPortDefPort(TString Ip, TIpPort Port, const TResult ExpectedResult, const bool ExpectedOk) {
+ if (Ip.find(':') == TString::npos) {
+ CheckIpDefPortAgainstIpPortDefPort_v4OrHost(Ip, Port, ExpectedResult, ExpectedOk);
+ } else {
+ CheckIpDefPortAgainstIpPortDefPort_v6(Ip, Port, ExpectedResult, ExpectedOk);
+ }
+}
+
+void TIpv6AddressTest::ParseHostAndMayBePortFromString_data() {
+ CheckIpDefPortAgainstIpPortDefPort("1.2.3.4", 123, IpRes("1.2.3.4", 123), true);
+ ParseHostAndMayBePortFromString("[1.2.3.4]", 123, {}, false);
+
+ ParseHostAndMayBePortFromString("[2001::7348]", 123, IpRes("2001::7348", 123), true);
+ CheckIpDefPortAgainstIpPortDefPort("2001::7348", 123, IpRes("2001::7348", 123), true);
+
+ CheckIpDefPortAgainstIpPortDefPort("ya.ru", 123, HostRes("ya.ru", 123), true);
+}
+
+void TIpv6AddressTest::CheckAddressValidity() {
+ bool Ok;
+
+ constexpr TIpv6Address partsV4 {12, 34, 56, 78};
+ static_assert(partsV4.Type() == TIpv6Address::Ipv4);
+
+ constexpr TIpv6Address intV4 {0x0C22384E, TIpv6Address::Ipv4};
+ static_assert(partsV4 == intV4);
+
+ const auto parsedV4 = TIpv6Address::FromString("12.34.56.78", Ok);
+ UNIT_ASSERT(Ok);
+ UNIT_ASSERT_EQUAL(parsedV4, partsV4);
+
+ constexpr TIpv6Address partsV6 {0xFB, 0x1634, 0x19, 0xABED, 0, 0x8001, 0x1670, 0x742};
+ static_assert(partsV6.Type() == TIpv6Address::Ipv6);
+
+ constexpr TIpv6Address intV6 {{0x00FB16340019ABED, 0x0000800116700742}, TIpv6Address::Ipv6};
+ static_assert(partsV6 == intV6);
+
+ const auto parsedV6 = TIpv6Address::FromString("FB:1634:19:ABED:0:8001:1670:742", Ok);
+ UNIT_ASSERT(Ok);
+ UNIT_ASSERT_EQUAL(parsedV6, partsV6);
+
+ static_assert(Get127001() == TIpv6Address(0x7F000001, TIpv6Address::Ipv4));
+ static_assert(Get1() == TIpv6Address(0, 0, 0, 0, 0, 0, 0, 1));
+}
+
+void TIpv6AddressTest::HashCompileTest() {
+ std::unordered_set<TIpv6Address> test;
+ Y_UNUSED(test);
+}
diff --git a/library/cpp/ipv6_address/ut/ya.make b/library/cpp/ipv6_address/ut/ya.make
new file mode 100644
index 0000000000..bd22899379
--- /dev/null
+++ b/library/cpp/ipv6_address/ut/ya.make
@@ -0,0 +1,9 @@
+OWNER(g:zora)
+
+UNITTEST_FOR(library/cpp/ipv6_address)
+
+SRCS(
+ ipv6_address_ut.cpp
+)
+
+END()
diff --git a/library/cpp/ipv6_address/ya.make b/library/cpp/ipv6_address/ya.make
new file mode 100644
index 0000000000..5c9bf7e383
--- /dev/null
+++ b/library/cpp/ipv6_address/ya.make
@@ -0,0 +1,15 @@
+LIBRARY()
+
+OWNER(g:zora)
+
+SRCS(
+ ipv6_address.cpp
+ ipv6_address.h
+ ipv6_address_p.h
+)
+
+PEERDIR(
+ library/cpp/int128
+)
+
+END()