]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3839] backport #3831 to v2_4
authorRazvan Becheriu <razvan@isc.org>
Tue, 20 May 2025 10:06:53 +0000 (13:06 +0300)
committerRazvan Becheriu <razvan@isc.org>
Tue, 20 May 2025 10:06:53 +0000 (13:06 +0300)
82 files changed:
ChangeLog
doc/examples/agent/comments.json
doc/examples/agent/simple.json
doc/examples/ddns/all-keys-netconf.json
doc/examples/ddns/all-keys.json
doc/examples/ddns/comments.json
doc/examples/ddns/sample1.json
doc/examples/ddns/template.json
doc/examples/kea4/advanced.json
doc/examples/kea4/all-keys-netconf.json
doc/examples/kea4/all-keys.json
doc/examples/kea4/comments.json
doc/examples/kea4/config-backend.json
doc/examples/kea4/dhcpv4-over-dhcpv6.json
doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json
doc/examples/kea4/ha-load-balancing-server2-mt.json
doc/examples/kea6/advanced.json
doc/examples/kea6/all-keys-netconf.json
doc/examples/kea6/all-keys.json
doc/examples/kea6/comments.json
doc/examples/kea6/config-backend.json
doc/examples/kea6/dhcpv4-over-dhcpv6.json
doc/examples/kea6/ha-hot-standby-server1-with-tls.json
doc/examples/kea6/ha-hot-standby-server2.json
doc/examples/netconf/comments.json
doc/examples/netconf/simple-dhcp4.json
doc/examples/netconf/simple-dhcp6.json
doc/sphinx/arm/agent.rst
doc/sphinx/arm/ddns.rst
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
doc/sphinx/arm/hooks.rst
src/bin/agent/ca_cfg_mgr.cc
src/bin/agent/ca_command_mgr.cc
src/bin/agent/simple_parser.cc
src/bin/agent/tests/ca_cfg_mgr_unittests.cc
src/bin/agent/tests/ca_command_mgr_unittests.cc
src/bin/agent/tests/ca_controller_unittests.cc
src/bin/agent/tests/get_config_unittest.cc
src/bin/agent/tests/parser_unittests.cc
src/bin/agent/tests/testdata/get_config.json
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/d2_command_unittest.cc
src/bin/d2/tests/get_config_unittest.cc
src/bin/d2/tests/testdata/get_config.json
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_test_utils.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp4/tests/get_config_unittest.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_test_utils.cc
src/bin/dhcp6/tests/dhcp6_test_utils.h
src/bin/dhcp6/tests/get_config_unittest.cc
src/bin/keactrl/kea-ctrl-agent.conf.pre
src/bin/keactrl/kea-dhcp-ddns.conf.pre
src/bin/keactrl/kea-dhcp4.conf.pre
src/bin/keactrl/kea-dhcp6.conf.pre
src/bin/keactrl/kea-netconf.conf.pre
src/bin/netconf/tests/testdata/get_config.json
src/hooks/dhcp/high_availability/ha_config_parser.cc
src/lib/asiolink/common_tls.cc
src/lib/config/Makefile.am
src/lib/config/command_mgr.cc
src/lib/config/tests/Makefile.am
src/lib/config/tests/command_mgr_unittests.cc
src/lib/config/tests/unix_command_config_unittests.cc [new file with mode: 0644]
src/lib/config/unix_command_config.cc [new file with mode: 0644]
src/lib/config/unix_command_config.h [new file with mode: 0644]
src/lib/http/basic_auth_config.cc
src/lib/mysql/mysql_connection.cc
src/lib/util/Makefile.am
src/lib/util/file_utilities.cc [deleted file]
src/lib/util/file_utilities.h [deleted file]
src/lib/util/filesystem.cc
src/lib/util/filesystem.h
src/lib/util/tests/Makefile.am
src/lib/util/tests/file_utilities_unittest.cc [deleted file]
src/lib/util/tests/filesystem_unittests.cc

index 3dbe84667bbeffaf12c9d9a41e016d3b9db79633..a2d4b2fc7cf728377ccf82bec87bc3b2ace2bf95 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-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
index 10c82e3fa3795869d5f2cd7fafa540fef93e3a46..6803165ae02858d409334a2ad9b1bff0a5b81c45 100644 (file)
@@ -43,7 +43,7 @@
             {
                 "comment": "control socket for DHCPv4 server",
                 "socket-type": "unix",
-                "socket-name": "/tmp/kea4-ctrl-socket"
+                "socket-name": "kea4-ctrl-socket"
             }
         },
 
index a7227de04f6f8fc218be8a306809b43a0172df96..155dd627cf157a2d6b6c08e0f648e31d9b0a0a18 100644 (file)
             {
                 "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 }
             }
         },
index 2ea6fad859ae055470ba3031902525936a501fa7..f8ea63bd78042d8fedc8608438d917626d4f10ae 100644 (file)
@@ -40,7 +40,7 @@
         // 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.
index 674c9860cf811e814a6aafef527ca282de05d454..f24d343550c88b6c36dd644319162ddfc6824a5d 100644 (file)
@@ -40,7 +40,7 @@
         // 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.
