Refactor socket code into platform::UdpSocket class.

This change creates a new class whose lifetime represents an open socket
and auto-closes the socket upon destruction. It also uses unique_ptrs to
force a code structure that is explicit about which client code owns a
socket (and its lifetime), and improves safety by adding OSP_DCHECKs to
document assumptions.

Adds improved handling of transient versus permanent errors in client
code. Fixed some bugs related to not checking and handling all possible
return values.

QUIC connection adapter code: Fixed a leak, where client sockets were
never being closed.

Change-Id: Ice1504c21bcf9e840e304acc61cef7c6cab02b7b
Reviewed-on: https://chromium-review.googlesource.com/c/1441171
Reviewed-by: mark a. foltz <mfoltz@chromium.org>
diff --git a/platform/api/socket.h b/platform/api/socket.h
index 0636225..59f5bcc 100644
--- a/platform/api/socket.h
+++ b/platform/api/socket.h
@@ -5,47 +5,86 @@
 #ifndef PLATFORM_API_SOCKET_H_
 #define PLATFORM_API_SOCKET_H_
 
+#include <cstdint>
+#include <memory>
+
 #include "base/error.h"
 #include "base/ip_address.h"
+#include "base/macros.h"
 #include "platform/api/network_interface.h"
-#include "third_party/abseil/src/absl/types/optional.h"
 
 namespace openscreen {
 namespace platform {
 
-// Opaque type for the platform to implement.
-struct UdpSocketPrivate;
-using UdpSocketPtr = UdpSocketPrivate*;
+class UdpSocket;
 
-// TODO(miu): These should return std::unique_ptr<>, so that code structure
-// auto-scopes the lifetime of the instance (i.e., auto-closing the socket too).
-UdpSocketPtr CreateUdpSocketIPv4();
-UdpSocketPtr CreateUdpSocketIPv6();
+// Platform-specific deleter of a UdpSocket instance returned by
+// UdpSocket::Create().
+struct UdpSocketDeleter {
+  void operator()(UdpSocket* socket) const;
+};
 
-// Returns true if |socket| is not null and it belongs to the IPv4/IPv6 address
-// family.
-bool IsIPv4Socket(UdpSocketPtr socket);
-bool IsIPv6Socket(UdpSocketPtr socket);
+using UdpSocketUniquePtr = std::unique_ptr<UdpSocket, UdpSocketDeleter>;
 
-// Closes the underlying platform socket and frees any allocated memory.
-void DestroyUdpSocket(UdpSocketPtr socket);
+// An open UDP socket for sending/receiving datagrams to/from either specific
+// endpoints or over IP multicast.
+//
+// Usage: The socket is created and opened by calling the Create() method. This
+// returns a unique pointer that auto-closes/destroys the socket when it goes
+// out-of-scope.
+//
+// Platform implementation note: There must only be one platform-specific
+// implementation of UdpSocket linked into the library/application. For that
+// reason, none of the methods here are declared virtual (i.e., the overhead is
+// pure waste). However, UdpSocket can be subclassed to include all extra
+// private state, such as OS-specific handles. See UdpSocketPosix for a
+// reference implementation.
+class UdpSocket {
+ public:
+  using Version = IPAddress::Version;
 
-Error BindUdpSocket(UdpSocketPtr socket,
-                    const IPEndpoint& endpoint,
-                    NetworkInterfaceIndex ifindex);
-Error JoinUdpMulticastGroup(UdpSocketPtr socket,
-                            const IPAddress& address,
-                            NetworkInterfaceIndex ifindex);
+  // Creates a new, scoped UdpSocket within the IPv4 or IPv6 family.
+  static ErrorOr<UdpSocketUniquePtr> Create(Version version);
 
-absl::optional<int64_t> ReceiveUdp(UdpSocketPtr socket,
-                                   void* data,
-                                   int64_t length,
-                                   IPEndpoint* src,
-                                   IPEndpoint* original_destination);
-absl::optional<int64_t> SendUdp(UdpSocketPtr socket,
-                                const void* data,
-                                int64_t length,
-                                const IPEndpoint& dest);
+  // Returns true if |socket| belongs to the IPv4/IPv6 address family.
+  bool IsIPv4() const;
+  bool IsIPv6() const;
+
+  // Sets the socket for address reuse, binds to the address/port.
+  Error Bind(const IPEndpoint& local_endpoint);
+
+  // Sets the device to use for outgoing multicast packets on the socket.
+  Error SetMulticastOutboundInterface(NetworkInterfaceIndex ifindex);
+
+  // Joins to the multicast group at the given address, using the specified
+  // interface.
+  Error JoinMulticastGroup(const IPAddress& address,
+                           NetworkInterfaceIndex ifindex);
+
+  // Performs a non-blocking read on the socket, returning the number of bytes
+  // received. Note that a non-Error return value of 0 is a valid result,
+  // indicating an empty message has been received. Also note that
+  // Error::Code::kAgain might be returned if there is no message currently
+  // ready for receive, which can be expected during normal operation. |src| and
+  // |original_destination| are optional output arguments that provide the
+  // source of the message and its intended destination, respectively.
+  ErrorOr<size_t> ReceiveMessage(void* data,
+                                 size_t length,
+                                 IPEndpoint* src,
+                                 IPEndpoint* original_destination);
+
+  // Sends a message and returns the number of bytes sent, on success.
+  // Error::Code::kAgain might be returned to indicate the operation would
+  // block, which can be expected during normal operation.
+  Error SendMessage(const void* data, size_t length, const IPEndpoint& dest);
+
+ protected:
+  UdpSocket();
+  ~UdpSocket();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+};
 
 }  // namespace platform
 }  // namespace openscreen