net/addr/sock: SockAddrBuf -> SockAddr conversion

This makes it trivial to convert buffers from the kernel into sockaddrs
that can be manipulated and printed.

Change-Id: I13f4998e5f33bae4a8af03ea3c2243254010a0b4
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/net/addr/sock.hpp b/include/stdplus/net/addr/sock.hpp
index 970fbd2..73b4edf 100644
--- a/include/stdplus/net/addr/sock.hpp
+++ b/include/stdplus/net/addr/sock.hpp
@@ -16,6 +16,11 @@
 
 namespace stdplus
 {
+namespace detail
+{
+struct SockAddrUnsafe
+{};
+} // namespace detail
 
 struct SockAddrBuf
 {
@@ -60,6 +65,26 @@
     In4Addr addr;
     std::uint16_t port;
 
+    static constexpr Sock4Addr fromBuf(const SockAddrBuf& buf)
+    {
+        if (buf.fam != AF_INET)
+        {
+            throw std::invalid_argument("Sock4Addr fromBuf");
+        }
+        return fromBuf(detail::SockAddrUnsafe(), buf);
+    }
+    static constexpr Sock4Addr fromBuf(detail::SockAddrUnsafe,
+                                       const SockAddrBuf& buf)
+    {
+        if (buf.len != sizeof(sockaddr_in))
+        {
+            throw std::invalid_argument("Sock4Addr fromBuf");
+        }
+        const auto& sin = reinterpret_cast<const sockaddr_in&>(buf);
+        return Sock4Addr{.addr = sin.sin_addr,
+                         .port = stdplus::ntoh(sin.sin_port)};
+    }
+
     constexpr bool operator==(Sock4Addr rhs) const noexcept
     {
         return addr == rhs.addr && port == rhs.port;
@@ -105,6 +130,27 @@
     std::uint16_t port;
     std::uint32_t scope;
 
+    static constexpr Sock6Addr fromBuf(const SockAddrBuf& buf)
+    {
+        if (buf.fam != AF_INET6)
+        {
+            throw std::invalid_argument("Sock6Addr fromBuf");
+        }
+        return fromBuf(detail::SockAddrUnsafe(), buf);
+    }
+    static constexpr Sock6Addr fromBuf(detail::SockAddrUnsafe,
+                                       const SockAddrBuf& buf)
+    {
+        if (buf.len != sizeof(sockaddr_in6))
+        {
+            throw std::invalid_argument("Sock6Addr fromBuf");
+        }
+        const auto& sin6 = reinterpret_cast<const sockaddr_in6&>(buf);
+        return Sock6Addr{.addr = sin6.sin6_addr,
+                         .port = stdplus::ntoh(sin6.sin6_port),
+                         .scope = sin6.sin6_scope_id};
+    }
+
     constexpr bool operator==(Sock6Addr rhs) const noexcept
     {
         return addr == rhs.addr && port == rhs.port && scope == rhs.scope;
@@ -170,6 +216,41 @@
         std::copy(path.begin() + 1, path.end(), buf_.begin() + 1);
     }
 
+    constexpr SockUAddr(detail::SockAddrUnsafe, std::string_view path) noexcept
+    {
+        if (path.empty())
+        {
+            len_ = 0;
+            return;
+        }
+        bool abstract = path[0] == '\0';
+        // Abstract sockets are not null terminated but path sockets are
+        len_ = path.size() - (abstract ? 0 : 1);
+        buf_[0] = abstract ? '@' : path[0];
+        std::copy_n(path.begin() + 1, len_ - 1, buf_.begin() + 1);
+    }
+
+    static constexpr SockUAddr fromBuf(const SockAddrBuf& buf)
+    {
+        if (buf.fam != AF_UNIX)
+        {
+            throw std::invalid_argument("SockUAddr fromBuf");
+        }
+        return fromBuf(detail::SockAddrUnsafe(), buf);
+    }
+    static constexpr SockUAddr fromBuf(detail::SockAddrUnsafe,
+                                       const SockAddrBuf& buf)
+    {
+        if (buf.len < sizeof(sockaddr_un{}.sun_family))
+        {
+            throw std::invalid_argument("SockUAddr fromBuf");
+        }
+        const auto& sun = reinterpret_cast<const sockaddr_un&>(buf);
+        return SockUAddr{
+            detail::SockAddrUnsafe(),
+            {sun.sun_path, buf.len - offsetof(sockaddr_un, sun_path)}};
+    }
+
     constexpr bool operator==(const SockUAddr& rhs) const noexcept
     {
         return path() == rhs.path();
@@ -288,6 +369,19 @@
     constexpr SockInAddr(Sock6Addr a) noexcept :
         detail::SockAnyAddr<detail::SockInAddrV>(a)
     {}
+
+    static constexpr SockInAddr fromBuf(const SockAddrBuf& buf)
+    {
+        if (buf.fam == AF_INET)
+        {
+            return Sock4Addr::fromBuf(detail::SockAddrUnsafe(), buf);
+        }
+        else if (buf.fam == AF_INET6)
+        {
+            return Sock6Addr::fromBuf(detail::SockAddrUnsafe(), buf);
+        }
+        throw std::invalid_argument("Unknown SockInAddr");
+    }
 };
 
 template <>
@@ -321,6 +415,23 @@
         detail::SockAnyAddr<detail::SockAnyAddrV>(
             std::visit([](auto v) { return detail::SockAnyAddrV(v); }, a))
     {}
+
+    static constexpr SockAnyAddr fromBuf(const SockAddrBuf& buf)
+    {
+        if (buf.fam == AF_INET)
+        {
+            return Sock4Addr::fromBuf(detail::SockAddrUnsafe(), buf);
+        }
+        else if (buf.fam == AF_INET6)
+        {
+            return Sock6Addr::fromBuf(detail::SockAddrUnsafe(), buf);
+        }
+        else if (buf.fam == AF_UNIX)
+        {
+            return SockUAddr::fromBuf(detail::SockAddrUnsafe(), buf);
+        }
+        throw std::invalid_argument("Unknown SockInAddr");
+    }
 };
 
 template <>
diff --git a/test/net/addr/sock.cpp b/test/net/addr/sock.cpp
index e5ed833..2a7487b 100644
--- a/test/net/addr/sock.cpp
+++ b/test/net/addr/sock.cpp
@@ -28,6 +28,22 @@
     EXPECT_EQ(sizeof(sockaddr_in), addr1.sockaddrLen());
 }
 
+TEST(Sock4Addr, FromBuf)
+{
+    SockAddrBuf buf = {};
+    auto& sin = reinterpret_cast<sockaddr_in&>(buf);
+    sin.sin_addr.s_addr = stdplus::hton(std::uint32_t{0xff000000});
+    sin.sin_port = stdplus::hton(std::uint16_t{33});
+    EXPECT_THROW(Sock4Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET;
+    EXPECT_THROW(Sock4Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET6;
+    buf.len = sizeof(sockaddr_in);
+    EXPECT_THROW(Sock4Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET;
+    EXPECT_EQ(Sock4Addr::fromBuf(buf), Sock4Addr(In4Addr{255, 0, 0, 0}, 33));
+}
+
 TEST(Sock4Addr, FromStr)
 {
     constexpr FromStr<Sock4Addr> fs;
@@ -68,6 +84,22 @@
     EXPECT_EQ(sizeof(sockaddr_in6), addr1.sockaddrLen());
 }
 
+TEST(Sock6Addr, FromBuf)
+{
+    SockAddrBuf buf = {};
+    auto& sin6 = reinterpret_cast<sockaddr_in6&>(buf);
+    sin6.sin6_addr.s6_addr[0] = 0xff;
+    sin6.sin6_port = stdplus::hton(std::uint16_t{33});
+    EXPECT_THROW(Sock6Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET6;
+    EXPECT_THROW(Sock6Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET;
+    buf.len = sizeof(sockaddr_in6);
+    EXPECT_THROW(Sock6Addr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET6;
+    EXPECT_EQ(Sock6Addr::fromBuf(buf), Sock6Addr(In6Addr{0xff}, 33, 0));
+}
+
 TEST(Sock6Addr, FromStr)
 {
     constexpr FromStr<Sock6Addr> fs;
@@ -134,6 +166,30 @@
     EXPECT_EQ(addr4.sockaddrLen(), sizeof(addr.sun_family));
 }
 
+TEST(SockUAddr, FromBuf)
+{
+    SockAddrBuf buf = {};
+    auto& sun = reinterpret_cast<sockaddr_un&>(buf);
+    auto path = "/my-path"sv;
+    std::copy(path.begin(), path.end(), sun.sun_path);
+    EXPECT_THROW(SockUAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_UNIX;
+    EXPECT_THROW(SockUAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET6;
+    buf.len = sizeof(buf.fam) + path.size() + 1;
+    EXPECT_THROW(SockUAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_UNIX;
+    EXPECT_EQ(SockUAddr::fromBuf(buf), SockUAddr(path));
+
+    path = "\0abs-path"sv;
+    std::copy(path.begin(), path.end(), sun.sun_path);
+    buf.len = sizeof(buf.fam) + path.size();
+    EXPECT_EQ(SockUAddr::fromBuf(buf), SockUAddr(path));
+
+    buf.len = sizeof(buf.fam);
+    EXPECT_EQ(SockUAddr::fromBuf(buf), SockUAddr(""));
+}
+
 TEST(SockUAddr, FromStr)
 {
     constexpr FromStr<SockUAddr> fs;
@@ -164,6 +220,23 @@
     EXPECT_EQ(addr2.sockaddrLen(), sizeof(sockaddr_in6));
 }
 
+TEST(SockInAddr, FromBuf)
+{
+    SockAddrBuf buf = {};
+    EXPECT_THROW(SockInAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_UNIX;
+    buf.len = sizeof(sockaddr_un);
+    EXPECT_THROW(SockInAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET6;
+    buf.len = sizeof(sockaddr_in);
+    EXPECT_THROW(SockInAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET;
+    EXPECT_EQ(SockInAddr::fromBuf(buf), SockInAddr(In4Addr{}, 0));
+    buf.fam = AF_INET6;
+    buf.len = sizeof(sockaddr_in6);
+    EXPECT_EQ(SockInAddr::fromBuf(buf), SockInAddr(In6Addr{}, 0));
+}
+
 TEST(SockInAddr, FromStr)
 {
     constexpr FromStr<SockInAddr> fs;
@@ -194,6 +267,24 @@
     EXPECT_EQ(addr2.sockaddrLen(), sizeof(sa_family_t) + 4);
 }
 
+TEST(SockAnyAddr, FromBuf)
+{
+    SockAddrBuf buf = {};
+    EXPECT_THROW(SockAnyAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_UNIX;
+    EXPECT_THROW(SockInAddr::fromBuf(buf), std::invalid_argument);
+    buf.len = sizeof(sockaddr_un{}.sun_family);
+    EXPECT_EQ(SockAnyAddr::fromBuf(buf), SockAnyAddr(""));
+    buf.fam = AF_INET6;
+    buf.len = sizeof(sockaddr_in);
+    EXPECT_THROW(SockAnyAddr::fromBuf(buf), std::invalid_argument);
+    buf.fam = AF_INET;
+    EXPECT_EQ(SockAnyAddr::fromBuf(buf), SockAnyAddr(In4Addr{}, 0));
+    buf.fam = AF_INET6;
+    buf.len = sizeof(sockaddr_in6);
+    EXPECT_EQ(SockAnyAddr::fromBuf(buf), SockAnyAddr(In6Addr{}, 0));
+}
+
 TEST(SockAnyAddr, FromStr)
 {
     constexpr FromStr<SockAnyAddr> fs;