diff options
Diffstat (limited to 'src/lib/config/cmd_response_creator.cc')
-rw-r--r-- | src/lib/config/cmd_response_creator.cc | 176 |
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 |