Support enough of Windows to make EndToEndTest.SimpleRequestResponse pass.

PiperOrigin-RevId: 529755002
diff --git a/build/source_list.bzl b/build/source_list.bzl
index fd20c48..de04f82 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -665,6 +665,7 @@
     "quic/core/quic_trace_visitor.cc",
     "quic/core/quic_transmission_info.cc",
     "quic/core/quic_types.cc",
+    "quic/core/quic_udp_socket.cc",
     "quic/core/quic_unacked_packet_map.cc",
     "quic/core/quic_utils.cc",
     "quic/core/quic_version_manager.cc",
@@ -991,7 +992,6 @@
     "quic/core/quic_default_packet_writer.cc",
     "quic/core/quic_packet_reader.cc",
     "quic/core/quic_syscall_wrapper.cc",
-    "quic/core/quic_udp_socket_posix.cc",
     "quic/masque/masque_client.cc",
     "quic/masque/masque_client_session.cc",
     "quic/masque/masque_client_tools.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 4d34746..ac54fca 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -665,6 +665,7 @@
     "src/quiche/quic/core/quic_trace_visitor.cc",
     "src/quiche/quic/core/quic_transmission_info.cc",
     "src/quiche/quic/core/quic_types.cc",
+    "src/quiche/quic/core/quic_udp_socket.cc",
     "src/quiche/quic/core/quic_unacked_packet_map.cc",
     "src/quiche/quic/core/quic_utils.cc",
     "src/quiche/quic/core/quic_version_manager.cc",
@@ -991,7 +992,6 @@
     "src/quiche/quic/core/quic_default_packet_writer.cc",
     "src/quiche/quic/core/quic_packet_reader.cc",
     "src/quiche/quic/core/quic_syscall_wrapper.cc",
-    "src/quiche/quic/core/quic_udp_socket_posix.cc",
     "src/quiche/quic/masque/masque_client.cc",
     "src/quiche/quic/masque/masque_client_session.cc",
     "src/quiche/quic/masque/masque_client_tools.cc",
diff --git a/build/source_list.json b/build/source_list.json
index d9867f7..4465eeb 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -664,6 +664,7 @@
     "quiche/quic/core/quic_trace_visitor.cc",
     "quiche/quic/core/quic_transmission_info.cc",
     "quiche/quic/core/quic_types.cc",
+    "quiche/quic/core/quic_udp_socket.cc",
     "quiche/quic/core/quic_unacked_packet_map.cc",
     "quiche/quic/core/quic_utils.cc",
     "quiche/quic/core/quic_version_manager.cc",
@@ -990,7 +991,6 @@
     "quiche/quic/core/quic_default_packet_writer.cc",
     "quiche/quic/core/quic_packet_reader.cc",
     "quiche/quic/core/quic_syscall_wrapper.cc",
-    "quiche/quic/core/quic_udp_socket_posix.cc",
     "quiche/quic/masque/masque_client.cc",
     "quiche/quic/masque/masque_client_session.cc",
     "quiche/quic/masque/masque_client_tools.cc",
diff --git a/quiche/quic/core/io/socket.h b/quiche/quic/core/io/socket.h
index d428f8c..8fc7732 100644
--- a/quiche/quic/core/io/socket.h
+++ b/quiche/quic/core/io/socket.h
@@ -28,9 +28,11 @@
 #if defined(_WIN32)
 using SocketFd = SOCKET;
 inline constexpr SocketFd kInvalidSocketFd = INVALID_SOCKET;
+inline constexpr int kSocketErrorMsgSize = WSAEMSGSIZE;
 #else
 using SocketFd = int;
 inline constexpr SocketFd kInvalidSocketFd = -1;
+inline constexpr int kSocketErrorMsgSize = EMSGSIZE;
 #endif
 
 // Low-level platform-agnostic socket operations. Closely follows the behavior
diff --git a/quiche/quic/core/quic_default_packet_writer.cc b/quiche/quic/core/quic_default_packet_writer.cc
index 78feee5..7eac827 100644
--- a/quiche/quic/core/quic_default_packet_writer.cc
+++ b/quiche/quic/core/quic_default_packet_writer.cc
@@ -8,7 +8,7 @@
 
 namespace quic {
 
-QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd)
+QuicDefaultPacketWriter::QuicDefaultPacketWriter(SocketFd fd)
     : fd_(fd), write_blocked_(false) {}
 
 QuicDefaultPacketWriter::~QuicDefaultPacketWriter() = default;
@@ -36,7 +36,7 @@
 void QuicDefaultPacketWriter::SetWritable() { write_blocked_ = false; }
 
 absl::optional<int> QuicDefaultPacketWriter::MessageTooBigErrorCode() const {
-  return EMSGSIZE;
+  return kSocketErrorMsgSize;
 }
 
 QuicByteCount QuicDefaultPacketWriter::GetMaxPacketSize(
diff --git a/quiche/quic/core/quic_default_packet_writer.h b/quiche/quic/core/quic_default_packet_writer.h
index b2a0a86..718f8eb 100644
--- a/quiche/quic/core/quic_default_packet_writer.h
+++ b/quiche/quic/core/quic_default_packet_writer.h
@@ -7,6 +7,7 @@
 
 #include <cstddef>
 
+#include "quiche/quic/core/io/socket.h"
 #include "quiche/quic/core/quic_packet_writer.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
@@ -18,7 +19,7 @@
 // Default packet writer which wraps QuicSocketUtils WritePacket.
 class QUIC_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter {
  public:
-  explicit QuicDefaultPacketWriter(int fd);
+  explicit QuicDefaultPacketWriter(SocketFd fd);
   QuicDefaultPacketWriter(const QuicDefaultPacketWriter&) = delete;
   QuicDefaultPacketWriter& operator=(const QuicDefaultPacketWriter&) = delete;
   ~QuicDefaultPacketWriter() override;
@@ -40,14 +41,14 @@
       const QuicSocketAddress& peer_address) override;
   WriteResult Flush() override;
 
-  void set_fd(int fd) { fd_ = fd; }
+  void set_fd(SocketFd fd) { fd_ = fd; }
 
  protected:
   void set_write_blocked(bool is_blocked);
-  int fd() { return fd_; }
+  SocketFd fd() { return fd_; }
 
  private:
-  int fd_;
+  SocketFd fd_;
   bool write_blocked_;
 };
 
