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) {