fd: Add TCP stream functions

This makes it possible to listen on stream sockets and accept new
connections.

Change-Id: I6a79035b4b7f308a62eab6c73075fef98df7a866
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/stdplus/fd/gmock.hpp b/src/stdplus/fd/gmock.hpp
index bd57b51..37dc3eb 100644
--- a/src/stdplus/fd/gmock.hpp
+++ b/src/stdplus/fd/gmock.hpp
@@ -20,6 +20,9 @@
     MOCK_METHOD(size_t, lseek, (off_t offset, Whence whence), (override));
     MOCK_METHOD(void, truncate, (off_t size), (override));
     MOCK_METHOD(void, bind, (span<const std::byte> sockaddr), (override));
+    MOCK_METHOD(void, listen, (int backlog), (override));
+    MOCK_METHOD((std::tuple<std::optional<int>, span<std::byte>>), accept,
+                (span<std::byte> sockaddr), (override));
     MOCK_METHOD(void, setsockopt,
                 (SockLevel level, SockOpt optname, span<const std::byte> opt),
                 (override));
diff --git a/src/stdplus/fd/impl.cpp b/src/stdplus/fd/impl.cpp
index 511bda4..4335cf5 100644
--- a/src/stdplus/fd/impl.cpp
+++ b/src/stdplus/fd/impl.cpp
@@ -115,6 +115,28 @@
         "bind");
 }
 
+void FdImpl::listen(int backlog)
+{
+    CHECK_ERRNO(::listen(get(), backlog), "listen");
+}
+
+std::tuple<std::optional<int>, span<std::byte>>
+    FdImpl::accept(span<std::byte> sockaddr)
+{
+    socklen_t len = sockaddr.size();
+    auto fd = ::accept(
+        get(), reinterpret_cast<struct sockaddr*>(sockaddr.data()), &len);
+    if (fd == -1)
+    {
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+        {
+            return {};
+        }
+        throw util::makeSystemError(errno, "accept");
+    }
+    return std::make_tuple(fd, sockaddr.subspan(0, len));
+}
+
 void FdImpl::setsockopt(SockLevel level, SockOpt optname,
                         span<const std::byte> opt)
 {
diff --git a/src/stdplus/fd/impl.hpp b/src/stdplus/fd/impl.hpp
index b321dfb..0487aba 100644
--- a/src/stdplus/fd/impl.hpp
+++ b/src/stdplus/fd/impl.hpp
@@ -19,6 +19,9 @@
     size_t lseek(off_t offset, Whence whence) override;
     void truncate(off_t size) override;
     void bind(span<const std::byte> sockaddr) override;
+    void listen(int backlog) override;
+    std::tuple<std::optional<int>, span<std::byte>>
+        accept(span<std::byte> sockaddr) override;
     void setsockopt(SockLevel level, SockOpt optname,
                     span<const std::byte> opt) override;
     int ioctl(unsigned long id, void* data) override;
diff --git a/src/stdplus/fd/intf.hpp b/src/stdplus/fd/intf.hpp
index aac85a2..bd8de13 100644
--- a/src/stdplus/fd/intf.hpp
+++ b/src/stdplus/fd/intf.hpp
@@ -1,9 +1,11 @@
 #pragma once
 #include <cstddef>
 #include <fcntl.h>
+#include <optional>
 #include <stdplus/flags.hpp>
 #include <stdplus/types.hpp>
 #include <sys/socket.h>
+#include <tuple>
 
 namespace stdplus
 {
@@ -91,6 +93,9 @@
     virtual size_t lseek(off_t offset, Whence whence) = 0;
     virtual void truncate(off_t size) = 0;
     virtual void bind(span<const std::byte> sockaddr) = 0;
+    virtual void listen(int backlog) = 0;
+    virtual std::tuple<std::optional<int>, span<std::byte>>
+        accept(span<std::byte> sockaddr) = 0;
     virtual void setsockopt(SockLevel level, SockOpt optname,
                             span<const std::byte> opt) = 0;
     virtual int ioctl(unsigned long id, void* data) = 0;
diff --git a/src/stdplus/fd/ops.hpp b/src/stdplus/fd/ops.hpp
index 317eb30..4bfc5ba 100644
--- a/src/stdplus/fd/ops.hpp
+++ b/src/stdplus/fd/ops.hpp
@@ -1,4 +1,6 @@
 #pragma once
+#include <stdexcept>
+#include <stdplus/fd/dupable.hpp>
 #include <stdplus/fd/intf.hpp>
 #include <stdplus/raw.hpp>
 #include <stdplus/types.hpp>
@@ -103,6 +105,36 @@
     return fd.bind(raw::asSpan<std::byte>(sockaddr));
 }
 
+inline void listen(Fd& fd, int backlog)
+{
+    return fd.listen(backlog);
+}
+
+template <typename SockAddr>
+inline std::optional<stdplus::DupableFd> accept(Fd& fd, SockAddr&& sockaddr)
+{
+    auto ret = fd.accept(raw::asSpan<std::byte>(sockaddr));
+    if (!std::get<0>(ret))
+    {
+        return std::nullopt;
+    }
+    if (std::get<1>(ret).size() != sizeof(sockaddr))
+    {
+        throw std::runtime_error("Invalid sockaddr type for accept");
+    }
+    return stdplus::DupableFd(std::move(*std::get<0>(ret)));
+}
+
+inline std::optional<stdplus::DupableFd> accept(Fd& fd)
+{
+    auto ret = std::get<0>(fd.accept(span<std::byte>{}));
+    if (!ret)
+    {
+        return std::nullopt;
+    }
+    return stdplus::DupableFd(std::move(*ret));
+}
+
 template <typename Opt>
 inline void setsockopt(Fd& fd, SockLevel level, SockOpt optname, Opt&& opt)
 {