diff --git a/quiche/quic/core/quic_udp_socket.cc b/quiche/quic/core/quic_udp_socket.cc
new file mode 100644
index 0000000..1a35b72
--- /dev/null
+++ b/quiche/quic/core/quic_udp_socket.cc
@@ -0,0 +1,205 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
+// This must be defined before including any system headers.
+#define __APPLE_USE_RFC_3542
+#endif  // defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
+
+#include "absl/base/optimization.h"
+#include "quiche/quic/core/io/socket.h"
+#include "quiche/quic/core/quic_udp_socket.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+
+// Common cmsg-related functions are defined below.
+// Windows and POSIX cmsg formats are actually fairly similar, except the
+// Windows ones have all of the macros prefixed with WSA_ and all the type names
+// are different.
+
+namespace quic {
+namespace {
+
+#if defined(_WIN32)
+using PlatformCmsghdr = ::WSACMSGHDR;
+#if !defined(CMSG_DATA)
+#define CMSG_DATA WSA_CMSG_DATA
+#endif  // !defined(CMSG_DATA)
+#else
+using PlatformCmsghdr = ::cmsghdr;
+#endif  // defined(_WIN32)
+
+void PopulatePacketInfoFromControlMessageBase(
+    PlatformCmsghdr* cmsg, QuicUdpPacketInfo* packet_info,
+    BitMask64 packet_info_interested) {
+  if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V6_SELF_IP)) {
+      const in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
+      const char* addr_data = reinterpret_cast<const char*>(&info->ipi6_addr);
+      int addr_len = sizeof(in6_addr);
+      QuicIpAddress self_v6_ip;
+      if (self_v6_ip.FromPackedString(addr_data, addr_len)) {
+        packet_info->SetSelfV6Ip(self_v6_ip);
+      } else {
+        QUIC_BUG(quic_bug_10751_1) << "QuicIpAddress::FromPackedString failed";
+      }
+    }
+    return;
+  }
+
+  if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V4_SELF_IP)) {
+      const in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
+      const char* addr_data = reinterpret_cast<const char*>(&info->ipi_addr);
+      int addr_len = sizeof(in_addr);
+      QuicIpAddress self_v4_ip;
+      if (self_v4_ip.FromPackedString(addr_data, addr_len)) {
+        packet_info->SetSelfV4Ip(self_v4_ip);
+      } else {
+        QUIC_BUG(quic_bug_10751_2) << "QuicIpAddress::FromPackedString failed";
+      }
+    }
+    return;
+  }
+}
+
+}  // namespace
+}  // namespace quic
+
+#if defined(_WIN32)
+#include "quiche/quic/core/quic_udp_socket_win.inc"
+#else
+#include "quiche/quic/core/quic_udp_socket_posix.inc"
+#endif
+
+namespace quic {
+
+QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family,
+                                         int receive_buffer_size,
+                                         int send_buffer_size, bool ipv6_only) {
+  // QUICHE_DCHECK here so the program exits early(before reading packets) in
+  // debug mode. This should have been a static_assert, however it can't be done
+  // on ios/osx because CMSG_SPACE isn't a constant expression there.
+  QUICHE_DCHECK_GE(kDefaultUdpPacketControlBufferSize, kMinCmsgSpaceForRead);
+
+  absl::StatusOr<SocketFd> socket = socket_api::CreateSocket(
+      quiche::FromPlatformAddressFamily(address_family),
+      socket_api::SocketProtocol::kUdp,
+      /*blocking=*/false);
+
+  if (!socket.ok()) {
+    QUIC_LOG_FIRST_N(ERROR, 100)
+        << "UDP non-blocking socket creation for address_family="
+        << address_family << " failed: " << socket.status();
+    return kQuicInvalidSocketFd;
+  }
+
+#if !defined(_WIN32)
+  SetGoogleSocketOptions(socket.value());
+#endif
+
+  if (!SetupSocket(socket.value(), address_family, receive_buffer_size,
+                   send_buffer_size, ipv6_only)) {
+    Destroy(socket.value());
+    return kQuicInvalidSocketFd;
+  }
+
+  return socket.value();
+}
+
+void QuicUdpSocketApi::Destroy(QuicUdpSocketFd fd) {
+  if (fd != kQuicInvalidSocketFd) {
+    absl::Status result = socket_api::Close(fd);
+    if (!result.ok()) {
+      QUIC_LOG_FIRST_N(WARNING, 100)
+          << "Failed to close UDP socket with error " << result;
+    }
+  }
+}
+
+bool QuicUdpSocketApi::Bind(QuicUdpSocketFd fd, QuicSocketAddress address) {
+  sockaddr_storage addr = address.generic_address();
+  int addr_len =
+      address.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
+  return 0 == bind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len);
+}
+
+bool QuicUdpSocketApi::BindInterface(QuicUdpSocketFd fd,
+                                     const std::string& interface_name) {
+#if defined(__linux__) && !defined(__ANDROID_API__)
+  if (interface_name.empty() || interface_name.size() >= IFNAMSIZ) {
+    QUIC_BUG(udp_bad_interface_name)
+        << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
+    return false;
+  }
+
+  return 0 == setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
+                         interface_name.c_str(), interface_name.length());
+#else
+  (void)fd;
+  (void)interface_name;
+  QUIC_BUG(interface_bind_not_implemented)
+      << "Interface binding is not implemented on this platform";
+  return false;
+#endif
+}
+
+bool QuicUdpSocketApi::EnableDroppedPacketCount(QuicUdpSocketFd fd) {
+#if defined(__linux__) && defined(SO_RXQ_OVFL)
+  int get_overflow = 1;
+  return 0 == setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
+                         sizeof(get_overflow));
+#else
+  (void)fd;
+  return false;
+#endif
+}
+
+bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd) {
+  int get_self_ip = 1;
+  return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO,
+                         reinterpret_cast<char*>(&get_self_ip),
+                         sizeof(get_self_ip));
+}
+
+bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd) {
+  int get_self_ip = 1;
+  return 0 == setsockopt(fd, IPPROTO_IPV6, kIpv6RecvPacketInfo,
+                         reinterpret_cast<char*>(&get_self_ip),
+                         sizeof(get_self_ip));
+}
+
+bool QuicUdpSocketApi::EnableReceiveTimestamp(QuicUdpSocketFd fd) {
+#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 21)
+  int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
+  return 0 == setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &timestamping,
+                         sizeof(timestamping));
+#else
+  (void)fd;
+  return false;
+#endif
+}
+
+bool QuicUdpSocketApi::EnableReceiveTtlForV4(QuicUdpSocketFd fd) {
+#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
+  int get_ttl = 1;
+  return 0 == setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &get_ttl, sizeof(get_ttl));
+#else
+  (void)fd;
+  return false;
+#endif
+}
+
+bool QuicUdpSocketApi::EnableReceiveTtlForV6(QuicUdpSocketFd fd) {
+#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
+  int get_ttl = 1;
+  return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &get_ttl,
+                         sizeof(get_ttl));
+#else
+  (void)fd;
+  return false;
+#endif
+}
+
+}  // namespace quic
diff --git a/quiche/quic/core/quic_udp_socket_posix.cc b/quiche/quic/core/quic_udp_socket_posix.inc
similarity index 77%
rename from quiche/quic/core/quic_udp_socket_posix.cc
rename to quiche/quic/core/quic_udp_socket_posix.inc
index 26d4ef4..deb406c 100644
--- a/quiche/quic/core/quic_udp_socket_posix.cc
+++ b/quiche/quic/core/quic_udp_socket_posix.inc
@@ -2,12 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "quiche/quic/core/quic_types.h"
-#if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
-// This must be defined before including any system headers.
-#define __APPLE_USE_RFC_3542
-#endif  // defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
-
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <net/if.h>
@@ -43,6 +37,8 @@
 // Explicit Congestion Notification is the last two bits of the TOS byte.
 constexpr uint8_t kEcnMask = 0x03;
 
