]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3477] Checkpoint: did libconfig
authorFrancis Dupont <fdupont@isc.org>
Sat, 6 Jul 2024 20:29:52 +0000 (22:29 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 1 Aug 2024 07:23:53 +0000 (09:23 +0200)
src/lib/config/config_messages.cc
src/lib/config/config_messages.h
src/lib/config/config_messages.mes
src/lib/config/http_command_mgr.cc
src/lib/config/http_command_mgr.h

index f2754a1f1fb3ffe020d966d7648555360966930d..24fd3e96f5c5cc78e4ae1aa3d57b5a9a7df241e7 100644 (file)
@@ -34,6 +34,9 @@ extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL = "COMMAND_SOCKET_WRI
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR = "COMMAND_WATCH_SOCKET_CLEAR_ERROR";
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR = "COMMAND_WATCH_SOCKET_CLOSE_ERROR";
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR = "COMMAND_WATCH_SOCKET_MARK_READY_ERROR";
+extern const isc::log::MessageID HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES = "HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES";
+extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED = "HTTP_COMMAND_MGR_SERVICE_STARTED";
+extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING = "HTTP_COMMAND_MGR_SERVICE_STOPPING";
 
 } // namespace config
 } // namespace isc
@@ -68,6 +71,9 @@ const char* values[] = {
     "COMMAND_WATCH_SOCKET_CLEAR_ERROR", "watch socket failed to clear: %1",
     "COMMAND_WATCH_SOCKET_CLOSE_ERROR", "watch socket failed to close: %1",
     "COMMAND_WATCH_SOCKET_MARK_READY_ERROR", "watch socket failed to mark ready: %1",
+    "HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES", "ignore a change in TLS setup of the http control socket",
+    "HTTP_COMMAND_MGR_SERVICE_STARTED", "started %1 service bound to address %2 port %3",
+    "HTTP_COMMAND_MGR_SERVICE_STOPPING", "stopping %1 service%2",
     NULL
 };
 
index 3aac871c75b6b1e0f44d528d8a4de22bf1552e75..1a8982ea5b45f1a196d2282bff34d60bd444adc6 100644 (file)
@@ -35,6 +35,9 @@ extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL;
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR;
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR;
 extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR;
+extern const isc::log::MessageID HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES;
+extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STARTED;
+extern const isc::log::MessageID HTTP_COMMAND_MGR_SERVICE_STOPPING;
 
 } // namespace config
 } // namespace isc
index 21a92b1a83a13b1e7770d5d3f7bced7b0a1cba48..4e798873e1dc49aeec823f8026942b78bef4711f 100644 (file)
@@ -152,3 +152,18 @@ This error message is issued when the command manager was unable to set
 ready status after scheduling asynchronous send. This is programmatic error
 that should be reported. The command manager may or may not continue
 to operate correctly.
+
+% HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES ignore a change in TLS setup of the http control socket
+The warning message is issued when the HTTP/HTTPS control socket was
+reconfigured with a different TLS setup but keeping the address and port.
+As these changes can't be applied without opening a new socket which
+will conflicts with the existing one they are ignored.
+
+% HTTP_COMMAND_MGR_SERVICE_STARTED started %1 service bound to address %2 port %3
+This informational message indicates that the server has started
+HTTP/HTTPS service on the specified address and port for receiving
+control commands.
+
+% HTTP_COMMAND_MGR_SERVICE_STOPPING stopping %1 service%2
+This informational message indicates that the server has stopped
+HTTP/HTTPS service. When known the address and port are displayed.
index 9f280bd64793d99a5875d4c6b61ec2612347fd28..bc9ad2a27b12056d1471f9c6d55a15c7f3372508 100644 (file)
 #include <config.h>
 
 #include <asiolink/asio_wrapper.h>
-#include <asiolink/io_service.h>
-#include <config/command_mgr.h>
-#include <cc/data.h>
-#include <cc/command_interpreter.h>
-#include <cc/json_feed.h>
-#include <dhcp/iface_mgr.h>
 #include <config/config_log.h>
+#include <config/http_command_mgr.h>
+#include <config/http_command_response_creator_factory.h>
+#include <config/http_command_response_creator.h>
 #include <config/timeouts.h>
