mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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
This commit is contained in:
@@ -938,11 +938,6 @@ func (e *endpoint) Disconnect() tcpip.Error {
|
||||
|
||||
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
|
||||
func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
|
||||
if addr.Port == 0 {
|
||||
// We don't support connecting to port zero.
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
@@ -1188,7 +1183,7 @@ func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
if e.EndpointState() != StateConnected {
|
||||
if e.EndpointState() != StateConnected || e.dstPort == 0 {
|
||||
return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user