| // 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 |