+constexpr int kIpv6RecvPacketInfo = IPV6_RECVPKTINFO;
+
 #if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 21)
 #define QUIC_UDP_SOCKET_SUPPORT_LINUX_TIMESTAMPING 1
 // This is the structure that SO_TIMESTAMPING fills into the cmsg header.
@@ -125,35 +121,8 @@
   }
 #endif
 
-  if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
-    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V6_SELF_IP)) {
-      const in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
-      const char* addr_data = reinterpret_cast<const char*>(&info->ipi6_addr);
-      int addr_len = sizeof(in6_addr);
-      QuicIpAddress self_v6_ip;
-      if (self_v6_ip.FromPackedString(addr_data, addr_len)) {
-        packet_info->SetSelfV6Ip(self_v6_ip);
-      } else {
-        QUIC_BUG(quic_bug_10751_1) << "QuicIpAddress::FromPackedString failed";
-      }
-    }
-    return;
-  }
-
-  if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
-    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V4_SELF_IP)) {
-      const in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
-      const char* addr_data = reinterpret_cast<const char*>(&info->ipi_addr);
-      int addr_len = sizeof(in_addr);
-      QuicIpAddress self_v4_ip;
-      if (self_v4_ip.FromPackedString(addr_data, addr_len)) {
-        packet_info->SetSelfV4Ip(self_v4_ip);
-      } else {
-        QUIC_BUG(quic_bug_10751_2) << "QuicIpAddress::FromPackedString failed";
-      }
-    }
-    return;
-  }
+  PopulatePacketInfoFromControlMessageBase(cmsg, packet_info,
+                                           packet_info_interested);
 
   if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) ||
       (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT)) {
@@ -214,37 +183,6 @@
 }
 }  // namespace
 
-QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family,
-                                         int receive_buffer_size,
-                                         int send_buffer_size, bool ipv6_only) {
-  // QUICHE_DCHECK here so the program exits early(before reading packets) in
-  // debug mode. This should have been a static_assert, however it can't be done
-  // on ios/osx because CMSG_SPACE isn't a constant expression there.
-  QUICHE_DCHECK_GE(kDefaultUdpPacketControlBufferSize, kMinCmsgSpaceForRead);
-
-  absl::StatusOr<SocketFd> socket = socket_api::CreateSocket(
-      quiche::FromPlatformAddressFamily(address_family),
-      socket_api::SocketProtocol::kUdp,
-      /*blocking=*/false);
-
-  if (!socket.ok()) {
-    QUIC_LOG_FIRST_N(ERROR, 100)
-        << "UDP non-blocking socket creation for address_family="
-        << address_family << " failed: " << socket.status();
-    return kQuicInvalidSocketFd;
-  }
-
-  SetGoogleSocketOptions(socket.value());
-
-  if (!SetupSocket(socket.value(), address_family, receive_buffer_size,
-                   send_buffer_size, ipv6_only)) {
-    Destroy(socket.value());
-    return kQuicInvalidSocketFd;
-  }
-
-  return socket.value();
-}
-
 bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, int address_family,
                                    int receive_buffer_size,
                                    int send_buffer_size, bool ipv6_only) {
@@ -298,98 +236,6 @@
   return true;
 }
 
