]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3840] Backport ctl channel socket restrictions
authorThomas Markwalder <tmark@isc.org>
Mon, 19 May 2025 19:49:23 +0000 (15:49 -0400)
committerRazvan Becheriu <razvan@isc.org>
Mon, 19 May 2025 20:24:03 +0000 (23:24 +0300)
On branch 3840-restrict-ctl-sockets-v6
Changes to be committed:
modified:   doc/examples/agent/comments.json
modified:   doc/examples/agent/simple.json
modified:   doc/examples/ddns/all-keys-netconf.json
modified:   doc/examples/ddns/all-keys.json
modified:   doc/examples/ddns/comments.json
modified:   doc/examples/ddns/sample1.json
modified:   doc/examples/ddns/template.json
modified:   doc/examples/kea4/advanced.json
modified:   doc/examples/kea4/all-keys-netconf.json
modified:   doc/examples/kea4/all-keys.json
modified:   doc/examples/kea4/comments.json
modified:   doc/examples/kea4/config-backend.json
modified:   doc/examples/kea4/dhcpv4-over-dhcpv6.json
modified:   doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json
modified:   doc/examples/kea4/ha-load-balancing-server2-mt.json
modified:   doc/examples/kea6/advanced.json
modified:   doc/examples/kea6/all-keys-netconf.json
modified:   doc/examples/kea6/all-keys.json
modified:   doc/examples/kea6/comments.json
modified:   doc/examples/kea6/config-backend.json
modified:   doc/examples/kea6/dhcpv4-over-dhcpv6.json
modified:   doc/examples/kea6/ha-hot-standby-server1-with-tls.json
modified:   doc/examples/kea6/ha-hot-standby-server2.json
modified:   doc/examples/netconf/comments.json
modified:   doc/examples/netconf/simple-dhcp4.json
modified:   doc/examples/netconf/simple-dhcp6.json
modified:   doc/sphinx/arm/agent.rst
modified:   doc/sphinx/arm/ddns.rst
modified:   doc/sphinx/arm/dhcp4-srv.rst
modified:   doc/sphinx/arm/dhcp6-srv.rst
modified:   doc/sphinx/arm/hooks.rst
modified:   src/bin/agent/ca_cfg_mgr.cc
modified:   src/bin/agent/ca_command_mgr.cc
modified:   src/bin/agent/simple_parser.cc
modified:   src/bin/agent/tests/ca_cfg_mgr_unittests.cc
modified:   src/bin/agent/tests/ca_command_mgr_unittests.cc
modified:   src/bin/agent/tests/ca_controller_unittests.cc
modified:   src/bin/agent/tests/get_config_unittest.cc
modified:   src/bin/agent/tests/parser_unittests.cc
modified:   src/bin/agent/tests/testdata/get_config.json
modified:   src/bin/d2/tests/d2_cfg_mgr_unittests.cc
modified:   src/bin/d2/tests/d2_command_unittest.cc
modified:   src/bin/d2/tests/get_config_unittest.cc
modified:   src/bin/d2/tests/testdata/get_config.json
modified:   src/bin/dhcp4/tests/config_parser_unittest.cc
modified:   src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
modified:   src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
modified:   src/bin/dhcp4/tests/dhcp4_test_utils.cc
modified:   src/bin/dhcp4/tests/dhcp4_test_utils.h
modified:   src/bin/dhcp4/tests/get_config_unittest.cc
modified:   src/bin/dhcp6/tests/config_parser_unittest.cc
modified:   src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
modified:   src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
modified:   src/bin/dhcp6/tests/dhcp6_test_utils.cc
modified:   src/bin/dhcp6/tests/dhcp6_test_utils.h
modified:   src/bin/dhcp6/tests/get_config_unittest.cc
modified:   src/bin/keactrl/kea-ctrl-agent.conf.pre
modified:   src/bin/keactrl/kea-dhcp-ddns.conf.pre
modified:   src/bin/keactrl/kea-dhcp4.conf.pre
modified:   src/bin/keactrl/kea-dhcp6.conf.pre
modified:   src/bin/keactrl/kea-netconf.conf.pre
modified:   src/bin/netconf/tests/testdata/get_config.json
modified:   src/lib/config/Makefile.am
modified:   src/lib/config/command_mgr.cc
modified:   src/lib/config/tests/Makefile.am
modified:   src/lib/config/tests/command_mgr_unittests.cc
new file:   src/lib/config/tests/unix_command_config_unittests.cc
new file:   src/lib/config/unix_command_config.cc
new file:   src/lib/config/unix_command_config.h
modified:   src/lib/util/filesystem.cc
modified:   src/lib/util/filesystem.h
modified:   src/lib/util/tests/filesystem_unittests.cc

