From c10f27fdb2d335954dd1017ce6d5800159427374 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sat, 17 Apr 2021 11:11:16 +0000 Subject: net: Make IPv6ToString do zero compression as described in RFC 5952 --- src/netaddress.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/netaddress.cpp b/src/netaddress.cpp index d56ae78e92..cbccb0a8a9 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -556,20 +556,57 @@ static std::string IPv4ToString(Span a) return strprintf("%u.%u.%u.%u", a[0], a[1], a[2], a[3]); } +// Return an IPv6 address text representation with zero compression as described in RFC 5952 +// ("A Recommendation for IPv6 Address Text Representation"). static std::string IPv6ToString(Span a) { assert(a.size() == ADDR_IPV6_SIZE); - // clang-format off - return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", - ReadBE16(&a[0]), - ReadBE16(&a[2]), - ReadBE16(&a[4]), - ReadBE16(&a[6]), - ReadBE16(&a[8]), - ReadBE16(&a[10]), - ReadBE16(&a[12]), - ReadBE16(&a[14])); - // clang-format on + const std::array groups{ + ReadBE16(&a[0]), + ReadBE16(&a[2]), + ReadBE16(&a[4]), + ReadBE16(&a[6]), + ReadBE16(&a[8]), + ReadBE16(&a[10]), + ReadBE16(&a[12]), + ReadBE16(&a[14]), + }; + + // The zero compression implementation is inspired by Rust's std::net::Ipv6Addr, see + // https://github.com/rust-lang/rust/blob/cc4103089f40a163f6d143f06359cba7043da29b/library/std/src/net/ip.rs#L1635-L1683 + struct ZeroSpan { + size_t start_index{0}; + size_t len{0}; + }; + + // Find longest sequence of consecutive all-zero fields. Use first zero sequence if two or more + // zero sequences of equal length are found. + ZeroSpan longest, current; + for (size_t i{0}; i < groups.size(); ++i) { + if (groups[i] != 0) { + current = {i + 1, 0}; + continue; + } + current.len += 1; + if (current.len > longest.len) { + longest = current; + } + } + + std::string r; + r.reserve(39); + for (size_t i{0}; i < groups.size(); ++i) { + // Replace the longest sequence of consecutive all-zero fields with two colons ("::"). + if (longest.len >= 2 && i >= longest.start_index && i < longest.start_index + longest.len) { + if (i == longest.start_index) { + r += "::"; + } + continue; + } + r += strprintf("%s%x", ((!r.empty() && r.back() != ':') ? ":" : ""), groups[i]); + } + + return r; } std::string CNetAddr::ToStringIP() const -- cgit v1.2.3 From 54548bae8004a8f49d73bd29aeca8b41894214c4 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Thu, 22 Apr 2021 15:52:03 +0000 Subject: net: Avoid calling getnameinfo when formatting IPv6 addresses in CNetAddr::ToStringIP --- src/netaddress.cpp | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src') diff --git a/src/netaddress.cpp b/src/netaddress.cpp index cbccb0a8a9..112e216c09 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -615,15 +615,6 @@ std::string CNetAddr::ToStringIP() const case NET_IPV4: return IPv4ToString(m_addr); case NET_IPV6: { - CService serv(*this, 0); - struct sockaddr_storage sockaddr; - socklen_t socklen = sizeof(sockaddr); - if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) { - char name[1025] = ""; - if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, - sizeof(name), nullptr, 0, NI_NUMERICHOST)) - return std::string(name); - } return IPv6ToString(m_addr); } case NET_ONION: -- cgit v1.2.3