types: Better IPv6 RFC5952 support

This makes sure our text output better conforms to standards.

Change-Id: I6d9ed5490d11b5e59c3c1bccabe0520fddeb6b56
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.cpp b/src/types.cpp
index 37eb69e..ff49313 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -34,23 +34,36 @@
     return {buf.begin(), buf.size()};
 }
 
-std::string_view AddrBufMaker<in_addr>::operator()(in_addr val) noexcept
+static char* makeInAddr(char* ptr, char* end, in_addr val) noexcept
 {
     auto v = bswap(ntoh(val.s_addr));
-    char* ptr = buf.begin();
     for (size_t i = 0; i < 3; ++i)
     {
-        const auto res = std::to_chars(ptr, buf.end(), v & 0xff, 10);
+        const auto res = std::to_chars(ptr, end, v & 0xff, 10);
         *res.ptr = '.';
         ptr = res.ptr + 1;
         v >>= 8;
     }
-    const auto res = std::to_chars(ptr, buf.end(), v & 0xff, 10);
-    return {buf.data(), res.ptr};
+    return std::to_chars(ptr, end, v & 0xff, 10).ptr;
+}
+
+std::string_view AddrBufMaker<in_addr>::operator()(in_addr val) noexcept
+{
+    return {buf.data(), makeInAddr(buf.data(), buf.end(), val)};
 }
 
 std::string_view AddrBufMaker<in6_addr>::operator()(in6_addr val) noexcept
 {
+    // IPv4 in IPv6 Addr
+    if (val.s6_addr32[0] == 0 && val.s6_addr32[1] == 0 &&
+        val.s6_addr32[2] == hton(uint32_t(0xffff)))
+    {
+        constexpr auto prefix = std::string_view("::ffff:");
+        return {buf.data(),
+                makeInAddr(std::copy(prefix.begin(), prefix.end(), buf.begin()),
+                           buf.end(), {val.s6_addr32[3]})};
+    }
+
     size_t skip_start = 0;
     size_t skip_size = 0;
     {
@@ -80,7 +93,7 @@
     char* ptr = buf.begin();
     for (size_t i = 0; i < 8; ++i)
     {
-        if (i == skip_start && skip_size > 0)
+        if (i == skip_start && skip_size > 1)
         {
             if (i == 0)
             {