72 files changed:
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/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/util/filesystem.cc
src/lib/util/filesystem.h
src/lib/util/tests/filesystem_unittests.cc

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 5e82b99530bd217d154358e986148364d8dec87e..1b249b2479e2a1549878b77390f6dd4a60a53f36 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 642258dc35d30ec46c5bbb739fcbb233027410b1..be481f6851e50f7b1a885485e34431c07b7f0e7a 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 bcb4735d9868b2772d2b5c472768e8cdcd52d1fe..5d562c85f453fe0f2668f7221c37e44bfdbab1e7 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 4d8148bc29f41c30bfe0a78d78eb8c2895da8a1e..2c43ba920ae2bc9f2533cbd072b37ff85840e10b 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 30d3333ac623cf1e5ecf85d6a9ecaed39ae3291c..f0b810ea4696eb2b32ea9b3e6c9e5c97f002946a 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 9182fdf1e6ed2fd711c530ca919880fc0533f1e1..42d50624045cc4b9be734e3fca124165f0ba3d6f 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 098119869dac89b50a6925c84fa2bbd9ebdfe806..ea00a073046710d76888f175e19a357b84bc36a7 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 82d36fb041df27bc6fdcd034904ae433c0095f4b..64899712132f8d523600aa10ef0982df2308a8c7 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 7909c72e6ec591fe916e158980f0127bcfb9928f..ff338470d8a582143d7d809dfee2a1a43cfc2876 100644 (file)
@@ -36,7 +36,7 @@
       "name": "kea-dhcp4",
       "output-options": [
           {
-            "output": "/tmp/kea-dhcp4.log"
+            "output": "kea-dhcp4.log"
           }
       ],
       "severity": "DEBUG",
index fdef507e70bbf4d87123c2a21c64f2889427b59f..8ece46642ed7f826d4451e345435baf5d5f142ab 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 154a129f8ac1d2337a5c1d477d6c0b18b10e0673..94f81cb6ed2fac6ca9dc6e32136f1f5cc80e5830 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 6d4537c64a3999eb9da00af49262bf165dd7a2f2..53edc1285c6a33e16ea2a93719da7cc20cf6211d 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 86a9f8c122e413b89514da9c29405111b9c3c432..b0c77438e7feaf36108c6acb62f418731824e198 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 4d4fbebeed5dfdc9ed8c29f728727490dd4cfbda..326324aaa204e11a6d9facc3762c597b2d530820 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 d269f3f120c98dfbd78725e1af48c90a052901fe..9db07342e172faa37f35a87ec08c3666e015221b 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 e723f69d01ae9ace0d6a7ee51f99f67a5fc06b75..698fc9648129ab1b2e440742399e3fcb2e37a45c 100644 (file)
@@ -46,7 +46,7 @@
       "name": "kea-dhcp6",
       "output-options": [
           {
-            "output": "/tmp/kea-dhcp6.log"
+            "output": "kea-dhcp6.log"
           }
       ],
       "severity": "DEBUG",
