1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright (C) 2017-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 <agent/ca_cfg_mgr.h>
#include <agent/ca_command_mgr.h>
#include <agent/ca_controller.h>
#include <agent/ca_process.h>
#include <agent/ca_response_creator.h>
#include <cc/data.h>
#include <hooks/callout_handle.h>
#include <hooks/hooks_log.h>
#include <hooks/hooks_manager.h>
#include <http/post_request_json.h>
#include <http/response_json.h>
#include <boost/pointer_cast.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <iostream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace isc::data;
using namespace isc::hooks;
using namespace isc::http;

namespace {

/// Structure that holds registered hook indexes.
struct CtrlAgentHooks {
    int hook_index_auth_;     ///< index of "auth" hook point.
    int hook_index_response_; ///< index of "response" hook point.

    /// Constructor that registers hook points.
    CtrlAgentHooks() {
        hook_index_auth_ = HooksManager::registerHook("auth");
        hook_index_response_ = HooksManager::registerHook("response");
    }
};

} // end of anonymous namespace.

// Declare a Hooks object. As this is outside any function or method, it
// will be instantiated (and the constructor run) when the module is loaded.
// As a result, the hook indexes will be defined before any method in this
// module is called.
CtrlAgentHooks Hooks;

namespace isc {
namespace agent {

HttpRequestPtr
CtrlAgentResponseCreator::createNewHttpRequest() const {
    return (HttpRequestPtr(new PostHttpRequestJson()));
}

HttpResponsePtr
CtrlAgentResponseCreator::
createStockHttpResponse(const HttpRequestPtr& request,
                        const HttpStatusCode& status_code) const {
    HttpResponsePtr response = createStockHttpResponseInternal(request, status_code);
    response->finalize();
    return (response);
}

HttpResponsePtr
CtrlAgentResponseCreator::
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
CtrlAgentResponseCreator::
createDynamicHttpResponse(HttpRequestPtr request) {
    // First check authentication.
    HttpResponseJsonPtr http_response;

    // Context will hold the server configuration.
    CtrlAgentCfgContextPtr ctx;

    // There is a hierarchy of the objects through which we need to pass to get
    // the configuration context. We may simplify this at some point but since
    // we're in the singleton we want to make sure that we're using most current
    // configuration.
    boost::shared_ptr<CtrlAgentController> controller =
        boost::dynamic_pointer_cast<CtrlAgentController>(CtrlAgentController::instance());
    if (controller) {
        CtrlAgentProcessPtr process = controller->getCtrlAgentProcess();
        if (process) {
            CtrlAgentCfgMgrPtr cfgmgr = process->getCtrlAgentCfgMgr();
            if (cfgmgr) {
                ctx = cfgmgr->getCtrlAgentCfgContext();
                if (ctx) {
                    const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
                    if (auth) {
                        // Check authentication.
                        http_response = auth->checkAuth(*this, request);
                    }
                }
            }
        }
    }

    // Callout point for "auth".
    bool reset_handle = false;
    if (HooksManager::calloutsPresent(Hooks.hook_index_auth_)) {
        // Get callout handle.
        CalloutHandlePtr callout_handle = request->getCalloutHandle();
        ScopedCalloutHandleState callout_handle_state(callout_handle);

        // Pass arguments.
        callout_handle->setArgument("request", request);
        callout_handle->setArgument("response", http_response);

        // Call callouts.
        HooksManager::callCallouts(Hooks.hook_index_auth_, *callout_handle);
        callout_handle->getArgument("request", request);
        callout_handle->getArgument("response", http_response);

        // Status other than continue means 'please reset the handle'.
        if (callout_handle->getStatus() != CalloutHandle::NEXT_STEP_CONTINUE) {
            reset_handle = true;
        }
    }

    // The basic HTTP authentication check or a callout failed and
    // left a response.
    if (http_response) {
        return (http_response);
    }

    // Reset the handle when a hook asks for.
    if (reset_handle) {
        request->resetCalloutHandle();
    }

    // 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();

    // 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 = CtrlAgentCommandMgr::instance().processCommand(command);
    if (!response) {
        // Notify the client that we have a problem with our server.
        return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
    }
    // 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();

    // Callout point for "response".
    if (HooksManager::calloutsPresent(Hooks.hook_index_response_)) {
        // Get callout handle.
        CalloutHandlePtr callout_handle = request->getCalloutHandle();
        ScopedCalloutHandleState callout_handle_state(callout_handle);

        // Pass arguments.
        callout_handle->setArgument("request", request);
        callout_handle->setArgument("response", http_response);

        // Call callouts.
        HooksManager::callCallouts(Hooks.hook_index_response_,
                                   *callout_handle);
        callout_handle->getArgument("response", http_response);

        // Ignore status as the HTTP response is used instead.
    }

    return (http_response);
}

} // end of namespace isc::agent
} // end of namespace isc