Add QUICHE socket SendTo() support PiperOrigin-RevId: 596051464
diff --git a/quiche/quic/core/io/socket.cc b/quiche/quic/core/io/socket.cc index d41106f..0e54102 100644 --- a/quiche/quic/core/io/socket.cc +++ b/quiche/quic/core/io/socket.cc
@@ -4,14 +4,17 @@ #include "quiche/quic/core/io/socket.h" -#include "absl/base/attributes.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_ip_address_family.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/common/platform/api/quiche_logging.h" @@ -270,4 +273,36 @@ } } +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 quic::socket_api
diff --git a/quiche/quic/core/io/socket.h b/quiche/quic/core/io/socket.h index 319dff4..57944be 100644 --- a/quiche/quic/core/io/socket.h +++ b/quiche/quic/core/io/socket.h
@@ -130,6 +130,12 @@ // send operation could not be immediately completed. absl::StatusOr<absl::string_view> Send(SocketFd fd, absl::string_view buffer); +// Same as Send() except a specific address (`peer_address`) is specified for +// where to send the data to. Typically used for non-connected sockets. +absl::StatusOr<absl::string_view> SendTo(SocketFd fd, + const QuicSocketAddress& peer_address, + absl::string_view buffer); + // Closes socket `fd`. absl::Status Close(SocketFd fd); } // namespace socket_api
diff --git a/quiche/quic/core/io/socket_posix.inc b/quiche/quic/core/io/socket_posix.inc index 2ce98b4..9f45945 100644 --- a/quiche/quic/core/io/socket_posix.inc +++ b/quiche/quic/core/io/socket_posix.inc
@@ -80,6 +80,10 @@ ssize_t SyscallSend(int sockfd, const void* buf, size_t len, int flags) { return SyscallWrapper(&::send, sockfd, buf, len, flags); } +ssize_t SyscallSendTo(int sockfd, const void* buf, size_t len, int flags, + const sockaddr* addr, socklen_t addrlen) { + return SyscallWrapper(&::sendto, sockfd, buf, len, flags, addr, addrlen); +} // Wrapper of absl::ErrnoToStatus that ensures the `unavailable_error_numbers` // and only those numbers result in `absl::StatusCode::kUnavailable`, converting
diff --git a/quiche/quic/core/io/socket_test.cc b/quiche/quic/core/io/socket_test.cc index 9e18cc1..4bfc27a 100644 --- a/quiche/quic/core/io/socket_test.cc +++ b/quiche/quic/core/io/socket_test.cc
@@ -12,12 +12,13 @@ #include "absl/types/span.h" #include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_socket_address.h" +#include "quiche/quic/test_tools/test_ip_packets.h" #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 quic::test { namespace { using quiche::test::QuicheTest; @@ -108,7 +109,7 @@ TEST(SocketTest, SetIpHeaderIncludedForRaw) { SocketFd socket = CreateTestRawSocket(/*blocking=*/true); if (socket == kInvalidSocketFd) { - return; + GTEST_SKIP(); } QUICHE_EXPECT_OK( @@ -247,5 +248,89 @@ QUICHE_EXPECT_OK(socket_api::Close(socket)); } +TEST(SocketTest, SendTo) { + SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp); + + // Send data to an arbitrarily-chosen ephemeral port. + char buffer[] = {12, 34, 56, 78}; + absl::StatusOr<absl::string_view> result = socket_api::SendTo( + socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/57290), + absl::string_view(buffer, sizeof(buffer))); + + // Expect at least some data to be sent successfully. + QUICHE_ASSERT_OK(result.status()); + EXPECT_THAT(result.value(), SizeIs(Lt(4))); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + +TEST(SocketTest, SendToWithConnection) { + SocketFd socket = CreateTestSocket(socket_api::SocketProtocol::kUdp); + // UDP, so "connecting" should succeed without any listening sockets. + QUICHE_ASSERT_OK(socket_api::Connect( + socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/0))); + + // Send data to an arbitrarily-chosen ephemeral port. + char buffer[] = {12, 34, 56, 78}; + absl::StatusOr<absl::string_view> result = socket_api::SendTo( + socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/50495), + absl::string_view(buffer, sizeof(buffer))); + // Expect at least some data to be sent successfully. + QUICHE_ASSERT_OK(result.status()); + EXPECT_THAT(result.value(), SizeIs(Lt(4))); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + +TEST(SocketTest, SendToForRaw) { + SocketFd socket = CreateTestRawSocket(/*blocking=*/true); + if (socket == kInvalidSocketFd) { + GTEST_SKIP(); + } + + QUICHE_EXPECT_OK( + socket_api::SetIpHeaderIncluded(socket, /*ip_header_included=*/false)); + + // Arbitrarily-chosen ephemeral ports. + QuicSocketAddress client_address(quiche::TestLoopback(), /*port=*/53368); + QuicSocketAddress server_address(quiche::TestLoopback(), /*port=*/56362); + std::string packet = CreateUdpPacket(client_address, server_address, "foo"); + absl::StatusOr<absl::string_view> result = socket_api::SendTo( + socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/56362), + packet); + + // Expect at least some data to be sent successfully. + QUICHE_ASSERT_OK(result.status()); + EXPECT_THAT(result.value(), SizeIs(Lt(packet.size()))); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + +TEST(SocketTest, SendToForRawWithIpHeader) { + SocketFd socket = CreateTestRawSocket(/*blocking=*/true); + if (socket == kInvalidSocketFd) { + GTEST_SKIP(); + } + + QUICHE_EXPECT_OK( + socket_api::SetIpHeaderIncluded(socket, /*ip_header_included=*/true)); + + // Arbitrarily-chosen ephemeral ports. + QuicSocketAddress client_address(quiche::TestLoopback(), /*port=*/53368); + QuicSocketAddress server_address(quiche::TestLoopback(), /*port=*/56362); + std::string packet = + CreateIpPacket(client_address.host(), server_address.host(), + CreateUdpPacket(client_address, server_address, "foo")); + absl::StatusOr<absl::string_view> result = socket_api::SendTo( + socket, QuicSocketAddress(quiche::TestLoopback(), /*port=*/56362), + packet); + + // Expect at least some data to be sent successfully. + QUICHE_ASSERT_OK(result.status()); + EXPECT_THAT(result.value(), SizeIs(Lt(packet.size()))); + + QUICHE_EXPECT_OK(socket_api::Close(socket)); +} + } // namespace -} // namespace quic +} // namespace quic::test
diff --git a/quiche/quic/core/io/socket_win.inc b/quiche/quic/core/io/socket_win.inc index 5fce989..c885722 100644 --- a/quiche/quic/core/io/socket_win.inc +++ b/quiche/quic/core/io/socket_win.inc
@@ -4,6 +4,8 @@ #include <winsock2.h> +#include <cstddef> + #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "quiche/quic/core/io/socket.h" @@ -46,6 +48,11 @@ 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) {