-void QuicUdpSocketApi::Destroy(QuicUdpSocketFd fd) {
-  if (fd != kQuicInvalidSocketFd) {
-    absl::Status result = socket_api::Close(fd);
-    if (!result.ok()) {
-      QUIC_LOG_FIRST_N(WARNING, 100)
-          << "Failed to close UDP socket with error " << result;
-    }
-  }
-}
-
-bool QuicUdpSocketApi::Bind(QuicUdpSocketFd fd, QuicSocketAddress address) {
-  sockaddr_storage addr = address.generic_address();
-  int addr_len =
-      address.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
-  return 0 == bind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len);
-}
-
-bool QuicUdpSocketApi::BindInterface(QuicUdpSocketFd fd,
-                                     const std::string& interface_name) {
-#if defined(__linux__) && !defined(__ANDROID_API__)
-  if (interface_name.empty() || interface_name.size() >= IFNAMSIZ) {
-    QUIC_BUG(udp_bad_interface_name)
-        << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
-    return false;
-  }
-
-  return 0 == setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
-                         interface_name.c_str(), interface_name.length());
-#else
-  (void)fd;
-  (void)interface_name;
-  QUIC_BUG(interface_bind_not_implemented)
-      << "Interface binding is not implemented on this platform";
-  return false;
-#endif
-}
-
-bool QuicUdpSocketApi::EnableDroppedPacketCount(QuicUdpSocketFd fd) {
-#if defined(__linux__) && defined(SO_RXQ_OVFL)
-  int get_overflow = 1;
-  return 0 == setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
-                         sizeof(get_overflow));
-#else
-  (void)fd;
-  return false;
-#endif
-}
-
-bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd) {
-  int get_self_ip = 1;
-  return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_self_ip,
-                         sizeof(get_self_ip));
-}
-
-bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd) {
-  int get_self_ip = 1;
-  return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_self_ip,
-                         sizeof(get_self_ip));
-}
-
-bool QuicUdpSocketApi::EnableReceiveTimestamp(QuicUdpSocketFd fd) {
-#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 21)
-  int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
-  return 0 == setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &timestamping,
-                         sizeof(timestamping));
-#else
-  (void)fd;
-  return false;
-#endif
-}
-
-bool QuicUdpSocketApi::EnableReceiveTtlForV4(QuicUdpSocketFd fd) {
-#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
-  int get_ttl = 1;
-  return 0 == setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &get_ttl, sizeof(get_ttl));
-#else
-  (void)fd;
-  return false;
-#endif
-}
-
-bool QuicUdpSocketApi::EnableReceiveTtlForV6(QuicUdpSocketFd fd) {
-#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
-  int get_ttl = 1;
-  return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &get_ttl,
-                         sizeof(get_ttl));
-#else
-  (void)fd;
-  return false;
-#endif
-}
-
 bool QuicUdpSocketApi::WaitUntilReadable(QuicUdpSocketFd fd,
                                          QuicTime::Delta timeout) {
   fd_set read_fds;
diff --git a/quiche/quic/core/quic_udp_socket_win.inc b/quiche/quic/core/quic_udp_socket_win.inc
new file mode 100644
index 0000000..429b8c7
--- /dev/null
+++ b/quiche/quic/core/quic_udp_socket_win.inc
@@ -0,0 +1,281 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// UDP socket implementation for Windows.  Feature overview:
+// * Supported: setting and getting self IP address.
+// * Unsupported but could work on a sufficiently new Windows version:
+//   - Timestamping
+//   - Setting and getting TTL.
+
+#include <mswsock.h>
+#include <winsock2.h>
+
+#include <cstddef>
+
+#include "quiche/quic/core/quic_utils.h"
+
+namespace quic {
+
+namespace {
+constexpr size_t kMinCmsgSpaceForRead =
+    CMSG_SPACE(sizeof(in_pktinfo))      // V4 Self IP
+    + CMSG_SPACE(sizeof(in6_pktinfo));  // V6 Self IP
+
+constexpr int kIpv6RecvPacketInfo = IPV6_PKTINFO;
+
+void SetV4SelfIpInControlMessage(const QuicIpAddress& self_address,
+                                 WSACMSGHDR* cmsg) {
+  QUICHE_DCHECK(self_address.IsIPv4());
+  in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(WSA_CMSG_DATA(cmsg));
+  memset(pktinfo, 0, sizeof(in_pktinfo));
+  pktinfo->ipi_addr = self_address.GetIPv4();
+}
+
+void SetV6SelfIpInControlMessage(const QuicIpAddress& self_address,
+                                 cmsghdr* cmsg) {
+  QUICHE_DCHECK(self_address.IsIPv6());
+  in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(WSA_CMSG_DATA(cmsg));
+  memset(pktinfo, 0, sizeof(in6_pktinfo));
+  pktinfo->ipi6_addr = self_address.GetIPv6();
+}
+
+bool NextCmsg(WSAMSG* hdr, char* control_buffer, size_t control_buffer_len,
+              int cmsg_level, int cmsg_type, size_t data_size,
+              WSACMSGHDR** cmsg /*in, out*/) {
+  // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will
+  // return nullptr.
+  hdr->Control.len += WSA_CMSG_SPACE(data_size);
+  if (hdr->Control.len > control_buffer_len) {
+    return false;
+  }
+
+  if ((*cmsg) == nullptr) {
+    QUICHE_DCHECK_EQ(nullptr, hdr->Control.buf);
+    memset(control_buffer, 0, control_buffer_len);
+    hdr->Control.buf = control_buffer;
+    (*cmsg) = WSA_CMSG_FIRSTHDR(hdr);
+  } else {
+    QUICHE_DCHECK_NE(nullptr, hdr->Control.buf);
+    (*cmsg) = WSA_CMSG_NXTHDR(hdr, (*cmsg));
+  }
+
+  if (nullptr == (*cmsg)) {
+    return false;
+  }
+
+  (*cmsg)->cmsg_len = WSA_CMSG_LEN(data_size);
+  (*cmsg)->cmsg_level = cmsg_level;
+  (*cmsg)->cmsg_type = cmsg_type;
+
+  return true;
+}
+}  // namespace
+
+bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, int address_family,
+                                   int receive_buffer_size,
+                                   int send_buffer_size, bool /*ipv6_only*/) {
+  // Receive buffer size.
+  if (absl::Status status =
+          socket_api::SetReceiveBufferSize(fd, receive_buffer_size);
+      !status.ok()) {
+    QUIC_LOG_FIRST_N(ERROR, 100)
+        << "Failed to set socket recv size: " << status;
+    return false;
+  }
+
+  // Send buffer size.
+  if (absl::Status status = socket_api::SetSendBufferSize(fd, send_buffer_size);
+      !status.ok()) {
+    QUIC_LOG_FIRST_N(ERROR, 100)
+        << "Failed to set socket send size: " << status;
+    return false;
+  }
+
+  if (address_family == AF_INET) {
+    if (!EnableReceiveSelfIpAddressForV4(fd)) {
+      QUIC_LOG_FIRST_N(ERROR, 100)
+          << "Failed to enable receiving of self v4 ip";
+      return false;
+    }
+  }
+
+  if (address_family == AF_INET6) {
+    if (!EnableReceiveSelfIpAddressForV6(fd)) {
+      QUIC_LOG_FIRST_N(ERROR, 100)
+          << "Failed to enable receiving of self v6 ip";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void QuicUdpSocketApi::ReadPacket(QuicUdpSocketFd fd,
+                                  BitMask64 packet_info_interested,
+                                  ReadPacketResult* result) {
+  result->ok = false;
+
+  // WSARecvMsg is an extension to Windows Socket API that requires us to fetch
+  // the function pointer via an ioctl.
+  DWORD recvmsg_fn_out_bytes;
+  LPFN_WSARECVMSG recvmsg_fn = nullptr;
+  GUID recvmsg_guid = WSAID_WSARECVMSG;
+  int ioctl_result =
+      WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &recvmsg_guid,
+               sizeof(recvmsg_guid), &recvmsg_fn, sizeof(recvmsg_fn),
+               &recvmsg_fn_out_bytes, nullptr, nullptr);
+  if (ioctl_result != 0) {
+    QUICHE_LOG(ERROR) << "Failed to load WSARecvMsg() function, error code: "
+                      << WSAGetLastError();
+    return;
+  }
+
+  BufferSpan& packet_buffer = result->packet_buffer;
+  BufferSpan& control_buffer = result->control_buffer;
+  QuicUdpPacketInfo* packet_info = &result->packet_info;
+
+  QUICHE_DCHECK_GE(control_buffer.buffer_len, kMinCmsgSpaceForRead);
+
+  WSABUF iov;
+  iov.buf = packet_buffer.buffer;
+  iov.len = packet_buffer.buffer_len;
+  sockaddr_storage raw_peer_address;
+
+  if (control_buffer.buffer_len > 0) {
+    reinterpret_cast<WSACMSGHDR*>(control_buffer.buffer)->cmsg_len =
+        control_buffer.buffer_len;
+  }
+
+  WSAMSG hdr;
+  memset(&hdr, 0, sizeof(hdr));
+  hdr.name = reinterpret_cast<sockaddr*>(&raw_peer_address);
+  hdr.namelen = sizeof(raw_peer_address);
+  hdr.lpBuffers = &iov;
+  hdr.dwBufferCount = 1;
+  hdr.dwFlags = 0;
+  hdr.Control.buf = control_buffer.buffer;
+  hdr.Control.len = control_buffer.buffer_len;
+
+  DWORD bytes_read;
+  int recvmsg_result = recvmsg_fn(fd, &hdr, &bytes_read, nullptr, nullptr);
+  if (recvmsg_result != 0) {
+    const int error_num = WSAGetLastError();
+    if (error_num != WSAEWOULDBLOCK) {
+      QUIC_LOG_FIRST_N(ERROR, 100) << "Error reading packet: " << error_num;
+    }
+    return;
+  }
+
+  packet_buffer.buffer_len = bytes_read;
+  if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) {
+    packet_info->SetPeerAddress(QuicSocketAddress(raw_peer_address));
+  }
+
+  if (hdr.Control.len > 0) {
+    for (WSACMSGHDR* cmsg = WSA_CMSG_FIRSTHDR(&hdr); cmsg != nullptr;
+         cmsg = WSA_CMSG_NXTHDR(&hdr, cmsg)) {
+      BitMask64 prior_bitmask = packet_info->bitmask();
+      PopulatePacketInfoFromControlMessageBase(cmsg, packet_info,
+                                               packet_info_interested);
+      if (packet_info->bitmask() == prior_bitmask) {
+        QUIC_DLOG(INFO) << "Ignored cmsg_level:" << cmsg->cmsg_level
+                        << ", cmsg_type:" << cmsg->cmsg_type;
+      }
+    }
+  }
+
+  result->ok = true;
+}
+
+size_t QuicUdpSocketApi::ReadMultiplePackets(QuicUdpSocketFd fd,
+                                             BitMask64 packet_info_interested,
+                                             ReadPacketResults* results) {
+  size_t num_packets = 0;
+  for (ReadPacketResult& result : *results) {
+    result.ok = false;
+  }
+  for (ReadPacketResult& result : *results) {
+    ReadPacket(fd, packet_info_interested, &result);
+    if (!result.ok && WSAGetLastError() == WSAEWOULDBLOCK) {
+      break;
+    }
+    ++num_packets;
+  }
+  return num_packets;
+}
+
+WriteResult QuicUdpSocketApi::WritePacket(
+    QuicUdpSocketFd fd, const char* packet_buffer, size_t packet_buffer_len,
+    const QuicUdpPacketInfo& packet_info) {
+  if (!packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) {
+    return WriteResult(WRITE_STATUS_ERROR, WSAEINVAL);
+  }
+
+  char control_buffer[512];
+  sockaddr_storage raw_peer_address =
+      packet_info.peer_address().generic_address();
+  WSABUF iov;
+  iov.buf = const_cast<char*>(packet_buffer);
+  iov.len = packet_buffer_len;
+
+  WSAMSG hdr;
+  memset(&hdr, 0, sizeof(hdr));
+  hdr.name = reinterpret_cast<sockaddr*>(&raw_peer_address);
+  hdr.namelen = packet_info.peer_address().host().IsIPv4()
+                    ? sizeof(sockaddr_in)
+                    : sizeof(sockaddr_in6);
+  hdr.lpBuffers = &iov;
+  hdr.dwBufferCount = 1;
+
+  WSACMSGHDR* cmsg = nullptr;
+
+  // Set self IP.
+  if (packet_info.HasValue(QuicUdpPacketInfoBit::V4_SELF_IP) &&
+      packet_info.self_v4_ip().IsInitialized()) {
+    if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IP,
+                  IP_PKTINFO, sizeof(in_pktinfo), &cmsg)) {
+      QUIC_LOG_FIRST_N(ERROR, 100)
+          << "Not enough buffer to set self v4 ip address.";
+      return WriteResult(WRITE_STATUS_ERROR, EINVAL);
+    }
+    SetV4SelfIpInControlMessage(packet_info.self_v4_ip(), cmsg);
+  } else if (packet_info.HasValue(QuicUdpPacketInfoBit::V6_SELF_IP) &&
+             packet_info.self_v6_ip().IsInitialized()) {
+    if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IPV6,
+                  IPV6_PKTINFO, sizeof(in6_pktinfo), &cmsg)) {
+      QUIC_LOG_FIRST_N(ERROR, 100)
+          << "Not enough buffer to set self v6 ip address.";
+      return WriteResult(WRITE_STATUS_ERROR, EINVAL);
+    }
+    SetV6SelfIpInControlMessage(packet_info.self_v6_ip(), cmsg);
+  }
+
+  DWORD bytes_sent;
+  int result =
+      WSASendMsg(fd, &hdr, /*dwFlags=*/0, &bytes_sent, nullptr, nullptr);
+  if (result == 0) {
+    return WriteResult(WRITE_STATUS_OK, bytes_sent);
+  }
+  int error = WSAGetLastError();
+  return WriteResult(
+      (error == WSAEWOULDBLOCK) ? WRITE_STATUS_BLOCKED : WRITE_STATUS_ERROR,
+      error);
+}
+
+bool QuicUdpSocketApi::WaitUntilReadable(QuicUdpSocketFd fd,
+                                         QuicTime::Delta timeout) {
+  WSAPOLLFD polled_fd;
+  polled_fd.fd = fd;
+  polled_fd.events = POLLIN;
+  polled_fd.revents = 0;
+
+  int result = ::WSAPoll(&polled_fd, 1, timeout.ToMilliseconds());
+  if (result == SOCKET_ERROR) {
+    QUICHE_LOG(ERROR) << "Error while calling WSAPoll(): " << WSAGetLastError();
+  }
+
+  return result > 0;
+}
+
+}  // namespace quic
diff --git a/quiche/quic/test_tools/quic_server_peer.cc b/quiche/quic/test_tools/quic_server_peer.cc
index 6f6c8f9..4ef81fb 100644
--- a/quiche/quic/test_tools/quic_server_peer.cc
+++ b/quiche/quic/test_tools/quic_server_peer.cc
@@ -14,8 +14,8 @@
 // static
 bool QuicServerPeer::SetSmallSocket(QuicServer* server) {
   int size = 1024 * 10;
-  return setsockopt(server->fd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) !=
-         -1;
+  return setsockopt(server->fd_, SOL_SOCKET, SO_RCVBUF,
+                    reinterpret_cast<char*>(&size), sizeof(size)) != -1;
 }
 
 // static
