summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorZeling Feng <zeling@google.com>2021-02-08 20:10:54 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-08 20:13:17 -0800
commit95500ece56f2acf34fcdc74e420f91beea888ada (patch)
treeb49879185b622de9e330607275b3d9b3876abaaa /test
parentbf4968e17d7d08299493835a34af1a6d8551c375 (diff)
Allow UDP sockets connect()ing to port 0
We previously return EINVAL when connecting to port 0, however this is not the observed behavior on Linux. One of the observable effects after connecting to port 0 on Linux is that getpeername() will fail with ENOTCONN. PiperOrigin-RevId: 356413451
Diffstat (limited to 'test')
-rw-r--r--test/syscalls/linux/udp_socket.cc81
1 files changed, 81 insertions, 0 deletions
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 650f12350..50f589708 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -2061,11 +2061,92 @@ TEST_P(UdpSocketTest, SendToZeroPort) {
SyscallSucceedsWithValue(sizeof(buf)));
}
+TEST_P(UdpSocketTest, ConnectToZeroPortUnbound) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+}
+
+TEST_P(UdpSocketTest, ConnectToZeroPortBound) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ ASSERT_NO_ERRNO(
+ BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr)));
+
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ socklen_t len = sizeof(sockaddr_storage);
+ ASSERT_THAT(
+ getsockname(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), &len),
+ SyscallSucceeds());
+ ASSERT_EQ(len, addrlen_);
+}
+
+TEST_P(UdpSocketTest, ConnectToZeroPortConnected) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ ASSERT_NO_ERRNO(
+ BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr)));
+
+ // Connect to an address with non-zero port should succeed.
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ sockaddr_storage peername;
+ socklen_t peerlen = sizeof(peername);
+ ASSERT_THAT(
+ getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername),
+ &peerlen),
+ SyscallSucceeds());
+ ASSERT_EQ(peerlen, addrlen_);
+ ASSERT_EQ(memcmp(&peername, &addr, addrlen_), 0);
+
+ // However connect() to an address with port 0 will make the following
+ // getpeername() fail.
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername),
+ &peerlen),
+ SyscallFailsWithErrno(ENOTCONN));
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, UdpSocketTest,
::testing::Values(AddressFamily::kIpv4,
AddressFamily::kIpv6,
AddressFamily::kDualStack));
+TEST(UdpInet6SocketTest, ConnectInet4Sockaddr) {
+ // glibc getaddrinfo expects the invariant expressed by this test to be held.
+ const sockaddr_in connect_sockaddr = {
+ .sin_family = AF_INET, .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}};
+ auto sock_ =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP));
+ ASSERT_THAT(
+ connect(sock_.get(),
+ reinterpret_cast<const struct sockaddr*>(&connect_sockaddr),
+ sizeof(sockaddr_in)),
+ SyscallSucceeds());
+ socklen_t len;
+ sockaddr_storage sockname;
+ ASSERT_THAT(getsockname(sock_.get(),
+ reinterpret_cast<struct sockaddr*>(&sockname), &len),
+ SyscallSucceeds());
+ ASSERT_EQ(sockname.ss_family, AF_INET6);
+ ASSERT_EQ(len, sizeof(sockaddr_in6));
+ auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&sockname);
+ char addr_buf[INET6_ADDRSTRLEN];
+ const char* addr;
+ ASSERT_NE(addr = inet_ntop(sockname.ss_family, &sockname, addr_buf,
+ sizeof(addr_buf)),
+ nullptr);
+ ASSERT_TRUE(IN6_IS_ADDR_V4MAPPED(sin6->sin6_addr.s6_addr)) << addr;
+}
+
} // namespace
} // namespace testing