+#include <sstream>
+#include <vector>
 
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::config;
-using namespace isc::data;
-namespace ph = std::placeholders;
+using namespace isc::http;
+using namespace std;
 
 namespace isc {
 namespace config {
 
+/// @brief Implementation of the @c HttpCommandMgr.
+class HttpCommandMgrImpl {
+public:
+
+    /// @brief Constructor.
+    HttpCommandMgrImpl()
+        : io_service_(), timeout_(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND),
+          current_config_(), http_listeners_(), active_(0) {
+    }
+
+    /// @brief Configure control socket from configuration.
+    void configure(HttpCommandConfigPtr config);
+
+    /// @brief Close control socket.
+    ///
+    /// @param remove When true remove the listeners immediately.
+    void close(bool remove);
+
+    /// @brief Removes listeners which are no longer in use.
+    void garbageCollectListeners();
+
+    /// @brief Returns a const pointer to the HTTP listener.
+    ConstHttpListenerPtr getHttpListener() const;
+
+    /// @brief Pointer to the IO service.
+    IOServicePtr io_service_;
+
+    /// @brief Connection timeout.
+    long timeout_;
+
+    /// @brief Current config.
+    HttpCommandConfigPtr current_config_;
+
+    /// @brief Active listeners.
+    vector<HttpListenerPtr> http_listeners_;
+
+    /// @brief Number of active listeners (0 or 1).
+    size_t active_;
+};
+
+void
+HttpCommandMgrImpl::configure(HttpCommandConfigPtr config) {
+    // First case: from no config to no config.
+    if (!config && http_listeners_.empty()) {
+        return;
+    }
+
+    // Second case: from config to no config.
+    if (!config && !http_listeners_.empty()) {
+        close(false);
+        return;
+   }
+
+    // Third case: no address or port change.
+    if (config && current_config_ &&
+        (config->getSocketAddress() == current_config_->getSocketAddress()) &&
+        (config->getSocketPort() == current_config_->getSocketPort())) {
+        // Overwrite the authentication setup in the response creator config.
+        current_config_->setAuthConfig(config->getAuthConfig());
+        // Overwrite the emulation flag in the response creator config.
+        current_config_->setEmulateAgentResponse(config->getEmulateAgentResponse());
+        // Check if TLS setup changed.
+        if ((config->getTrustAnchor() != current_config_->getTrustAnchor()) ||
+            (config->getCertFile() != current_config_->getCertFile()) ||
+            (config->getKeyFile() != current_config_->getKeyFile()) ||
+            (config->getCertRequired() != current_config_->getCertRequired())) {
+            LOG_WARN(command_logger, HTTP_COMMAND_MGR_IGNORED_TLS_SETUP_CHANGES);
+        } else {
+            current_config_ = config;
+        }
+        return;
+    }
+
+    // Last case: from no config, or address or port change.
+    /////
+    current_config_ = config;
+    IOAddress server_address = config->getSocketAddress();
+    uint16_t server_port = config->getSocketPort();
+    bool use_https = false;
+    TlsContextPtr tls_context;
+    if (!config->getCertFile().empty()) {
+        TlsContext::configure(tls_context,
+                              TlsRole::SERVER,
+                              config->getTrustAnchor(),
+                              config->getCertFile(),
+                              config->getKeyFile(),
+                              config->getCertRequired());
+        use_https = true;
+    }
+    // Create response creator factory first. It will be used to
+    // generate response creators. Each response creator will be used
+    // to generate answer to specific request.
+    HttpResponseCreatorFactoryPtr rfc(new HttpCommandResponseCreatorFactory(config));
+    // Create HTTP listener. It will open up a TCP socket and be
+    // prepared to accept incoming connection.
+    HttpListenerPtr http_listener
+        (new HttpListener(io_service_,
+                          server_address,
+                          server_port,
+                          tls_context,
+                          rfc,
+                          HttpListener::RequestTimeout(TIMEOUT_AGENT_RECEIVE_COMMAND),
+                          HttpListener::IdleTimeout(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT)));
+    // Instruct the HTTP listener to actually open socket, install
+    // callback and start listening.
+    http_listener->start();
+
+    // The new listener is running so add it to the collection of
+    // active listeners. The next step will be to remove all other
+    // active listeners, but we do it inside the main process loop.
+    http_listeners_.push_back(http_listener);
+    active_ = 1;
+
+    // Ok, seems we're good to go.
+    LOG_INFO(command_logger, HTTP_COMMAND_MGR_SERVICE_STARTED)
+        .arg(use_https ? "HTTPS" : "HTTP")
+        .arg(server_address.toText())
+        .arg(server_port);
+}
+
+void
+HttpCommandMgrImpl::close(bool remove) {
+    bool use_https = false;
+    ostringstream ep;
+    if (current_config_) {
+        use_https = !current_config_->getCertFile().empty();
+        ep << " bound to address " << current_config_->getSocketAddress()
+           << " port " << current_config_->getSocketPort();
+    }
+    LOG_INFO(command_logger, HTTP_COMMAND_MGR_SERVICE_STOPPING)
+        .arg(use_https ? "HTTPS" : "HTTP")
+        .arg(ep.str());
+    current_config_.reset();
+    active_ = 0;
+    if (remove) {
+        garbageCollectListeners();
+    }
+}
+
+void
+HttpCommandMgrImpl::garbageCollectListeners() {
+    // We expect only one active listener. If there are more (most likely 2),
+    // it means we have just reconfigured the server and need to shut down all
+    // listeners except the most recently added.
+    if (http_listeners_.size() > active_) {
+        // Stop no longer used listeners.
+        for (auto l = http_listeners_.begin();
+             l != http_listeners_.end() - active_;
+             ++l) {
+            (*l)->stop();
+        }
+        // We have stopped listeners but there may be some pending handlers
+        // related to these listeners. Need to invoke these handlers.
+        try {
+            io_service_->poll();
+        } catch (...) {
+        }
+        // Finally, we're ready to remove no longer used listeners.
+        http_listeners_.erase(http_listeners_.begin(),
+                              http_listeners_.end() - active_);
+    }
+}
+
+ConstHttpListenerPtr
+HttpCommandMgrImpl::getHttpListener() const {
+    // Return the most recent listener or null.
+    return (http_listeners_.empty() ? ConstHttpListenerPtr() :
+            http_listeners_.back());
+}
+
+HttpCommandMgr&
+HttpCommandMgr::instance() {
+    static HttpCommandMgr http_cmd_mgr;
+    return (http_cmd_mgr);
+}
+
+HttpCommandMgr::HttpCommandMgr()
+    : HookedCommandMgr(), impl_(new HttpCommandMgrImpl()) {
+}
+
+void
+HttpCommandMgr::setIOService(const IOServicePtr& io_service) {
+    impl_->io_service_ = io_service;
+}
+
+void
+HttpCommandMgr::setConnectionTimeout(const long timeout) {
+    impl_->timeout_ = timeout;
+}
+
+void
+HttpCommandMgr::configure(HttpCommandConfigPtr config) {
+    impl_->configure(config);
+}
+
+void
+HttpCommandMgr::close(bool remove) {
+    impl_->close(remove);
+}
+
+void
+HttpCommandMgr::garbageCollectListeners() {
+    impl_->garbageCollectListeners();
+}
+
+ConstHttpListenerPtr
+HttpCommandMgr::getHttpListener() const {
+    return (impl_->getHttpListener());
+}
+
 } // end of isc::config
 } // end of isc
