diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/ipv6_address | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/ipv6_address')
-rw-r--r-- | library/cpp/ipv6_address/ipv6_address.cpp | 437 | ||||
-rw-r--r-- | library/cpp/ipv6_address/ipv6_address.h | 238 | ||||
-rw-r--r-- | library/cpp/ipv6_address/ipv6_address_p.h | 7 | ||||
-rw-r--r-- | library/cpp/ipv6_address/ut/ipv6_address_ut.cpp | 112 | ||||
-rw-r--r-- | library/cpp/ipv6_address/ut/ya.make | 9 | ||||
-rw-r--r-- | library/cpp/ipv6_address/ya.make | 15 |
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() |