aboutsummaryrefslogtreecommitdiff
path: root/src/netaddress.cpp
diff options
context:
space:
mode:
authorpracticalswift <practicalswift@users.noreply.github.com>2021-04-17 11:11:16 +0000
committerpracticalswift <practicalswift@users.noreply.github.com>2021-04-22 15:53:53 +0000
commitc10f27fdb2d335954dd1017ce6d5800159427374 (patch)
tree4b2de67489fc87a3d30bc3968c3dbe573eaf9380 /src/netaddress.cpp
parent4b5659c6b115315c9fd2902b4edd4b960a5e066e (diff)
net: Make IPv6ToString do zero compression as described in RFC 5952
Diffstat (limited to 'src/netaddress.cpp')
-rw-r--r--src/netaddress.cpp59
1 files changed, 48 insertions, 11 deletions
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<const uint8_t> 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<const uint8_t> 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