blob: f149b9587fa310cc6d4ebcccf6fcb9041d0185e9 [file] [log] [blame]
Eric Newberrya98bf932015-09-21 00:58:47 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Eric Newberry0c841642018-01-17 15:01:00 -07002/*
Davide Pesavento4c957712024-01-01 15:40:06 -05003 * Copyright (c) 2014-2024, Regents of the University of California,
Eric Newberrya98bf932015-09-21 00:58:47 -07004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
Junxiao Shicde37ad2015-12-24 01:02:05 -070026#include "face/unix-stream-channel.hpp"
27
Davide Pesavento9a00fab2016-09-27 11:22:46 +020028#include "channel-fixture.hpp"
Eric Newberrya98bf932015-09-21 00:58:47 -070029
Davide Pesavento401d1a42024-12-19 21:10:22 -050030#include <filesystem>
Spencer Lee75142a12016-04-13 16:55:10 -070031#include <fstream>
Davide Pesavento401d1a42024-12-19 21:10:22 -050032#include <system_error>
33
34BOOST_TEST_DONT_PRINT_LOG_VALUE(::std::filesystem::file_type)
35BOOST_TEST_DONT_PRINT_LOG_VALUE(::std::filesystem::perms)
Spencer Lee75142a12016-04-13 16:55:10 -070036
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040037namespace nfd::tests {
Eric Newberrya98bf932015-09-21 00:58:47 -070038
Davide Pesavento401d1a42024-12-19 21:10:22 -050039namespace fs = std::filesystem;
Spencer Lee75142a12016-04-13 16:55:10 -070040namespace local = boost::asio::local;
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040041using face::UnixStreamChannel;
Junxiao Shicde37ad2015-12-24 01:02:05 -070042
Davide Pesavento9a00fab2016-09-27 11:22:46 +020043class UnixStreamChannelFixture : public ChannelFixture<UnixStreamChannel, unix_stream::Endpoint>
Spencer Lee75142a12016-04-13 16:55:10 -070044{
45protected:
46 UnixStreamChannelFixture()
Spencer Lee75142a12016-04-13 16:55:10 -070047 {
Davide Pesavento401d1a42024-12-19 21:10:22 -050048 listenerEp = unix_stream::Endpoint(socketPath);
Davide Pesavento4b1921f2024-01-12 20:25:45 -050049 }
50
51 ~UnixStreamChannelFixture() override
52 {
Davide Pesavento401d1a42024-12-19 21:10:22 -050053 std::error_code ec;
Davide Pesavento152874a2024-02-20 22:07:07 -050054 fs::remove_all(testDir, ec); // ignore error
Spencer Lee75142a12016-04-13 16:55:10 -070055 }
56
Alexander Afanasyev3a2339a2020-05-27 23:05:06 -040057 shared_ptr<UnixStreamChannel>
Davide Pesavento9a00fab2016-09-27 11:22:46 +020058 makeChannel() final
Spencer Lee75142a12016-04-13 16:55:10 -070059 {
Alexander Afanasyev3a2339a2020-05-27 23:05:06 -040060 return std::make_shared<UnixStreamChannel>(listenerEp, false);
Spencer Lee75142a12016-04-13 16:55:10 -070061 }
62
63 void
64 listen()
65 {
66 listenerChannel = makeChannel();
67 listenerChannel->listen(
68 [this] (const shared_ptr<Face>& newFace) {
69 BOOST_REQUIRE(newFace != nullptr);
70 connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
71 listenerFaces.push_back(newFace);
72 limitedIo.afterOp();
73 },
Davide Pesavento9a00fab2016-09-27 11:22:46 +020074 ChannelFixture::unexpectedFailure);
Spencer Lee75142a12016-04-13 16:55:10 -070075 }
76
77 void
Davide Pesavento9a00fab2016-09-27 11:22:46 +020078 clientConnect(local::stream_protocol::socket& client)
Spencer Lee75142a12016-04-13 16:55:10 -070079 {
80 client.async_connect(listenerEp,
Davide Pesavento4c957712024-01-01 15:40:06 -050081 [this] (const auto& error) {
Spencer Lee75142a12016-04-13 16:55:10 -070082 BOOST_REQUIRE_EQUAL(error, boost::system::errc::success);
83 limitedIo.afterOp();
84 });
85 }
Davide Pesavento4c957712024-01-01 15:40:06 -050086
87protected:
Davide Pesavento152874a2024-02-20 22:07:07 -050088 static inline const fs::path testDir = fs::path(UNIT_TESTS_TMPDIR) / "unix-stream-channel";
89 static inline const fs::path socketPath = testDir / "test" / "foo.sock";
Spencer Lee75142a12016-04-13 16:55:10 -070090};
91
Davide Pesavento9a00fab2016-09-27 11:22:46 +020092BOOST_AUTO_TEST_SUITE(Face)
Spencer Lee75142a12016-04-13 16:55:10 -070093BOOST_FIXTURE_TEST_SUITE(TestUnixStreamChannel, UnixStreamChannelFixture)
94
Davide Pesavento9a00fab2016-09-27 11:22:46 +020095BOOST_AUTO_TEST_CASE(Uri)
96{
97 auto channel = makeChannel();
98 BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(listenerEp));
99}
100
Spencer Lee75142a12016-04-13 16:55:10 -0700101BOOST_AUTO_TEST_CASE(Listen)
102{
103 auto channel = makeChannel();
104 BOOST_CHECK_EQUAL(channel->isListening(), false);
105
106 channel->listen(nullptr, nullptr);
107 BOOST_CHECK_EQUAL(channel->isListening(), true);
108
109 // listen() is idempotent
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500110 channel->listen(nullptr, nullptr);
Spencer Lee75142a12016-04-13 16:55:10 -0700111 BOOST_CHECK_EQUAL(channel->isListening(), true);
112}
113
114BOOST_AUTO_TEST_CASE(MultipleAccepts)
115{
116 this->listen();
117
118 BOOST_CHECK_EQUAL(listenerChannel->isListening(), true);
Davide Pesaventoc19408d2017-04-08 00:42:55 -0400119 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
Spencer Lee75142a12016-04-13 16:55:10 -0700120
121 local::stream_protocol::socket client1(g_io);
Davide Pesavento9a00fab2016-09-27 11:22:46 +0200122 this->clientConnect(client1);
Spencer Lee75142a12016-04-13 16:55:10 -0700123
Davide Pesavento14e71f02019-03-28 17:35:25 -0400124 BOOST_CHECK_EQUAL(limitedIo.run(2, 1_s), LimitedIo::EXCEED_OPS);
Davide Pesaventoc19408d2017-04-08 00:42:55 -0400125 BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
Spencer Lee75142a12016-04-13 16:55:10 -0700126
127 local::stream_protocol::socket client2(g_io);
128 local::stream_protocol::socket client3(g_io);
Davide Pesavento9a00fab2016-09-27 11:22:46 +0200129 this->clientConnect(client2);
130 this->clientConnect(client3);
Spencer Lee75142a12016-04-13 16:55:10 -0700131
Davide Pesavento14e71f02019-03-28 17:35:25 -0400132 BOOST_CHECK_EQUAL(limitedIo.run(4, 1_s), LimitedIo::EXCEED_OPS);
Davide Pesaventoc19408d2017-04-08 00:42:55 -0400133 BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
Spencer Lee75142a12016-04-13 16:55:10 -0700134 BOOST_CHECK_EQUAL(listenerFaces.size(), 3);
135
Alexander Afanasyev3a2339a2020-05-27 23:05:06 -0400136 // check face persistency and channel association
Spencer Lee75142a12016-04-13 16:55:10 -0700137 for (const auto& face : listenerFaces) {
138 BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
Alexander Afanasyev3a2339a2020-05-27 23:05:06 -0400139 BOOST_CHECK_EQUAL(face->getChannel().lock(), listenerChannel);
Spencer Lee75142a12016-04-13 16:55:10 -0700140 }
141}
142
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500143BOOST_AUTO_TEST_SUITE(SocketFile)
144
145BOOST_AUTO_TEST_CASE(CreateAndRemove)
Spencer Lee75142a12016-04-13 16:55:10 -0700146{
Spencer Lee75142a12016-04-13 16:55:10 -0700147 auto channel = makeChannel();
Davide Pesavento401d1a42024-12-19 21:10:22 -0500148 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::not_found);
Spencer Lee75142a12016-04-13 16:55:10 -0700149
150 channel->listen(nullptr, nullptr);
151 auto status = fs::symlink_status(socketPath);
Davide Pesavento401d1a42024-12-19 21:10:22 -0500152 BOOST_TEST(status.type() == fs::file_type::socket);
153 BOOST_TEST(status.permissions() ==
154 (fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read |
155 fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write));
Spencer Lee75142a12016-04-13 16:55:10 -0700156
157 channel.reset();
Davide Pesavento401d1a42024-12-19 21:10:22 -0500158 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::not_found);
Spencer Lee75142a12016-04-13 16:55:10 -0700159}
160
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500161BOOST_AUTO_TEST_CASE(InUse)
Spencer Lee75142a12016-04-13 16:55:10 -0700162{
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500163 auto channel = makeChannel();
Davide Pesavento4c957712024-01-01 15:40:06 -0500164 fs::create_directories(socketPath.parent_path());
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500165
Spencer Lee75142a12016-04-13 16:55:10 -0700166 local::stream_protocol::acceptor acceptor(g_io, listenerEp);
Davide Pesavento401d1a42024-12-19 21:10:22 -0500167 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::socket);
Spencer Lee75142a12016-04-13 16:55:10 -0700168
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500169 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500170 return e.code() == std::errc::address_in_use &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500171 e.path1() == socketPath &&
172 std::string_view(e.what()).find("UnixStreamChannel::listen") != std::string_view::npos;
173 });
174}
Davide Pesavento4c957712024-01-01 15:40:06 -0500175
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500176BOOST_AUTO_TEST_CASE(Stale)
177{
178 auto channel = makeChannel();
179 fs::create_directories(socketPath.parent_path());
180
181 local::stream_protocol::acceptor acceptor(g_io, listenerEp);
Davide Pesavento4c957712024-01-01 15:40:06 -0500182 acceptor.close();
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500183 // the socket file is not removed when the acceptor is closed
Davide Pesavento401d1a42024-12-19 21:10:22 -0500184 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::socket);
Davide Pesavento4c957712024-01-01 15:40:06 -0500185
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500186 // drop write permission from the parent directory
Davide Pesavento401d1a42024-12-19 21:10:22 -0500187 fs::permissions(socketPath.parent_path(), fs::perms::owner_all & ~fs::perms::owner_write);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500188 // removal of the "stale" socket fails due to insufficient permissions
189 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500190 return e.code() == std::errc::permission_denied &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500191 e.path1() == socketPath &&
192 std::string_view(e.what()).find("remove") != std::string_view::npos;
193 });
Davide Pesavento401d1a42024-12-19 21:10:22 -0500194 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::socket);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500195
196 // restore all permissions
Davide Pesavento401d1a42024-12-19 21:10:22 -0500197 fs::permissions(socketPath.parent_path(), fs::perms::owner_all);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500198 // the socket file should be considered "stale" and overwritten
199 channel->listen(nullptr, nullptr);
Davide Pesavento401d1a42024-12-19 21:10:22 -0500200 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::socket);
Spencer Lee75142a12016-04-13 16:55:10 -0700201}
202
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500203BOOST_AUTO_TEST_CASE(NotASocket)
Spencer Lee75142a12016-04-13 16:55:10 -0700204{
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500205 auto channel = makeChannel();
Spencer Lee75142a12016-04-13 16:55:10 -0700206
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500207 fs::create_directories(socketPath);
Davide Pesavento401d1a42024-12-19 21:10:22 -0500208 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::directory);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500209 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500210 return e.code() == std::errc::not_a_socket &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500211 e.path1() == socketPath &&
212 std::string_view(e.what()).find("UnixStreamChannel::listen") != std::string_view::npos;
213 });
214
215 fs::remove(socketPath);
Davide Pesavento401d1a42024-12-19 21:10:22 -0500216 std::ofstream f(socketPath);
Spencer Lee75142a12016-04-13 16:55:10 -0700217 f.close();
Davide Pesavento401d1a42024-12-19 21:10:22 -0500218 BOOST_TEST(fs::symlink_status(socketPath).type() == fs::file_type::regular);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500219 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500220 return e.code() == std::errc::not_a_socket &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500221 e.path1() == socketPath &&
222 std::string_view(e.what()).find("UnixStreamChannel::listen") != std::string_view::npos;
223 });
Spencer Lee75142a12016-04-13 16:55:10 -0700224}
Junxiao Shicde37ad2015-12-24 01:02:05 -0700225
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500226BOOST_AUTO_TEST_CASE(ParentConflict)
227{
228 auto channel = makeChannel();
229 fs::create_directories(testDir);
230
231 auto parent = socketPath.parent_path();
Davide Pesavento401d1a42024-12-19 21:10:22 -0500232 std::ofstream f(parent);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500233 f.close();
Davide Pesavento401d1a42024-12-19 21:10:22 -0500234 BOOST_TEST(fs::symlink_status(parent).type() == fs::file_type::regular);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500235 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500236 return (e.code() == std::errc::not_a_directory || // libstdc++
237 e.code() == std::errc::file_exists) && // libc++
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500238 e.path1() == parent &&
Davide Pesavento401d1a42024-12-19 21:10:22 -0500239 std::string_view(e.what()).find("create") != std::string_view::npos;
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500240 });
241}
242
243BOOST_AUTO_TEST_CASE(PermissionDenied)
244{
245 auto channel = makeChannel();
246 fs::create_directories(testDir);
247
Davide Pesavento401d1a42024-12-19 21:10:22 -0500248 fs::permissions(testDir, fs::perms::none);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500249 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500250 return e.code() == std::errc::permission_denied &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500251 e.path1() == socketPath.parent_path() &&
Davide Pesavento401d1a42024-12-19 21:10:22 -0500252 std::string_view(e.what()).find("create") != std::string_view::npos;
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500253 });
254
Davide Pesavento401d1a42024-12-19 21:10:22 -0500255 fs::permissions(testDir, fs::perms::owner_read | fs::perms::owner_exec);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500256 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500257 return e.code() == std::errc::permission_denied &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500258 e.path1() == socketPath.parent_path() &&
Davide Pesavento401d1a42024-12-19 21:10:22 -0500259 std::string_view(e.what()).find("create") != std::string_view::npos;
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500260 });
261
Davide Pesavento401d1a42024-12-19 21:10:22 -0500262 fs::permissions(testDir, fs::perms::owner_all);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500263 fs::create_directories(socketPath.parent_path());
264
Davide Pesavento401d1a42024-12-19 21:10:22 -0500265 fs::permissions(socketPath.parent_path(), fs::perms::none);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500266 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500267 std::string_view what(e.what());
268 return e.code() == std::errc::permission_denied &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500269 e.path1() == socketPath &&
Davide Pesavento401d1a42024-12-19 21:10:22 -0500270 (what.find("symlink_status") != std::string_view::npos || // libstdc++
271 what.find("posix_stat") != std::string_view::npos); // libc++
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500272 });
273
Davide Pesavento401d1a42024-12-19 21:10:22 -0500274 fs::permissions(socketPath.parent_path(), fs::perms::owner_read | fs::perms::owner_exec);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500275 BOOST_CHECK_EXCEPTION(channel->listen(nullptr, nullptr), fs::filesystem_error, [&] (const auto& e) {
Davide Pesavento401d1a42024-12-19 21:10:22 -0500276 return e.code() == std::errc::permission_denied &&
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500277 e.path1() == socketPath &&
278 std::string_view(e.what()).find("bind") != std::string_view::npos;
279 });
280
Davide Pesavento401d1a42024-12-19 21:10:22 -0500281 fs::permissions(socketPath.parent_path(), fs::perms::owner_all);
Davide Pesavento4b1921f2024-01-12 20:25:45 -0500282}
283
284BOOST_AUTO_TEST_SUITE_END() // SocketFile
285
Junxiao Shicde37ad2015-12-24 01:02:05 -0700286BOOST_AUTO_TEST_SUITE_END() // TestUnixStreamChannel
287BOOST_AUTO_TEST_SUITE_END() // Face
288
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400289} // namespace nfd::tests