| // 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 <cerrno> | |
| #include <climits> | |
| #include <cstddef> | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/span.h" | |
| #include "quiche/quic/core/io/socket_internal.h" | |
| #include "quiche/quic/core/quic_types.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 { | |
| namespace 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}; | |
| } else { | |
| return peer_address.status(); | |
| } | |
| } | |
| absl::Status SetSockOptInt(SocketFd fd, int level, int option, int value) { | |
| QUICHE_DCHECK_NE(fd, kInvalidSocketFd); | |
| int result = SyscallSetsockopt(fd, level, 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, SOL_SOCKET, 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, SOL_SOCKET, 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->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->fd).ok()) { | |
| QUICHE_LOG_FIRST_N(ERROR, 100) | |
| << "Failed to close socket " << accept_result->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; | |
| } | |
| } | |
| absl::StatusOr<absl::string_view> SendTo(SocketFd fd, | |
| const QuicSocketAddress& peer_address, | |
| absl::string_view buffer) { | |
| QUICHE_DCHECK_NE(fd, kInvalidSocketFd); | |
| QUICHE_DCHECK(peer_address.IsInitialized()); | |
| QUICHE_DCHECK(!buffer.empty()); | |
| sockaddr_storage addr = peer_address.generic_address(); | |
| PlatformSocklen addrlen = GetAddrlen(peer_address.host().address_family()); | |
| PlatformSsizeT num_sent = | |
| SyscallSendTo(fd, buffer.data(), buffer.size(), | |
| /*flags=*/0, reinterpret_cast<sockaddr*>(&addr), addrlen); | |
| 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 | |
| << " to address: " << peer_address.ToString() << " than buffer size (" | |
| << buffer.size() << ")."; | |
| return absl::OutOfRangeError( | |
| "::sendto(): Sent more bytes than buffer size."); | |
| } else if (num_sent >= 0) { | |
| return buffer.substr(num_sent); | |
| } else { | |
| absl::Status status = LastSocketOperationError("::sendto()"); | |
| QUICHE_DVLOG(1) << "Failed to send to socket: " << fd | |
| << " to address: " << peer_address.ToString() | |
| << " with error: " << status; | |
| return status; | |
| } | |
| } | |
| } // namespace socket_api | |
| void OwnedSocketFd::reset() { | |
| if (valid()) { | |
| absl::Status status = socket_api::Close(fd_); | |
| QUICHE_DLOG_IF(WARNING, !status.ok()) << "Failed to close FD: " << status; | |
| fd_ = kInvalidSocketFd; | |
| } | |
| } | |
| SocketFd OwnedSocketFd::release() { | |
| SocketFd fd = fd_; | |
| fd_ = kInvalidSocketFd; | |
| return fd; | |
| } | |
| } // namespace quic |