index 80f8b2f92debaad897f4a4036be2f5bcb8eb3e88..10f1d8b3705920458eb679ff7d4d76516f99aa5c 100644 (file)
@@ -8,15 +8,79 @@
 #define HTTP_COMMAND_MGR_H
 
 #include <asiolink/io_service.h>
-#include <cc/data.h>
+#include <config/http_command_config.h>
 #include <config/hooked_command_mgr.h>
-#include <exceptions/exceptions.h>
+#include <http/listener.h>
 #include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace config {
 
+/// @brief Declaration of the implementation class.
+class HttpCommandMgrImpl;
+
+/// @brief HTTP Commands Manager implementation for the Kea servers.
+///
+/// Similar to @c CommandMgr but using HTTP/HTTPS instead of UNIX sockets.
+class HttpCommandMgr : public HookedCommandMgr, public boost::noncopyable {
+public:
+
+    /// @brief HttpCommandMgr is a singleton class. This method
+    /// returns reference to its sole instance.
+    ///
+    /// @return The only existing instance of the manager.
+    static HttpCommandMgr& instance();
+
+    /// @brief Sets IO service to be used by the command manager.
+    ///
+    /// The server should use this method to provide the Command
+    /// Manager with the common IO service used by the server.
+    /// @param io_service Pointer to the IO service.
+    void setIOService(const asiolink::IOServicePtr& io_service);
+
+    /// @brief Override default connection timeout.
+    ///
+    /// @param timeout New connection timeout in milliseconds.
+    void setConnectionTimeout(const long timeout);
+
+    /// @brief Configure control socket from configuration.
+    ///
+    /// @param config Configuration of the control socket.
+    void configure(HttpCommandConfigPtr config);
+
+    /// @brief Close control socket.
+    ///
+    /// @note When remove is false @c garbageCollectListeners must
+    /// be called after.
+    ///
+    /// @param remove When true remove the listeners immediately.
+    void close(bool remove = true);
+
+    /// @brief Removes listeners which are no longer in use.
+    ///
+    /// This method should be called after server reconfiguration to
+    /// remove listeners used previously (no longer used because the
+    /// listening address and port has changed as a result of the
+    /// reconfiguration). If there are no listeners additional to the
+    /// one that is currently in use, the method has no effect.
+    /// This method is reused to remove all listeners at shutdown time.
+    void garbageCollectListeners();
+
+    /// @brief Returns a const pointer to the HTTP listener.
+    ///
+    /// @return Const pointer to the currently used listener or null pointer if
+    /// we're not listening.
+    isc::http::ConstHttpListenerPtr getHttpListener() const;
+
+private:
+
+    /// @brief Private constructor.
+    HttpCommandMgr();
+
+    /// @brief Pointer to the implementation of the @ref HttpCommandMgr.
+    boost::shared_ptr<HttpCommandMgrImpl> impl_;
+};
+
 } // end of isc::config namespace
 } // end of isc namespace
 #endif