-2262. [sec]* fdupont
+2171. [sec]* fdupont
Change the umask to no group write and no other access
at the entry of Kea server/agent binaries.
CVE:2025-32803
{
"comment": "control socket for DHCPv4 server",
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
}
},
{
"comment": "socket to DHCPv4 server",
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Location of the DHCPv6 command channel socket.
"dhcp6":
{
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Location of the D2 command channel socket.
"d2":
{
"socket-type": "unix",
- "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-name": "kea-ddns-ctrl-socket",
"user-context": { "in-use": false }
}
},
// Location of the UNIX domain socket file the DHCP-DDNS server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-name": "kea-ddns-ctrl-socket",
// Control socket type used by the Kea DHCP-DDNS server.
// The 'unix' socket is currently the only supported type.
// Location of the UNIX domain socket file the DHCP-DDNS server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-name": "kea-ddns-ctrl-socket",
// Control socket type used by the Kea DHCP-DDNS server.
// The 'unix' socket is currently the only supported type.
{
"comment": "Control channel",
"socket-type": "unix",
- "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ "socket-name": "kea-ddns-ctrl-socket"
},
"forward-ddns":
"control-socket":
{
"socket-type": "unix",
- "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ "socket-name": "kea-ddns-ctrl-socket"
},
// ----------------- Hooks Libraries -----------------
// "control-socket":
// {
// "socket-type": "unix",
-// "socket-name": "/tmp/kea-ddns-ctrl-socket"
+// "socket-name": "kea-ddns-ctrl-socket"
// },
// ----------------- Forward DDNS ------------------
// Guide for list of supported commands.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Addresses will be assigned with a lifetime of 4000 seconds.
// Location of the UNIX domain socket file the DHCPv4 server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-name": "kea4-ctrl-socket",
// Control socket type used by the Kea DHCPv4 server. The 'unix'
// socket is currently the only supported type.
// Location of the UNIX domain socket file the DHCPv4 server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-name": "kea4-ctrl-socket",
// Control socket type used by the Kea DHCPv4 server. The 'unix'
// socket is currently the only supported type.
// In control socket (more for the agent)
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-name": "kea4-ctrl-socket",
"user-context": { "comment": "Indirect comment" }
},
// details.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Hooks libraries that enable configuration backend are loaded.
"name": "kea-dhcp4",
"output_options": [
{
- "output": "/tmp/kea-dhcp4.log"
+ "output": "kea-dhcp4.log"
}
],
"severity": "DEBUG",
// The Control Agent is used only to handle user commands.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Multi-threading parameters.
// The Control Agent is used only to handle user commands.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Multi-threading parameters.
// Guide for list of supported commands.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Addresses will be assigned with preferred and valid lifetimes
// Location of the UNIX domain socket file the DHCPv6 server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea6-ctrl-socket",
+ "socket-name": "kea6-ctrl-socket",
// Control socket type used by the Kea DHCPv6 server. The 'unix'
// socket is currently the only supported type.
// Location of the UNIX domain socket file the DHCPv6 server uses
// to receive control commands from the Kea Control Agent or the
// local server administrator.
- "socket-name": "/tmp/kea6-ctrl-socket",
+ "socket-name": "kea6-ctrl-socket",
// Control socket type used by the Kea DHCPv6 server. The 'unix'
// socket is currently the only supported type.
// In control socket (more for the agent)
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket",
+ "socket-name": "kea6-ctrl-socket",
"user-context": { "comment": "Indirect comment" }
},
// details.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Hooks libraries that enable configuration backend are loaded.
"name": "kea-dhcp6",
"output_options": [
{
- "output": "/tmp/kea-dhcp6.log"
+ "output": "kea-dhcp6.log"
}
],
"severity": "DEBUG",
// API between the HA peers.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Use Memfile lease database backend to store leases in a CSV file.
// API between the HA peers.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Use Memfile lease database backend to store leases in a CSV file.
{
"comment": "using unix/local socket",
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
}
}
},
"control-socket":
{
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Comment is optional. You can put some notes here.
"control-socket":
{
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Comment is optional. You can put some notes here.
:ref:`d2-ctrl-channel` to learn how the socket configuration is
specified for the DHCPv4, DHCPv6, and D2 services.
+.. note::
+
+ As of Kea 2.4.2, control sockets may only reside in the directory
+ determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This
+ path may be overridden at startup by setting the environment variable
+ ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than
+ this value is used in ``socket-name``, Kea will emit an error and refuse to
+ start or, if already running, log an unrecoverable error. For ease of use in
+ simply omit the path component from ``socket-name``.
+
User contexts can store arbitrary data as long as they are in valid JSON
syntax and their top-level element is a map (i.e. the data must be
enclosed in curly brackets). Some hook libraries may expect specific
different operating systems, between 91 and 107 characters. Typical
values are 107 on Linux and 103 on FreeBSD.
+.. note::
+
+ As of Kea 2.4.2, control sockets may only reside in the directory
+ determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This
+ path may be overridden at startup by setting the environment variable
+ ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than
+ this value is used in ``socket-name``, Kea will emit an error and refuse to
+ start or, if already running, log an unrecoverable error. For ease of use in
+ simply omit the path component from ``socket-name``.
+
Communication over the control channel is conducted using JSON structures.
See the `Control Channel section in the Kea Developer's
Guide <https://reports.kea.isc.org/dev_guide/d2/d96/ctrlSocket.html>`__
different operating systems, between 91 and 107 characters. Typical
values are 107 on Linux and 103 on FreeBSD.
+.. note::
+
+ As of Kea 2.4.2, control sockets may only reside in the directory
+ determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This
+ path may be overridden at startup by setting the environment variable
+ ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than
+ this value is used in ``socket-name``, Kea will emit an error and refuse to
+ start or, if already running, log an unrecoverable error. For ease of use in
+ simply omit the path component from ``socket-name``.
+
Communication over the control channel is conducted using JSON
structures. See the
`Control Channel section in the Kea Developer's Guide
different operating systems, between 91 and 107 characters. Typical
values are 107 on Linux and 103 on FreeBSD.
+.. note::
+
+ As of Kea 2.4.2, control sockets may only reside in the directory
+ determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This
+ path may be overridden at startup by setting the environment variable
+ ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than
+ this value is used in ``socket-name``, Kea will emit an error and refuse to
+ start or, if already running, log an unrecoverable error. For ease of use in
+ simply omit the path component from ``socket-name``.
+
Communication over the control channel is conducted using JSON
structures. See the
`Control Channel section in the Kea Developer's Guide
As of Kea 2.4.2, hook libraries may only be loaded from the default installation
directory determined during compilation and shown in the config report as
-"Hooks directory". This value may be overridden at startup by setting the
+"Hooks directory". This value may be overridden at startup by setting the
environment variable ``KEA_HOOKS_PATH`` to the desired path. If a path other
than this value is used in a ``library`` element Kea will emit an error and refuse
to load the library. For ease of use ``library`` elements may simply omit path
ca->set("hooks-libraries", hooks_config_.toElement());
// Set control-sockets
ElementPtr control_sockets = Element::createMap();
- for (auto si = ctrl_sockets_.cbegin(); si != ctrl_sockets_.cend(); ++si) {
- ConstElementPtr socket = UserContext::toElement(si->second);
- control_sockets->set(si->first, socket);
+ for (auto const& si : ctrl_sockets_) {
+ // Remove validated_path.
+ auto mutable_socket_info = boost::const_pointer_cast<Element>(
+ UserContext::toElement(si.second));
+ mutable_socket_info->remove("validated-socket-name");
+ control_sockets->set(si.first, mutable_socket_info);
}
+
ca->set("control-sockets", control_sockets);
// Set Control-agent
ElementPtr result = Element::createMap();
" for the server type " << service);
}
- // If the configuration does its job properly the socket-name must be
- // specified and must be a string value.
- std::string socket_name = socket_info->get("socket-name")->stringValue();
+ // If the configuration does its job properly the validated-socket-name
+ // should be present.
+ if (!socket_info->get("validated-socket-name")) {
+ isc_throw(Unexpected, "validated-socket-name missing from "
+ << " socket_info: " << socket_info->str()
+ << " for the server type " << service);
+ }
+
+ auto socket_name = socket_info->get("validated-socket-name")->stringValue();
// Forward command and receive reply.
IOServicePtr io_service(new IOService());;
#include <agent/simple_parser.h>
#include <cc/data.h>
#include <cc/dhcp_config_error.h>
+#include <config/unix_command_config.h>
#include <hooks/hooks_manager.h>
#include <hooks/hooks_parser.h>
#include <http/basic_auth_config.h>
-#include <boost/foreach.hpp>
using namespace isc::data;
+using namespace isc::asiolink;
+using namespace isc::config;
namespace isc {
namespace agent {
// Control sockets are third.
ConstElementPtr ctrl_sockets = config->get("control-sockets");
if (ctrl_sockets) {
- auto sockets_map = ctrl_sockets->mapValue();
- for (auto cs = sockets_map.cbegin(); cs != sockets_map.cend(); ++cs) {
- ctx->setControlSocketInfo(cs->second, cs->first);
+ auto const& sockets_map = ctrl_sockets->mapValue();
+ for (auto const& cs : sockets_map) {
+ // Add a validated socket name so we can suppress it in
+ // toElement() but don't have to revalidate it every time we
+ // want to use it.
+ auto mutable_socket_info = boost::const_pointer_cast<Element>(cs.second);
+ std::string socket_name = mutable_socket_info->get("socket-name")->stringValue();
+ auto validated_name = UnixCommandConfig::validatePath(socket_name);
+ mutable_socket_info->set("validated-socket-name", Element::create(validated_name));
+ ctx->setControlSocketInfo(mutable_socket_info, cs.first);
}
}
#include <config.h>
#include <agent/ca_cfg_mgr.h>
#include <agent/parser_context.h>
+#include <config/unix_command_config.h>
#include <exceptions/exceptions.h>
#include <process/testutils/d_test_stubs.h>
#include <process/d_cfg_mgr.h>
#include <http/basic_auth_config.h>
#include <hooks/hooks_parser.h>
+#include <util/filesystem.h>
#include <agent/tests/test_callout_libraries.h>
#include <agent/tests/test_data_files_config.h>
#include <boost/pointer_cast.hpp>
using namespace isc::hooks;
using namespace isc::http;
using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
namespace {
auth->setRealm("foobar");
auth->setDirectory("/tmp");
- auth->add("", "/tmp/foo", "", "/tmp/bar");
- auth->add("", "/tmp/test", "", "/tmp/pwd");
+ auth->add("", "foo", "", "bar");
+ auth->add("", "test", "", "pwd");
const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig();
ASSERT_TRUE(stored_auth);
" \"http-port\": 8001,\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" }\n"
" }\n"
"}",
" \"http-port\": 8001,\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" },\n"
" \"dhcp6\": {\n"
- " \"socket-name\": \"/tmp/socket-v6\"\n"
+ " \"socket-name\": \"socket-v6\"\n"
" },\n"
" \"d2\": {\n"
- " \"socket-name\": \"/tmp/socket-d2\"\n"
+ " \"socket-name\": \"socket-d2\"\n"
" }\n"
" }\n"
"}",
" \"http-port\": 8001,\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" }\n"
" },\n"
" \"hooks-libraries\": ["
" \"http-port\": 8001,\n"
" \"control-sockets\": {\n"
" \"d2\": {\n"
- " \"socket-name\": \"/tmp/socket-d2\"\n"
+ " \"socket-name\": \"socket-d2\"\n"
" }\n"
" }\n"
"}",
" \"http-port\": 8001,\n"
" \"control-sockets\": {\n"
" \"dhcp6\": {\n"
- " \"socket-name\": \"/tmp/socket-v6\"\n"
+ " \"socket-name\": \"socket-v6\"\n"
" }\n"
" }\n"
"}",
" },\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" }\n"
" }\n"
"}",
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
" \"comment\": \"dhcp4 socket\",\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" },\n"
" \"dhcp6\": {\n"
- " \"socket-name\": \"/tmp/socket-v6\",\n"
+ " \"socket-name\": \"socket-v6\",\n"
" \"user-context\": { \"version\": 1 }\n"
" }\n"
" }\n"
" },\n"
" \"control-sockets\": {\n"
" \"dhcp4\": {\n"
- " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " \"socket-name\": \"socket-v4\"\n"
" }\n"
" }\n"
"}"
public:
virtual void SetUp() {
resetHooksPath();
+ setSocketTestPath();
}
virtual void TearDown() {
resetHooksPath();
+ resetSocketPath();
}
/// @brief Sets the Hooks path from which hooks can be loaded.
HooksLibrariesParser::getHooksPath(true);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Tries to load input text as a configuration
///
/// @param config text containing input configuration
return (txt);
}
+ /// @brief Make expected contents of socket info with socket path added.
+ ///
+ /// @param name name of the socket
+ /// @return expected string
+ std::string makeSocketStr(const std::string& name) {
+ std::ostringstream os;
+ os << "{ \"socket-name\": \""
+ << name << "\", \"socket-type\": \"unix\","
+ << " \"validated-socket-name\": \""
+ << UnixCommandConfig::getSocketPath() << "/" << name
+ << "\" }";
+ return (os.str());
+ }
+
/// Configuration Manager (used in tests)
NakedAgentCfgMgr cfg_mgr_;
};
// This test checks if a config with only HTTP parameters is parsed properly.
TEST_F(AgentParserTest, configParseHttpOnly) {
- setHooksTestPath();
-
configParse(AGENT_CONFIGS[1], 0);
CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
ASSERT_TRUE(ctx);
ConstElementPtr socket = ctx->getControlSocketInfo("dhcp4");
ASSERT_TRUE(socket);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
- socket->str());
+ EXPECT_EQ(makeSocketStr("socket-v4"), socket->str());
EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6"));
EXPECT_FALSE(ctx->getControlSocketInfo("d2"));
}
ASSERT_TRUE(ctx);
ConstElementPtr socket = ctx->getControlSocketInfo("d2");
ASSERT_TRUE(socket);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
- socket->str());
-
+ EXPECT_EQ(makeSocketStr("socket-d2"), socket->str());
EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4"));
EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6"));
}
ASSERT_TRUE(ctx);
ConstElementPtr socket = ctx->getControlSocketInfo("dhcp6");
ASSERT_TRUE(socket);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
- socket->str());
+ EXPECT_EQ(makeSocketStr("socket-v6"), socket->str());
EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4"));
EXPECT_FALSE(ctx->getControlSocketInfo("d2"));
}
ConstElementPtr socket4 = ctx->getControlSocketInfo("dhcp4");
ConstElementPtr socket6 = ctx->getControlSocketInfo("dhcp6");
ASSERT_TRUE(socket2);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
- socket2->str());
+ EXPECT_EQ(makeSocketStr("socket-d2"), socket2->str());
ASSERT_TRUE(socket4);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
- socket4->str());
+ EXPECT_EQ(makeSocketStr("socket-v4"), socket4->str());
ASSERT_TRUE(socket6);
- EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
- socket6->str());
+ EXPECT_EQ(makeSocketStr("socket-v6"), socket6->str());
}
// This test checks that the config file with hook library specified can be
#include <asiolink/testutils/test_server_unix_socket.h>
#include <cc/command_interpreter.h>
#include <cc/data.h>
+#include <config/unix_command_config.h>
#include <process/testutils/d_test_stubs.h>
+#include <util/filesystem.h>
#include <boost/pointer_cast.hpp>
#include <gtest/gtest.h>
#include <testutils/sandbox.h>
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
namespace {
: DControllerTest(CtrlAgentController::instance),
mgr_(CtrlAgentCommandMgr::instance()) {
mgr_.deregisterAll();
+ setSocketTestPath();
removeUnixSocketFile();
initProcess();
}
virtual ~CtrlAgentCommandMgrTest() {
mgr_.deregisterAll();
removeUnixSocketFile();
+ resetSocketPath();
}
/// @brief Verifies received answer
}
/// @brief Returns socket file path.
- ///
- /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
- /// socket file is created in the location pointed to by this variable.
- /// Otherwise, it is created in the build directory.
std::string unixSocketFilePath() {
- std::string socket_path;
- const char* env = getenv("KEA_SOCKET_TEST_DIR");
- if (env) {
- socket_path = std::string(env) + "/test-socket";
- } else {
- socket_path = sandbox.join("test-socket");
- }
- return (socket_path);
+ return (UnixCommandConfig::getSocketPath() + "/test-socket");
+ }
+
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
}
/// @brief Removes unix socket descriptor.
ASSERT_TRUE(ctx);
ElementPtr control_socket = Element::createMap();
- control_socket->set("socket-name",
+ control_socket->set("validated-socket-name",
Element::create(unixSocketFilePath()));
ctx->setControlSocketInfo(control_socket, service);
}
#include <agent/ca_command_mgr.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
+#include <config/unix_command_config.h>
#include <process/testutils/d_test_stubs.h>
+#include <util/filesystem.h>
+#include <testutils/gtest_utils.h>
+
#include <boost/pointer_cast.hpp>
+
#include <sstream>
+
#include <unistd.h>
using namespace isc::asiolink::test;
using namespace isc::data;
using namespace isc::http;
using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
using namespace boost::posix_time;
using namespace std;
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/first/dhcp4/socket\""
+ " \"socket-name\": \"first_socket4\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/first/dhcp6/socket\""
+ " \"socket-name\": \"first_socket6\""
" }"
" }"
"}";
/// @brief Constructor.
CtrlAgentControllerTest()
: DControllerTest(CtrlAgentController::instance) {
+ setSocketTestPath();
+ }
+
+ /// @brief Destructor.
+ virtual ~CtrlAgentControllerTest() {
+ resetSocketPath();
}
/// @brief Returns pointer to CtrlAgentProcess instance.
return (p);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Tests that socket info structure contains 'unix' socket-type
/// value and the expected socket-name.
///
ASSERT_TRUE(sock_info->contains("socket-type"));
EXPECT_EQ("unix", sock_info->get("socket-type")->stringValue());
ASSERT_TRUE(sock_info->contains("socket-name"));
- EXPECT_EQ(exp_socket_name,
- sock_info->get("socket-name")->stringValue());
+ EXPECT_EQ(exp_socket_name, sock_info->get("socket-name")->stringValue());
}
/// @brief Compares the status in the given parse result to a given value.
EXPECT_FALSE(checkProcess());
}
-
// Tests basic command line processing.
// Verifies that:
// 1. Standard command line options are supported.
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp4/socket\""
+ " \"socket-name\": \"second_socket4\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp6/socket\""
+ " \"socket-name\": \"second_socket6\""
" }"
" }"
"}";
EXPECT_EQ(8080, ctx->getHttpPort());
// The forwarding configuration should have been updated too.
- testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
- testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
+ testUnixSocketInfo("dhcp4", "second_socket4");
+ testUnixSocketInfo("dhcp6", "second_socket6");
// After the shutdown the HTTP listener no longer exists.
CtrlAgentProcessPtr process = getCtrlAgentProcess();
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp4/socket\""
+ " \"socket-name\": \"second_socket4\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp6/socket\""
+ " \"socket-name\": \"second_socket6\""
" }"
" }"
"}";
EXPECT_EQ(8081, ctx->getHttpPort());
// Same for forwarding.
- testUnixSocketInfo("dhcp4", "/first/dhcp4/socket");
- testUnixSocketInfo("dhcp6", "/first/dhcp6/socket");
+ testUnixSocketInfo("dhcp4", "first_socket4");
+ testUnixSocketInfo("dhcp6", "first_socket6");
// After the shutdown the HTTP listener no longer exists.
CtrlAgentProcessPtr process = getCtrlAgentProcess();
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp4/socket\""
+ " \"socket-name\": \"second_socket4\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/second/dhcp6/socket\""
+ " \"socket-name\": \"second_socket6\""
" }"
" }"
"}";
EXPECT_EQ(8081, ctx->getHttpPort());
// The forwarding configuration should have been updated.
- testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
- testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
+ testUnixSocketInfo("dhcp4", "second_socket4");
+ testUnixSocketInfo("dhcp6", "second_socket6");
CtrlAgentProcessPtr process = getCtrlAgentProcess();
ASSERT_TRUE(process);
answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload",
params, cmd);
-
// Verify the reload was successful.
string expected = "{ \"result\": 0, \"text\": "
"\"Configuration applied successfully.\" }";
ctrl->deregisterCommands();
}
-
TEST_F(CtrlAgentControllerTest, shutdownExitValue) {
ASSERT_NO_THROW(initProcess());
EXPECT_TRUE(checkProcess());
#include <cc/data.h>
#include <cc/command_interpreter.h>
+#include <config/unix_command_config.h>
#include <testutils/user_context_utils.h>
#include <process/testutils/d_test_stubs.h>
#include <agent/ca_cfg_mgr.h>
#include <agent/parser_context.h>
#include <hooks/hooks_parser.h>
+#include <util/filesystem.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace isc::config;
using namespace isc::data;
using namespace isc::process;
-using namespace isc::test;
using namespace isc::hooks;
+using namespace isc::test;
+using namespace isc::config;
+using namespace isc::util;
namespace {
srv_.reset(new NakedAgentCfgMgr());
// Create fresh context.
resetConfiguration();
+ setSocketTestPath();
}
~CtrlAgentGetCfgTest() {
resetConfiguration();
resetHooksPath();
+ resetSocketPath();
}
/// @brief Sets the Hooks path from which hooks can be loaded.
HooksLibrariesParser::getHooksPath(true);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea4-ctrl-socket\""
+ " \"socket-name\": \"kea4-ctrl-socket\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea6-ctrl-socket\""
+ " \"socket-name\": \"kea6-ctrl-socket\""
" },"
" \"d2\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\""
+ " \"socket-name\": \"kea-ddns-ctrl-socket\""
" }"
" },"
" \"hooks-libraries\": ["
" \"control-sockets\": {"
" \"dhcp4\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea4-ctrl-socket\""
+ " \"socket-name\": \"kea4-ctrl-socket\""
" },"
" \"dhcp6\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea6-ctrl-socket\""
+ " \"socket-name\": \"kea6-ctrl-socket\""
" },"
" \"d2\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\""
+ " \"socket-name\": \"kea-ddns-ctrl-socket\""
" }"
" },"
" \"hooks-libraries\": ["
"Control-agent": {
"control-sockets": {
"d2": {
- "socket-name": "/tmp/kea-dhcp-ddns-ctrl.sock",
+ "socket-name": "kea-dhcp-ddns-ctrl.sock",
"socket-type": "unix",
},
"dhcp4": {
- "socket-name": "/tmp/kea-dhcp4-ctrl.sock",
+ "socket-name": "kea-dhcp4-ctrl.sock",
"socket-type": "unix",
},
"dhcp6": {
- "socket-name": "/tmp/kea-dhcp6-ctrl.sock",
+ "socket-name": "kea-dhcp6-ctrl.sock",
"socket-type": "unix",
},
},
},
"control-sockets": {
"d2": {
- "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-name": "kea-ddns-ctrl-socket",
"socket-type": "unix",
"user-context": {
"in-use": false
}
},
"dhcp4": {
- "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-name": "kea4-ctrl-socket",
"socket-type": "unix",
"user-context": {
"comment": "socket to DHCPv4 server"
}
},
"dhcp6": {
- "socket-name": "/tmp/kea6-ctrl-socket",
+ "socket-name": "kea6-ctrl-socket",
"socket-type": "unix"
}
},
#include <config.h>
+#include <config/unix_command_config.h>
#include <d2/parser_context.h>
#include <d2/tests/parser_unittest.h>
#include <d2/tests/test_callout_libraries.h>
#include <process/testutils/d_test_stubs.h>
#include <test_data_files_config.h>
#include <util/encode/base64.h>
+#include <util/filesystem.h>
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
using namespace isc::d2;
using namespace isc::hooks;
using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
namespace {
/// @brief Constructor
D2CfgMgrTest():cfg_mgr_(new D2CfgMgr()), d2_params_() {
resetHooksPath();
+ resetSocketPath();
}
/// @brief Destructor
~D2CfgMgrTest() {
resetHooksPath();
+ resetSocketPath();
}
/// @brief Sets the Hooks path from which hooks can be loaded.
HooksLibrariesParser::getHooksPath(true);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Configuration manager instance.
D2CfgMgrPtr cfg_mgr_;
/// event.
TEST_F(D2CfgMgrTest, fullConfig) {
setHooksTestPath();
+ setSocketTestPath();
// Create a configuration with all of application level parameters, plus
// both the forward and reverse ddns managers. Both managers have two
"\"ncr-format\": \"JSON\", "
"\"control-socket\" : {"
" \"socket-type\" : \"unix\" ,"
- " \"socket-name\" : \"/tmp/d2-ctrl-channel\" "
+ " \"socket-name\" : \"d2-ctrl-channel\" "
"},"
"\"hooks-libraries\": ["
"{"
ASSERT_TRUE(ctrl_sock->get("socket-type"));
EXPECT_EQ("\"unix\"", ctrl_sock->get("socket-type")->str());
ASSERT_TRUE(ctrl_sock->get("socket-name"));
- EXPECT_EQ("\"/tmp/d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str());
+ EXPECT_EQ("\"d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str());
// Verify that the hooks libraries can be retrieved.
const HookLibsCollection libs = context->getHooksConfig().get();
/// @brief Tests comments.
TEST_F(D2CfgMgrTest, comments) {
+ setSocketTestPath();
std::string config = "{ "
"\"comment\": \"D2 config\" , "
"\"ip-address\" : \"192.168.1.33\" , "
"\"control-socket\": {"
" \"comment\": \"Control channel\" , "
" \"socket-type\": \"unix\" ,"
- " \"socket-name\": \"/tmp/d2-ctrl-channel\" "
+ " \"socket-name\": \"d2-ctrl-channel\" "
"},"
"\"tsig-keys\": ["
"{"
#include <asiolink/io_service.h>
#include <cc/command_interpreter.h>
#include <config/command_mgr.h>
+#include <config/unix_command_config.h>
#include <config/timeouts.h>
#include <testutils/io_utils.h>
#include <testutils/unix_control_client.h>
#include <d2/d2_controller.h>
#include <d2/d2_process.h>
#include <d2/parser_context.h>
+#include <util/filesystem.h>
#include <gtest/gtest.h>
#include <testutils/sandbox.h>
#include <boost/pointer_cast.hpp>
using namespace isc::data;
using namespace isc::dhcp::test;
using namespace isc::process;
+using namespace isc::util;
using namespace boost::asio;
namespace ph = std::placeholders;
/// Sets socket path to its default value.
CtrlChannelD2Test()
: server_(NakedD2Controller::instance()) {
- const char* env = getenv("KEA_SOCKET_TEST_DIR");
- if (env) {
- socket_path_ = string(env) + "/d2.sock";
- } else {
- socket_path_ = sandbox.join("d2.sock");
- }
+ setSocketTestPath();
::remove(socket_path_.c_str());
}
// Reset command manager.
CommandMgr::instance().deregisterAll();
CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND);
+ resetSocketPath();
}
/// @brief Returns pointer to the server's IO service.
return (server_ ? d2Controller()->getIOService() : IOServicePtr());
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ socket_path_ = path + "/d2.sock";
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Runs parser in DHCPDDNS mode
///
/// @param config input configuration
#include <cc/command_interpreter.h>
#include <cc/data.h>
+#include <config/unix_command_config.h>
#include <d2/parser_context.h>
#include <d2srv/d2_cfg_mgr.h>
#include <d2srv/d2_config.h>
#include <hooks/hooks_parser.h>
#include <process/testutils/d_test_stubs.h>
#include <testutils/user_context_utils.h>
+#include <util/filesystem.h>
#include <gtest/gtest.h>
#include <iostream>
using namespace isc::d2;
using namespace isc::data;
using namespace isc::process;
-using namespace isc::hooks;
using namespace isc::test;
+using namespace isc::hooks;
+using namespace isc::util;
namespace {
D2GetConfigTest()
: rcode_(-1) {
resetHooksPath();
+ resetSocketPath();
srv_.reset(new D2CfgMgr());
// Enforce not verbose mode.
Daemon::setVerbose(false);
~D2GetConfigTest() {
resetConfiguration();
resetHooksPath();
+ resetSocketPath();
}
/// @brief Sets the Hooks path from which hooks can be loaded.
HooksLibrariesParser::getHooksPath(true);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
/// Test a configuration
TEST_F(D2GetConfigTest, sample1) {
setHooksTestPath();
+ setSocketTestPath();
// get the sample1 configuration
std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json";
{
"DhcpDdns": {
"control-socket": {
- "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-name": "kea-ddns-ctrl-socket",
"socket-type": "unix"
},
"dns-server-timeout": 1000,
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::hooks;
+using namespace isc::test;
+using namespace isc::util;
using namespace std;
namespace {
" ],"
" \"control-socket\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea4-ctrl-socket\","
+ " \"socket-name\": \"kea4-ctrl-socket\","
" \"user-context\": { \"comment\": \"Indirect comment\" }"
" },"
" \"shared-networks\": [ {"
virtual void SetUp() {
std::vector<std::string> libraries = HooksManager::getLibraryNames();
ASSERT_TRUE(libraries.empty());
- resetHooksPath();
}
public:
// Create fresh context.
resetConfiguration();
resetHooksPath();
+ Dhcpv4SrvTest::resetSocketPath();
}
public:
// ... and delete the hooks library marker files if present
static_cast<void>(remove(LOAD_MARKER_FILE));
static_cast<void>(remove(UNLOAD_MARKER_FILE));
+
+ resetHooksPath();
+ Dhcpv4SrvTest::resetSocketPath();
};
/// @brief Returns an interface configuration used by the most of the
// This test checks comments. Please keep it last.
TEST_F(Dhcp4ParserTest, comments) {
+ Dhcpv4SrvTest::setSocketTestPath();
string config = PARSER_CONFIGS[5];
extractConfig(config);
ASSERT_TRUE(socket->get("socket-type"));
EXPECT_EQ("\"unix\"", socket->get("socket-type")->str());
ASSERT_TRUE(socket->get("socket-name"));
- EXPECT_EQ("\"/tmp/kea4-ctrl-socket\"", socket->get("socket-name")->str());
+ EXPECT_EQ("\"kea4-ctrl-socket\"", socket->get("socket-name")->str());
// Check control socket comment and user context.
ConstElementPtr ctx_socket = socket->get("user-context");
#include <asiolink/io_service.h>
#include <cc/command_interpreter.h>
#include <config/command_mgr.h>
+#include <config/unix_command_config.h>
#include <config/timeouts.h>
#include <dhcp/dhcp4.h>
#include <dhcp/libdhcp++.h>
/// Sets socket path to its default value.
CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") {
resetLogPath();
- const char* env = getenv("KEA_SOCKET_TEST_DIR");
- if (env) {
- socket_path_ = string(env) + "/kea4.sock";
- } else {
- socket_path_ = sandbox.join("kea4.sock");
- }
+ setSocketTestPath();
reset();
IfaceMgr::instance().setTestMode(false);
IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
/// @brief Destructor
~CtrlChannelDhcpv4SrvTest() {
resetLogPath();
+ Dhcpv4SrvTest::resetSocketPath();
LeaseMgrFactory::destroy();
StatsMgr::instance().removeAll();
LogConfigParser::getLogPath(true);
}
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ Dhcpv4SrvTest::setSocketTestPath(explicit_path);
+ socket_path_ = UnixCommandConfig::getSocketPath() + "/kea4.sock";
+ }
+
/// @brief Returns pointer to the server's IO service.
///
/// @return Pointer to the server's IO service or null pointer if the server
void
Dhcpv4SrvTest::checkConfigFiles() {
+ setSocketTestPath();
DBInitializer dbi;
IfaceMgrTestConfig test_config(true);
string path = CFG_EXAMPLES;
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
+#include <config/unix_command_config.h>
#include <dhcp4/json_config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp/libdhcp++.h>
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::util;
+using namespace isc::config;
namespace isc {
namespace dhcp {
}
Dhcpv4SrvTest::~Dhcpv4SrvTest() {
+ resetSocketPath();
// Make sure that we revert to default value
CfgMgr::instance().clear();
MultiThreadingMgr::instance().apply(false, 0, 0);
}
+void
+Dhcpv4SrvTest::setSocketTestPath(const std::string explicit_path /* = "" */) {
+ UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+}
+
+void
+Dhcpv4SrvTest::resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+}
+
void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) {
OptionUint8ArrayPtr option_prl =
/// Removes existing configuration.
virtual ~Dhcpv4SrvTest();
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ static void setSocketTestPath(const std::string explicit_path = "");
+
+ /// @brief Resets the socket path to the default.
+ static void resetSocketPath();
+
/// @brief Add 'Parameter Request List' option to the packet.
///
/// This function adds PRL option comprising the following option codes:
" }\n"
" ],\n"
" \"control-socket\": {\n"
-" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n"
+" \"socket-name\": \"kea4-ctrl-socket\",\n"
" \"socket-type\": \"unix\",\n"
" \"user-context\": {\n"
" \"comment\": \"Indirect comment\"\n"
" }\n"
" ],\n"
" \"control-socket\": {\n"
-" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n"
+" \"socket-name\": \"kea4-ctrl-socket\",\n"
" \"socket-type\": \"unix\",\n"
" \"user-context\": {\n"
" \"comment\": \"Indirect comment\"\n"
srv_.reset(new ControlledDhcpv4Srv(0));
// Create fresh context.
resetConfiguration();
+ Dhcpv4SrvTest::setSocketTestPath();
}
~Dhcp4GetConfigTest() {
resetConfiguration();
+ Dhcpv4SrvTest::resetSocketPath();
};
/// @brief Parse and Execute configuration
" ],"
" \"control-socket\": {"
" \"socket-type\": \"unix\","
- " \"socket-name\": \"/tmp/kea6-ctrl-socket\","
+ " \"socket-name\": \"kea6-ctrl-socket\","
" \"user-context\": { \"comment\": \"Indirect comment\" }"
" },"
" \"shared-networks\": [ {"
// This test checks comments. Please keep it last.
TEST_F(Dhcp6ParserTest, comments) {
+ setSocketTestPath();
string config = PARSER_CONFIGS[9];
extractConfig(config);
ASSERT_TRUE(socket->get("socket-type"));
EXPECT_EQ("\"unix\"", socket->get("socket-type")->str());
ASSERT_TRUE(socket->get("socket-name"));
- EXPECT_EQ("\"/tmp/kea6-ctrl-socket\"", socket->get("socket-name")->str());
+ EXPECT_EQ("\"kea6-ctrl-socket\"", socket->get("socket-name")->str());
// Check control socket comment and user context.
ConstElementPtr ctx_socket = socket->get("user-context");
#include <asiolink/io_address.h>
#include <cc/command_interpreter.h>
#include <config/command_mgr.h>
+#include <config/unix_command_config.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
///
/// Sets socket path to its default value.
CtrlChannelDhcpv6SrvTest() : interfaces_("\"*\"") {
- const char* env = getenv("KEA_SOCKET_TEST_DIR");
- if (env) {
- socket_path_ = string(env) + "/kea6.sock";
- } else {
- socket_path_ = sandbox.join("/kea6.sock");
- }
reset();
IfaceMgr::instance().setTestMode(false);
IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
IfaceMgr::instancePtr().get(), ph::_1));
+ setSocketTestPath();
+ socket_path_ = UnixCommandConfig::getSocketPath() + "/kea6.sock";
}
/// @brief Destructor
IfaceMgr::instance().clearIfaces();
IfaceMgr::instance().closeSockets();
IfaceMgr::instance().detectIfaces();
- };
+ }
/// @brief Returns pointer to the server's IO service.
///
void
Dhcpv6SrvTest::checkConfigFiles() {
+ setSocketTestPath();
DBInitializer dbi;
IfaceMgrTestConfig test_config(true);
string path = CFG_EXAMPLES;
#include <config.h>
#include <gtest/gtest.h>
#include <cc/command_interpreter.h>
+#include <config/unix_command_config.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/tests/pkt_captures.h>
#include <dhcpsrv/cfg_multi_threading.h>
using namespace isc::stats;
using namespace isc::util;
using namespace isc::process;
+using namespace isc::config;
namespace isc {
namespace dhcp {
original_datadir_ = CfgMgr::instance().getDataDir();
CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
resetLogPath();
+ resetSocketPath();
}
BaseServerTest::~BaseServerTest() {
// Revert to unit test logging, in case the test reconfigured it.
isc::log::initLogger();
resetLogPath();
+ resetSocketPath();
}
void
LogConfigParser::getLogPath(true);
}
+void
+BaseServerTest::setSocketTestPath(const std::string explicit_path /* = "" */) {
+ UnixCommandConfig::getSocketPath(true,
+ (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+}
+
+void
+BaseServerTest::resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+}
+
Dhcpv6SrvTest::Dhcpv6SrvTest()
: NakedDhcpv6SrvTest(), srv_(0), multi_threading_(false) {
subnet_ = Subnet6::create(isc::asiolink::IOAddress("2001:db8:1::"),
/// @brief Resets the log path to TEST_DATA_BUILDDIR.
void resetLogPath();
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the socket path.
+ static void setSocketTestPath(const std::string explicit_path = "");
+
+ /// @brief Resets the socket path to the default.
+ static void resetSocketPath();
+
private:
/// @brief Holds the original data directory.
" }\n"
" ],\n"
" \"control-socket\": {\n"
-" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n"
+" \"socket-name\": \"kea6-ctrl-socket\",\n"
" \"socket-type\": \"unix\",\n"
" \"user-context\": {\n"
" \"comment\": \"Indirect comment\"\n"
" }\n"
" ],\n"
" \"control-socket\": {\n"
-" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n"
+" \"socket-name\": \"kea6-ctrl-socket\",\n"
" \"socket-type\": \"unix\",\n"
" \"user-context\": {\n"
" \"comment\": \"Indirect comment\"\n"
// Reset configuration for each test.
resetConfiguration();
+ BaseServerTest::setSocketTestPath();
}
~Dhcp6GetConfigTest() {
// Reset configuration database after each test.
resetConfiguration();
+ BaseServerTest::resetSocketPath();
};
/// @brief Parse and Execute configuration
"control-sockets": {
"dhcp4": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
"dhcp6": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
"d2": {
"socket-type": "unix",
- "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ "socket-name": "kea-ddns-ctrl-socket"
}
},
"port": 53001,
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ "socket-name": "kea-ddns-ctrl-socket"
},
"tsig-keys": [],
"forward-ddns" : {},
// more. For detailed description, see Sections 8.8, 16 and 15.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
},
// Use Memfile lease database backend to store leases in a CSV file.
// description, see Sections 9.12, 16 and 15.
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
},
// Use Memfile lease database backend to store leases in a CSV file.
"dhcp4": {
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea4-ctrl-socket"
+ "socket-name": "kea4-ctrl-socket"
}
},
"dhcp6": {
"control-socket": {
"socket-type": "unix",
- "socket-name": "/tmp/kea6-ctrl-socket"
+ "socket-name": "kea6-ctrl-socket"
}
}
},
"dhcp4": {
"boot-update": true,
"control-socket": {
- "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-name": "kea4-ctrl-socket",
"socket-type": "unix",
"socket-url": "http://127.0.0.1:8000/"
},
#include <ha_log.h>
#include <ha_service_states.h>
#include <cc/dhcp_config_error.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <limits>
#include <set>
#include <asiolink/asio_wrapper.h>
#include <asiolink/crypto_tls.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
using namespace isc::cryptolink;
using namespace isc::util;
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+control_socket_dir = @runstatedir@/@PACKAGE@
+AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
libkea_cfgclient_la_SOURCES += cmd_http_listener.cc cmd_http_listener.h
libkea_cfgclient_la_SOURCES += cmd_response_creator.cc cmd_response_creator.h
libkea_cfgclient_la_SOURCES += cmd_response_creator_factory.h
+libkea_cfgclient_la_SOURCES += unix_command_config.cc unix_command_config.h
libkea_cfgclient_la_LIBADD = $(top_builddir)/src/lib/http/libkea-http.la
libkea_cfgclient_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
#include <asiolink/unix_domain_socket_acceptor.h>
#include <asiolink/unix_domain_socket_endpoint.h>
#include <config/command_mgr.h>
+#include <config/unix_command_config.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <cc/json_feed.h>
isc_throw(BadSocketInfo, "'socket-name' parameter expected to be a string");
}
- socket_name_ = name->stringValue();
+ try {
+ socket_name_ = UnixCommandConfig::validatePath(name->stringValue());
+ } catch (const std::exception& ex) {
+ isc_throw(BadSocketInfo, "'socket-name' is invalid: " << ex.what());
+ }
// First let's open lock file.
std::string lock_name = getLockName();
doAccept();
} catch (const std::exception& ex) {
+ close(lock_fd);
+ static_cast<void>(::remove(getLockName().c_str()));
isc_throw(SocketError, ex.what());
}
}
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\"
AM_CPPFLAGS += -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/testutils/ca\"
+control_socket_dir = @runstatedir@/@PACKAGE@
+AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
run_unittests_SOURCES += cmd_http_listener_unittests.cc
run_unittests_SOURCES += cmd_response_creator_unittests.cc
run_unittests_SOURCES += cmd_response_creator_factory_unittests.cc
+run_unittests_SOURCES += unix_command_config_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
#include <asiolink/io_service.h>
#include <config/base_command_mgr.h>
#include <config/command_mgr.h>
+#include <config/unix_command_config.h>
#include <config/hooked_command_mgr.h>
#include <cc/command_interpreter.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
#include <hooks/library_handle.h>
+#include <util/filesystem.h>
#include <string>
#include <vector>
using namespace isc::config;
using namespace isc::data;
using namespace isc::hooks;
+using namespace isc::util;
using namespace std;
// Test class for Command Manager
/// Default constructor
CommandMgrTest()
: io_service_(new IOService()) {
+ resetSocketPath();
CommandMgr::instance().setIOService(io_service_);
CommandMgr::instance().deregisterAll();
CommandMgr::instance().closeCommandSocket();
resetCalloutIndicators();
+ resetSocketPath();
}
- /// @brief Returns socket path (using either hardcoded path or env variable)
- /// @return path to the unix socket
- std::string getSocketPath() {
- std::string socket_path;
- const char* env = getenv("KEA_SOCKET_TEST_DIR");
- if (env) {
- socket_path = std::string(env) + "/test-socket";
- } else {
- socket_path = sandbox.join("test-socket");
- }
- return (socket_path);
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the hooks path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ UnixCommandConfig::getSocketPath(true,
+ (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
}
/// @brief Resets indicators related to callout invocation.
// This test verifies that a Unix socket can be opened properly and that input
// parameters (socket-type and socket-name) are verified.
TEST_F(CommandMgrTest, unixCreate) {
+ setSocketTestPath();
// Null pointer is obviously a bad idea.
EXPECT_THROW(CommandMgr::instance().openCommandSocket(ConstElementPtr()),
isc::config::BadSocketInfo);
EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
isc::config::BadSocketInfo);
- socket_info->set("socket-name", Element::create(getSocketPath()));
+ socket_info->set("socket-name", Element::create("test-socket"));
EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
// This test checks that when unix path is too long, the socket cannot be opened.
TEST_F(CommandMgrTest, unixCreateTooLong) {
+ setSocketTestPath();
ElementPtr socket_info = Element::fromJSON("{ \"socket-type\": \"unix\","
- "\"socket-name\": \"/tmp/toolongtoolongtoolongtoolongtoolongtoolong"
+ "\"socket-name\": \"toolongtoolongtoolongtoolongtoolongtoolong"
"toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong"
"\" }");
// Verifies that a socket cannot be concurrently opened more than once.
TEST_F(CommandMgrTest, exclusiveOpen) {
+ setSocketTestPath();
// Pass in valid parameters.
ElementPtr socket_info = Element::createMap();
socket_info->set("socket-type", Element::create("unix"));
- socket_info->set("socket-name", Element::create(getSocketPath()));
+ socket_info->set("socket-name", Element::create("test-socket"));
EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
--- /dev/null
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <config/unix_command_config.h>
+#include <util/filesystem.h>
+#include <testutils/gtest_utils.h>
+#include <testutils/env_var_wrapper.h>
+
+using namespace isc;
+using namespace isc::config;
+using namespace isc::test;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+/// @brief Test fixture for UNIX control socket configuration.
+class UnixCommandConfigTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ UnixCommandConfigTest() {
+ setSocketTestPath();
+ }
+
+ /// @brief Destructor.
+ ~UnixCommandConfigTest() {
+ resetSocketPath();
+ }
+
+ /// @brief Sets the path in which the socket can be created.
+ /// @param explicit_path path to use as the hooks path.
+ void setSocketTestPath(const std::string explicit_path = "") {
+ UnixCommandConfig::getSocketPath(true,
+ (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+
+ auto path = UnixCommandConfig::getSocketPath();
+ UnixCommandConfig::setSocketPathPerms(file::getPermissions(path));
+ }
+
+ /// @brief Resets the socket path to the default.
+ void resetSocketPath() {
+ UnixCommandConfig::getSocketPath(true);
+ UnixCommandConfig::setSocketPathPerms();
+ }
+};
+
+TEST(SocketPathTest, socketDir) {
+ EnvVarWrapper env("KEA_CONTROL_SOCKET_DIR");
+ env.setValue("");
+
+ auto path = UnixCommandConfig::getSocketPath(true);
+ ASSERT_EQ(path, std::string(CONTROL_SOCKET_DIR));
+
+ env.setValue(TEST_DATA_BUILDDIR);
+ path = UnixCommandConfig::getSocketPath(true);
+ ASSERT_EQ(path, std::string(TEST_DATA_BUILDDIR));
+}
+
+} // end of anonymous namespace
--- /dev/null
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <config/unix_command_config.h>
+#include <util/filesystem.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc;
+using namespace isc::config;
+using namespace isc::util::file;
+using namespace std;
+
+namespace isc {
+namespace config {
+
+namespace {
+ // Singleton PathChecker to set and hold valid unix socket path.
+ PathCheckerPtr socket_path_checker_;
+
+};
+
+const mode_t UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS = (S_IRWXU | S_IRGRP | S_IXGRP);
+
+mode_t UnixCommandConfig::socket_path_perms_ = UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS;
+
+std::string
+UnixCommandConfig::getSocketPath(bool reset /* = false */,
+ const std::string explicit_path /* = "" */) {
+ if (!socket_path_checker_ || reset) {
+ socket_path_checker_.reset(new PathChecker(CONTROL_SOCKET_DIR,
+ "KEA_CONTROL_SOCKET_DIR"));
+ if (!explicit_path.empty()) {
+ socket_path_checker_->getPath(true, explicit_path);
+ }
+ }
+
+ return (socket_path_checker_->getPath());
+}
+
+std::string
+UnixCommandConfig::validatePath(const std::string socket_path,
+ bool enforce /* = true */) {
+ if (!socket_path_checker_) {
+ getSocketPath();
+ }
+
+ auto valid_path = socket_path_checker_->validatePath(socket_path, enforce);
+ if (enforce && !(socket_path_checker_->pathHasPermissions(socket_path_perms_))) {
+ isc_throw (BadValue,
+ "socket path:" << socket_path_checker_->getPath()
+ << " does not exist or does not have permssions = "
+ << std::oct << socket_path_perms_);
+ }
+
+ return (valid_path);
+}
+
+mode_t
+UnixCommandConfig::getSocketPathPerms() {
+ return (socket_path_perms_);
+}
+
+void
+UnixCommandConfig::setSocketPathPerms(mode_t perms
+ /* = DEFAULT_SOCKET_PATH_PERMS */) {
+ socket_path_perms_ = perms;
+}
+
+} // end of isc::config
+} // end of isc
--- /dev/null
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef UNIX_COMMAND_CONFIG_H
+#define UNIX_COMMAND_CONFIG_H
+
+#include <util/filesystem.h>
+#include <string>
+
+namespace isc {
+namespace config {
+
+/// @brief UNIX command config aka UNIX control socket info class.
+class UnixCommandConfig {
+public:
+ /// @brief Defines the default permissions for unix socket parent directory.
+ static const mode_t DEFAULT_SOCKET_PATH_PERMS;
+
+ /// @brief Stores the default permissions for unix socket parent directory.
+ static mode_t socket_path_perms_;
+
+ /// @brief Constructor.
+ UnixCommandConfig() = default;
+
+ /// @brief Virtual destructor.
+ ~UnixCommandConfig() = default;
+
+ /// @brief Fetches the supported control socket path.
+ ///
+ /// The first call to this function with no arguments will set the default
+ /// path to either the value of CONTROL_SOCKET_DIR or the environment
+ /// variable KEA_CONTROL_SOCKET_DIR if it is defined. Subsequent calls with
+ /// no arguments will simply return this value.
+ ///
+ /// @param reset recalculate when true, defaults to false.
+ /// @param explicit_path set the default socket path to this value. This is
+ /// for testing purposes only.
+ ///
+ /// @return String containing the default socket path.
+ static std::string getSocketPath(bool reset = false,
+ const std::string explicit_path = "");
+
+ /// @brief Validates a path against the supported path for unix control
+ /// sockets.
+ ///
+ /// @param socket_path path to validate.
+ /// @param enforce enables validation against the supported path and
+ /// permissions.
+ /// If false simply returns the input path.
+ ///
+ /// @return validated path
+ static std::string validatePath(const std::string socket_path,
+ bool enforce = true);
+
+ /// @brief Fetches the required socket path permissions mask
+ ///
+ /// @return permissions mask
+ static mode_t getSocketPathPerms();
+
+ /// @brief Sets the required socket path permissions mask
+ ///
+ /// This is for testing purposes only.
+ /// @param perms permissions mask to use
+ static void setSocketPathPerms(mode_t perms = DEFAULT_SOCKET_PATH_PERMS);
+};
+
+} // end of isc::config namespace
+} // end of isc namespace
+#endif
#include <http/auth_log.h>
#include <http/basic_auth_config.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <util/strutil.h>
using namespace isc;
#include <database/db_log.h>
#include <exceptions/exceptions.h>
#include <mysql/mysql_connection.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <boost/lexical_cast.hpp>
libkea_util_la_SOURCES += csv_file.h csv_file.cc
libkea_util_la_SOURCES += dhcp_space.h dhcp_space.cc
libkea_util_la_SOURCES += doubles.h
-libkea_util_la_SOURCES += file_utilities.h file_utilities.cc
libkea_util_la_SOURCES += filename.h filename.cc
libkea_util_la_SOURCES += hash.h
libkea_util_la_SOURCES += labeled_value.h labeled_value.cc
csv_file.h \
dhcp_space.h \
doubles.h \
- file_utilities.h \
filename.h \
hash.h \
io_utilities.h \
+++ /dev/null
-// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/filename.h>
-#include <cerrno>
-#include <cstring>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-using namespace std;
-
-namespace isc {
-namespace util {
-namespace file {
-
-string
-getContent(const string& file_name) {
- // Open the file.
- int fd = ::open(file_name.c_str(), O_RDONLY);
- if (fd < 0) {
- isc_throw(BadValue, "can't open file '" << file_name << "': "
- << std::strerror(errno));
- }
- try {
- struct stat stats;
- if (fstat(fd, &stats) < 0) {
- isc_throw(BadValue, "can't stat file '" << file_name << "': "
- << std::strerror(errno));
- }
- if ((stats.st_mode & S_IFMT) != S_IFREG) {
- isc_throw(BadValue, "'" << file_name
- << "' must be a regular file");
- }
- string content(stats.st_size, ' ');
- ssize_t got = ::read(fd, &content[0], stats.st_size);
- if (got < 0) {
- isc_throw(BadValue, "can't read file '" << file_name << "': "
- << std::strerror(errno));
- }
- if (got != stats.st_size) {
- isc_throw(BadValue, "can't read whole file '" << file_name
- << "' (got " << got << " of " << stats.st_size << ")");
- }
- static_cast<void>(close(fd));
- return (content);
- } catch (const std::exception&) {
- static_cast<void>(close(fd));
- throw;
- }
-}
-
-bool
-isDir(const string& name) {
- struct stat stats;
- if (::stat(name.c_str(), &stats) < 0) {
- return (false);
- }
- return ((stats.st_mode & S_IFMT) == S_IFDIR);
-}
-
-} // namespace file
-} // namespace log
-} // namespace isc
+++ /dev/null
-// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef FILE_UTILITIES_H
-#define FILE_UTILITIES_H
-
-#include <string>
-
-namespace isc {
-namespace util {
-namespace file {
-
-/// @brief Get the content of a regular file.
-///
-/// @param file_name The file name.
-/// @return The content of the file_name file.
-/// @throw BadValue when the file can't be opened or is not a regular one.
-std::string getContent(const std::string& file_name);
-
-/// @brief Is a directory predicate.
-///
-/// @param name The file or directory name.
-/// @return True if the name points to a directory, false otherwise including
-/// if the pointed location does not exist.
-bool isDir(const std::string& name);
-
-} // namespace file
-} // namespace util
-} // namespace isc
-
-#endif // FILE_UTILITIES_H
#include <cstdlib>
#include <fstream>
#include <string>
+#include <filesystem>
#include <iostream>
#include <dirent.h>
namespace util {
namespace file {
+string
+getContent(string const& file_name) {
+ if (!exists(file_name)) {
+ isc_throw(BadValue, "Expected a file at path '" << file_name << "'");
+ }
+ if (!isFile(file_name)) {
+ isc_throw(BadValue, "Expected '" << file_name << "' to be a regular file");
+ }
+ ifstream file(file_name, ios::in);
+ if (!file.is_open()) {
+ isc_throw(BadValue, "Cannot open '" << file_name);
+ }
+ string content;
+ file >> content;
+ return (content);
+}
+
bool
exists(string const& path) {
struct stat statbuf;
return (::stat(path.c_str(), &statbuf) == 0);
}
+mode_t
+getPermissions(const std::string path) {
+ struct stat statbuf;
+ if (::stat(path.c_str(), &statbuf) < 0) {
+ return (0);
+ }
+
+ return (statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+}
+
+bool
+hasPermissions(const std::string path, const mode_t& permissions) {
+ return (getPermissions(path) == permissions);
+}
+
+bool
+isDir(string const& path) {
+ struct stat statbuf;
+ if (::stat(path.c_str(), &statbuf) < 0) {
+ return (false);
+ }
+ return ((statbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+
bool
isFile(string const& path) {
struct stat statbuf;
return (valid_path);
}
+bool
+PathChecker::pathHasPermissions(mode_t permissions) {
+ return(hasPermissions(path_, permissions));
+}
+
} // namespace file
} // namespace util
} // namespace isc
namespace util {
namespace file {
+/// @brief Get the content of a regular file.
+///
+/// @param file_name The file name.
+///
+/// @return The content of the file.
+/// @throw BadValue when the file can't be opened or is not a regular one.
+std::string
+getContent(const std::string& file_name);
+
/// @brief Check if there is a file or directory at the given path.
///
/// @param path The path being checked.
bool
exists(const std::string& path);
+/// @brief Check if there is a directory at the given path.
+///
+/// @param path The path being checked.
+///
+/// @return True if the path points to a directory, false otherwise including
+/// if the pointed location does not exist.
+bool
+isDir(const std::string& path);
+
+/// @brief Fetches the file permissions mask.
+///
+/// @param path The path being checked.
+/// @return File permissios mask or 0 if the path does not exist.
+mode_t
+getPermissions(const std::string path);
+
+/// @brief Check if there if file or directory has the given permissions.
+///
+/// @param path The path being checked.
+/// @param permissions mask of expected permissions.
+///
+/// @return True if the path points to a file or a directory, false otherwise.
+bool
+hasPermissions(const std::string path, const mode_t& permissions);
+
/// @brief Check if there is a file at the given path.
///
/// @param path The path being checked.
std::string validatePath(const std::string input_path_str,
bool enforce_path = true) const;
+ /// @brief Tests that the supported path has the given permissions.
+ ///
+ /// @param permissions mode_t mask of required permissions.
+ /// @return True if the path's permissions exactly match the permissions
+ /// parameter.
+ bool pathHasPermissions(mode_t permissions);
+
/// @brief Fetches the default path.
std::string getDefaultPath() const {
return (default_path_);
run_unittests_SOURCES += doubles_unittest.cc
run_unittests_SOURCES += fd_share_tests.cc
run_unittests_SOURCES += fd_tests.cc
-run_unittests_SOURCES += file_utilities_unittest.cc
run_unittests_SOURCES += filename_unittest.cc
run_unittests_SOURCES += hash_unittest.cc
run_unittests_SOURCES += hex_unittest.cc
+++ /dev/null
-// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/file_utilities.h>
-#include <gtest/gtest.h>
-#include <fstream>
-
-using namespace isc;
-using namespace isc::util::file;
-using namespace std;
-
-namespace {
-
-/// @brief Test fixture class for testing operations on files.
-class FileUtilTest : public ::testing::Test {
-public:
-
- /// @brief Destructor.
- ///
- /// Deletes the test file if any.
- virtual ~FileUtilTest();
-};
-
-FileUtilTest::~FileUtilTest() {
- string test_file_name = string(TEST_DATA_BUILDDIR) + "/fu.test";
- static_cast<void>(remove(test_file_name.c_str()));
-}
-
-/// @brief Check an error is returned by getContent on not existent file.
-TEST_F(FileUtilTest, notExists) {
- string file_name("/this/does/not/exists");
- try {
- string c = getContent(file_name);
- FAIL() << "this test must throw before this line";
- } catch (const BadValue& ex) {
- string expected = "can't open file '" + file_name;
- expected += "': No such file or directory";
- EXPECT_EQ(string(ex.what()), expected);
- } catch (const std::exception& ex) {
- FAIL() << "unexpected exception: " << ex.what();
- }
-}
-
-/// @note No easy can't stat.
-
-/// @brief Check an error is returned by getContent on not regular file.
-TEST_F(FileUtilTest, notRegular) {
- string file_name("/");
- try {
- string c = getContent(file_name);
- FAIL() << "this test must throw before this line";
- } catch (const BadValue& ex) {
- string expected = "'" + file_name + "' must be a regular file";
- EXPECT_EQ(string(ex.what()), expected);
- } catch (const std::exception& ex) {
- FAIL() << "unexpected exception: " << ex.what();
- }
-}
-
-/// @brief Check getContent works.
-TEST_F(FileUtilTest, basic) {
- string file_name = string(TEST_DATA_BUILDDIR) + "/fu.test";
- ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc);
- ASSERT_TRUE(fs.is_open());
- fs << "abdc";
- fs.close();
- string content;
- EXPECT_NO_THROW(content = getContent(file_name));
- EXPECT_EQ("abdc", content);
-}
-
-/// @brief Check isDir works.
-TEST_F(FileUtilTest, isDir) {
- EXPECT_TRUE(isDir("/dev"));
- EXPECT_FALSE(isDir("/dev/null"));
- EXPECT_FALSE(isDir("/this/does/not/exists"));
- EXPECT_FALSE(isDir("/etc/hosts"));
-}
-
-}
namespace {
+/// @brief Test fixture class for testing operations on files.
+struct FileUtilTest : ::testing::Test {
+ /// @brief Destructor.
+ ///
+ /// Deletes the test file if any.
+ virtual ~FileUtilTest() {
+ string test_file_name(TEST_DATA_BUILDDIR "/fu.test");
+ static_cast<void>(remove(test_file_name.c_str()));
+ }
+};
+
+/// @brief Check that an error is returned by getContent on non-existent file.
+TEST_F(FileUtilTest, notExist) {
+ EXPECT_THROW_MSG(getContent("/does/not/exist"), BadValue,
+ "Expected a file at path '/does/not/exist'");
+}
+
+/// @brief Check that an error is returned by getContent on not regular file.
+TEST_F(FileUtilTest, notRegular) {
+ EXPECT_THROW_MSG(getContent("/"), BadValue, "Expected '/' to be a regular file");
+}
+
+/// @brief Check getContent.
+TEST_F(FileUtilTest, getContent) {
+ string file_name(TEST_DATA_BUILDDIR "/fu.test");
+ ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "abdc";
+ fs.close();
+ string content;
+ EXPECT_NO_THROW_LOG(content = getContent(file_name));
+ EXPECT_EQ("abdc", content);
+}
+
+/// @brief Check isDir.
+TEST_F(FileUtilTest, isDir) {
+ EXPECT_TRUE(isDir("/dev"));
+ EXPECT_FALSE(isDir("/dev/null"));
+ EXPECT_FALSE(isDir("/this/does/not/exist"));
+ EXPECT_FALSE(isDir("/etc/hosts"));
+}
+
+/// @brief Check isFile.
+TEST_F(FileUtilTest, isFile) {
+ EXPECT_TRUE(isFile(ABS_SRCDIR "/filesystem_unittests.cc"));
+ EXPECT_FALSE(isFile(TEST_DATA_BUILDDIR));
+}
+
/// @brief Test fixture class for testing operations on umask.
struct UMaskUtilTest : ::testing::Test {
/// @brief Constructor.
}
}
+/// @brief Check hasPermissions.
+TEST_F(FileUtilTest, hasPermissions) {
+ const std::string path = ABS_SRCDIR "/filesystem_unittests.cc";
+ ASSERT_TRUE(isFile(path));
+ mode_t current_permissions = getPermissions(path);
+ EXPECT_TRUE(hasPermissions(path, current_permissions));
+ current_permissions = ~current_permissions;
+ EXPECT_FALSE(hasPermissions(path, current_permissions));
+}
+
} // namespace