| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| /// Helpers for socket tests. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIBC_TEST_SRC_SYS_SOCKET_LINUX_SOCKET_TEST_SUPPORT_H |
| #define LLVM_LIBC_TEST_SRC_SYS_SOCKET_LINUX_SOCKET_TEST_SUPPORT_H |
| |
| #include "hdr/fcntl_macros.h" |
| #include "hdr/sys_socket_macros.h" |
| #include "hdr/types/size_t.h" |
| #include "hdr/types/socklen_t.h" |
| #include "hdr/types/struct_sockaddr_un.h" |
| #include "src/__support/CPP/string_view.h" |
| #include "src/__support/common.h" |
| #include "src/fcntl/open.h" |
| #include "src/string/strncpy.h" |
| #include "src/string/strnlen.h" |
| #include "src/unistd/chdir.h" |
| #include "src/unistd/close.h" |
| #include "src/unistd/fchdir.h" |
| #include "test/UnitTest/LibcTest.h" |
| |
| namespace LIBC_NAMESPACE_DECL { |
| namespace testing { |
| |
| // In Bazel tests, the absolute path to the test output directory exceeds the |
| // 108-byte limit of struct sockaddr_un::sun_path. To create AF_UNIX filesystem |
| // sockets safely, this RAII class saves the current working directory, changes |
| // into the writable test file directory (so short relative filenames can be |
| // used), and resets CWD when destroyed. |
| class TestDirectoryScope { |
| int cwd_fd; |
| |
| public: |
| TestDirectoryScope() |
| : cwd_fd(LIBC_NAMESPACE::open(".", O_DIRECTORY | O_CLOEXEC)) { |
| if (cwd_fd < 0) |
| __builtin_trap(); |
| if (LIBC_NAMESPACE::chdir(libc_make_test_file_path(".")) != 0) |
| __builtin_trap(); |
| } |
| ~TestDirectoryScope() { |
| if (cwd_fd >= 0) { |
| int fchdir_ret = LIBC_NAMESPACE::fchdir(cwd_fd); |
| int close_ret = LIBC_NAMESPACE::close(cwd_fd); |
| if (fchdir_ret != 0 || close_ret != 0) |
| __builtin_trap(); |
| } |
| } |
| |
| TestDirectoryScope(const TestDirectoryScope &) = delete; |
| TestDirectoryScope &operator=(const TestDirectoryScope &) = delete; |
| }; |
| |
| [[nodiscard]] LIBC_INLINE bool make_sockaddr_un(cpp::string_view path, |
| struct sockaddr_un &sun) { |
| sun.sun_family = AF_UNIX; |
| // The kernel accepts addresses which fill the entire sun_path buffer (without |
| // the terminating '\0' character), but we don't do that as it makes matching |
| // the returned values more difficult. |
| if (path.size() + 1 > sizeof(sun.sun_path)) |
| return false; |
| strncpy(sun.sun_path, path.data(), sizeof(sun.sun_path)); |
| return true; |
| } |
| |
| struct SocketAddress { |
| struct sockaddr_un addr; |
| socklen_t addrlen; |
| }; |
| |
| class SocketAddressMatcher : public Matcher<SocketAddress> { |
| cpp::string_view expected_path; |
| struct sockaddr_un actual_addr; |
| socklen_t actual_addrlen; |
| |
| static constexpr size_t SUN_PATH_OFFSET = |
| offsetof(struct sockaddr_un, sun_path); |
| |
| public: |
| explicit SocketAddressMatcher(cpp::string_view path) : expected_path(path) {} |
| |
| bool match(const SocketAddress &actual) { |
| actual_addr = actual.addr; |
| actual_addrlen = actual.addrlen; |
| if (actual_addr.sun_family != AF_UNIX) |
| return false; |
| if (actual_addrlen > sizeof(actual_addr)) |
| return false; |
| size_t expected_path_len = expected_path.size(); |
| if (expected_path_len + 1 + SUN_PATH_OFFSET > actual_addrlen) |
| return false; |
| if (actual_addrlen < SUN_PATH_OFFSET) |
| return false; |
| cpp::string_view actual_path( |
| actual_addr.sun_path, |
| strnlen(actual_addr.sun_path, actual_addrlen - SUN_PATH_OFFSET)); |
| return actual_path == expected_path; |
| } |
| |
| void explainError() override { |
| if (actual_addr.sun_family != AF_UNIX) { |
| tlog << "Expected address family to be AF_UNIX but got " |
| << actual_addr.sun_family << "\n"; |
| return; |
| } |
| if (actual_addrlen > sizeof(actual_addr)) { |
| tlog << "Expected address length to be less than or equal to " |
| << sizeof(actual_addr) << " but got " << actual_addrlen << "\n"; |
| return; |
| } |
| size_t expected_path_len = expected_path.size(); |
| if (expected_path_len + 1 + SUN_PATH_OFFSET > actual_addrlen) { |
| tlog << "Expected address length to be less than or equal to " |
| << expected_path_len + 1 + SUN_PATH_OFFSET << " but got " |
| << actual_addrlen << "\n"; |
| return; |
| } |
| if (actual_addrlen < SUN_PATH_OFFSET) { |
| tlog << "Expected address length to be greater than or equal to " |
| << SUN_PATH_OFFSET << " but got " << actual_addrlen << "\n"; |
| return; |
| } |
| cpp::string_view actual_path( |
| actual_addr.sun_path, |
| strnlen(actual_addr.sun_path, actual_addrlen - SUN_PATH_OFFSET)); |
| if (actual_path != expected_path) { |
| tlog << "Expected address path to be " << expected_path << " but got " |
| << actual_path << "\n"; |
| return; |
| } |
| } |
| }; |
| |
| LIBC_INLINE SocketAddressMatcher MatchesAddress(cpp::string_view path) { |
| return SocketAddressMatcher(path); |
| } |
| |
| } // namespace testing |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LLVM_LIBC_TEST_SRC_SYS_SOCKET_LINUX_SOCKET_TEST_SUPPORT_H |