Kea  2.1.7-git
cmd_response_creator.cc
Go to the documentation of this file.
1 // Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <config/command_mgr.h>
11 #include <config/config_log.h>
12 #include <cc/command_interpreter.h>
13 #include <http/post_request_json.h>
14 #include <http/response_json.h>
15 #include <boost/pointer_cast.hpp>
16 #include <iostream>
17 
18 using namespace isc::config;
19 using namespace isc::data;
20 using namespace isc::http;
21 using namespace std;
22 
23 namespace isc {
24 namespace config {
25 
27 
28 unordered_set<string> CmdResponseCreator::command_accept_list_;
29 
32  return (HttpRequestPtr(new PostHttpRequestJson()));
33 }
34 
38  const HttpStatusCode& status_code) const {
39  HttpResponsePtr response = createStockHttpResponseInternal(request, status_code);
40  response->finalize();
41  return (response);
42 }
43 
45 CmdResponseCreator::
46 createStockHttpResponseInternal(const HttpRequestPtr& request,
47  const HttpStatusCode& status_code) const {
48  // The request hasn't been finalized so the request object
49  // doesn't contain any information about the HTTP version number
50  // used. But, the context should have this data (assuming the
51  // HTTP version is parsed OK).
52  HttpVersion http_version(request->context()->http_version_major_,
53  request->context()->http_version_minor_);
54  // We only accept HTTP version 1.0 or 1.1. If other version number is found
55  // we fall back to HTTP/1.0.
56  if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
57  http_version.major_ = 1;
58  http_version.minor_ = 0;
59  }
60  // This will generate the response holding JSON content.
61  HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
62  return (response);
63 }
64 
66 CmdResponseCreator::createDynamicHttpResponse(HttpRequestPtr request) {
67  HttpResponseJsonPtr http_response;
68 
69  // Check the basic HTTP authentication.
70  if (http_auth_config_) {
71  http_response = http_auth_config_->checkAuth(*this, request);
72  if (http_response) {
73  return (http_response);
74  }
75  }
76 
77  // The request is always non-null, because this is verified by the
78  // createHttpResponse method. Let's try to convert it to the
79  // PostHttpRequestJson type as this is the type generated by the
80  // createNewHttpRequest. If the conversion result is null it means that
81  // the caller did not use createNewHttpRequest method to create this
82  // instance. This is considered an error in the server logic.
83  PostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
84  PostHttpRequestJson>(request);
85  if (!request_json) {
86  // Notify the client that we have a problem with our server.
87  return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
88  }
89 
90  // We have already checked that the request is finalized so the call
91  // to getBodyAsJson must not trigger an exception.
92  ConstElementPtr command = request_json->getBodyAsJson();
93 
94  // Filter the command.
95  http_response = filterCommand(request, command, command_accept_list_);
96  if (http_response) {
97  return (http_response);
98  }
99 
100  // Process command doesn't generate exceptions but can possibly return
101  // null response, if the handler is not implemented properly. This is
102  // again an internal server issue.
104 
105  if (!response) {
106  // Notify the client that we have a problem with our server.
107  return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
108  }
109 
110  // Normal Responses coming from the Kea Control Agent must always be wrapped in
111  // a list as they may contain responses from multiple daemons.
112  // If we're emulating that for backward compatibility, then we need to wrap
113  // the answer in a list if it isn't in one already.
114  if (emulateAgentResponse() && (response->getType() != Element::list)) {
115  ElementPtr response_list = Element::createList();
116  response_list->add(boost::const_pointer_cast<Element>(response));
117  response = response_list;
118  }
119 
120  // The response is OK, so let's create new HTTP response with the status OK.
121  http_response = boost::dynamic_pointer_cast<
122  HttpResponseJson>(createStockHttpResponseInternal(request, HttpStatusCode::OK));
123  http_response->setBodyAsJson(response);
124  http_response->finalize();
125 
126  return (http_response);
127 }
128 
131  const ConstElementPtr& body,
132  const unordered_set<string>& accept) {
133  HttpResponseJsonPtr response;
134  if (!body || accept.empty()) {
135  return (response);
136  }
137  if (body->getType() != Element::map) {
138  return (response);
139  }
140  ConstElementPtr elem = body->get(CONTROL_COMMAND);
141  if (!elem || (elem->getType() != Element::string)) {
142  return (response);
143  }
144  string command = elem->stringValue();
145  if (command.empty() || accept.count(command)) {
146  return (response);
147  }
148 
149  // Reject the command.
152  .arg(command)
153  .arg(request->getRemote());
154  // From CtrlAgentResponseCreator::createStockHttpResponseInternal.
155  HttpVersion http_version(request->context()->http_version_major_,
156  request->context()->http_version_minor_);
157  if ((http_version < HttpVersion(1, 0)) ||
158  (HttpVersion(1, 1) < http_version)) {
159  http_version.major_ = 1;
160  http_version.minor_ = 0;
161  }
162  HttpStatusCode status_code = HttpStatusCode::FORBIDDEN;
163  response.reset(new HttpResponseJson(http_version, status_code));
164  ElementPtr response_body = Element::createMap();
165  uint16_t result = HttpResponse::statusCodeToNumber(status_code);
166  response_body->set(CONTROL_RESULT,
167  Element::create(static_cast<long long>(result)));
168  const string& text = HttpResponse::statusCodeToString(status_code);
169  response_body->set(CONTROL_TEXT, Element::create(text));
170  response->setBodyAsJson(response_body);
171  response->finalize();
172  return (response);
173 }
174 
175 } // end of namespace isc::config
176 } // end of namespace isc
Represents HTTP POST request with JSON body.
virtual http::HttpRequestPtr createNewHttpRequest() const
Create a new request.
const char * CONTROL_RESULT
String used for result, i.e. integer status ("result")
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
Definition: response_json.h:24
static std::unordered_set< std::string > command_accept_list_
The server command accept list.
HTTP protocol version.
Definition: http_types.h:14
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
STL namespace.
const int DBG_COMMAND
Definition: config_log.h:24
http::HttpResponseJsonPtr filterCommand(const http::HttpRequestPtr &request, const data::ConstElementPtr &body, const std::unordered_set< std::string > &accept)
Filter commands.
unsigned major_
Major HTTP version.
Definition: http_types.h:15
static http::HttpAuthConfigPtr http_auth_config_
The server current authentication configuration.
data::ConstElementPtr getBodyAsJson() const
Retrieves JSON body.
const isc::log::MessageID COMMAND_HTTP_LISTENER_COMMAND_REJECTED
virtual http::HttpResponsePtr createStockHttpResponse(const http::HttpRequestPtr &request, const http::HttpStatusCode &status_code) const
Creates stock HTTP response.
isc::log::Logger command_logger("commands")
Command processing Logger.
Definition: config_log.h:21
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:78
const char * CONTROL_TEXT
String used for storing textual description ("text")
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
Represents HTTP response with JSON content.
Definition: response_json.h:34
Defines the logger used by the top-level component of kea-lfc.
virtual isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr &cmd)
Triggers command processing.
This file contains several functions and constants that are used for handling commands and responses ...
const char * CONTROL_COMMAND
String used for commands ("command")
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition: request.h:27
void setBodyAsJson(const data::ConstElementPtr &json_body)
Generates JSON content from the data structures represented as data::ConstElementPtr.
boost::shared_ptr< HttpAuthConfig > HttpAuthConfigPtr
Type of shared pointers to HTTP authentication configuration.
Definition: auth_config.h:97
static CommandMgr & instance()
CommandMgr is a singleton class.
Definition: command_mgr.cc:650
HttpStatusCode
HTTP status codes (cf RFC 2068)
Definition: response.h:30