Add a Windows implementation of socket.h
PiperOrigin-RevId: 527328777
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 3ddc596..50d3f48 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -956,6 +956,7 @@
"quic/core/io/quic_event_loop.h",
"quic/core/io/quic_poll_event_loop.h",
"quic/core/io/socket.h",
+ "quic/core/io/socket_internal.h",
"quic/core/quic_default_packet_writer.h",
"quic/core/quic_packet_reader.h",
"quic/core/quic_syscall_wrapper.h",
@@ -982,7 +983,9 @@
"quic/core/io/event_loop_socket_factory.cc",
"quic/core/io/quic_default_event_loop.cc",
"quic/core/io/quic_poll_event_loop.cc",
- "quic/core/io/socket_posix.cc",
+ "quic/core/io/socket.cc",
+ "quic/core/io/socket_posix.inc",
+ "quic/core/io/socket_win.inc",
"quic/core/quic_default_packet_writer.cc",
"quic/core/quic_packet_reader.cc",
"quic/core/quic_syscall_wrapper.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 3950cd3..c1baf44 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -956,6 +956,7 @@
"src/quiche/quic/core/io/quic_event_loop.h",
"src/quiche/quic/core/io/quic_poll_event_loop.h",
"src/quiche/quic/core/io/socket.h",
+ "src/quiche/quic/core/io/socket_internal.h",
"src/quiche/quic/core/quic_default_packet_writer.h",
"src/quiche/quic/core/quic_packet_reader.h",
"src/quiche/quic/core/quic_syscall_wrapper.h",
@@ -982,7 +983,9 @@
"src/quiche/quic/core/io/event_loop_socket_factory.cc",
"src/quiche/quic/core/io/quic_default_event_loop.cc",
"src/quiche/quic/core/io/quic_poll_event_loop.cc",
- "src/quiche/quic/core/io/socket_posix.cc",
+ "src/quiche/quic/core/io/socket.cc",
+ "src/quiche/quic/core/io/socket_posix.inc",
+ "src/quiche/quic/core/io/socket_win.inc",
"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",
diff --git a/build/source_list.json b/build/source_list.json
index 3e8c573..e51e004 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -955,6 +955,7 @@
"quiche/quic/core/io/quic_event_loop.h",
"quiche/quic/core/io/quic_poll_event_loop.h",
"quiche/quic/core/io/socket.h",
+ "quiche/quic/core/io/socket_internal.h",
"quiche/quic/core/quic_default_packet_writer.h",
"quiche/quic/core/quic_packet_reader.h",
"quiche/quic/core/quic_syscall_wrapper.h",
@@ -981,7 +982,9 @@
"quiche/quic/core/io/event_loop_socket_factory.cc",
"quiche/quic/core/io/quic_default_event_loop.cc",
"quiche/quic/core/io/quic_poll_event_loop.cc",
- "quiche/quic/core/io/socket_posix.cc",
+ "quiche/quic/core/io/socket.cc",
+ "quiche/quic/core/io/socket_posix.inc",
+ "quiche/quic/core/io/socket_win.inc",
"quiche/quic/core/quic_default_packet_writer.cc",
"quiche/quic/core/quic_packet_reader.cc",
"quiche/quic/core/quic_syscall_wrapper.cc",
diff --git a/quiche/quic/core/io/socket.cc b/quiche/quic/core/io/socket.cc
new file mode 100644
index 0000000..d914ffd
--- /dev/null
+++ b/quiche/quic/core/io/socket.cc
@@ -0,0 +1,266 @@
+// Copyright 2022 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.
+
+#include "quiche/quic/core/io/socket.h"
+
+#include "absl/base/attributes.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/io/socket_internal.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_ip_address_family.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+#if defined(_WIN32)
+#include "quiche/quic/core/io/socket_win.inc"
+#else
+#include "quiche/quic/core/io/socket_posix.inc"
+#endif
+
+namespace quic::socket_api {
+
+namespace {
+
+absl::StatusOr<AcceptResult> AcceptInternal(SocketFd fd) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ sockaddr_storage peer_addr;
+ PlatformSocklen peer_addr_len = sizeof(peer_addr);
+ SocketFd connection_socket = SyscallAccept(
+ fd, reinterpret_cast<struct sockaddr*>(&peer_addr), &peer_addr_len);
+
+ if (connection_socket == kInvalidSocketFd) {
+ absl::Status status = LastSocketOperationError("::accept()");
+ QUICHE_DVLOG(1) << "Failed to accept connection from socket " << fd
+ << " with error: " << status;
+ return status;
+ }
+
+ absl::StatusOr<QuicSocketAddress> peer_address =
+ ValidateAndConvertAddress(peer_addr, peer_addr_len);
+
+ if (peer_address.ok()) {
+ return AcceptResult{connection_socket, peer_address.value()};
+ } else {
+ return peer_address.status();
+ }
+}
+
+absl::Status SetSockOptInt(SocketFd fd, int option, int value) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ int result = SyscallSetsockopt(fd, SOL_SOCKET, option, &value, sizeof(value));
+
+ if (result >= 0) {
+ return absl::OkStatus();
+ } else {
+ absl::Status status = LastSocketOperationError("::setsockopt()");
+ QUICHE_DVLOG(1) << "Failed to set socket " << fd << " option " << option
+ << " to " << value << " with error: " << status;
+ return status;
+ }
+}
+
+} // namespace
+
+absl::Status SetReceiveBufferSize(SocketFd fd, QuicByteCount size) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX});
+
+ return SetSockOptInt(fd, SO_RCVBUF, static_cast<int>(size));
+}
+
+absl::Status SetSendBufferSize(SocketFd fd, QuicByteCount size) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX});
+
+ return SetSockOptInt(fd, SO_SNDBUF, static_cast<int>(size));
+}
+
+absl::Status Connect(SocketFd fd, const QuicSocketAddress& peer_address) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK(peer_address.IsInitialized());
+
+ sockaddr_storage addr = peer_address.generic_address();
+ PlatformSocklen addrlen = GetAddrlen(peer_address.host().address_family());
+
+ int connect_result =
+ SyscallConnect(fd, reinterpret_cast<sockaddr*>(&addr), addrlen);
+
+ if (connect_result >= 0) {
+ return absl::OkStatus();
+ } else {
+ // For ::connect(), only `EINPROGRESS` indicates unavailable.
+ absl::Status status =
+ LastSocketOperationError("::connect()", /*unavailable_error_numbers=*/
+ {EINPROGRESS});
+ QUICHE_DVLOG(1) << "Failed to connect socket " << fd
+ << " to address: " << peer_address.ToString()
+ << " with error: " << status;
+ return status;
+ }
+}
+
+absl::Status GetSocketError(SocketFd fd) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ int socket_error = 0;
+ PlatformSocklen len = sizeof(socket_error);
+ int sockopt_result =
+ SyscallGetsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &len);
+
+ if (sockopt_result >= 0) {
+ if (socket_error == 0) {
+ return absl::OkStatus();
+ } else {
+ return ToStatus(socket_error, "SO_ERROR");
+ }
+ } else {
+ absl::Status status = LastSocketOperationError("::getsockopt()");
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Failed to get socket error information from socket " << fd
+ << " with error: " << status;
+ return status;
+ }
+}
+
+absl::Status Bind(SocketFd fd, const QuicSocketAddress& address) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK(address.IsInitialized());
+
+ sockaddr_storage addr = address.generic_address();
+ PlatformSocklen addr_len = GetAddrlen(address.host().address_family());
+
+ int result = SyscallBind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len);
+
+ if (result >= 0) {
+ return absl::OkStatus();
+ } else {
+ absl::Status status = LastSocketOperationError("::bind()");
+ QUICHE_DVLOG(1) << "Failed to bind socket " << fd
+ << " to address: " << address.ToString()
+ << " with error: " << status;
+ return status;
+ }
+}
+
+absl::StatusOr<QuicSocketAddress> GetSocketAddress(SocketFd fd) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ sockaddr_storage addr;
+ PlatformSocklen addr_len = sizeof(addr);
+
+ int result =
+ SyscallGetsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addr_len);
+
+ if (result >= 0) {
+ return ValidateAndConvertAddress(addr, addr_len);
+ } else {
+ absl::Status status = LastSocketOperationError("::getsockname()");
+ QUICHE_DVLOG(1) << "Failed to get socket " << fd
+ << " name with error: " << status;
+ return status;
+ }
+}
+
+absl::Status Listen(SocketFd fd, int backlog) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK_GT(backlog, 0);
+
+ int result = SyscallListen(fd, backlog);
+
+ if (result >= 0) {
+ return absl::OkStatus();
+ } else {
+ absl::Status status = LastSocketOperationError("::listen()");
+ QUICHE_DVLOG(1) << "Failed to mark socket: " << fd
+ << " to listen with error :" << status;
+ return status;
+ }
+}
+
+absl::StatusOr<AcceptResult> Accept(SocketFd fd, bool blocking) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+#if defined(HAS_ACCEPT4)
+ if (!blocking) {
+ return AcceptWithFlags(fd, SOCK_NONBLOCK);
+ }
+#endif
+
+ absl::StatusOr<AcceptResult> accept_result = AcceptInternal(fd);
+ if (!accept_result.ok() || blocking) {
+ return accept_result;
+ }
+
+#if !defined(__linux__) || !defined(SOCK_NONBLOCK)
+ // If non-blocking could not be set directly on socket acceptance, need to
+ // do it now.
+ absl::Status set_non_blocking_result =
+ SetSocketBlocking(accept_result.value().fd, /*blocking=*/false);
+ if (!set_non_blocking_result.ok()) {
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Failed to set socket " << fd << " as non-blocking on acceptance.";
+ if (!Close(accept_result.value().fd).ok()) {
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Failed to close socket " << accept_result.value().fd
+ << " after error setting non-blocking on acceptance.";
+ }
+ return set_non_blocking_result;
+ }
+#endif
+
+ return accept_result;
+}
+
+absl::StatusOr<absl::Span<char>> Receive(SocketFd fd, absl::Span<char> buffer,
+ bool peek) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK(!buffer.empty());
+
+ PlatformSsizeT num_read = SyscallRecv(fd, buffer.data(), buffer.size(),
+ /*flags=*/peek ? MSG_PEEK : 0);
+
+ if (num_read > 0 && static_cast<size_t>(num_read) > buffer.size()) {
+ QUICHE_LOG_FIRST_N(WARNING, 100)
+ << "Received more bytes (" << num_read << ") from socket " << fd
+ << " than buffer size (" << buffer.size() << ").";
+ return absl::OutOfRangeError(
+ "::recv(): Received more bytes than buffer size.");
+ } else if (num_read >= 0) {
+ return buffer.subspan(0, num_read);
+ } else {
+ absl::Status status = LastSocketOperationError("::recv()");
+ QUICHE_DVLOG(1) << "Failed to receive from socket: " << fd
+ << " with error: " << status;
+ return status;
+ }
+}
+
+absl::StatusOr<absl::string_view> Send(SocketFd fd, absl::string_view buffer) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK(!buffer.empty());
+
+ PlatformSsizeT num_sent =
+ SyscallSend(fd, buffer.data(), buffer.size(), /*flags=*/0);
+
+ if (num_sent > 0 && static_cast<size_t>(num_sent) > buffer.size()) {
+ QUICHE_LOG_FIRST_N(WARNING, 100)
+ << "Sent more bytes (" << num_sent << ") to socket " << fd
+ << " than buffer size (" << buffer.size() << ").";
+ return absl::OutOfRangeError("::send(): Sent more bytes than buffer size.");
+ } else if (num_sent >= 0) {
+ return buffer.substr(num_sent);
+ } else {
+ absl::Status status = LastSocketOperationError("::send()");
+ QUICHE_DVLOG(1) << "Failed to send to socket: " << fd
+ << " with error: " << status;
+ return status;
+ }
+}
+
+} // namespace quic::socket_api
diff --git a/quiche/quic/core/io/socket.h b/quiche/quic/core/io/socket.h
index 7298f7e..d428f8c 100644
--- a/quiche/quic/core/io/socket.h
+++ b/quiche/quic/core/io/socket.h
@@ -19,6 +19,8 @@
#if defined(_WIN32)
#include <winsock2.h>
+#else
+#include <sys/socket.h>
#endif // defined(_WIN32)
namespace quic {
diff --git a/quiche/quic/core/io/socket_internal.h b/quiche/quic/core/io/socket_internal.h
new file mode 100644
index 0000000..3fc9f00
--- /dev/null
+++ b/quiche/quic/core/io/socket_internal.h
@@ -0,0 +1,78 @@
+// Copyright 2022 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.
+
+// Internal socket tools shared between Windows and POSIX implementations.
+
+#ifndef QUICHE_QUIC_CORE_IO_SOCKET_INTERNAL_H_
+#define QUICHE_QUIC_CORE_IO_SOCKET_INTERNAL_H_
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "quiche/quic/core/io/socket.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+namespace quic::socket_api {
+
+inline int ToPlatformSocketType(SocketProtocol protocol) {
+ switch (protocol) {
+ case SocketProtocol::kUdp:
+ return SOCK_DGRAM;
+ case SocketProtocol::kTcp:
+ return SOCK_STREAM;
+ }
+
+ QUICHE_NOTREACHED();
+ return -1;
+}
+
+inline int ToPlatformProtocol(SocketProtocol protocol) {
+ switch (protocol) {
+ case SocketProtocol::kUdp:
+ return IPPROTO_UDP;
+ case SocketProtocol::kTcp:
+ return IPPROTO_TCP;
+ }
+
+ QUICHE_NOTREACHED();
+ return -1;
+}
+
+// A wrapper around QuicSocketAddress(sockaddr_storage) constructor that
+// validates the supplied address.
+inline absl::StatusOr<QuicSocketAddress> ValidateAndConvertAddress(
+ const sockaddr_storage& addr, socklen_t addr_len) {
+ if (addr.ss_family != AF_INET && addr.ss_family != AF_INET6) {
+ QUICHE_DVLOG(1) << "Socket did not have recognized address family: "
+ << addr.ss_family;
+ return absl::UnimplementedError("Unrecognized address family.");
+ }
+
+ if ((addr.ss_family == AF_INET && addr_len != sizeof(sockaddr_in)) ||
+ (addr.ss_family == AF_INET6 && addr_len != sizeof(sockaddr_in6))) {
+ QUICHE_DVLOG(1) << "Socket did not have expected address size ("
+ << (addr.ss_family == AF_INET ? sizeof(sockaddr_in)
+ : sizeof(sockaddr_in6))
+ << "), had: " << addr_len;
+ return absl::UnimplementedError("Unhandled address size.");
+ }
+
+ return QuicSocketAddress(addr);
+}
+
+inline socklen_t GetAddrlen(IpAddressFamily family) {
+ switch (family) {
+ case IpAddressFamily::IP_V4:
+ return sizeof(sockaddr_in);
+ case IpAddressFamily::IP_V6:
+ return sizeof(sockaddr_in6);
+ default:
+ QUICHE_NOTREACHED();
+ return 0;
+ }
+}
+
+} // namespace quic::socket_api
+
+#endif // QUICHE_QUIC_CORE_IO_SOCKET_INTERNAL_H_
diff --git a/quiche/quic/core/io/socket_posix.cc b/quiche/quic/core/io/socket_posix.cc
deleted file mode 100644
index a15140b..0000000
--- a/quiche/quic/core/io/socket_posix.cc
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright 2022 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.
-
-#include <fcntl.h>
-#include <sys/socket.h>
-
-#include <climits>
-
-#include "absl/base/attributes.h"
-#include "absl/container/flat_hash_set.h"
-#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-#include "absl/strings/string_view.h"
-#include "quiche/quic/core/io/socket.h"
-#include "quiche/quic/platform/api/quic_ip_address_family.h"
-#include "quiche/common/platform/api/quiche_logging.h"
-
-// accept4() is a Linux-specific extension that is available in glibc 2.10+.
-#if defined(__linux__) && defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
-#if __GLIBC_PREREQ(2, 10)
-#define HAS_ACCEPT4
-#endif
-#endif
-
-namespace quic::socket_api {
-
-namespace {
-
-int ToPlatformSocketType(SocketProtocol protocol) {
- switch (protocol) {
- case SocketProtocol::kUdp:
- return SOCK_DGRAM;
- case SocketProtocol::kTcp:
- return SOCK_STREAM;
- }
-
- QUICHE_NOTREACHED();
- return -1;
-}
-
-int ToPlatformProtocol(SocketProtocol protocol) {
- switch (protocol) {
- case SocketProtocol::kUdp:
- return IPPROTO_UDP;
- case SocketProtocol::kTcp:
- return IPPROTO_TCP;
- }
-
- QUICHE_NOTREACHED();
- return -1;
-}
-
-// Wrapper of absl::ErrnoToStatus that ensures the `unavailable_error_numbers`
-// and only those numbers result in `absl::StatusCode::kUnavailable`, converting
-// any other would-be-unavailable Statuses to `absl::StatusCode::kNotFound`.
-absl::Status ToStatus(int error_number, absl::string_view method_name,
- absl::flat_hash_set<int> unavailable_error_numbers = {
- EAGAIN, EWOULDBLOCK}) {
- QUICHE_DCHECK_NE(error_number, 0);
- QUICHE_DCHECK_NE(error_number, EINTR);
-
- absl::Status status = absl::ErrnoToStatus(error_number, method_name);
- QUICHE_DCHECK(!status.ok());
-
- if (!absl::IsUnavailable(status) &&
- unavailable_error_numbers.contains(error_number)) {
- status = absl::UnavailableError(status.message());
- } else if (absl::IsUnavailable(status) &&
- !unavailable_error_numbers.contains(error_number)) {
- status = absl::NotFoundError(status.message());
- }
-
- return status;
-}
-
-absl::Status SetSocketFlags(SocketFd fd, int to_add, int to_remove) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK(to_add || to_remove);
- QUICHE_DCHECK(!(to_add & to_remove));
-
- int flags;
- do {
- flags = ::fcntl(fd, F_GETFL);
- } while (flags < 0 && errno == EINTR);
- if (flags < 0) {
- absl::Status status = ToStatus(errno, "::fcntl()");
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Could not get flags for socket " << fd << " with error: " << status;
- return status;
- }
-
- QUICHE_DCHECK(!(flags & to_add) || (flags & to_remove));
-
- int fcntl_result;
- do {
- fcntl_result = ::fcntl(fd, F_SETFL, (flags | to_add) & ~to_remove);
- } while (fcntl_result < 0 && errno == EINTR);
- if (fcntl_result < 0) {
- absl::Status status = ToStatus(errno, "::fcntl()");
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Could not set flags for socket " << fd << " with error: " << status;
- return status;
- }
-
- return absl::OkStatus();
-}
-
-absl::StatusOr<QuicSocketAddress> ValidateAndConvertAddress(
- const sockaddr_storage& addr, socklen_t addr_len) {
- if (addr.ss_family != AF_INET && addr.ss_family != AF_INET6) {
- QUICHE_DVLOG(1) << "Socket did not have recognized address family: "
- << addr.ss_family;
- return absl::UnimplementedError("Unrecognized address family.");
- }
-
- if ((addr.ss_family == AF_INET && addr_len != sizeof(sockaddr_in)) ||
- (addr.ss_family == AF_INET6 && addr_len != sizeof(sockaddr_in6))) {
- QUICHE_DVLOG(1) << "Socket did not have expected address size ("
- << (addr.ss_family == AF_INET ? sizeof(sockaddr_in)
- : sizeof(sockaddr_in6))
- << "), had: " << addr_len;
- return absl::UnimplementedError("Unhandled address size.");
- }
-
- return QuicSocketAddress(addr);
-}
-
-absl::StatusOr<SocketFd> CreateSocketWithFlags(IpAddressFamily address_family,
- SocketProtocol protocol,
- int flags) {
- int address_family_int = quiche::ToPlatformAddressFamily(address_family);
-
- int type_int = ToPlatformSocketType(protocol);
- type_int |= flags;
-
- int protocol_int = ToPlatformProtocol(protocol);
-
- SocketFd fd;
- do {
- fd = ::socket(address_family_int, type_int, protocol_int);
- } while (fd < 0 && errno == EINTR);
-
- if (fd >= 0) {
- return fd;
- } else {
- absl::Status status = ToStatus(errno, "::socket()");
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Failed to create socket with error: " << status;
- return status;
- }
-}
-
-absl::StatusOr<AcceptResult> AcceptInternal(SocketFd fd) {
- QUICHE_DCHECK_GE(fd, 0);
-
- sockaddr_storage peer_addr;
- socklen_t peer_addr_len = sizeof(peer_addr);
- SocketFd connection_socket;
- do {
- connection_socket = ::accept(
- fd, reinterpret_cast<struct sockaddr*>(&peer_addr), &peer_addr_len);
- } while (connection_socket < 0 && errno == EINTR);
-
- if (connection_socket < 0) {
- absl::Status status = ToStatus(errno, "::accept()");
- QUICHE_DVLOG(1) << "Failed to accept connection from socket " << fd
- << " with error: " << status;
- return status;
- }
-
- absl::StatusOr<QuicSocketAddress> peer_address =
- ValidateAndConvertAddress(peer_addr, peer_addr_len);
-
- if (peer_address.ok()) {
- return AcceptResult{connection_socket, peer_address.value()};
- } else {
- return peer_address.status();
- }
-}
-
-#if defined(HAS_ACCEPT4)
-absl::StatusOr<AcceptResult> AcceptWithFlags(SocketFd fd, int flags) {
- QUICHE_DCHECK_GE(fd, 0);
-
- sockaddr_storage peer_addr;
- socklen_t peer_addr_len = sizeof(peer_addr);
- SocketFd connection_socket;
- do {
- connection_socket =
- ::accept4(fd, reinterpret_cast<struct sockaddr*>(&peer_addr),
- &peer_addr_len, flags);
- } while (connection_socket < 0 && errno == EINTR);
-
- if (connection_socket < 0) {
- absl::Status status = ToStatus(errno, "::accept4()");
- QUICHE_DVLOG(1) << "Failed to accept connection from socket " << fd
- << " with error: " << status;
- return status;
- }
-
- absl::StatusOr<QuicSocketAddress> peer_address =
- ValidateAndConvertAddress(peer_addr, peer_addr_len);
-
- if (peer_address.ok()) {
- return AcceptResult{connection_socket, peer_address.value()};
- } else {
- return peer_address.status();
- }
-}
-#endif // defined(HAS_ACCEPT4)
-
-socklen_t GetAddrlen(IpAddressFamily family) {
- switch (family) {
- case IpAddressFamily::IP_V4:
- return sizeof(sockaddr_in);
- case IpAddressFamily::IP_V6:
- return sizeof(sockaddr_in6);
- default:
- QUICHE_NOTREACHED();
- return 0;
- }
-}
-
-absl::Status SetSockOptInt(SocketFd fd, int option, int value) {
- QUICHE_DCHECK_GE(fd, 0);
-
- int result;
- do {
- result = ::setsockopt(fd, SOL_SOCKET, option, &value, sizeof(value));
- } while (result < 0 && errno == EINTR);
-
- if (result >= 0) {
- return absl::OkStatus();
- } else {
- absl::Status status = ToStatus(errno, "::setsockopt()");
- QUICHE_DVLOG(1) << "Failed to set socket " << fd << " option " << option
- << " to " << value << " with error: " << status;
- return status;
- }
-}
-
-} // namespace
-
-absl::StatusOr<SocketFd> CreateSocket(IpAddressFamily address_family,
- SocketProtocol protocol, bool blocking) {
- int flags = 0;
-#if defined(__linux__) && defined(SOCK_NONBLOCK)
- if (!blocking) {
- flags = SOCK_NONBLOCK;
- }
-#endif
-
- absl::StatusOr<SocketFd> socket =
- CreateSocketWithFlags(address_family, protocol, flags);
- if (!socket.ok() || blocking) {
- return socket;
- }
-
-#if !defined(__linux__) || !defined(SOCK_NONBLOCK)
- // If non-blocking could not be set directly on socket creation, need to do
- // it now.
- absl::Status set_non_blocking_result =
- SetSocketBlocking(socket.value(), /*blocking=*/false);
- if (!set_non_blocking_result.ok()) {
- QUICHE_LOG_FIRST_N(ERROR, 100) << "Failed to set socket " << socket.value()
- << " as non-blocking on creation.";
- if (!Close(socket.value()).ok()) {
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Failed to close socket " << socket.value()
- << " after set-non-blocking error on creation.";
- }
- return set_non_blocking_result;
- }
-#endif
-
- return socket;
-}
-
-absl::Status SetSocketBlocking(SocketFd fd, bool blocking) {
- if (blocking) {
- return SetSocketFlags(fd, /*to_add=*/0, /*to_remove=*/O_NONBLOCK);
- } else {
- return SetSocketFlags(fd, /*to_add=*/O_NONBLOCK, /*to_remove=*/0);
- }
-}
-
-absl::Status SetReceiveBufferSize(SocketFd fd, QuicByteCount size) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX});
-
- return SetSockOptInt(fd, SO_RCVBUF, static_cast<int>(size));
-}
-
-absl::Status SetSendBufferSize(SocketFd fd, QuicByteCount size) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK_LE(size, QuicByteCount{INT_MAX});
-
- return SetSockOptInt(fd, SO_SNDBUF, static_cast<int>(size));
-}
-
-absl::Status Connect(SocketFd fd, const QuicSocketAddress& peer_address) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK(peer_address.IsInitialized());
-
- sockaddr_storage addr = peer_address.generic_address();
- socklen_t addrlen = GetAddrlen(peer_address.host().address_family());
-
- int connect_result;
- do {
- connect_result = ::connect(fd, reinterpret_cast<sockaddr*>(&addr), addrlen);
- } while (connect_result < 0 && errno == EINTR);
-
- if (connect_result >= 0) {
- return absl::OkStatus();
- } else {
- // For ::connect(), only `EINPROGRESS` indicates unavailable.
- absl::Status status =
- ToStatus(errno, "::connect()", /*unavailable_error_numbers=*/
- {EINPROGRESS});
- QUICHE_DVLOG(1) << "Failed to connect socket " << fd
- << " to address: " << peer_address.ToString()
- << " with error: " << status;
- return status;
- }
-}
-
-absl::Status GetSocketError(SocketFd fd) {
- QUICHE_DCHECK_GE(fd, 0);
-
- int socket_error = 0;
- socklen_t len = sizeof(socket_error);
- int sockopt_result;
- do {
- sockopt_result =
- ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &len);
- } while (sockopt_result < 0 && errno == EINTR);
-
- if (sockopt_result >= 0) {
- if (socket_error == 0) {
- return absl::OkStatus();
- } else {
- return ToStatus(socket_error, "SO_ERROR");
- }
- } else {
- absl::Status status = ToStatus(errno, "::getsockopt()");
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Failed to get socket error information from socket " << fd
- << " with error: " << status;
- return status;
- }
-}
-
-absl::Status Bind(SocketFd fd, const QuicSocketAddress& address) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK(address.IsInitialized());
-
- sockaddr_storage addr = address.generic_address();
- socklen_t addr_len = GetAddrlen(address.host().address_family());
-
- int result;
- do {
- result = ::bind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len);
- } while (result < 0 && errno == EINTR);
-
- if (result >= 0) {
- return absl::OkStatus();
- } else {
- absl::Status status = ToStatus(errno, "::bind()");
- QUICHE_DVLOG(1) << "Failed to bind socket " << fd
- << " to address: " << address.ToString()
- << " with error: " << status;
- return status;
- }
-}
-
-absl::StatusOr<QuicSocketAddress> GetSocketAddress(SocketFd fd) {
- QUICHE_DCHECK_GE(fd, 0);
-
- sockaddr_storage addr;
- socklen_t addr_len = sizeof(addr);
-
- int result;
- do {
- result = ::getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addr_len);
- } while (result < 0 && errno == EINTR);
-
- if (result >= 0) {
- return ValidateAndConvertAddress(addr, addr_len);
- } else {
- absl::Status status = ToStatus(errno, "::getsockname()");
- QUICHE_DVLOG(1) << "Failed to get socket " << fd
- << " name with error: " << status;
- return status;
- }
-}
-
-absl::Status Listen(SocketFd fd, int backlog) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK_GT(backlog, 0);
-
- int result;
- do {
- result = ::listen(fd, backlog);
- } while (result < 0 && errno == EINTR);
-
- if (result >= 0) {
- return absl::OkStatus();
- } else {
- absl::Status status = ToStatus(errno, "::listen()");
- QUICHE_DVLOG(1) << "Failed to mark socket: " << fd
- << " to listen with error :" << status;
- return status;
- }
-}
-
-absl::StatusOr<AcceptResult> Accept(SocketFd fd, bool blocking) {
- QUICHE_DCHECK_GE(fd, 0);
-
-#if defined(HAS_ACCEPT4)
- if (!blocking) {
- return AcceptWithFlags(fd, SOCK_NONBLOCK);
- }
-#endif
-
- absl::StatusOr<AcceptResult> accept_result = AcceptInternal(fd);
- if (!accept_result.ok() || blocking) {
- return accept_result;
- }
-
-#if !defined(__linux__) || !defined(SOCK_NONBLOCK)
- // If non-blocking could not be set directly on socket acceptance, need to
- // do it now.
- absl::Status set_non_blocking_result =
- SetSocketBlocking(accept_result.value().fd, /*blocking=*/false);
- if (!set_non_blocking_result.ok()) {
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Failed to set socket " << fd << " as non-blocking on acceptance.";
- if (!Close(accept_result.value().fd).ok()) {
- QUICHE_LOG_FIRST_N(ERROR, 100)
- << "Failed to close socket " << accept_result.value().fd
- << " after error setting non-blocking on acceptance.";
- }
- return set_non_blocking_result;
- }
-#endif
-
- return accept_result;
-}
-
-absl::StatusOr<absl::Span<char>> Receive(SocketFd fd, absl::Span<char> buffer,
- bool peek) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK(!buffer.empty());
-
- ssize_t num_read;
- do {
- num_read =
- ::recv(fd, buffer.data(), buffer.size(), /*flags=*/peek ? MSG_PEEK : 0);
- } while (num_read < 0 && errno == EINTR);
-
- if (num_read > 0 && static_cast<size_t>(num_read) > buffer.size()) {
- QUICHE_LOG_FIRST_N(WARNING, 100)
- << "Received more bytes (" << num_read << ") from socket " << fd
- << " than buffer size (" << buffer.size() << ").";
- return absl::OutOfRangeError(
- "::recv(): Received more bytes than buffer size.");
- } else if (num_read >= 0) {
- return buffer.subspan(0, num_read);
- } else {
- absl::Status status = ToStatus(errno, "::recv()");
- QUICHE_DVLOG(1) << "Failed to receive from socket: " << fd
- << " with error: " << status;
- return status;
- }
-}
-
-absl::StatusOr<absl::string_view> Send(SocketFd fd, absl::string_view buffer) {
- QUICHE_DCHECK_GE(fd, 0);
- QUICHE_DCHECK(!buffer.empty());
-
- ssize_t num_sent;
- do {
- num_sent = ::send(fd, buffer.data(), buffer.size(), /*flags=*/0);
- } while (num_sent < 0 && errno == EINTR);
-
- if (num_sent > 0 && static_cast<size_t>(num_sent) > buffer.size()) {
- QUICHE_LOG_FIRST_N(WARNING, 100)
- << "Sent more bytes (" << num_sent << ") to socket " << fd
- << " than buffer size (" << buffer.size() << ").";
- return absl::OutOfRangeError("::send(): Sent more bytes than buffer size.");
- } else if (num_sent >= 0) {
- return buffer.substr(num_sent);
- } else {
- absl::Status status = ToStatus(errno, "::send()");
- QUICHE_DVLOG(1) << "Failed to send to socket: " << fd
- << " with error: " << status;
- return status;
- }
-}
-
-absl::Status Close(SocketFd fd) {
- QUICHE_DCHECK_GE(fd, 0);
-
- int close_result = ::close(fd);
-
- if (close_result >= 0) {
- return absl::OkStatus();
- } else if (errno == EINTR) {
- // Ignore EINTR on close because the socket is left in an undefined state
- // and can't be acted on again.
- QUICHE_DVLOG(1) << "Socket " << fd << " close unspecified due to EINTR.";
- return absl::OkStatus();
- } else {
- absl::Status status = ToStatus(errno, "::close()");
- QUICHE_DVLOG(1) << "Failed to close socket: " << fd
- << " with error: " << status;
- return status;
- }
-}
-
-} // namespace quic::socket_api
diff --git a/quiche/quic/core/io/socket_posix.inc b/quiche/quic/core/io/socket_posix.inc
new file mode 100644
index 0000000..c646784
--- /dev/null
+++ b/quiche/quic/core/io/socket_posix.inc
@@ -0,0 +1,262 @@
+// Copyright 2022 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.
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <climits>
+
+#include "absl/base/attributes.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/io/socket.h"
+#include "quiche/quic/core/io/socket_internal.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_ip_address_family.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+// accept4() is a Linux-specific extension that is available in glibc 2.10+.
+#if defined(__linux__) && defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
+#if __GLIBC_PREREQ(2, 10)
+#define HAS_ACCEPT4
+#endif
+#endif
+
+namespace quic::socket_api {
+
+namespace {
+
+using PlatformSocklen = socklen_t;
+using PlatformSsizeT = ssize_t;
+
+template <typename Result, typename... Args>
+Result SyscallWrapper(Result (*syscall)(Args...), Args... args) {
+ while (true) {
+ auto result = syscall(args...);
+ if (result < 0 && errno == EINTR) {
+ continue;
+ }
+ return result;
+ }
+}
+
+int SyscallGetsockopt(int sockfd, int level, int optname, void* optval,
+ socklen_t* optlen) {
+ return SyscallWrapper(&::getsockopt, sockfd, level, optname, optval, optlen);
+}
+int SyscallSetsockopt(int sockfd, int level, int optname, const void* optval,
+ socklen_t optlen) {
+ return SyscallWrapper(&::setsockopt, sockfd, level, optname, optval, optlen);
+}
+int SyscallGetsockname(int sockfd, sockaddr* addr, socklen_t* addrlen) {
+ return SyscallWrapper(&::getsockname, sockfd, addr, addrlen);
+}
+int SyscallAccept(int sockfd, sockaddr* addr, socklen_t* addrlen) {
+ return SyscallWrapper(&::accept, sockfd, addr, addrlen);
+}
+int SyscallConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
+ return SyscallWrapper(&::connect, sockfd, addr, addrlen);
+}
+int SyscallBind(int sockfd, const sockaddr* addr, socklen_t addrlen) {
+ return SyscallWrapper(&::bind, sockfd, addr, addrlen);
+}
+int SyscallListen(int sockfd, int backlog) {
+ return SyscallWrapper(&::listen, sockfd, backlog);
+}
+ssize_t SyscallRecv(int sockfd, void* buf, size_t len, int flags) {
+ return SyscallWrapper(&::recv, sockfd, buf, len, flags);
+}
+ssize_t SyscallSend(int sockfd, const void* buf, size_t len, int flags) {
+ return SyscallWrapper(&::send, sockfd, buf, len, flags);
+}
+
+// Wrapper of absl::ErrnoToStatus that ensures the `unavailable_error_numbers`
+// and only those numbers result in `absl::StatusCode::kUnavailable`, converting
+// any other would-be-unavailable Statuses to `absl::StatusCode::kNotFound`.
+absl::Status ToStatus(int error_number, absl::string_view method_name,
+ absl::flat_hash_set<int> unavailable_error_numbers = {
+ EAGAIN, EWOULDBLOCK}) {
+ QUICHE_DCHECK_NE(error_number, 0);
+ QUICHE_DCHECK_NE(error_number, EINTR);
+
+ absl::Status status = absl::ErrnoToStatus(error_number, method_name);
+ QUICHE_DCHECK(!status.ok());
+
+ if (!absl::IsUnavailable(status) &&
+ unavailable_error_numbers.contains(error_number)) {
+ status = absl::UnavailableError(status.message());
+ } else if (absl::IsUnavailable(status) &&
+ !unavailable_error_numbers.contains(error_number)) {
+ status = absl::NotFoundError(status.message());
+ }
+
+ return status;
+}
+
+absl::Status LastSocketOperationError(
+ absl::string_view method_name,
+ absl::flat_hash_set<int> unavailable_error_numbers = {EAGAIN,
+ EWOULDBLOCK}) {
+ return ToStatus(errno, method_name, unavailable_error_numbers);
+}
+
+absl::Status SetSocketFlags(SocketFd fd, int to_add, int to_remove) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+ QUICHE_DCHECK(to_add || to_remove);
+ QUICHE_DCHECK(!(to_add & to_remove));
+
+ int flags;
+ do {
+ flags = ::fcntl(fd, F_GETFL);
+ } while (flags < 0 && errno == EINTR);
+ if (flags < 0) {
+ absl::Status status = LastSocketOperationError("::fcntl()");
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Could not get flags for socket " << fd << " with error: " << status;
+ return status;
+ }
+
+ QUICHE_DCHECK(!(flags & to_add) || (flags & to_remove));
+
+ int fcntl_result;
+ do {
+ fcntl_result = ::fcntl(fd, F_SETFL, (flags | to_add) & ~to_remove);
+ } while (fcntl_result < 0 && errno == EINTR);
+ if (fcntl_result < 0) {
+ absl::Status status = LastSocketOperationError("::fcntl()");
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Could not set flags for socket " << fd << " with error: " << status;
+ return status;
+ }
+
+ return absl::OkStatus();
+}
+
+absl::StatusOr<SocketFd> CreateSocketWithFlags(IpAddressFamily address_family,
+ SocketProtocol protocol,
+ int flags) {
+ int address_family_int = quiche::ToPlatformAddressFamily(address_family);
+
+ int type_int = ToPlatformSocketType(protocol);
+ type_int |= flags;
+
+ int protocol_int = ToPlatformProtocol(protocol);
+
+ SocketFd fd;
+ do {
+ fd = SyscallWrapper(&::socket, address_family_int, type_int, protocol_int);
+ } while (fd < 0 && errno == EINTR);
+
+ if (fd >= 0) {
+ return fd;
+ } else {
+ absl::Status status = LastSocketOperationError("::socket()");
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Failed to create socket with error: " << status;
+ return status;
+ }
+}
+
+#if defined(HAS_ACCEPT4)
+absl::StatusOr<AcceptResult> AcceptWithFlags(SocketFd fd, int flags) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ sockaddr_storage peer_addr;
+ socklen_t peer_addr_len = sizeof(peer_addr);
+ SocketFd connection_socket;
+ do {
+ connection_socket = SyscallWrapper(
+ &::accept4, fd, reinterpret_cast<struct sockaddr*>(&peer_addr),
+ &peer_addr_len, flags);
+ } while (connection_socket < 0 && errno == EINTR);
+
+ if (connection_socket < 0) {
+ absl::Status status = LastSocketOperationError("::accept4()");
+ QUICHE_DVLOG(1) << "Failed to accept connection from socket " << fd
+ << " with error: " << status;
+ return status;
+ }
+
+ absl::StatusOr<QuicSocketAddress> peer_address =
+ ValidateAndConvertAddress(peer_addr, peer_addr_len);
+
+ if (peer_address.ok()) {
+ return AcceptResult{connection_socket, peer_address.value()};
+ } else {
+ return peer_address.status();
+ }
+}
+#endif // defined(HAS_ACCEPT4)
+
+} // namespace
+
+absl::Status SetSocketBlocking(SocketFd fd, bool blocking) {
+ if (blocking) {
+ return SetSocketFlags(fd, /*to_add=*/0, /*to_remove=*/O_NONBLOCK);
+ } else {
+ return SetSocketFlags(fd, /*to_add=*/O_NONBLOCK, /*to_remove=*/0);
+ }
+}
+
+absl::StatusOr<SocketFd> CreateSocket(IpAddressFamily address_family,
+ SocketProtocol protocol, bool blocking) {
+ int flags = 0;
+#if defined(__linux__) && defined(SOCK_NONBLOCK)
+ if (!blocking) {
+ flags = SOCK_NONBLOCK;
+ }
+#endif
+
+ absl::StatusOr<SocketFd> socket =
+ CreateSocketWithFlags(address_family, protocol, flags);
+ if (!socket.ok() || blocking) {
+ return socket;
+ }
+
+#if !defined(__linux__) || !defined(SOCK_NONBLOCK)
+ // If non-blocking could not be set directly on socket creation, need to do
+ // it now.
+ absl::Status set_non_blocking_result =
+ SetSocketBlocking(socket.value(), /*blocking=*/false);
+ if (!set_non_blocking_result.ok()) {
+ QUICHE_LOG_FIRST_N(ERROR, 100) << "Failed to set socket " << socket.value()
+ << " as non-blocking on creation.";
+ if (!Close(socket.value()).ok()) {
+ QUICHE_LOG_FIRST_N(ERROR, 100)
+ << "Failed to close socket " << socket.value()
+ << " after set-non-blocking error on creation.";
+ }
+ return set_non_blocking_result;
+ }
+#endif
+
+ return socket;
+}
+
+absl::Status Close(SocketFd fd) {
+ QUICHE_DCHECK_NE(fd, kInvalidSocketFd);
+
+ int close_result = ::close(fd);
+
+ if (close_result >= 0) {
+ return absl::OkStatus();
+ } else if (errno == EINTR) {
+ // Ignore EINTR on close because the socket is left in an undefined state
+ // and can't be acted on again.
+ QUICHE_DVLOG(1) << "Socket " << fd << " close unspecified due to EINTR.";
+ return absl::OkStatus();
+ } else {
+ absl::Status status = LastSocketOperationError("::close()");
+ QUICHE_DVLOG(1) << "Failed to close socket: " << fd
+ << " with error: " << status;
+ return status;
+ }
+}
+
+} // namespace quic::socket_api
diff --git a/quiche/quic/core/io/socket_test.cc b/quiche/quic/core/io/socket_test.cc
index 9afe0f6..da1d24a 100644
--- a/quiche/quic/core/io/socket_test.cc
+++ b/quiche/quic/core/io/socket_test.cc
@@ -16,11 +16,13 @@
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/platform/api/quiche_test_loopback.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
namespace quic {
namespace {
using quiche::test::QuicheTest;
+using quiche::test::StatusIs;
using testing::Lt;
using testing::SizeIs;
@@ -42,47 +44,46 @@
absl::StatusOr<SocketFd> created_socket = socket_api::CreateSocket(
localhost_address.address_family(), socket_api::SocketProtocol::kUdp);
- EXPECT_TRUE(created_socket.ok());
+ QUICHE_EXPECT_OK(created_socket.status());
- EXPECT_TRUE(socket_api::Close(created_socket.value()).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(created_socket.value()));
}
TEST(SocketTest, SetSocketBlocking) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp,
/*blocking=*/true);
- EXPECT_TRUE(socket_api::SetSocketBlocking(socket, /*blocking=*/false).ok());
+ QUICHE_EXPECT_OK(socket_api::SetSocketBlocking(socket, /*blocking=*/false));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, SetReceiveBufferSize) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp,
/*blocking=*/true);
- EXPECT_TRUE(socket_api::SetReceiveBufferSize(socket, /*size=*/100).ok());
+ QUICHE_EXPECT_OK(socket_api::SetReceiveBufferSize(socket, /*size=*/100));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, SetSendBufferSize) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp,
/*blocking=*/true);
- EXPECT_TRUE(socket_api::SetSendBufferSize(socket, /*size=*/100).ok());
+ QUICHE_EXPECT_OK(socket_api::SetSendBufferSize(socket, /*size=*/100));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Connect) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp);
// UDP, so "connecting" should succeed without any listening sockets.
- EXPECT_TRUE(socket_api::Connect(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
+ QUICHE_EXPECT_OK(socket_api::Connect(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, GetSocketError) {
@@ -90,62 +91,57 @@
/*blocking=*/true);
absl::Status error = socket_api::GetSocketError(socket);
- EXPECT_TRUE(error.ok());
+ QUICHE_EXPECT_OK(error);
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Bind) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp);
- EXPECT_TRUE(socket_api::Bind(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
+ QUICHE_EXPECT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, GetSocketAddress) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp);
- ASSERT_TRUE(socket_api::Bind(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
+ QUICHE_ASSERT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
absl::StatusOr<QuicSocketAddress> address =
socket_api::GetSocketAddress(socket);
- EXPECT_TRUE(address.ok());
+ QUICHE_EXPECT_OK(address);
EXPECT_TRUE(address.value().IsInitialized());
EXPECT_EQ(address.value().host(), quiche::TestLoopback());
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Listen) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kTcp);
- ASSERT_TRUE(socket_api::Bind(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
+ QUICHE_ASSERT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
- EXPECT_TRUE(socket_api::Listen(socket, /*backlog=*/5).ok());
+ QUICHE_EXPECT_OK(socket_api::Listen(socket, /*backlog=*/5));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Accept) {
// Need a non-blocking socket to avoid waiting when no connection comes.
SocketFd socket =
CreateTestSocket(socket_api::SocketProtocol::kTcp, /*blocking=*/false);
- ASSERT_TRUE(socket_api::Bind(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
- ASSERT_TRUE(socket_api::Listen(socket, /*backlog=*/5).ok());
+ QUICHE_ASSERT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
+ QUICHE_ASSERT_OK(socket_api::Listen(socket, /*backlog=*/5));
// Nothing set up to connect, so expect kUnavailable.
absl::StatusOr<socket_api::AcceptResult> result = socket_api::Accept(socket);
- ASSERT_FALSE(result.ok());
- EXPECT_TRUE(absl::IsUnavailable(result.status()));
+ EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnavailable));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Receive) {
@@ -153,13 +149,16 @@
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp,
/*blocking=*/false);
+ // On Windows, recv() fails on a socket that is connectionless and not bound.
+ QUICHE_ASSERT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
+
std::string buffer(100, 0);
absl::StatusOr<absl::Span<char>> result =
socket_api::Receive(socket, absl::MakeSpan(buffer));
- ASSERT_FALSE(result.ok());
- EXPECT_TRUE(absl::IsUnavailable(result.status()));
+ EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnavailable));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Peek) {
@@ -167,30 +166,32 @@
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp,
/*blocking=*/false);
+ // On Windows, recv() fails on a socket that is connectionless and not bound.
+ QUICHE_ASSERT_OK(socket_api::Bind(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
+
std::string buffer(100, 0);
absl::StatusOr<absl::Span<char>> result =
socket_api::Receive(socket, absl::MakeSpan(buffer), /*peek=*/true);
- ASSERT_FALSE(result.ok());
- EXPECT_TRUE(absl::IsUnavailable(result.status()));
+ EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnavailable));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
TEST(SocketTest, Send) {
SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp);
// UDP, so "connecting" should succeed without any listening sockets.
- ASSERT_TRUE(socket_api::Connect(
- socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))
- .ok());
+ QUICHE_ASSERT_OK(socket_api::Connect(
+ socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0)));
char buffer[] = {12, 34, 56, 78};
// Expect at least some data to be sent successfully.
absl::StatusOr<absl::string_view> result =
socket_api::Send(socket, absl::string_view(buffer, sizeof(buffer)));
- ASSERT_TRUE(result.ok());
+ QUICHE_ASSERT_OK(result.status());
EXPECT_THAT(result.value(), SizeIs(Lt(4)));
- EXPECT_TRUE(socket_api::Close(socket).ok());
+ QUICHE_EXPECT_OK(socket_api::Close(socket));
}
} // namespace
diff --git a/quiche/quic/core/io/socket_win.inc b/quiche/quic/core/io/socket_win.inc
new file mode 100644
index 0000000..5fce989
--- /dev/null
+++ b/quiche/quic/core/io/socket_win.inc
@@ -0,0 +1,164 @@
+// 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.
+
+#include <winsock2.h>
+
+#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
+#include "quiche/quic/core/io/socket.h"
+#include "quiche/quic/core/io/socket_internal.h"
+#include "quiche/common/quiche_status_utils.h"
+
+namespace quic::socket_api {
+
+namespace {
+
+using PlatformSocklen = int;
+using PlatformSsizeT = int;
+
+int SyscallGetsockopt(int sockfd, int level, int optname, void* optval,
+ int* optlen) {
+ return ::getsockopt(sockfd, level, optname, reinterpret_cast<char*>(optval),
+ optlen);
+}
+int SyscallSetsockopt(int sockfd, int level, int optname, const void* optval,
+ int optlen) {
+ return ::setsockopt(sockfd, level, optname,
+ reinterpret_cast<const char*>(optval), optlen);
+}
+int SyscallGetsockname(int sockfd, sockaddr* addr, int* addrlen) {
+ return ::getsockname(sockfd, addr, addrlen);
+}
+int SyscallAccept(int sockfd, sockaddr* addr, int* addrlen) {
+ return ::accept(sockfd, addr, addrlen);
+}
+int SyscallConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
+ return ::connect(sockfd, addr, addrlen);
+}
+int SyscallBind(int sockfd, const sockaddr* addr, socklen_t addrlen) {
+ return ::bind(sockfd, addr, addrlen);
+}
+int SyscallListen(int sockfd, int backlog) { return ::listen(sockfd, backlog); }
+int SyscallRecv(int sockfd, void* buf, size_t len, int flags) {
+ return ::recv(sockfd, reinterpret_cast<char*>(buf), len, flags);
+}
+int SyscallSend(int sockfd, const void* buf, size_t len, int flags) {
+ return ::send(sockfd, reinterpret_cast<const char*>(buf), len, flags);
+}
+
+absl::StatusCode RemapErrorCode(int code) {
+ switch (code) {
+ // Note that kUnavailable is the special status that always has to map to
+ // EWOULDBLOCK (see the API documentation).
+ case WSAEWOULDBLOCK:
+ return absl::StatusCode::kUnavailable;
+
+ case WSANOTINITIALISED:
+ case WSAENETDOWN:
+ case WSAEAFNOSUPPORT:
+ case WSAEPROTONOSUPPORT:
+ case WSAESHUTDOWN:
+ return absl::StatusCode::kFailedPrecondition;
+
+ case WSAEFAULT:
+ case WSAEINVAL:
+ case WSAEPROTOTYPE:
+ case WSAESOCKTNOSUPPORT:
+ case WSAEADDRNOTAVAIL:
+ case WSAENOTSOCK:
+ case WSAEOPNOTSUPP:
+ return absl::StatusCode::kInvalidArgument;
+
+ case WSAEADDRINUSE:
+ case WSAEISCONN:
+ return absl::StatusCode::kAlreadyExists;
+
+ case WSAEMFILE:
+ case WSAENOBUFS:
+ return absl::StatusCode::kResourceExhausted;
+
+ case WSAECONNREFUSED:
+ case WSAENETUNREACH:
+ case WSAEHOSTUNREACH:
+ return absl::StatusCode::kNotFound;
+
+ case WSAETIMEDOUT:
+ return absl::StatusCode::kDeadlineExceeded;
+
+ case WSAEACCES:
+ return absl::StatusCode::kPermissionDenied;
+
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ return absl::StatusCode::kAborted;
+
+ case WSAEMSGSIZE:
+ return absl::StatusCode::kOutOfRange;
+
+ default:
+ return absl::StatusCode::kInternal;
+ }
+}
+
+std::string SystemErrorCodeToString(int code) {
+ LPSTR message = nullptr;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ nullptr, code, 0, reinterpret_cast<LPSTR>(&message), 0,
+ nullptr);
+ std::string owned_message(absl::StripAsciiWhitespace(message));
+ LocalFree(message);
+ return owned_message;
+}
+
+absl::Status ToStatus(int error_number, absl::string_view method_name,
+ absl::flat_hash_set<int> unavailable_error_numbers = {}) {
+ (void)unavailable_error_numbers; // Suppress "unused variable" error.
+ return absl::Status(
+ RemapErrorCode(error_number),
+ absl::StrCat(method_name, ": Winsock error ", error_number, ": ",
+ SystemErrorCodeToString(error_number)));
+}
+
+absl::Status LastSocketOperationError(
+ absl::string_view method_name,
+ absl::flat_hash_set<int> unavailable_error_numbers = {}) {
+ return ToStatus(WSAGetLastError(), method_name, unavailable_error_numbers);
+}
+
+absl::Status StatusForLastCall(int result, absl::string_view method_name) {
+ return result == 0 ? absl::OkStatus() : LastSocketOperationError(method_name);
+}
+
+} // namespace
+
+absl::StatusOr<SocketFd> CreateSocket(IpAddressFamily address_family,
+ SocketProtocol protocol, bool blocking) {
+ int family_int = ToPlatformAddressFamily(address_family);
+ int type_int = ToPlatformSocketType(protocol);
+ int protocol_int = ToPlatformProtocol(protocol);
+ SocketFd result = ::WSASocket(family_int, type_int, protocol_int, nullptr, 0,
+ WSA_FLAG_OVERLAPPED);
+ if (result == INVALID_SOCKET) {
+ return LastSocketOperationError("socket()");
+ }
+ if (!blocking) {
+ // Windows sockets are blocking by default.
+ QUICHE_RETURN_IF_ERROR(SetSocketBlocking(result, blocking));
+ }
+ return result;
+}
+
+absl::Status SetSocketBlocking(SocketFd fd, bool blocking) {
+ u_long mode = !blocking;
+ int result = ::ioctlsocket(fd, FIONBIO, &mode);
+ return StatusForLastCall(result, "SetSocketBlocking()");
+}
+
+absl::Status Close(SocketFd fd) {
+ int result = ::closesocket(fd);
+ return StatusForLastCall(result, "close()");
+}
+
+} // namespace quic::socket_api