diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index e61b127e42102..a79df86d512e5 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -287,6 +287,8 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.socket.accept4 libc.src.sys.socket.bind libc.src.sys.socket.connect + libc.src.sys.socket.getpeername + libc.src.sys.socket.getsockname libc.src.sys.socket.getsockopt libc.src.sys.socket.listen libc.src.sys.socket.recv diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 7a34cc5fba201..c5528365e28a5 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -287,6 +287,8 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.socket.accept4 libc.src.sys.socket.bind libc.src.sys.socket.connect + libc.src.sys.socket.getpeername + libc.src.sys.socket.getsockname libc.src.sys.socket.getsockopt libc.src.sys.socket.listen libc.src.sys.socket.recv diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 00c94e1e9b5a0..df4f7a052d7ae 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -303,6 +303,8 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.socket.accept4 libc.src.sys.socket.bind libc.src.sys.socket.connect + libc.src.sys.socket.getpeername + libc.src.sys.socket.getsockname libc.src.sys.socket.getsockopt libc.src.sys.socket.listen libc.src.sys.socket.recv diff --git a/libc/include/sys/socket.yaml b/libc/include/sys/socket.yaml index b1a1a2adb7ebe..e488bb9e43353 100644 --- a/libc/include/sys/socket.yaml +++ b/libc/include/sys/socket.yaml @@ -51,6 +51,22 @@ functions: - type: int - type: const struct sockaddr * - type: socklen_t + - name: getpeername + standards: + - POSIX + return_type: int + arguments: + - type: int + - type: struct sockaddr *__restrict + - type: socklen_t *__restrict + - name: getsockname + standards: + - POSIX + return_type: int + arguments: + - type: int + - type: struct sockaddr *__restrict + - type: socklen_t *__restrict - name: getsockopt standards: - POSIX diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt index bbe76fece3bdd..7a088fd6eb4bb 100644 --- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt +++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt @@ -94,6 +94,34 @@ add_header_library( libc.include.sys_syscall ) +add_header_library( + getpeername + HDRS + getpeername.h + DEPENDS + libc.src.__support.common + libc.src.__support.error_or + libc.src.__support.OSUtil.osutil + libc.src.__support.macros.config + libc.hdr.types.socklen_t + libc.hdr.types.struct_sockaddr + libc.include.sys_syscall +) + +add_header_library( + getsockname + HDRS + getsockname.h + DEPENDS + libc.src.__support.common + libc.src.__support.error_or + libc.src.__support.OSUtil.osutil + libc.src.__support.macros.config + libc.hdr.types.socklen_t + libc.hdr.types.struct_sockaddr + libc.include.sys_syscall +) + add_header_library( listen HDRS diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpeername.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpeername.h new file mode 100644 index 0000000000000..20c51d51da946 --- /dev/null +++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpeername.h @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Syscall wrapper for getpeername. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPEERNAME_H +#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPEERNAME_H + +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl +#include "src/__support/common.h" +#include "src/__support/error_or.h" +#include "src/__support/macros/config.h" +#include // For syscall numbers + +namespace LIBC_NAMESPACE_DECL { +namespace linux_syscalls { + +LIBC_INLINE ErrorOr getpeername(int sockfd, struct sockaddr *addr, + socklen_t *addrlen) { + int ret = syscall_impl(SYS_getpeername, sockfd, addr, addrlen); + if (ret < 0) + return Error(-static_cast(ret)); + return ret; +} + +} // namespace linux_syscalls +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPEERNAME_H diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getsockname.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getsockname.h new file mode 100644 index 0000000000000..6069fc58c5816 --- /dev/null +++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getsockname.h @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Syscall wrapper for getsockname. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETSOCKNAME_H +#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETSOCKNAME_H + +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl +#include "src/__support/common.h" +#include "src/__support/error_or.h" +#include "src/__support/macros/config.h" +#include // For syscall numbers + +namespace LIBC_NAMESPACE_DECL { +namespace linux_syscalls { + +LIBC_INLINE ErrorOr getsockname(int sockfd, struct sockaddr *addr, + socklen_t *addrlen) { + int ret = syscall_impl(SYS_getsockname, sockfd, addr, addrlen); + if (ret < 0) + return Error(-static_cast(ret)); + return ret; +} + +} // namespace linux_syscalls +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETSOCKNAME_H diff --git a/libc/src/sys/socket/CMakeLists.txt b/libc/src/sys/socket/CMakeLists.txt index b602ab99c8a9d..a100e90fe3f2d 100644 --- a/libc/src/sys/socket/CMakeLists.txt +++ b/libc/src/sys/socket/CMakeLists.txt @@ -37,6 +37,20 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.connect ) +add_entrypoint_object( + getpeername + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.getpeername +) + +add_entrypoint_object( + getsockname + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.getsockname +) + add_entrypoint_object( getsockopt ALIAS diff --git a/libc/src/sys/socket/getpeername.h b/libc/src/sys/socket/getpeername.h new file mode 100644 index 0000000000000..4e7fddd738337 --- /dev/null +++ b/libc/src/sys/socket/getpeername.h @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for getpeername. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SYS_SOCKET_GETPEERNAME_H +#define LLVM_LIBC_SRC_SYS_SOCKET_GETPEERNAME_H + +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int getpeername(int sockfd, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_SOCKET_GETPEERNAME_H diff --git a/libc/src/sys/socket/getsockname.h b/libc/src/sys/socket/getsockname.h new file mode 100644 index 0000000000000..e9520932bec8f --- /dev/null +++ b/libc/src/sys/socket/getsockname.h @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for getsockname. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SYS_SOCKET_GETSOCKNAME_H +#define LLVM_LIBC_SRC_SYS_SOCKET_GETSOCKNAME_H + +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int getsockname(int sockfd, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_SOCKET_GETSOCKNAME_H diff --git a/libc/src/sys/socket/linux/CMakeLists.txt b/libc/src/sys/socket/linux/CMakeLists.txt index e39345410e879..c86991a3e42ea 100644 --- a/libc/src/sys/socket/linux/CMakeLists.txt +++ b/libc/src/sys/socket/linux/CMakeLists.txt @@ -66,6 +66,36 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + getpeername + SRCS + getpeername.cpp + HDRS + ../getpeername.h + DEPENDS + libc.include.sys_socket + libc.src.__support.OSUtil.osutil + libc.hdr.types.struct_sockaddr + libc.hdr.types.socklen_t + libc.src.__support.OSUtil.linux.syscall_wrappers.getpeername + libc.src.errno.errno +) + +add_entrypoint_object( + getsockname + SRCS + getsockname.cpp + HDRS + ../getsockname.h + DEPENDS + libc.include.sys_socket + libc.src.__support.OSUtil.osutil + libc.hdr.types.struct_sockaddr + libc.hdr.types.socklen_t + libc.src.__support.OSUtil.linux.syscall_wrappers.getsockname + libc.src.errno.errno +) + add_entrypoint_object( getsockopt SRCS diff --git a/libc/src/sys/socket/linux/getpeername.cpp b/libc/src/sys/socket/linux/getpeername.cpp new file mode 100644 index 0000000000000..8e19f5bbc3f94 --- /dev/null +++ b/libc/src/sys/socket/linux/getpeername.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Linux implementation of getpeername. +/// +//===----------------------------------------------------------------------===// + +#include "src/sys/socket/getpeername.h" +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/OSUtil/linux/syscall_wrappers/getpeername.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, getpeername, + (int sockfd, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen)) { + auto result = linux_syscalls::getpeername(sockfd, addr, addrlen); + if (!result.has_value()) { + libc_errno = result.error(); + return -1; + } + + return result.value(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sys/socket/linux/getsockname.cpp b/libc/src/sys/socket/linux/getsockname.cpp new file mode 100644 index 0000000000000..e5734b307bf71 --- /dev/null +++ b/libc/src/sys/socket/linux/getsockname.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Linux implementation of getsockname. +/// +//===----------------------------------------------------------------------===// + +#include "src/sys/socket/getsockname.h" +#include "hdr/types/socklen_t.h" +#include "hdr/types/struct_sockaddr.h" +#include "src/__support/OSUtil/linux/syscall_wrappers/getsockname.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, getsockname, + (int sockfd, struct sockaddr *__restrict addr, + socklen_t *__restrict addrlen)) { + auto result = linux_syscalls::getsockname(sockfd, addr, addrlen); + if (!result.has_value()) { + libc_errno = result.error(); + return -1; + } + + return result.value(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/sys/socket/linux/CMakeLists.txt b/libc/test/src/sys/socket/linux/CMakeLists.txt index e29140ee06b24..5b10ed2e5df0e 100644 --- a/libc/test/src/sys/socket/linux/CMakeLists.txt +++ b/libc/test/src/sys/socket/linux/CMakeLists.txt @@ -138,6 +138,32 @@ add_libc_unittest( libc.test.UnitTest.ErrnoSetterMatcher ) +add_libc_unittest( + sockname_test + SUITE + libc_sys_socket_unittests + SRCS + sockname_test.cpp + DEPENDS + .socket_test_support + libc.include.sys_socket + libc.hdr.sys_socket_macros + libc.hdr.types.struct_sockaddr_un + libc.src.errno.errno + libc.src.sys.socket.accept + libc.src.sys.socket.bind + libc.src.sys.socket.connect + libc.src.sys.socket.getpeername + libc.src.sys.socket.getsockname + libc.src.sys.socket.listen + libc.src.sys.socket.socket + libc.src.stdio.remove + libc.src.unistd.close + libc.src.__support.CPP.scope + libc.test.UnitTest.ErrnoCheckingTest + libc.test.UnitTest.ErrnoSetterMatcher +) + add_libc_unittest( send_recv_test SUITE diff --git a/libc/test/src/sys/socket/linux/sockname_test.cpp b/libc/test/src/sys/socket/linux/sockname_test.cpp new file mode 100644 index 0000000000000..0ee7b5b75bbeb --- /dev/null +++ b/libc/test/src/sys/socket/linux/sockname_test.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Unittests for getsockname and getpeername. +/// +//===----------------------------------------------------------------------===// + +#include "hdr/sys_socket_macros.h" +#include "hdr/types/struct_sockaddr_un.h" +#include "src/__support/CPP/scope.h" +#include "src/stdio/remove.h" +#include "src/sys/socket/accept.h" +#include "src/sys/socket/bind.h" +#include "src/sys/socket/connect.h" +#include "src/sys/socket/getpeername.h" +#include "src/sys/socket/getsockname.h" +#include "src/sys/socket/listen.h" +#include "src/sys/socket/socket.h" +#include "src/unistd/close.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/src/sys/socket/linux/socket_test_support.h" + +using LIBC_NAMESPACE::testing::make_sockaddr_un; +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; +using LlvmLibcSockNameTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; +using LIBC_NAMESPACE::cpp::scope_exit; + +TEST_F(LlvmLibcSockNameTest, GetSockNameAndPeerName) { + // 1. Invalid Socket + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + ASSERT_THAT(LIBC_NAMESPACE::getsockname( + -1, reinterpret_cast(&addr), &addr_len), + Fails(EBADF)); + ASSERT_THAT(LIBC_NAMESPACE::getpeername( + -1, reinterpret_cast(&addr), &addr_len), + Fails(EBADF)); + + // 2. Unbound Socket + int sock = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0); + ASSERT_GE(sock, 0); + ASSERT_ERRNO_SUCCESS(); + scope_exit close_sock( + [&] { ASSERT_THAT(LIBC_NAMESPACE::close(sock), Succeeds(0)); }); + + // getsockname on unbound socket should succeed + addr_len = sizeof(addr); + ASSERT_THAT(LIBC_NAMESPACE::getsockname( + sock, reinterpret_cast(&addr), &addr_len), + Succeeds(0)); + ASSERT_GE(addr_len, static_cast(sizeof(sa_family_t))); + ASSERT_EQ(addr.sun_family, static_cast(AF_UNIX)); + + // getpeername on unbound/unconnected socket should fail with ENOTCONN + addr_len = sizeof(addr); + ASSERT_THAT(LIBC_NAMESPACE::getpeername( + sock, reinterpret_cast(&addr), &addr_len), + Fails(ENOTCONN)); + + // 3. Connected Sockets + const char *client_file = "getsockname_client.test"; + const auto client_path = libc_make_test_file_path(client_file); + struct sockaddr_un client_addr; + ASSERT_TRUE(make_sockaddr_un(client_path, client_addr)); + + const char *server_file = "getsockname_server.test"; + const auto server_path = libc_make_test_file_path(server_file); + struct sockaddr_un server_addr; + ASSERT_TRUE(make_sockaddr_un(server_path, server_addr)); + + int server_sock = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0); + ASSERT_GE(server_sock, 0); + ASSERT_ERRNO_SUCCESS(); + scope_exit close_server_sock( + [&] { ASSERT_THAT(LIBC_NAMESPACE::close(server_sock), Succeeds(0)); }); + + ASSERT_THAT(LIBC_NAMESPACE::bind( + server_sock, + reinterpret_cast(&server_addr), + sizeof(struct sockaddr_un)), + Succeeds(0)); + scope_exit remove_server_path( + [&] { ASSERT_THAT(LIBC_NAMESPACE::remove(server_path), Succeeds(0)); }); + + ASSERT_THAT(LIBC_NAMESPACE::listen(server_sock, 1), Succeeds(0)); + + int client_sock = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0); + ASSERT_GE(client_sock, 0); + ASSERT_ERRNO_SUCCESS(); + scope_exit close_client_sock( + [&] { ASSERT_THAT(LIBC_NAMESPACE::close(client_sock), Succeeds(0)); }); + + ASSERT_THAT(LIBC_NAMESPACE::bind( + client_sock, + reinterpret_cast(&client_addr), + sizeof(struct sockaddr_un)), + Succeeds(0)); + scope_exit remove_client_path( + [&] { ASSERT_THAT(LIBC_NAMESPACE::remove(client_path), Succeeds(0)); }); + + ASSERT_THAT(LIBC_NAMESPACE::connect( + client_sock, + reinterpret_cast(&server_addr), + sizeof(struct sockaddr_un)), + Succeeds(0)); + + int accepted_sock = LIBC_NAMESPACE::accept(server_sock, nullptr, nullptr); + ASSERT_GE(accepted_sock, 0); + ASSERT_ERRNO_SUCCESS(); + scope_exit close_accepted_sock( + [&] { ASSERT_THAT(LIBC_NAMESPACE::close(accepted_sock), Succeeds(0)); }); + + // Test getsockname on client_sock (should be client_path) + addr_len = sizeof(addr); + ASSERT_THAT( + LIBC_NAMESPACE::getsockname( + client_sock, reinterpret_cast(&addr), &addr_len), + Succeeds(0)); + ASSERT_THAT((LIBC_NAMESPACE::testing::SocketAddress{addr, addr_len}), + LIBC_NAMESPACE::testing::MatchesAddress(client_path)); + + // Test getpeername on client_sock (should be server_path) + addr_len = sizeof(addr); + ASSERT_THAT( + LIBC_NAMESPACE::getpeername( + client_sock, reinterpret_cast(&addr), &addr_len), + Succeeds(0)); + ASSERT_THAT((LIBC_NAMESPACE::testing::SocketAddress{addr, addr_len}), + LIBC_NAMESPACE::testing::MatchesAddress(server_path)); + + // Test getsockname on accepted_sock (should be server_path) + addr_len = sizeof(addr); + ASSERT_THAT( + LIBC_NAMESPACE::getsockname( + accepted_sock, reinterpret_cast(&addr), &addr_len), + Succeeds(0)); + ASSERT_THAT((LIBC_NAMESPACE::testing::SocketAddress{addr, addr_len}), + LIBC_NAMESPACE::testing::MatchesAddress(server_path)); + + // Test getpeername on accepted_sock (should be client_path) + addr_len = sizeof(addr); + ASSERT_THAT( + LIBC_NAMESPACE::getpeername( + accepted_sock, reinterpret_cast(&addr), &addr_len), + Succeeds(0)); + ASSERT_THAT((LIBC_NAMESPACE::testing::SocketAddress{addr, addr_len}), + LIBC_NAMESPACE::testing::MatchesAddress(client_path)); +}