diff --git a/quiche/quic/test_tools/quic_test_client.cc b/quiche/quic/test_tools/quic_test_client.cc
index a617348..1f17227 100644
--- a/quiche/quic/test_tools/quic_test_client.cc
+++ b/quiche/quic/test_tools/quic_test_client.cc
@@ -365,7 +365,7 @@
   client_->SetUserAgentID(user_agent_id);
 }
 
-ssize_t QuicTestClient::SendRequest(const std::string& uri) {
+int64_t QuicTestClient::SendRequest(const std::string& uri) {
   spdy::Http2HeaderBlock headers;
   if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
     return 0;
@@ -373,7 +373,7 @@
   return SendMessage(headers, "");
 }
 
-ssize_t QuicTestClient::SendRequestAndRstTogether(const std::string& uri) {
+int64_t QuicTestClient::SendRequestAndRstTogether(const std::string& uri) {
   spdy::Http2HeaderBlock headers;
   if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
     return 0;
@@ -381,7 +381,7 @@
 
   QuicSpdyClientSession* session = client()->client_session();
   QuicConnection::ScopedPacketFlusher flusher(session->connection());
-  ssize_t ret = SendMessage(headers, "", /*fin=*/true, /*flush=*/false);
+  int64_t ret = SendMessage(headers, "", /*fin=*/true, /*flush=*/false);
 
   QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(
       session->transport_version(), 0);
@@ -398,7 +398,7 @@
   }
 }
 
-ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest(
+int64_t QuicTestClient::GetOrCreateStreamAndSendRequest(
     const spdy::Http2HeaderBlock* headers, absl::string_view body, bool fin,
     quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
         ack_listener) {
@@ -426,7 +426,7 @@
   }
   QuicSpdyStreamPeer::set_ack_listener(stream, ack_listener);
 
-  ssize_t ret = 0;
+  int64_t ret = 0;
   if (headers != nullptr) {
     spdy::Http2HeaderBlock spdy_headers(headers->Clone());
     if (spdy_headers[":authority"].as_string().empty()) {
@@ -441,23 +441,23 @@
   return ret;
 }
 
-ssize_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
+int64_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
                                     absl::string_view body) {
   return SendMessage(headers, body, /*fin=*/true);
 }
 
-ssize_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
+int64_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
                                     absl::string_view body, bool fin) {
   return SendMessage(headers, body, fin, /*flush=*/true);
 }
 
-ssize_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
+int64_t QuicTestClient::SendMessage(const spdy::Http2HeaderBlock& headers,
                                     absl::string_view body, bool fin,
                                     bool flush) {
   // Always force creation of a stream for SendMessage.
   latest_created_stream_ = nullptr;
 
-  ssize_t ret = GetOrCreateStreamAndSendRequest(&headers, body, fin, nullptr);
+  int64_t ret = GetOrCreateStreamAndSendRequest(&headers, body, fin, nullptr);
 
   if (flush) {
     WaitForWriteToFlush();
@@ -465,11 +465,11 @@
   return ret;
 }
 
-ssize_t QuicTestClient::SendData(const std::string& data, bool last_data) {
+int64_t QuicTestClient::SendData(const std::string& data, bool last_data) {
   return SendData(data, last_data, nullptr);
 }
 
-ssize_t QuicTestClient::SendData(
+int64_t QuicTestClient::SendData(
     const std::string& data, bool last_data,
     quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
         ack_listener) {
@@ -645,7 +645,7 @@
   return true;
 }
 
-ssize_t QuicTestClient::Send(absl::string_view data) {
+int64_t QuicTestClient::Send(absl::string_view data) {
   return SendData(std::string(data), false);
 }
 
diff --git a/quiche/quic/test_tools/quic_test_client.h b/quiche/quic/test_tools/quic_test_client.h
index 9abab23..9f2a869 100644
--- a/quiche/quic/test_tools/quic_test_client.h
+++ b/quiche/quic/test_tools/quic_test_client.h
@@ -115,18 +115,18 @@
   void SetUserAgentID(const std::string& user_agent_id);
 
   // Wraps data in a quic packet and sends it.
-  ssize_t SendData(const std::string& data, bool last_data);
+  int64_t SendData(const std::string& data, bool last_data);
   // As above, but |delegate| will be notified when |data| is ACKed.
-  ssize_t SendData(
+  int64_t SendData(
       const std::string& data, bool last_data,
       quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
           ack_listener);
 
   // Clears any outstanding state and sends a simple GET of 'uri' to the
   // server.  Returns 0 if the request failed and no bytes were written.
-  ssize_t SendRequest(const std::string& uri);
+  int64_t SendRequest(const std::string& uri);
   // Send a request R and a RST_FRAME which resets R, in the same packet.
-  ssize_t SendRequestAndRstTogether(const std::string& uri);
+  int64_t SendRequestAndRstTogether(const std::string& uri);
   // Sends requests for all the urls and waits for the responses.  To process
   // the individual responses as they are returned, the caller should use the
   // set the response_listener on the client().
@@ -134,18 +134,18 @@
       const std::vector<std::string>& url_list);
   // Sends a request containing |headers| and |body| and returns the number of
   // bytes sent (the size of the serialized request headers and body).
-  ssize_t SendMessage(const spdy::Http2HeaderBlock& headers,
+  int64_t SendMessage(const spdy::Http2HeaderBlock& headers,
                       absl::string_view body);
   // Sends a request containing |headers| and |body| with the fin bit set to
   // |fin| and returns the number of bytes sent (the size of the serialized
   // request headers and body).
-  ssize_t SendMessage(const spdy::Http2HeaderBlock& headers,
+  int64_t SendMessage(const spdy::Http2HeaderBlock& headers,
                       absl::string_view body, bool fin);
   // Sends a request containing |headers| and |body| with the fin bit set to
   // |fin| and returns the number of bytes sent (the size of the serialized
   // request headers and body). If |flush| is true, will wait for the message to
   // be flushed before returning.
-  ssize_t SendMessage(const spdy::Http2HeaderBlock& headers,
+  int64_t SendMessage(const spdy::Http2HeaderBlock& headers,
                       absl::string_view body, bool fin, bool flush);
   // Sends a request containing |headers| and |body|, waits for the response,
   // and returns the response body.
@@ -161,7 +161,7 @@
   QuicSocketAddress local_address() const;
   void ClearPerRequestState();
   bool WaitUntil(int timeout_ms, std::function<bool()> trigger);
-  ssize_t Send(absl::string_view data);
+  int64_t Send(absl::string_view data);
   bool connected() const;
   bool buffer_body() const;
   void set_buffer_body(bool buffer_body);
@@ -259,7 +259,7 @@
   // Calls GetOrCreateStream(), sends the request on the stream, and
   // stores the request in case it needs to be resent.  If |headers| is
   // null, only the body will be sent on the stream.
-  ssize_t GetOrCreateStreamAndSendRequest(
+  int64_t GetOrCreateStreamAndSendRequest(
       const spdy::Http2HeaderBlock* headers, absl::string_view body, bool fin,
       quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
           ack_listener);
diff --git a/quiche/quic/tools/quic_client_default_network_helper.cc b/quiche/quic/tools/quic_client_default_network_helper.cc
index ab9e1b1..424b8b9 100644
--- a/quiche/quic/tools/quic_client_default_network_helper.cc
+++ b/quiche/quic/tools/quic_client_default_network_helper.cc
@@ -22,7 +22,7 @@
 // listener whenever the socket gets blocked.
 class LevelTriggeredPacketWriter : public QuicDefaultPacketWriter {
  public:
-  explicit LevelTriggeredPacketWriter(int fd, QuicEventLoop* event_loop)
+  explicit LevelTriggeredPacketWriter(SocketFd fd, QuicEventLoop* event_loop)
       : QuicDefaultPacketWriter(fd), event_loop_(event_loop) {
     QUICHE_DCHECK(!event_loop->SupportsEdgeTriggered());
   }
@@ -68,11 +68,11 @@
 bool QuicClientDefaultNetworkHelper::CreateUDPSocketAndBind(
     QuicSocketAddress server_address, QuicIpAddress bind_to_address,
     int bind_to_port) {
-  int fd = CreateUDPSocket(server_address, &overflow_supported_);
-  if (fd < 0) {
+  SocketFd fd = CreateUDPSocket(server_address, &overflow_supported_);
+  if (fd == kInvalidSocketFd) {
     return false;
   }
-  auto closer = absl::MakeCleanup([fd] { close(fd); });
+  auto closer = absl::MakeCleanup([fd] { (void)socket_api::Close(fd); });
 
   QuicSocketAddress client_address;
   if (bind_to_address.IsInitialized()) {
@@ -124,7 +124,7 @@
   return false;
 }
 
-void QuicClientDefaultNetworkHelper::CleanUpUDPSocket(int fd) {
+void QuicClientDefaultNetworkHelper::CleanUpUDPSocket(SocketFd fd) {
   CleanUpUDPSocketImpl(fd);
   fd_address_map_.erase(fd);
 }
@@ -136,12 +136,12 @@
   fd_address_map_.clear();
 }
 
-void QuicClientDefaultNetworkHelper::CleanUpUDPSocketImpl(int fd) {
-  if (fd > -1) {
+void QuicClientDefaultNetworkHelper::CleanUpUDPSocketImpl(SocketFd fd) {
+  if (fd != kInvalidSocketFd) {
     bool success = event_loop_->UnregisterSocket(fd);
     QUICHE_DCHECK(success || fds_unregistered_externally_);
-    int rc = close(fd);
-    QUICHE_DCHECK_EQ(0, rc);
+    absl::Status rc = socket_api::Close(fd);
+    QUICHE_DCHECK(rc.ok()) << rc;
   }
 }
 
@@ -207,7 +207,7 @@
   return fd_address_map_.back().second;
 }
 
-int QuicClientDefaultNetworkHelper::GetLatestFD() const {
+SocketFd QuicClientDefaultNetworkHelper::GetLatestFD() const {
   if (fd_address_map_.empty()) {
     return -1;
   }
@@ -221,13 +221,13 @@
   client_->session()->ProcessUdpPacket(self_address, peer_address, packet);
 }
 
-int QuicClientDefaultNetworkHelper::CreateUDPSocket(
+SocketFd QuicClientDefaultNetworkHelper::CreateUDPSocket(
     QuicSocketAddress server_address, bool* overflow_supported) {
   QuicUdpSocketApi api;
-  int fd = api.Create(server_address.host().AddressFamilyToInt(),
-                      /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
-                      /*send_buffer_size =*/kDefaultSocketReceiveBuffer);
-  if (fd < 0) {
+  SocketFd fd = api.Create(server_address.host().AddressFamilyToInt(),
+                           /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
+                           /*send_buffer_size =*/kDefaultSocketReceiveBuffer);
+  if (fd == kInvalidSocketFd) {
     return fd;
   }
 
@@ -242,7 +242,7 @@
   return fd;
 }
 
-bool QuicClientDefaultNetworkHelper::BindInterfaceNameIfNeeded(int fd) {
+bool QuicClientDefaultNetworkHelper::BindInterfaceNameIfNeeded(SocketFd fd) {
   QuicUdpSocketApi api;
   std::string interface_name = client_->interface_name();
   if (!interface_name.empty()) {
diff --git a/quiche/quic/tools/quic_client_default_network_helper.h b/quiche/quic/tools/quic_client_default_network_helper.h
index 07b0753..f62bd80 100644
--- a/quiche/quic/tools/quic_client_default_network_helper.h
+++ b/quiche/quic/tools/quic_client_default_network_helper.h
@@ -58,20 +58,19 @@
 
   // Accessors provided for convenience, not part of any interface.
   QuicEventLoop* event_loop() { return event_loop_; }
-  const quiche::QuicheLinkedHashMap<int, QuicSocketAddress>& fd_address_map()
-      const {
+  const quiche::QuicheLinkedHashMap<SocketFd, QuicSocketAddress>&
+  fd_address_map() const {
     return fd_address_map_;
   }
 
   // If the client has at least one UDP socket, return the latest created one.
   // Otherwise, return -1.
-  int GetLatestFD() const;
+  SocketFd GetLatestFD() const;
 
-  // Create socket for connection to |server_address| with default socket
-  // options.
-  // Return fd index.
-  virtual int CreateUDPSocket(QuicSocketAddress server_address,
-                              bool* overflow_supported);
+  // Create a socket for connection to |server_address| with default socket
+  // options. Returns the FD of the resulting socket.
+  virtual SocketFd CreateUDPSocket(QuicSocketAddress server_address,
+                                   bool* overflow_supported);
 
   QuicClientBase* client() { return client_; }
 
@@ -80,7 +79,7 @@
   }
   // If |fd| is an open UDP socket, unregister and close it. Otherwise, do
   // nothing.
-  void CleanUpUDPSocket(int fd);
+  void CleanUpUDPSocket(SocketFd fd);
 
   // Used for testing.
   void SetClientPort(int port);
@@ -94,10 +93,10 @@
   }
 
   // Bind a socket to a specific network interface.
-  bool BindInterfaceNameIfNeeded(int fd);
+  bool BindInterfaceNameIfNeeded(SocketFd fd);
 
   // Actually clean up |fd|.
-  virtual void CleanUpUDPSocketImpl(int fd);
+  virtual void CleanUpUDPSocketImpl(SocketFd fd);
 
  private:
   // Listens for events on the client socket.
@@ -105,7 +104,7 @@
 
   // Map mapping created UDP sockets to their addresses. By using linked hash
   // map, the order of socket creation can be recorded.
-  quiche::QuicheLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
+  quiche::QuicheLinkedHashMap<SocketFd, QuicSocketAddress> fd_address_map_;
 
   // If overflow_supported_ is true, this will be the number of packets dropped
   // during the lifetime of the server.
diff --git a/quiche/quic/tools/quic_server.cc b/quiche/quic/tools/quic_server.cc
index 738d8c9..63e9aca 100644
--- a/quiche/quic/tools/quic_server.cc
+++ b/quiche/quic/tools/quic_server.cc
@@ -97,8 +97,8 @@
 }
 
 QuicServer::~QuicServer() {
-  close(fd_);
-  fd_ = -1;
+  (void)socket_api::Close(fd_);
+  fd_ = kInvalidSocketFd;
 
   // Should be fine without because nothing should send requests to the backend
   // after `this` is destroyed, but for extra pointer safety, clear the socket