index 9fab5429c932d2e6d5476be139e642e76b9686e7..6e67645b57b03716a92e3e37b0bf97f4408f92e9 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 6a9a680ed9d1199846fc368c83f5fb836dcc0d21..037fd978107427f131bd1a4b1448d27d1b239524 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 887c673065fb6eb35ea05d9511aacf6d7cc3f494..6a761d9a833585ef0b821de719e6397276a1d97a 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 1829a44a2b2d0decc0907c0c0ce0c505fcd6373e..03b6ab811842306f227828ee39bda8b121c1f6a4 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 328c03a8945f218df32120435014f2ee614fa982..1581a05976419f6218838ed9f00e621bdffbf1ed 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.6.3, 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 4fac99100169fe030850b105ed3fbc8de5c4821c..7cb114dac5c267c9e61ceb19c8edcfb3e8924db4 100644 (file)
@@ -303,6 +303,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.6.3, 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 25bea8dab6583e930d23a10683cc4011e64682b6..8b9e21f36fb9d4ddc021ddeb7ed72094a51337fd 100644 (file)
@@ -7588,6 +7588,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.6.3, 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 09931616ad796600161c0e883f005c2e3cd42709..9f30d2820603ba0ec4f2cb486ede0363bb87c3c0 100644 (file)
@@ -7419,6 +7419,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.6.3, 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 8d037a57350d1908aba4651ace2f119e07cdc8ee..f420848ed20dde7f08781991c30854d7a00fd2c6 100644 (file)
@@ -215,7 +215,7 @@ configuration would be:
 
 As of Kea 2.6.3, 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 f1d1047917366e03fb9c2af5a11b97c63b6cb484..a5ad667bbf74e07839753921fc1b0808cd7dca30 100644 (file)