index a7717d17c83026d05a3bd2a0d37793673c1bcbf2..750c27d09906085321c4dd7af8e7fe7c2205acf3 100644 (file)
@@ -16,7 +16,7 @@
     {
         "comment": "Control channel",
         "socket-type": "unix",
-        "socket-name": "/tmp/kea-ddns-ctrl-socket"
+        "socket-name": "kea-ddns-ctrl-socket"
     },
 
     "forward-ddns":
index 7ff4960c9aefaba43c188d341b0130c82c94bb68..4d6a3046c4c9204d4ff7fc2788f512c39a669111 100644 (file)
@@ -32,7 +32,7 @@
     "control-socket":
     {
         "socket-type": "unix",
-        "socket-name": "/tmp/kea-ddns-ctrl-socket"
+        "socket-name": "kea-ddns-ctrl-socket"
     },
 
 // ----------------- Hooks Libraries -----------------
index c4da2508e5323816f1b3f0a0584f8697a6742374..74dffdede5636b66fff59f00402ae43a3d9e0401 100644 (file)
@@ -19,7 +19,7 @@
 //    "control-socket":
 //    {
 //        "socket-type": "unix",
-//        "socket-name": "/tmp/kea-ddns-ctrl-socket"
+//        "socket-name": "kea-ddns-ctrl-socket"
 //    },
 
 // ----------------- Forward DDNS  ------------------
index 9191226aaf32914a4fa0fdf3ca4adc1e24bf9dec..daeec26886503f6428a002ef971ee8a12bf21c36 100644 (file)
@@ -89,7 +89,7 @@
     // 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.
index ef4a9e8e0ddeb058ee95338b9fabf4f8e63beaa3..15fdd266dab8c73bcf4e855b942377a239d243d9 100644 (file)
             // 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.
index 6c6dbb90f3ccbcc2544c19abfb72ec1c7c602bcf..38c23367ff301976fc80a8d0aa467d3a52390cca 100644 (file)
             // 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.
index a5cfbdcd5c76d23b79b57ea5742a72fe28a1f229..c8f4175fd3ada7da2629851536d478451fd6c399 100644 (file)
@@ -54,7 +54,7 @@
    // 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" }
    },
 
index 272243a1ffc1bede50c32568d25737ff49bdd8bb..4dc4842923750198a5397c71fa38f80738f5d019 100644 (file)
@@ -52,7 +52,7 @@
     // 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.
index c383004a7d8831c848f9c0439af9c39ffc4e0740..c76427eec6b64131a4df7dba7c223550275a0740 100644 (file)
@@ -36,7 +36,7 @@
       "name": "kea-dhcp4",
       "output_options": [
           {
-            "output": "/tmp/kea-dhcp4.log"
+            "output": "kea-dhcp4.log"
           }
       ],
       "severity": "DEBUG",
index 5e6802647c93f29fce52b42129be32db4e092393..9ac988d0012c0dd678950ff5b4afbb4a7232ad22 100644 (file)
@@ -25,7 +25,7 @@
     // 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.
index ddfadb0cfdc04dfecdff6bdbe954a47a6159bcc7..b084095cd2e92915bd4684f6ca0b1fb8b5da63e3 100644 (file)
@@ -24,7 +24,7 @@
     // 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.
index 995f6937155e5b1d06b401b3a0dc437c89a02d6a..c2b2686834777fb65fe5027ea766ff8260ae971f 100644 (file)
@@ -80,7 +80,7 @@
     // 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
index 16eb541d82db437a22e8105852590d1e25b27a89..8b52a57452bb0752e424c142f013460af47539b9 100644 (file)
@@ -98,7 +98,7 @@
             // 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.
index f990aadd547db60f1db24f8a089bc1baf6491dde..bc71d66cefb49c288402c9a39f135ec7825db553 100644 (file)
@@ -98,7 +98,7 @@
             // 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.
index 88cdd413762ac9d04f55616781cd433924b37f9d..65ad2f0e2595453b14f63431a8a181d8b8fdca1e 100644 (file)
@@ -54,7 +54,7 @@
    // 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" }
    },
 
index 4793ee2e0d8624355c04cdfa1f8da9ffcf8dbd41..e169249bece7cf27fa89da437ec0b5be2813c607 100644 (file)
@@ -52,7 +52,7 @@
     // 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.
index 983b4898dbd8ab14b7cb3fcce664526897cc5e0f..b9dc35fb89a6e796959198228e540480c69c531c 100644 (file)
@@ -46,7 +46,7 @@
       "name": "kea-dhcp6",
       "output_options": [
           {
-            "output": "/tmp/kea-dhcp6.log"
+            "output": "kea-dhcp6.log"
           }
       ],
       "severity": "DEBUG",
index 3a586f9c162e94d5ab44e3824d717f4218bec4d9..8833cd94adc1b85e35f605f5bdf5a0f8bdf1559c 100644 (file)
@@ -24,7 +24,7 @@
     // 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.
index 43c751bf12075c5294362c7eb0603f4f71f8898a..bb0f7bbffb146cc733a36ea8492007498c16c17c 100644 (file)
@@ -23,7 +23,7 @@
     // 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.
index 99014e78503847212d2e40fa3c4b4dba41d98351..12e1276efcc57cea31d92174217cb285b119c2ad 100644 (file)
@@ -20,7 +20,7 @@
                 {
                     "comment": "using unix/local socket",
                     "socket-type": "unix",
-                    "socket-name": "/tmp/kea4-ctrl-socket"
+                    "socket-name": "kea4-ctrl-socket"
                 }
             }
         },
