diff --git a/starboard/CHANGELOG.md b/starboard/CHANGELOG.md index 855025ac139e..ab35b0160075 100644 --- a/starboard/CHANGELOG.md +++ b/starboard/CHANGELOG.md @@ -9,9 +9,9 @@ since the version previous to it. ## Version 16 -### Added standard POSIX socket/close APIs. -The standard API socket can be used from and -close can be called to close the socket by including . +### Added standard POSIX socket/close/setsockopt APIs. +The standard API `socket`, `setsockopt` can be used from and +`close` can be called to close the socket by including . ### Changed InstallCrashpadHandler API This API doesn't support the option to start the crashpad handler at the diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc index a908e811d928..451b931d5898 100644 --- a/starboard/elf_loader/exported_symbols.cc +++ b/starboard/elf_loader/exported_symbols.cc @@ -422,6 +422,7 @@ ExportedSymbols::ExportedSymbols() { REGISTER_SYMBOL(munmap); REGISTER_SYMBOL(posix_memalign); REGISTER_SYMBOL(realloc); + REGISTER_SYMBOL(setsockopt); REGISTER_SYMBOL(socket); REGISTER_SYMBOL(snprintf); REGISTER_SYMBOL(sprintf); diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn index 1d2005be8eab..43753641cff7 100644 --- a/starboard/nplb/BUILD.gn +++ b/starboard/nplb/BUILD.gn @@ -141,6 +141,7 @@ target(gtest_target_type, "nplb") { "posix_compliance/posix_mutex_create_test.cc", "posix_compliance/posix_mutex_destroy_test.cc", "posix_compliance/posix_socket_create_test.cc", + "posix_compliance/posix_socket_set_options_test.cc", "posix_compliance/posix_string_compare_no_case_n_test.cc", "posix_compliance/posix_string_compare_no_case_test.cc", "posix_compliance/posix_string_format_test.cc", diff --git a/starboard/nplb/posix_compliance/posix_socket_set_options_test.cc b/starboard/nplb/posix_compliance/posix_socket_set_options_test.cc new file mode 100644 index 000000000000..8b92649a1b94 --- /dev/null +++ b/starboard/nplb/posix_compliance/posix_socket_set_options_test.cc @@ -0,0 +1,130 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#if SB_API_VERSION >= 16 + +#include +#include +#include +#include + +#include "starboard/configuration.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +class PosixSocketSetOptionsTest : public ::testing::TestWithParam { + public: + int GetSocketDomain() { return GetParam(); } +}; + +TEST_P(PosixSocketSetOptionsTest, TryThemAllTCP) { + int socket_fd = socket(GetSocketDomain(), SOCK_STREAM, IPPROTO_TCP); + + int true_val = 1; + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &true_val, + sizeof(true_val)), + 0); + + int value = 16 * 1024; + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)), + 0); + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)), + 0); + + // Test TCP keepalive option. + int period_seconds = static_cast(30'000'000 / 1'000'000); + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &true_val, + sizeof(true_val)), + 0); +#if defined(__APPLE__) + // In tvOS, TCP_KEEPIDLE and SOL_TCP are not available. + // For reference: + // https://stackoverflow.com/questions/15860127/how-to-configure-tcp-keepalive-under-mac-os-x + EXPECT_EQ(setsockopt(socket_fd, IPPROTO_TCP, TCP_KEEPALIVE, &period_seconds, + sizeof(period_seconds)), + 0); +#elif !defined(_WIN32) + // In Windows, the SOL_TCP and TCP_KEEPIDLE options are not available. + // For reference: + // https://stackoverflow.com/questions/8176821/how-to-set-the-keep-alive-interval-for-winsock + EXPECT_EQ(setsockopt(socket_fd, SOL_TCP, TCP_KEEPIDLE, &period_seconds, + sizeof(period_seconds)), + 0); + EXPECT_EQ(setsockopt(socket_fd, SOL_TCP, TCP_KEEPINTVL, &period_seconds, + sizeof(period_seconds)), + 0); +#endif + + EXPECT_EQ(setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &true_val, + sizeof(true_val)), + 0); + + close(socket_fd); +} + +TEST_P(PosixSocketSetOptionsTest, TryThemAllUDP) { + int socket_fd = socket(GetSocketDomain(), SOCK_DGRAM, IPPROTO_UDP); + + int true_val = 1; + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &true_val, + sizeof(true_val)), + 0); + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &true_val, + sizeof(true_val)), + 0); + + int value = 16 * 1024; + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)), + 0); + EXPECT_EQ(setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)), + 0); + + close(socket_fd); +} + +TEST_P(PosixSocketSetOptionsTest, RainyDayInvalidSocket) { + int true_val = 1; + EXPECT_EQ( + setsockopt(-1, SOL_SOCKET, SO_REUSEADDR, &true_val, sizeof(true_val)), + -1); + + int value = 16 * 1024; + EXPECT_EQ(setsockopt(-1, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)), -1); + EXPECT_EQ(setsockopt(-1, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)), -1); + + EXPECT_EQ( + setsockopt(-1, SOL_SOCKET, SO_KEEPALIVE, &true_val, sizeof(true_val)), + -1); + + EXPECT_EQ( + setsockopt(-1, IPPROTO_TCP, TCP_NODELAY, &true_val, sizeof(true_val)), + -1); +} + +#if SB_HAS(IPV6) +INSTANTIATE_TEST_SUITE_P(PosixSocketAddressTypes, + PosixSocketSetOptionsTest, + ::testing::Values(AF_INET, AF_INET6)); +#else +INSTANTIATE_TEST_SUITE_P(PosixSocketAddressTypes, + PosixSocketSetOptionsTest, + ::testing::Values(AF_INET)); +#endif + +} // namespace +} // namespace nplb +} // namespace starboard +#endif // SB_API_VERSION >= 16 diff --git a/starboard/shared/win32/posix_emu/include/netinet/tcp.h b/starboard/shared/win32/posix_emu/include/netinet/tcp.h new file mode 100644 index 000000000000..96944e25ed7a --- /dev/null +++ b/starboard/shared/win32/posix_emu/include/netinet/tcp.h @@ -0,0 +1,20 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_NETINET_TCP_H_ +#define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_NETINET_TCP_H_ + +#include + +#endif // STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_NETINET_TCP_H_ diff --git a/starboard/shared/win32/posix_emu/include/sys/socket.h b/starboard/shared/win32/posix_emu/include/sys/socket.h index 7a9da5927416..a0c74a6381fc 100644 --- a/starboard/shared/win32/posix_emu/include/sys/socket.h +++ b/starboard/shared/win32/posix_emu/include/sys/socket.h @@ -25,6 +25,13 @@ extern "C" { int sb_socket(int domain, int type, int protocol); #define socket sb_socket +int sb_setsockopt(int socket, + int level, + int option_name, + const void* option_value, + int option_len); +#define setsockopt sb_setsockopt + #ifdef __cplusplus } #endif diff --git a/starboard/shared/win32/posix_emu/socket.cc b/starboard/shared/win32/posix_emu/socket.cc index de0954a8393b..d99572e5aef2 100644 --- a/starboard/shared/win32/posix_emu/socket.cc +++ b/starboard/shared/win32/posix_emu/socket.cc @@ -88,6 +88,8 @@ int sb_socket(int domain, int type, int protocol) { SOCKET socket_handle = socket(domain, type, protocol); if (socket_handle == INVALID_SOCKET) { // TODO: update errno with file operation error + int last_error = WSAGetLastError(); + SB_DLOG(ERROR) << "Failed to create socket, last_error = " << last_error; return -1; } @@ -105,4 +107,29 @@ int close(int fd) { return _close(fd); } +int sb_setsockopt(int socket, + int level, + int option_name, + const void* option_value, + int option_len) { + SOCKET socket_handle = handle_db_get(socket, false); + + if (socket_handle == INVALID_SOCKET) { + return -1; + } + + int result = + setsockopt(socket_handle, level, option_name, + reinterpret_cast(option_value), option_len); + // TODO(b/321999529): Windows returns SOCKET_ERROR on failure. The specific + // error code can be retrieved by calling WSAGetLastError(), and Posix returns + // -1 on failure and sets errno to the error’s value. + if (result == SOCKET_ERROR) { + int last_error = WSAGetLastError(); + SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket " << socket + << ", last_error = " << last_error; + return -1; + } + return 0; +} } // extern "C" diff --git a/starboard/tools/api_leak_detector/api_leak_detector.py b/starboard/tools/api_leak_detector/api_leak_detector.py index 87176b918d15..9010505a189e 100755 --- a/starboard/tools/api_leak_detector/api_leak_detector.py +++ b/starboard/tools/api_leak_detector/api_leak_detector.py @@ -98,6 +98,7 @@ 'strcasecmp', 'strncasecmp', 'socket', + 'setsockopt', 'time', 'mmap', 'munmap',