@@ -201,9 +201,13 @@ CtrlAgentCfgContext::toElement() const {
     // Set control-sockets
     ElementPtr control_sockets = Element::createMap();
     for (auto const& si : ctrl_sockets_) {
-        ConstElementPtr socket = UserContext::toElement(si.second);
-        control_sockets->set(si.first, socket);
+        // 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 9411181dc46f75a4a09374e5e7921cdd96563daa..7c51c72246f29d83c837705297064f937cba95a5 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 7a6a76372cd9d8e14439ab45e8c2625c02f9610c..87fea25cee909581680f3fe2c81a6ba93db39dc4 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-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
 #include <asiolink/io_service_mgr.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>
 
 using namespace isc::data;
 using namespace isc::asiolink;
+using namespace isc::config;
 
 namespace isc {
 namespace agent {
@@ -149,7 +151,14 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
     if (ctrl_sockets) {
         auto const& sockets_map = ctrl_sockets->mapValue();
         for (auto const& cs : sockets_map) {
-            ctx->setControlSocketInfo(cs.second, cs.first);
+            // 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 20263d74b4da0b76bfea56550f4e7a67a2449fdb..b932ba26b2e7289193f9d4893bdb13b87621b0d2 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"
     "}"
@@ -427,11 +431,13 @@ public:
     /// @brief Constructor.
     AgentParserTest() {
         resetHooksPath();
+        setSocketTestPath();
     }
 
     /// @brief Destructor.
     virtual ~AgentParserTest() {
         resetHooksPath();
+        resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -447,6 +453,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
@@ -470,6 +490,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_;
 };
@@ -503,8 +537,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 +552,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 +567,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 +582,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 4cef0426cd69a81286bfa36c8003c87d1d7f09fb..31f36bedefb3d48ab6a303aa02beedf55b108239 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 44f2b2e4a2e1c1aa1f99bc30d99a2a50b329065c..e34649c8f4a2551ada48fef2741ceea3559277a4 100644 (file)
@@ -12,7 +12,9 @@
 #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>
@@ -26,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;
 
@@ -39,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\""
     "    }"
     "  }"
     "}";
@@ -60,6 +64,12 @@ public:
     /// @brief Constructor.
     CtrlAgentControllerTest()
         : DControllerTest(CtrlAgentController::instance) {
+        setSocketTestPath();
+    }
+
+    /// @brief Destructor.
+    virtual ~CtrlAgentControllerTest() {
+        resetSocketPath();
     }
 
     /// @brief Returns pointer to CtrlAgentProcess instance.
@@ -85,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.
     ///
@@ -100,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.
@@ -279,11 +302,11 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) {
         "  \"control-sockets\": {"
         "    \"dhcp4\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp4/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    },"
         "    \"dhcp6\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp6/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    }"
         "  }"
         "}";
@@ -322,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_socket6");
+    testUnixSocketInfo("dhcp6", "second_socket6");
 
     // After the shutdown the HTTP listener no longer exists.
     CtrlAgentProcessPtr process = getCtrlAgentProcess();
@@ -345,11 +368,11 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) {
         "  \"control-sockets\": {"
         "    \"dhcp4\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp4/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    },"
         "    \"dhcp6\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp6/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    }"
         "  }"
         "}";
@@ -388,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();
@@ -411,11 +434,11 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) {
         "  \"control-sockets\": {"
         "    \"dhcp4\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp4/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    },"
         "    \"dhcp6\": {"
         "      \"socket-type\": \"unix\","
-        "      \"socket-name\": \"/second/dhcp6/socket\""
+        "      \"socket-name\": \"second_socket6\""
         "    }"
         "  }"
         "}";
@@ -453,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_socket6");
+    testUnixSocketInfo("dhcp6", "second_socket6");
 
     CtrlAgentProcessPtr process = getCtrlAgentProcess();
     ASSERT_TRUE(process);
index 6f8cb93764b9205ac77827b8b8f3f1a997d2df65..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>
 
@@ -30,6 +32,8 @@ using namespace isc::data;
 using namespace isc::process;
 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 3432cff8a6c81159b25f637bd091c6e018429186..0884f10a8df40e3cd8632b09b7362c6b4533eed9 100644 (file)
@@ -136,15 +136,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\": ["
@@ -173,15 +173,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\": ["
@@ -888,15 +888,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 88da8c681284078aeac9f0b1760fda3e3f566b36..c8fd3b1724e486a8302cda454cfa88d537336a93 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/encode.h>
+#include <util/filesystem.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -26,6 +28,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 {
 
@@ -49,11 +53,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.
@@ -69,6 +75,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_;
 
@@ -477,6 +499,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
@@ -986,6 +1009,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\" , "
index 1f3a84bd3375433be5ac5466dad36a5858040b1b..b901826f4d37cb0a3f201c21e7ac3ca567dc8f5d 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 6eee45d0666d3b215b88e9e08187a51f0c17ae65..c14b3e608794e06c55dd8a621beb8234aa24a740 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>
@@ -30,6 +32,7 @@ using namespace isc::data;
 using namespace isc::process;
 using namespace isc::test;
 using namespace isc::hooks;
+using namespace isc::util;
 
 namespace {
 
@@ -142,6 +145,7 @@ public:
     D2GetConfigTest()
     : rcode_(-1) {
         resetHooksPath();
+        resetSocketPath();
         srv_.reset(new D2CfgMgr());
         // Enforce not verbose mode.
         Daemon::setVerbose(false);
@@ -155,6 +159,7 @@ public:
         static_cast<void>(remove(test_file_name.c_str()));
         resetConfiguration();
         resetHooksPath();
+        resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -170,6 +175,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.
@@ -279,6 +300,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 ee3deccbb01d2266c5ff69d1a2e6f766b9db96d9..b5329233e80b22d79d6e1a216d6e07cee5bde406 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 800fd4cd4ad2812ce97e5e5d9dd99774c20e3bb6..f84623fb829d9f0689c2dbf6ec776263791f2987 100644 (file)
@@ -37,6 +37,7 @@
 #include <testutils/test_to_element.h>
 #include <util/chrono_time_utils.h>
 #include <util/doubles.h>
+#include <util/filesystem.h>
 
 #include <boost/scoped_ptr.hpp>
 
@@ -62,6 +63,7 @@ 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 {
@@ -237,7 +239,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\": [ {"
@@ -314,6 +316,7 @@ public:
         resetConfiguration();
 
         resetHooksPath();
+        Dhcpv4SrvTest::resetSocketPath();
     }
 
     ~Dhcp4ParserTest() {
@@ -325,6 +328,7 @@ public:
         static_cast<void>(remove(UNLOAD_MARKER_FILE));
 
         resetHooksPath();
+        Dhcpv4SrvTest::resetSocketPath();
     }
 
     /// @brief Sets the Hooks path from which hooks can be loaded.
@@ -6984,6 +6988,7 @@ TEST_F(Dhcp4ParserTest, hostsDatabases) {
 
 // This test checks comments. Please keep it last.
 TEST_F(Dhcp4ParserTest, comments) {
+    Dhcpv4SrvTest::setSocketTestPath();
 
     string config = PARSER_CONFIGS[6];
     extractConfig(config);
@@ -7081,7 +7086,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 d99b7b3233aa2cc01e905350736491fd1f65f0d7..c0604d54bb8ec496d8042c6668fa80e59623058c 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();
 
@@ -160,6 +157,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 b97776e72ee6d6f822cfcbc82ac5d3da3c7538b0..3f4970075f8d21b4d2a017930115b2dfdfcca4b5 100644 (file)
@@ -2987,6 +2987,7 @@ class DBInitializer {
 
 void
 Dhcpv4SrvTest::checkConfigFiles() {
+    setSocketTestPath();
     DBInitializer dbi;
     IfaceMgrTestConfig test_config(true);
     string path = CFG_EXAMPLES;
index acc62bea803fb6251be31fb3e68385aeff6d33f8..175f72ad9ff8e2b3d50456ace37c99ede1ce5cad 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;
 using namespace boost::posix_time;
 
 namespace isc {
@@ -89,6 +91,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
 }
 
 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
+    resetSocketPath();
     // Make sure that we revert to default value
     CfgMgr::instance().clear();
 
@@ -101,6 +104,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 269e6906f01eaf9fbf51bfac18a19823714a1e4c..b4f9902a6f9e8060ef0df050e2179c919bd93bd0 100644 (file)
@@ -384,6 +384,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 781981d582107475762c2e47e0d5ba66b0a4f6bd..75f6855fec91387dc29fb86c149858fb6c341372 100644 (file)
@@ -2089,7 +2089,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"
@@ -11280,7 +11280,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"
@@ -12721,10 +12721,12 @@ public:
         srv_.reset(new ControlledDhcpv4Srv(0));
         // Create fresh context.
         resetConfiguration();
+        Dhcpv4SrvTest::setSocketTestPath();
     }
 
     ~Dhcp4GetConfigTest() {
         resetConfiguration();
+        Dhcpv4SrvTest::resetSocketPath();
     };
 
     /// @brief Parse and Execute configuration
index ab95e57e1ed9fcc0135c66896baedefe6f2ec672..75132ae696e1afc99486aa72dea40ff7d06f560c 100644 (file)
@@ -318,7 +318,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\": [ {"
@@ -7755,6 +7755,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);
@@ -7864,7 +7865,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 21f85020625cf1c8f47eae6cc048bf8c29d8ce5e..708845991d5e460dcc81a9b3025007cd2190e746 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 <config/timeouts.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/testutils/iface_mgr_test_config.h>
@@ -149,16 +150,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
index 78cc98dea3f0596179a7d66b6668a938f21f4e2a..d89941218dd231e81b85208eeb62994a4b80e412 100644 (file)
@@ -311,6 +311,7 @@ class DBInitializer {
 
 void
 Dhcpv6SrvTest::checkConfigFiles() {
+    setSocketTestPath();
     DBInitializer dbi;
     IfaceMgrTestConfig test_config(true);
     string path = CFG_EXAMPLES;
index b7c486fdb3f3ca25f75ddec418580d839432465d..8c94ec09f3b41cf0c01d9b41c11e6a0ec2c04ec5 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/testutils/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;
 using namespace boost::posix_time;
 
 namespace isc {
@@ -40,6 +42,7 @@ BaseServerTest::BaseServerTest() {
     original_datadir_ = CfgMgr::instance().getDataDir();
     CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
     resetLogPath();
+    resetSocketPath();
 }
 
 BaseServerTest::~BaseServerTest() {
@@ -59,6 +62,7 @@ BaseServerTest::~BaseServerTest() {
     // Revert to unit test logging, in case the test reconfigured it.
     isc::log::initLogger();
     resetLogPath();
+    resetSocketPath();
 }
 
 void
@@ -72,6 +76,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 938d67ca4816ec28cfb50f9f96fe5a64a209d38b..e8c8abdf6488b38c3669d074d1a6e88527752d26 100644 (file)
@@ -132,6 +132,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 6bc8471fa2da5e0fc88bb1f74a8545fa5335146b..093f1e25e583fccbdca39adfeea0595e81b1edda 100644 (file)
@@ -1983,7 +1983,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"
@@ -11045,7 +11045,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"
@@ -12851,11 +12851,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 f31096044d0373d29b9e4bbe51cd75122f199e79..9569919fce4e08f3d1a05da18985d8aa6e4c5da2 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 0d7f11536e364061a665a324c513dd8cc69a2f62..867f01802901787343895dd557d1879524eacddb 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 06070508fc8c99a46aede140b48c4d35e4a5fbe7..07511ccef8392f035164e35d6d280301c83313f9 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 183866d58b21e4891a25d713963480855ea2c8d7..221381e68f5c2fb8065932b4eb9c183d58d7f723 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 bc7b3926f4203891d1eacde4d4d50d4e0bd211be..b559604ecd821e1672ac2921d319238829bd93aa 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 d5120626d425a1def268bf02b81e1315dce45152..8bd20b3e5c33085cc3976541281761be6edc03df 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 cf21a697abc96742bd90296a0fb01f9feca45006..8093f44068d7bf1baf5750a77f837fc2b13add38 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>
@@ -544,7 +545,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();
@@ -584,6 +589,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 547d0ef1321c6dc0be6f733cd9df06cd4bac31f7..138ce1d09b2e9b90ab336f6bccf17968e2ff1e5f 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)
 
@@ -24,6 +26,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)
@@ -38,6 +41,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 492bcfa825f9540d4187e9cf1af995c1dc8693e6..3856971716c829ff5bef61518a9cbf3c67ecfce2 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.
@@ -430,6 +439,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);
@@ -448,7 +458,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);
 
@@ -458,8 +468,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"
         "\" }");
 
@@ -545,10 +556,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..6c8da27
--- /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 3b01ee644593aeb7f4971ff221ed0f4a43677ccd..cf7a506f2a7c1c718c272cc12abcfa1d366b4e1b 100644 (file)
@@ -51,6 +51,21 @@ exists(string const& path) {
     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;
@@ -274,6 +289,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 951dd622db02b91a1c9d0b18eebcac0b2c9b4430..45be0c76a0b6439dfdfe3006fe8f69484cf5acd4 100644 (file)
@@ -41,6 +41,22 @@ exists(const std::string& path);
 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.
@@ -201,6 +217,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 40723726c16f4f295ebfdb430be4a2b6430675dc..ca6b1540f797e873e43b3cfbd2f03fdfc36006fd 100644 (file)
@@ -424,4 +424,15 @@ 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