| // 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 <cstddef> | |
| #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/quic/platform/api/quic_ip_address_family.h" | |
| #include "quiche/common/platform/api/quiche_bug_tracker.h" | |
| #include "quiche/common/platform/api/quiche_logging.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); | |
| } | |
| int SyscallSendTo(int sockfd, const void* buf, size_t len, int flags, | |
| const sockaddr* addr, socklen_t addrlen) { | |
| return ::sendto(sockfd, reinterpret_cast<const char*>(buf), len, flags, addr, | |
| addrlen); | |
| } | |
| 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 SetIpHeaderIncluded(SocketFd fd, IpAddressFamily address_family, | |
| bool ip_header_included) { | |
| QUICHE_DCHECK_NE(fd, kInvalidSocketFd); | |
| int level; | |
| int option; | |
| switch (address_family) { | |
| case IpAddressFamily::IP_V4: | |
| level = IPPROTO_IP; | |
| option = IP_HDRINCL; | |
| break; | |
| case IpAddressFamily::IP_V6: | |
| level = IPPROTO_IPV6; | |
| option = IPV6_HDRINCL; | |
| break; | |
| default: | |
| QUICHE_BUG(set_ip_header_included_invalid_family) | |
| << "Invalid address family: " << static_cast<int>(address_family); | |
| return absl::InvalidArgumentError("Invalid address family."); | |
| } | |
| int value = static_cast<int>(ip_header_included); | |
| int result = ::setsockopt( | |
| fd, level, option, reinterpret_cast<const char*>(&value), sizeof(value)); | |
| if (result >= 0) { | |
| return absl::OkStatus(); | |
| } else { | |
| absl::Status status = StatusForLastCall(result, "::setsockopt()"); | |
| QUICHE_DVLOG(1) << "Failed to set socket " << fd << " option " << option | |
| << " to " << value << " with error: " << status; | |
| return status; | |
| } | |
| } | |
| absl::Status Close(SocketFd fd) { | |
| int result = ::closesocket(fd); | |
| return StatusForLastCall(result, "close()"); | |
| } | |
| } // namespace quic::socket_api |