summaryrefslogtreecommitdiffstats
path: root/src/lib/config/cmd_response_creator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/config/cmd_response_creator.cc')
-rw-r--r--src/lib/config/cmd_response_creator.cc176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/lib/config/cmd_response_creator.cc b/src/lib/config/cmd_response_creator.cc
new file mode 100644
index 0000000..c586d98
--- /dev/null
+++ b/src/lib/config/cmd_response_creator.cc
@@ -0,0 +1,176 @@
+// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <config/cmd_response_creator.h>
+#include <config/command_mgr.h>
+#include <config/config_log.h>
+#include <cc/command_interpreter.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <boost/pointer_cast.hpp>
+#include <iostream>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+using namespace std;
+
+namespace isc {
+namespace config {
+
+HttpAuthConfigPtr CmdResponseCreator::http_auth_config_;
+
+unordered_set<string> CmdResponseCreator::command_accept_list_;
+
+HttpRequestPtr
+CmdResponseCreator::createNewHttpRequest() const {
+ return (HttpRequestPtr(new PostHttpRequestJson()));
+}
+
+HttpResponsePtr
+CmdResponseCreator::
+createStockHttpResponse(const HttpRequestPtr& request,
+ const HttpStatusCode& status_code) const {
+ HttpResponsePtr response = createStockHttpResponseInternal(request, status_code);
+ response->finalize();
+ return (response);
+}
+
+HttpResponsePtr
+CmdResponseCreator::
+createStockHttpResponseInternal(const HttpRequestPtr& request,
+ const HttpStatusCode& status_code) const {
+ // The request hasn't been finalized so the request object
+ // doesn't contain any information about the HTTP version number
+ // used. But, the context should have this data (assuming the
+ // HTTP version is parsed OK).
+ HttpVersion http_version(request->context()->http_version_major_,
+ request->context()->http_version_minor_);
+ // We only accept HTTP version 1.0 or 1.1. If other version number is found
+ // we fall back to HTTP/1.0.
+ if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
+ http_version.major_ = 1;
+ http_version.minor_ = 0;
+ }
+ // This will generate the response holding JSON content.
+ HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
+ return (response);
+}
+
+HttpResponsePtr
+CmdResponseCreator::createDynamicHttpResponse(HttpRequestPtr request) {
+ HttpResponseJsonPtr http_response;
+
+ // Check the basic HTTP authentication.
+ if (http_auth_config_) {
+ http_response = http_auth_config_->checkAuth(*this, request);
+ if (http_response) {
+ return (http_response);
+ }
+ }
+
+ // The request is always non-null, because this is verified by the
+ // createHttpResponse method. Let's try to convert it to the
+ // PostHttpRequestJson type as this is the type generated by the
+ // createNewHttpRequest. If the conversion result is null it means that
+ // the caller did not use createNewHttpRequest method to create this
+ // instance. This is considered an error in the server logic.
+ PostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
+ PostHttpRequestJson>(request);
+ if (!request_json) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+
+ // We have already checked that the request is finalized so the call
+ // to getBodyAsJson must not trigger an exception.
+ ConstElementPtr command = request_json->getBodyAsJson();
+
+ // Filter the command.
+ http_response = filterCommand(request, command, command_accept_list_);
+ if (http_response) {
+ return (http_response);
+ }
+
+ // Process command doesn't generate exceptions but can possibly return
+ // null response, if the handler is not implemented properly. This is
+ // again an internal server issue.
+ ConstElementPtr response = config::CommandMgr::instance().processCommand(command);
+
+ if (!response) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+
+ // Normal Responses coming from the Kea Control Agent must always be wrapped in
+ // a list as they may contain responses from multiple daemons.
+ // If we're emulating that for backward compatibility, then we need to wrap
+ // the answer in a list if it isn't in one already.
+ if (emulateAgentResponse() && (response->getType() != Element::list)) {
+ ElementPtr response_list = Element::createList();
+ response_list->add(boost::const_pointer_cast<Element>(response));
+ response = response_list;
+ }
+
+ // The response is OK, so let's create new HTTP response with the status OK.
+ http_response = boost::dynamic_pointer_cast<
+ HttpResponseJson>(createStockHttpResponseInternal(request, HttpStatusCode::OK));
+ http_response->setBodyAsJson(response);
+ http_response->finalize();
+
+ return (http_response);
+}
+
+HttpResponseJsonPtr
+CmdResponseCreator::filterCommand(const HttpRequestPtr& request,
+ const ConstElementPtr& body,
+ const unordered_set<string>& accept) {
+ HttpResponseJsonPtr response;
+ if (!body || accept.empty()) {
+ return (response);
+ }
+ if (body->getType() != Element::map) {
+ return (response);
+ }
+ ConstElementPtr elem = body->get(CONTROL_COMMAND);
+ if (!elem || (elem->getType() != Element::string)) {
+ return (response);
+ }
+ string command = elem->stringValue();
+ if (command.empty() || accept.count(command)) {
+ return (response);
+ }
+
+ // Reject the command.
+ LOG_DEBUG(command_logger, DBG_COMMAND,
+ COMMAND_HTTP_LISTENER_COMMAND_REJECTED)
+ .arg(command)
+ .arg(request->getRemote());
+ // From CtrlAgentResponseCreator::createStockHttpResponseInternal.
+ HttpVersion http_version(request->context()->http_version_major_,
+ request->context()->http_version_minor_);
+ if ((http_version < HttpVersion(1, 0)) ||
+ (HttpVersion(1, 1) < http_version)) {
+ http_version.major_ = 1;
+ http_version.minor_ = 0;
+ }
+ HttpStatusCode status_code = HttpStatusCode::FORBIDDEN;
+ response.reset(new HttpResponseJson(http_version, status_code));
+ ElementPtr response_body = Element::createMap();
+ uint16_t result = HttpResponse::statusCodeToNumber(status_code);
+ response_body->set(CONTROL_RESULT,
+ Element::create(static_cast<long long>(result)));
+ const string& text = HttpResponse::statusCodeToString(status_code);
+ response_body->set(CONTROL_TEXT, Element::create(text));
+ response->setBodyAsJson(response_body);
+ response->finalize();
+ return (response);
+}
+
+} // end of namespace isc::config
+} // end of namespace isc