index 309badf862d60b0098186ca1e1cb7d075dce83b7..cbcd0a7c0b3a980b0452bddc2675bb1046151e6f 100644 (file)
@@ -57,7 +57,7 @@
                 "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.
index 91e57bece3f926f2882c18ac255644808040299d..967e3dbdbef7a1e4a739ef2ab2e90ba4f07d5419 100644 (file)
@@ -58,7 +58,7 @@
                 "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.
index 31be5f73e2e28cc375adbf918a8b86aaa9464bfe..4e30d6a22aa2a0f9b2988fe6a7d7e09d8fb5e2d3 100644 (file)
@@ -135,6 +135,16 @@ for the DHCPv4 server and the CA (for that server) must match. Consult
 :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
index b559255070e23bc4bea85897c522131aa552cdbc..fd972c243876d9d50b1f24a3d08392db0a81df13 100644 (file)
@@ -295,6 +295,16 @@ operating system, i.e. the size of the ``sun_path`` field in the
 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>`__
index c3839f857a0ffa28a212ba788fcf8ce47e289807..9792ec544e6c3dd913e0b58d5b94baf35089f0bc 100644 (file)
@@ -7443,6 +7443,16 @@ operating system, i.e. the size of the ``sun_path`` field in the
 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
index 39a742dc41fb82bea3b2b1e6d971744d78f535a2..9bfbd4622ed491426206753ba861c0f9dbe0954c 100644 (file)
@@ -7225,6 +7225,16 @@ operating system, i.e. the size of the ``sun_path`` field in the
 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
index 156a094d809c9af5a97e1ffc6ebe8dc31df2af42..7675032179252ee52ae833556c08b7d5463d0b9d 100644 (file)
@@ -218,7 +218,7 @@ configuration would be:
 
 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
index f2f8caa92af346a926bb934347655550e0f942e8..a3bbb2774417cfbb63f8954efbb2e149d85e28ba 100644 (file)
@@ -200,10 +200,14 @@ CtrlAgentCfgContext::toElement() const {
     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();
index 848753d333afd1e101a56ea5f98f674f2ee8f0ef..4cd26cdf3f61e6993c68147952d85ec8815c89d5 100644 (file)
@@ -220,9 +220,15 @@ CtrlAgentCommandMgr::forwardCommand(const std::string& service,
                   " 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());;
index 218ee21c568e9b53a5b6319bd10ed2061f7eaddb..a19b6f9c8814c810e2a96e7aec299c238431c094 100644 (file)
@@ -9,12 +9,14 @@
 #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 {
@@ -146,9 +148,16 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
     // 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);
         }
     }
 
index 73f5a85cd69e48600ac9a84d780526f3fa25a509..4828ce00dc1e4e222abb44de5dff137983175c3e 100644 (file)
@@ -7,11 +7,13 @@
 #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>
@@ -23,6 +25,8 @@ using namespace isc::data;
 using namespace isc::hooks;
 using namespace isc::http;
 using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
 
 namespace  {
 
@@ -239,8 +243,8 @@ TEST(CtrlAgentCfgMgr, contextAuthConfigFile) {
 
     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);
@@ -265,7 +269,7 @@ const char* AGENT_CONFIGS[] = {
     "    \"http-port\": 8001,\n"
     "    \"control-sockets\": {\n"
     "        \"dhcp4\": {\n"
-    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "            \"socket-name\": \"socket-v4\"\n"
     "        }\n"
     "    }\n"
     "}",
@@ -276,13 +280,13 @@ const char* AGENT_CONFIGS[] = {
     "    \"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"
     "}",
@@ -295,7 +299,7 @@ const char* AGENT_CONFIGS[] = {
     "    \"http-port\": 8001,\n"
     "    \"control-sockets\": {\n"
     "        \"dhcp4\": {\n"
-    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "            \"socket-name\": \"socket-v4\"\n"
     "        }\n"
     "   },\n"
     "    \"hooks-libraries\": ["
@@ -314,7 +318,7 @@ const char* AGENT_CONFIGS[] = {
     "    \"http-port\": 8001,\n"
     "    \"control-sockets\": {\n"
     "        \"d2\": {\n"
-    "            \"socket-name\": \"/tmp/socket-d2\"\n"
+    "            \"socket-name\": \"socket-d2\"\n"
     "        }\n"
     "    }\n"
     "}",
@@ -325,7 +329,7 @@ const char* AGENT_CONFIGS[] = {
     "    \"http-port\": 8001,\n"
     "    \"control-sockets\": {\n"
     "        \"dhcp6\": {\n"
-    "            \"socket-name\": \"/tmp/socket-v6\"\n"
+    "            \"socket-name\": \"socket-v6\"\n"
     "        }\n"
     "    }\n"
     "}",
@@ -349,7 +353,7 @@ const char* AGENT_CONFIGS[] = {
     "    },\n"
     "    \"control-sockets\": {\n"
     "        \"dhcp4\": {\n"
-    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "            \"socket-name\": \"socket-v4\"\n"
     "        }\n"
     "    }\n"
     "}",
@@ -377,10 +381,10 @@ const char* AGENT_CONFIGS[] = {
     "    \"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"
@@ -415,7 +419,7 @@ const char* AGENT_CONFIGS[] = {
     "    },\n"
     "    \"control-sockets\": {\n"
     "        \"dhcp4\": {\n"
-    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "            \"socket-name\": \"socket-v4\"\n"
     "        }\n"
     "    }\n"
     "}"
@@ -426,10 +430,12 @@ class AgentParserTest : public isc::process::ConfigParseTest {
 public:
     virtual void SetUp() {
         resetHooksPath();
+        setSocketTestPath();
     }
 
     virtual void TearDown() {
         resetHooksPath();
+        resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -445,6 +451,20 @@ public:
         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
@@ -468,6 +488,20 @@ public:
         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_;
 };
@@ -483,8 +517,6 @@ TEST_F(AgentParserTest, DISABLED_configParseEmpty) {
 
 // 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();
@@ -503,8 +535,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp4) {
     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"));
 }
@@ -519,9 +550,7 @@ TEST_F(AgentParserTest, configParseSocketD2) {
     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"));
 }
@@ -536,8 +565,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp6) {
     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"));
 }
@@ -552,14 +580,11 @@ TEST_F(AgentParserTest, configParse3Sockets) {
     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
index 6353916be140e904732828058539de9797072e12..b5bb9a587bdd2f1eabb85b6b8a7d9ec03ace5d4d 100644 (file)
@@ -15,7 +15,9 @@
 #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>
@@ -28,6 +30,8 @@ using namespace isc::agent;
 using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
 
 namespace {
 
@@ -50,6 +54,7 @@ public:
         : DControllerTest(CtrlAgentController::instance),
           mgr_(CtrlAgentCommandMgr::instance()) {
         mgr_.deregisterAll();
+        setSocketTestPath();
         removeUnixSocketFile();
         initProcess();
     }
@@ -60,6 +65,7 @@ public:
     virtual ~CtrlAgentCommandMgrTest() {
         mgr_.deregisterAll();
         removeUnixSocketFile();
+        resetSocketPath();
     }
 
     /// @brief Verifies received answer
@@ -106,19 +112,22 @@ public:
     }
 
     /// @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.
@@ -158,7 +167,7 @@ public:
         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);
     }
index 54881d4a7709aa5576045086642902059a9c074a..57c2eb29e0b7a4af7a9c6c61a1c345ea29faba08 100644 (file)
 #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;
@@ -22,6 +28,8 @@ using namespace isc::agent;
 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;
 
@@ -35,11 +43,11 @@ const char* valid_agent_config =
     "  \"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\""
     "    }"
     "  }"
     "}";
@@ -56,6 +64,12 @@ public:
     /// @brief Constructor.
     CtrlAgentControllerTest()
         : DControllerTest(CtrlAgentController::instance) {
+        setSocketTestPath();
+    }
+
+    /// @brief Destructor.
+    virtual ~CtrlAgentControllerTest() {
+        resetSocketPath();
     }
 
     /// @brief Returns pointer to CtrlAgentProcess instance.
@@ -81,6 +95,20 @@ public:
         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.
     ///
@@ -96,8 +124,7 @@ public:
         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.
@@ -184,7 +211,6 @@ TEST_F(CtrlAgentControllerTest, basicInstanceTesting) {
     EXPECT_FALSE(checkProcess());
 }
 
-
 // Tests basic command line processing.
 // Verifies that:
 // 1. Standard command line options are supported.
@@ -276,11 +302,11 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) {
         "  \"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\""
         "    }"
         "  }"
         "}";
@@ -319,8 +345,8 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) {
     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();
@@ -342,11 +368,11 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) {
         "  \"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\""
         "    }"
         "  }"
         "}";
@@ -385,8 +411,8 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) {
     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();
@@ -408,11 +434,11 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
         "  \"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\""
         "    }"
         "  }"
         "}";
@@ -450,8 +476,8 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
     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);
@@ -727,7 +753,6 @@ TEST_F(CtrlAgentControllerTest, configReloadFileValid) {
     answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload",
                                                            params, cmd);
 
-
     // Verify the reload was successful.
     string expected = "{ \"result\": 0, \"text\": "
         "\"Configuration applied successfully.\" }";
@@ -832,7 +857,6 @@ TEST_F(CtrlAgentControllerTest, shutdown) {
     ctrl->deregisterCommands();
 }
 
-
 TEST_F(CtrlAgentControllerTest, shutdownExitValue) {
     ASSERT_NO_THROW(initProcess());
     EXPECT_TRUE(checkProcess());
index e5f741925ff300535b554d8e9dc3983b94bdc0cf..a0848415e80122d403ed20e810c319efef085812 100644 (file)
@@ -8,11 +8,13 @@
 
 #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>
 
@@ -28,8 +30,10 @@ using namespace isc::agent;
 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 {
 
@@ -140,11 +144,13 @@ public:
         srv_.reset(new NakedAgentCfgMgr());
         // Create fresh context.
         resetConfiguration();
+        setSocketTestPath();
     }
 
     ~CtrlAgentGetCfgTest() {
         resetConfiguration();
         resetHooksPath();
+        resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -160,6 +166,20 @@ public:
         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.
index df80ab250a5d9294cbac3056a539286742100094..d4335942aa83dfb7137132ef85e5186fc79a72b1 100644 (file)
@@ -142,15 +142,15 @@ TEST(ParserTest, keywordAgent) {
         "    \"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\": ["
@@ -179,15 +179,15 @@ TEST(ParserTest, keywordSubAgent) {
         "    \"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\": ["
@@ -884,15 +884,15 @@ TEST_F(TrailingCommasTest, tests) {
   "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",
       },
     },
index 3117d66fc32eaa52d4fef36f75c0e036b7c3899a..775732a15522a5076220538a0fc1edd9cc61564e 100644 (file)
         },
         "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"
             }
         },
index 88af349e3e4d6cba8c5a77b6e1fb188152e84e9f..fd9dbb441342efc816e817eb741101c937907d87 100644 (file)
@@ -6,6 +6,7 @@
 
 #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>
@@ -17,6 +18,7 @@
 #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>
@@ -27,6 +29,8 @@ using namespace isc;
 using namespace isc::d2;
 using namespace isc::hooks;
 using namespace isc::process;
+using namespace isc::config;
+using namespace isc::util;
 
 namespace {
 
@@ -50,11 +54,13 @@ public:
     /// @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.
@@ -70,6 +76,22 @@ public:
         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_;
 
@@ -487,6 +509,7 @@ TEST(D2CfgMgr, construction) {
 /// 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
@@ -499,7 +522,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
                         "\"ncr-format\": \"JSON\", "
                         "\"control-socket\" : {"
                         " \"socket-type\" : \"unix\" ,"
-                        " \"socket-name\" : \"/tmp/d2-ctrl-channel\" "
+                        " \"socket-name\" : \"d2-ctrl-channel\" "
                         "},"
                         "\"hooks-libraries\": ["
                         "{"
@@ -586,7 +609,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
     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();
@@ -997,6 +1020,7 @@ TEST_F(D2CfgMgrTest, configPermutations) {
 
 /// @brief Tests comments.
 TEST_F(D2CfgMgrTest, comments) {
+    setSocketTestPath();
     std::string config = "{ "
                         "\"comment\": \"D2 config\" , "
                         "\"ip-address\" : \"192.168.1.33\" , "
@@ -1004,7 +1028,7 @@ TEST_F(D2CfgMgrTest, comments) {
                         "\"control-socket\": {"
                         " \"comment\": \"Control channel\" , "
                         " \"socket-type\": \"unix\" ,"
-                        " \"socket-name\": \"/tmp/d2-ctrl-channel\" "
+                        " \"socket-name\": \"d2-ctrl-channel\" "
                         "},"
                         "\"tsig-keys\": ["
                         "{"
index 1f3a84bd3375433be5ac5466dad36a5858040b1b..7eb2ac47c52d6c773c7e30ac3e92d6ac12ecf0be 100644 (file)
 #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>
@@ -33,6 +35,7 @@ using namespace isc::d2;
 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;
 
@@ -122,12 +125,7 @@ public:
     /// 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());
     }
 
@@ -143,6 +141,7 @@ public:
         // Reset command manager.
         CommandMgr::instance().deregisterAll();
         CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND);
+        resetSocketPath();
     }
 
     /// @brief Returns pointer to the server's IO service.
@@ -153,6 +152,23 @@ public:
         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
index 7ca53b86c45e920768cbfe4b6c234b52e852d548..2dc7044fbd7091e8add267f8ab4c71eafb936ee3 100644 (file)
@@ -8,12 +8,14 @@
 
 #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>
@@ -28,8 +30,9 @@ using namespace isc::config;
 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 {
 
@@ -117,6 +120,7 @@ public:
     D2GetConfigTest()
     : rcode_(-1) {
         resetHooksPath();
+        resetSocketPath();
         srv_.reset(new D2CfgMgr());
         // Enforce not verbose mode.
         Daemon::setVerbose(false);
@@ -127,6 +131,7 @@ public:
     ~D2GetConfigTest() {
         resetConfiguration();
         resetHooksPath();
+        resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -142,6 +147,22 @@ public:
         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.
@@ -248,6 +269,7 @@ public:
 /// Test a configuration
 TEST_F(D2GetConfigTest, sample1) {
     setHooksTestPath();
+    setSocketTestPath();
 
     // get the sample1 configuration
     std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json";
index d9613a3610c10ef4443aa23bd8698f13b44d12e4..8c2159d4e65bac4e7a8b4ceddddfc3d31edfe921 100644 (file)
@@ -1,7 +1,7 @@
 {
     "DhcpDdns": {
         "control-socket": {
-            "socket-name": "/tmp/kea-ddns-ctrl-socket",
+            "socket-name": "kea-ddns-ctrl-socket",
             "socket-type": "unix"
         },
         "dns-server-timeout": 1000,
index 8765795e785240bc0469c8aa89ec4c8ff3d09f27..b2c2801ab058768d82001c1ea21ec6804b629ae7 100644 (file)
@@ -58,6 +58,8 @@ using namespace isc::data;
 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 {
@@ -206,7 +208,7 @@ const char* PARSER_CONFIGS[] = {
     "        ],"
     "    \"control-socket\": {"
     "        \"socket-type\": \"unix\","
-    "        \"socket-name\": \"/tmp/kea4-ctrl-socket\","
+    "        \"socket-name\": \"kea4-ctrl-socket\","
     "        \"user-context\": { \"comment\": \"Indirect comment\" }"
     "    },"
     "    \"shared-networks\": [ {"
@@ -279,7 +281,6 @@ protected:
     virtual void SetUp() {
         std::vector<std::string> libraries = HooksManager::getLibraryNames();
         ASSERT_TRUE(libraries.empty());
-        resetHooksPath();
     }
 
 public:
@@ -292,6 +293,7 @@ public:
         // Create fresh context.
         resetConfiguration();
         resetHooksPath();
+        Dhcpv4SrvTest::resetSocketPath();
     }
 
 public:
@@ -376,6 +378,9 @@ 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
@@ -6932,6 +6937,7 @@ TEST_F(Dhcp4ParserTest, hostsDatabases) {
 
 // This test checks comments. Please keep it last.
 TEST_F(Dhcp4ParserTest, comments) {
+    Dhcpv4SrvTest::setSocketTestPath();
 
     string config = PARSER_CONFIGS[5];
     extractConfig(config);
@@ -7029,7 +7035,7 @@ TEST_F(Dhcp4ParserTest, comments) {
     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");
index 0634b68b21845536146f3a14a8064cf921e89440..bcfa7faa638c515c9dbf03fbc6cd29483ecd39a5 100644 (file)
@@ -10,6 +10,7 @@
 #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>
@@ -116,12 +117,7 @@ public:
     /// 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,
@@ -131,6 +127,7 @@ public:
     /// @brief Destructor
     ~CtrlChannelDhcpv4SrvTest() {
         resetLogPath();
+        Dhcpv4SrvTest::resetSocketPath();
         LeaseMgrFactory::destroy();
         StatsMgr::instance().removeAll();
 
@@ -159,6 +156,13 @@ public:
         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
index 7a1c4604a2c97e622260b459264667ce051c1308..80c8ae91fca23917c5dbe5338009816279241fc0 100644 (file)
@@ -2955,6 +2955,7 @@ class DBInitializer {
 
 void
 Dhcpv4SrvTest::checkConfigFiles() {
+    setSocketTestPath();
     DBInitializer dbi;
     IfaceMgrTestConfig test_config(true);
     string path = CFG_EXAMPLES;
index fb6fa567a5c1a289e75fc0f6e1292f7365a58657..fe1fd83ab2265a9654b78cab665af60ada0c7ff5 100644 (file)
@@ -9,6 +9,7 @@
 #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>
@@ -33,6 +34,7 @@ using namespace std;
 using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::util;
+using namespace isc::config;
 
 namespace isc {
 namespace dhcp {
@@ -88,6 +90,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
 }
 
 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
+    resetSocketPath();
     // Make sure that we revert to default value
     CfgMgr::instance().clear();
 
@@ -100,6 +103,21 @@ Dhcpv4SrvTest::~Dhcpv4SrvTest() {
     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 =
index fa4cf7e0c725c77ff6b785258aa64b83d0b0f108..9bb8bee0ab93c46b598e09583ca9a78116509b23 100644 (file)
@@ -350,6 +350,13 @@ public:
     /// 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:
index 9ae42adb7d28ff5458f44db34f83498702b53909..92a982b4bd87393916892a78a894a80aca8ff98b 100644 (file)
@@ -2103,7 +2103,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "            }\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"
@@ -11077,7 +11077,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\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"
@@ -12280,10 +12280,12 @@ public:
         srv_.reset(new ControlledDhcpv4Srv(0));
         // Create fresh context.
         resetConfiguration();
+        Dhcpv4SrvTest::setSocketTestPath();
     }
 
     ~Dhcp4GetConfigTest() {
         resetConfiguration();
+        Dhcpv4SrvTest::resetSocketPath();
     };
 
     /// @brief Parse and Execute configuration
index 293f8567fc2ec8835790f87c4a526f06f4263a3b..56365df8696412c11dde2125dcff078f8df656f9 100644 (file)
@@ -314,7 +314,7 @@ const char* PARSER_CONFIGS[] = {
     "        ],"
     "    \"control-socket\": {"
     "        \"socket-type\": \"unix\","
-    "        \"socket-name\": \"/tmp/kea6-ctrl-socket\","
+    "        \"socket-name\": \"kea6-ctrl-socket\","
     "        \"user-context\": { \"comment\": \"Indirect comment\" }"
     "    },"
     "    \"shared-networks\": [ {"
@@ -7477,6 +7477,7 @@ TEST_F(Dhcp6ParserTest, hostsDatabases) {
 
 // This test checks comments. Please keep it last.
 TEST_F(Dhcp6ParserTest, comments) {
+    setSocketTestPath();
 
     string config = PARSER_CONFIGS[9];
     extractConfig(config);
@@ -7586,7 +7587,7 @@ TEST_F(Dhcp6ParserTest, comments) {
     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");
index f6654669e9965195ebebde180a86f2e2f04f685c..d8e96460efcf6be313169bdf3f666fec096533b4 100644 (file)
@@ -9,6 +9,7 @@
 #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>
@@ -148,16 +149,12 @@ public:
     ///
     /// 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
@@ -170,7 +167,7 @@ public:
         IfaceMgr::instance().clearIfaces();
         IfaceMgr::instance().closeSockets();
         IfaceMgr::instance().detectIfaces();
-    };
+    }
 
     /// @brief Returns pointer to the server's IO service.
     ///
index 40ee751caecc37f5034496a24c559ea406eb4833..8ddd8dd7f34a5cdac4259081cccb053426eea72a 100644 (file)
@@ -324,6 +324,7 @@ class DBInitializer {
 
 void
 Dhcpv6SrvTest::checkConfigFiles() {
+    setSocketTestPath();
     DBInitializer dbi;
     IfaceMgrTestConfig test_config(true);
     string path = CFG_EXAMPLES;
index e885b9ee6a0d5727338269b2caa92244b4cea5b7..11a62888f1e85b58f270d2cac737ae19d1ab6a2b 100644 (file)
@@ -7,6 +7,7 @@
 #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>
@@ -26,6 +27,7 @@ using namespace isc::asiolink;
 using namespace isc::stats;
 using namespace isc::util;
 using namespace isc::process;
+using namespace isc::config;
 
 namespace isc {
 namespace dhcp {
@@ -39,6 +41,7 @@ BaseServerTest::BaseServerTest() {
     original_datadir_ = CfgMgr::instance().getDataDir();
     CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
     resetLogPath();
+    resetSocketPath();
 }
 
 BaseServerTest::~BaseServerTest() {
@@ -58,6 +61,7 @@ BaseServerTest::~BaseServerTest() {
     // Revert to unit test logging, in case the test reconfigured it.
     isc::log::initLogger();
     resetLogPath();
+    resetSocketPath();
 }
 
 void
@@ -71,6 +75,22 @@ BaseServerTest::resetLogPath() {
     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::"),
index d2b2d45af6376690b47544c5a6545ec8c015db94..6983823d0705897eb3952288620af0492fcd5b49 100644 (file)
@@ -130,6 +130,13 @@ public:
     /// @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.
index c22f4a7adfcf292e27baeecbe2fa3da5803be4ee..9e09df5b5100d03007dd87f624b71144eec4c7cd 100644 (file)
@@ -1838,7 +1838,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "            }\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"
@@ -9863,7 +9863,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\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"
@@ -10932,11 +10932,13 @@ public:
 
         // Reset configuration for each test.
         resetConfiguration();
+        BaseServerTest::setSocketTestPath();
     }
 
     ~Dhcp6GetConfigTest() {
         // Reset configuration database after each test.
         resetConfiguration();
+        BaseServerTest::resetSocketPath();
     };
 
     /// @brief Parse and Execute configuration
index 873dfd7affa5c864ff5342c9831154f7611857cd..c4e8ee1312382da8b862cb17420745d2ae62d1a7 100644 (file)
     "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"
         }
     },
 
index f88a3a393f958ec9a3392c1b513f3f246ff276c2..524fc81b87f2aa12a8e5a3fc284fccbe72137db5 100644 (file)
@@ -23,7 +23,7 @@
   "port": 53001,
   "control-socket": {
       "socket-type": "unix",
-      "socket-name": "/tmp/kea-ddns-ctrl-socket"
+      "socket-name": "kea-ddns-ctrl-socket"
   },
   "tsig-keys": [],
   "forward-ddns" : {},
index adb5647914bbf63449fe403afd9ef47b00c9f671..ed1d3f97c1a97587fb1723288973e0c8d983f92f 100644 (file)
@@ -49,7 +49,7 @@
     // 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.
index ae2f01f88ae866b724e6c5657b3939e77391b7ef..5632110c86d62f917bf092393c1dbfd950ffbc16 100644 (file)
@@ -43,7 +43,7 @@
     // 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.
index 80372eb266e72fb3bc1cafcdf4cc517f84ce861a..2e47b4d27f5b301bc0547566ec6603382d2188b6 100644 (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"
             }
         }
     },
index e2f5d31486863da81e3d21e7d6e4e85548f9fbc5..1875333698ee0882e2ff5471f7213b8fb1e6526f 100644 (file)
@@ -6,7 +6,7 @@
             "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/"
                 },
index 0dcad36e6c4ec3cd7ee079349a18503eb7759cc7..7c00b0bfdedd5c70d696a805c86982f748662bba 100644 (file)
@@ -10,7 +10,7 @@
 #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>
 
index 35ca637bdc41f5909027a73ffe15d56a9adf0158..4363be4878d3c77aaf6a5eb598c3a4fee1aecba0 100644 (file)
@@ -10,7 +10,7 @@
 
 #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;
index a38f1bd4d733883b9f5899be0afbd1b3e0d121fa..168f32368391d173d96c852d35c7cafdd47cb3ff 100644 (file)
@@ -2,6 +2,8 @@ SUBDIRS = . tests
 
 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)
 
@@ -17,6 +19,7 @@ libkea_cfgclient_la_SOURCES += timeouts.h
 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
index 831d56e78cb61cc21ee505bbcc68c24584d67715..c61005699d0f15c8d3e5e358d809157d8d5f77e1 100644 (file)
@@ -13,6 +13,7 @@
 #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>
@@ -547,7 +548,11 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info)
         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();
@@ -587,6 +592,8 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info)
         doAccept();
 
     } catch (const std::exception& ex) {
+        close(lock_fd);
+        static_cast<void>(::remove(getLockName().c_str()));
         isc_throw(SocketError, ex.what());
     }
 }
index 546c35748ad1fd97354c68409086b2de0bedc282..2a8357d5f8c8e4bc5c1069233c23c48e7e133604 100644 (file)
@@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 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)
 
@@ -25,6 +27,7 @@ run_unittests_SOURCES += command_mgr_unittests.cc
 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)
@@ -39,6 +42,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 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
index b607d3e4144ce275e043af1890205f8754366401..f74c51f7d3f3b6eb24f3d3a79f2330df9241d72b 100644 (file)
 #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>
 
@@ -24,6 +26,7 @@ using namespace isc::asiolink;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::hooks;
+using namespace isc::util;
 using namespace std;
 
 // Test class for Command Manager
@@ -34,6 +37,7 @@ public:
     /// Default constructor
     CommandMgrTest()
         : io_service_(new IOService()) {
+        resetSocketPath();
 
         CommandMgr::instance().setIOService(io_service_);
 
@@ -55,19 +59,24 @@ public:
         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.
@@ -431,6 +440,7 @@ TEST_F(CommandMgrTest, delegateListCommands) {
 // 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);
@@ -449,7 +459,7 @@ TEST_F(CommandMgrTest, unixCreate) {
     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);
 
@@ -459,8 +469,9 @@ TEST_F(CommandMgrTest, unixCreate) {
 
 // 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"
         "\" }");
 
@@ -546,10 +557,11 @@ TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) {
 
 // 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);
diff --git a/src/lib/config/tests/unix_command_config_unittests.cc b/src/lib/config/tests/unix_command_config_unittests.cc
new file mode 100644 (file)
index 0000000..f5b3f70
--- /dev/null
@@ -0,0 +1,65 @@
+// 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
diff --git a/src/lib/config/unix_command_config.cc b/src/lib/config/unix_command_config.cc
new file mode 100644 (file)
index 0000000..7c7d5b3
--- /dev/null
@@ -0,0 +1,75 @@
+// 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
diff --git a/src/lib/config/unix_command_config.h b/src/lib/config/unix_command_config.h
new file mode 100644 (file)
index 0000000..ca2aaa4
--- /dev/null
@@ -0,0 +1,72 @@
+// 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
index 0c98a4a3c3b386adc938d1b40ecdb5607ae60bbb..6e33ea63eb54a0c0296cecedb3ef8b14ac2c60b9 100644 (file)
@@ -8,7 +8,7 @@
 
 #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;
index 652793e64d21e1443dbd9f547afccdb8cc55e1f9..6acbe03a76b24f5fdb038eea4ee1d79fbe68b8c4 100644 (file)
@@ -9,7 +9,7 @@
 #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>
 
index f631523d0c76949ca0b3e987ee94ab7df4c9a5a3..f4438f460ce63fe06265caf78339318bc9ff6a27 100644 (file)
@@ -15,7 +15,6 @@ libkea_util_la_SOURCES += chrono_time_utils.h chrono_time_utils.cc
 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
@@ -66,7 +65,6 @@ libkea_util_include_HEADERS = \
        csv_file.h \
        dhcp_space.h \
        doubles.h \
-       file_utilities.h \
        filename.h \
        hash.h \
        io_utilities.h \
diff --git a/src/lib/util/file_utilities.cc b/src/lib/util/file_utilities.cc
deleted file mode 100644 (file)
index dfe5e89..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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
diff --git a/src/lib/util/file_utilities.h b/src/lib/util/file_utilities.h
deleted file mode 100644 (file)
index 8c34822..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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
index a28cf3ddf1031bb88cd2dc747c4c6f2d48c793c6..cc5a87de542f496c65a2874456553af688108fa3 100644 (file)
@@ -14,6 +14,7 @@
 #include <cstdlib>
 #include <fstream>
 #include <string>
+#include <filesystem>
 #include <iostream>
 
 #include <dirent.h>
@@ -27,12 +28,53 @@ namespace isc {
 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;
@@ -256,6 +298,11 @@ PathChecker::validatePath(const std::string input_path_str,
     return (valid_path);
 }
 
+bool
+PathChecker::pathHasPermissions(mode_t permissions) {
+    return(hasPermissions(path_, permissions));
+}
+
 }  // namespace file
 }  // namespace util
 }  // namespace isc
index 8db8fecb4106fa27bbee6637b0815eb908de1dc0..3b36f3d5bd34fe2f2c50be4863dc3871cb721d2e 100644 (file)
@@ -15,6 +15,15 @@ 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.
@@ -23,6 +32,31 @@ namespace file {
 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.
@@ -186,6 +220,13 @@ public:
     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_);
index 935ec09712059b71397ed2198ce908eccabc48f8..3b6e902441999b1424de0b9f6e5e6499ed9195b2 100644 (file)
@@ -35,7 +35,6 @@ run_unittests_SOURCES += dhcp_space_unittest.cc
 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
diff --git a/src/lib/util/tests/file_utilities_unittest.cc b/src/lib/util/tests/file_utilities_unittest.cc
deleted file mode 100644 (file)
index 4ee9093..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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"));
-}
-
-}
index 88d84fe5a79e6227e488eca0dca61390a4a5ad57..7da8d3cfd501ddea7799170bb039c13f237ff63b 100644 (file)
@@ -22,6 +22,54 @@ using namespace std;
 
 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.
@@ -376,4 +424,14 @@ TEST(PathChecker, validatePathEnforcePathFalse) {
     }
 }
 
+/// @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