source: nscp/modules/NSCPClient/NSCPClient.cpp @ 84cdb9b

0.4.00.4.10.4.2
Last change on this file since 84cdb9b was 84cdb9b, checked in by Michael Medin <michael@…>, 15 months ago

Cleaned up the API helper classes to make inclusion simpler and more modular.
Also removed some dead code no longer used

  • Property mode set to 100644
File size: 15.8 KB
Line 
1/**************************************************************************
2*   Copyright (C) 2004-2007 by Michael Medin <michael@medin.name>         *
3*                                                                         *
4*   This code is part of NSClient++ - http://trac.nakednuns.org/nscp      *
5*                                                                         *
6*   This program is free software; you can redistribute it and/or modify  *
7*   it under the terms of the GNU General Public License as published by  *
8*   the Free Software Foundation; either version 2 of the License, or     *
9*   (at your option) any later version.                                   *
10*                                                                         *
11*   This program is distributed in the hope that it will be useful,       *
12*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14*   GNU General Public License for more details.                          *
15*                                                                         *
16*   You should have received a copy of the GNU General Public License     *
17*   along with this program; if not, write to the                         *
18*   Free Software Foundation, Inc.,                                       *
19*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20***************************************************************************/
21#include "stdafx.h"
22#include "NSCPClient.h"
23#include <time.h>
24#include <boost/filesystem.hpp>
25
26#include <strEx.h>
27#include <net/net.hpp>
28#include <nscp/client/socket.hpp>
29
30#include <protobuf/plugin.pb.h>
31
32#include <settings/client/settings_client.hpp>
33#include <nscapi/nscapi_protobuf_functions.hpp>
34
35namespace sh = nscapi::settings_helper;
36
37const std::wstring NSCPClient::command_prefix = _T("nscp");
38/**
39 * Default c-tor
40 * @return
41 */
42NSCPClient::NSCPClient() {}
43
44/**
45 * Default d-tor
46 * @return
47 */
48NSCPClient::~NSCPClient() {}
49
50/**
51 * Load (initiate) module.
52 * Start the background collector thread and let it run until unloadModule() is called.
53 * @return true
54 */
55bool NSCPClient::loadModule() {
56        return false;
57}
58
59bool NSCPClient::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
60        std::map<std::wstring,std::wstring> commands;
61
62        try {
63
64
65                sh::settings_registry settings(get_settings_proxy());
66                settings.set_alias(_T("NSCP"), alias, _T("client"));
67                target_path = settings.alias().get_settings_path(_T("targets"));
68
69                settings.alias().add_path_to_settings()
70                        (_T("NSCP CLIENT SECTION"), _T("Section for NSCP active/passive check module."))
71
72                        (_T("handlers"), sh::fun_values_path(boost::bind(&NSCPClient::add_command, this, _1, _2)),
73                        _T("CLIENT HANDLER SECTION"), _T(""))
74
75                        (_T("targets"), sh::fun_values_path(boost::bind(&NSCPClient::add_target, this, _1, _2)),
76                        _T("REMOTE TARGET DEFINITIONS"), _T(""))
77
78                        ;
79
80                settings.alias().add_key_to_settings()
81                        (_T("channel"), sh::wstring_key(&channel_, _T("NSCP")),
82                        _T("CHANNEL"), _T("The channel to listen to."))
83
84                        ;
85
86                settings.register_all();
87                settings.notify();
88
89                targets.add_missing(get_settings_proxy(), target_path, _T("default"), _T(""), true);
90
91
92                get_core()->registerSubmissionListener(get_id(), channel_);
93                register_command(_T("nscp_query"), _T("Submit a query to a remote host via NSCP"));
94                register_command(_T("nscp_forward"), _T("Forward query to remote NSCP host"));
95                register_command(_T("nscp_submit"), _T("Submit a query to a remote host via NSCP"));
96                register_command(_T("nscp_exec"), _T("Execute remote command on a remote host via NSCP"));
97                register_command(_T("nscp_help"), _T("Help on using NSCP Client"));
98
99        } catch (nscapi::nscapi_exception &e) {
100                NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what()));
101                return false;
102        } catch (std::exception &e) {
103                NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what()));
104                return false;
105        } catch (...) {
106                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
107                return false;
108        }
109        return true;
110}
111std::string get_command(std::string alias, std::string command = "") {
112        if (!alias.empty())
113                return alias;
114        if (!command.empty())
115                return command;
116        return "_NRPE_CHECK";
117}
118
119//////////////////////////////////////////////////////////////////////////
120// Settings helpers
121//
122
123void NSCPClient::add_target(std::wstring key, std::wstring arg) {
124        try {
125                targets.add(get_settings_proxy(), target_path , key, arg);
126        } catch (const std::exception &e) {
127                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key + _T(", ") + utf8::to_unicode(e.what()));
128        } catch (...) {
129                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key);
130        }
131}
132
133void NSCPClient::add_command(std::wstring name, std::wstring args) {
134        try {
135                std::wstring key = commands.add_command(name, args);
136                if (!key.empty())
137                        register_command(key.c_str(), _T("NSCP relay for: ") + name);
138        } catch (boost::program_options::validation_error &e) {
139                NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what()));
140        } catch (...) {
141                NSC_LOG_ERROR_STD(_T("Could not add command ") + name);
142        }
143}
144
145/**
146 * Unload (terminate) module.
147 * Attempt to stop the background processing thread.
148 * @return true if successfully, false if not (if not things might be bad)
149 */
150bool NSCPClient::unloadModule() {
151        return true;
152}
153
154NSCAPI::nagiosReturn NSCPClient::handleRAWCommand(const wchar_t* char_command, const std::string &request, std::string &result) {
155        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
156
157        Plugin::QueryRequestMessage message;
158        message.ParseFromString(request);
159
160        client::configuration config;
161        setup(config, message.header());
162
163        return commands.process_query(cmd, config, message, result);
164}
165
166NSCAPI::nagiosReturn NSCPClient::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &result) {
167        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
168
169        Plugin::ExecuteRequestMessage message;
170        message.ParseFromString(request);
171
172        client::configuration config;
173        setup(config, message.header());
174
175        return commands.process_exec(cmd, config, message, result);
176}
177
178NSCAPI::nagiosReturn NSCPClient::handleRAWNotification(const wchar_t* channel, std::string request, std::string &result) {
179        Plugin::SubmitRequestMessage message;
180        message.ParseFromString(request);
181
182        client::configuration config;
183        setup(config, message.header());
184
185        return client::command_line_parser::do_relay_submit(config, message, result);
186}
187
188//////////////////////////////////////////////////////////////////////////
189// Parser setup/Helpers
190//
191
192void NSCPClient::add_local_options(po::options_description &desc, client::configuration::data_type data) {
193        desc.add_options()
194                ("certificate,c", po::value<std::string>()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "certificate", _1)),
195                        "Length of payload (has to be same as on the server)")
196/*
197                ("no-ssl,n", po::value<bool>(&command_data.no_ssl)->zero_tokens()->default_value(false), "Do not initial an ssl handshake with the server, talk in plain text.")
198
199                ("cert,c", po::value<std::wstring>(&command_data.cert)->default_value(cert_), "Certificate to use.")
200                */
201                ;
202}
203
204void NSCPClient::setup(client::configuration &config, const ::Plugin::Common_Header& header) {
205        boost::shared_ptr<clp_handler_impl> handler = boost::shared_ptr<clp_handler_impl>(new clp_handler_impl(this));
206        add_local_options(config.local, config.data);
207
208        config.data->recipient.id = header.recipient_id();
209        std::wstring recipient = utf8::cvt<std::wstring>(config.data->recipient.id);
210        if (!targets.has_object(recipient)) {
211                NSC_LOG_ERROR(_T("Target not found (using default): ") + recipient);
212                recipient = _T("default");
213        }
214        nscapi::targets::optional_target_object opt = targets.find_object(recipient);
215
216        if (opt) {
217                nscapi::targets::target_object t = *opt;
218                nscapi::functions::destination_container def = t.to_destination_container();
219                config.data->recipient.apply(def);
220        }
221        config.data->host_self.id = "self";
222        //config.data->host_self.host = hostname_;
223
224        config.target_lookup = handler;
225        config.handler = handler;
226}
227
228NSCPClient::connection_data NSCPClient::parse_header(const ::Plugin::Common_Header &header, client::configuration::data_type data) {
229        nscapi::functions::destination_container recipient;
230        nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true);
231        return connection_data(recipient, data->recipient);
232}
233
234//////////////////////////////////////////////////////////////////////////
235// Parser implementations
236//
237
238std::string gather_and_log_errors(std::string  &payload) {
239        NSCPIPC::ErrorMessage message;
240        message.ParseFromString(payload);
241        std::string ret;
242        for (int i=0;i<message.error_size();i++) {
243                ret += message.error(i).message();
244                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::cvt<std::wstring>(message.error(i).message()));
245        }
246        return ret;
247}
248int NSCPClient::clp_handler_impl::query(client::configuration::data_type data, const Plugin::QueryRequestMessage &request_message, std::string &reply) {
249        const ::Plugin::Common_Header& request_header = request_message.header();
250        int ret = NSCAPI::returnUNKNOWN;
251        connection_data con = parse_header(request_header, data);
252
253        Plugin::QueryResponseMessage response_message;
254        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
255
256        std::list<nscp::packet> chunks;
257        chunks.push_back(nscp::factory::create_envelope_request(1));
258        chunks.push_back(nscp::factory::create_payload(nscp::data::command_request, request_message.SerializeAsString(), 0));
259        chunks = instance->send(con, chunks);
260        BOOST_FOREACH(nscp::packet &chunk, chunks) {
261                if (nscp::checks::is_query_response(chunk)) {
262                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
263                } else if (nscp::checks::is_error(chunk)) {
264                        std::string error = gather_and_log_errors(chunk.payload);
265                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
266                        ret = NSCAPI::returnUNKNOWN;
267                } else {
268                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
269                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
270                        ret = NSCAPI::returnUNKNOWN;
271                }
272        }
273        response_message.SerializeToString(&reply);
274        return ret;
275}
276
277int NSCPClient::clp_handler_impl::submit(client::configuration::data_type data, const Plugin::SubmitRequestMessage &request_message, std::string &reply) {
278        const ::Plugin::Common_Header& request_header = request_message.header();
279        int ret = NSCAPI::returnUNKNOWN;
280        connection_data con = parse_header(request_header, data);
281        Plugin::SubmitResponseMessage response_message;
282        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
283
284        std::list<nscp::packet> chunks;
285        chunks.push_back(nscp::factory::create_payload(nscp::data::command_response, request_message.SerializeAsString(), 0));
286        chunks = instance->send(con, chunks);
287        BOOST_FOREACH(nscp::packet &chunk, chunks) {
288                if (nscp::checks::is_submit_response(chunk)) {
289                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
290                } else if (nscp::checks::is_error(chunk)) {
291                        std::string error = gather_and_log_errors(chunk.payload);
292                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
293                        ret = NSCAPI::returnUNKNOWN;
294                } else {
295                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
296                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
297                        ret = NSCAPI::returnUNKNOWN;
298                }
299        }
300        response_message.SerializeToString(&reply);
301        return ret;
302}
303
304int NSCPClient::clp_handler_impl::exec(client::configuration::data_type data, const Plugin::ExecuteRequestMessage &request_message, std::string &reply) {
305        const ::Plugin::Common_Header& request_header = request_message.header();
306        int ret = NSCAPI::returnOK;
307        connection_data con = parse_header(request_header, data);
308
309        Plugin::ExecuteResponseMessage response_message;
310        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
311
312        std::list<nscp::packet> chunks;
313        chunks.push_back(nscp::factory::create_envelope_request(1));
314        chunks.push_back(nscp::factory::create_payload(nscp::data::exec_request, request_message.SerializeAsString(), 0));
315        chunks = instance->send(con, chunks);
316        BOOST_FOREACH(nscp::packet &chunk, chunks) {
317                if (nscp::checks::is_exec_response(chunk)) {
318                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
319                } else if (nscp::checks::is_error(chunk)) {
320                        std::string error = gather_and_log_errors(chunk.payload);
321                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
322                        ret = NSCAPI::returnUNKNOWN;
323                } else {
324                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
325                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
326                        ret = NSCAPI::returnUNKNOWN;
327                }
328        }
329        response_message.SerializeToString(&reply);
330        return ret;
331}
332
333//////////////////////////////////////////////////////////////////////////
334// Protocol implementations
335//
336
337std::list<nscp::packet> NSCPClient::send(connection_data con, std::list<nscp::packet> &chunks) {
338        NSC_DEBUG_MSG_STD(_T("NRPE Connection details: ") + con.to_wstring());
339        chunks.push_front(nscp::factory::create_envelope_request(1));
340        std::list<nscp::packet> tmp, result;
341        if (con.use_ssl) {
342#ifdef USE_SSL
343                tmp = send_ssl(con.host, con.port, con.cert, con.timeout, chunks);
344#else
345                NSC_LOG_ERROR_STD(_T("SSL not avalible (not compiled with USE_SSL)"));
346                result.push_back(nscp::factory::create_error(_T("SSL support not available (compiled without USE_SSL)!")));
347#endif
348        } else {
349                tmp = send_nossl(con.host, con.port, con.timeout, chunks);
350        }
351        BOOST_FOREACH(nscp::packet &p, tmp) {
352                if (nscp::checks::is_envelope_response(p)) {
353                } else {
354                        result.push_back(p);
355                }
356        }
357        return result;
358}
359
360#ifdef USE_SSL
361std::list<nscp::packet> NSCPClient::send_ssl(std::string host, std::string port, std::wstring cert, int timeout, const std::list<nscp::packet> &chunks) {
362        NSC_DEBUG_MSG_STD(_T("Connecting SSL to: ") + utf8::cvt<std::wstring>(host + ":" + port));
363        boost::asio::io_service io_service;
364        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
365        SSL_CTX_set_cipher_list(ctx.impl(), "ADH");
366        ctx.use_tmp_dh_file(to_string(cert));
367        ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
368        nscp::client::ssl_socket socket(io_service, ctx, host, port);
369        socket.send(chunks, boost::posix_time::seconds(timeout));
370        return socket.recv(boost::posix_time::seconds(timeout));
371}
372#endif
373
374std::list<nscp::packet> NSCPClient::send_nossl(std::string host, std::string port, int timeout, const std::list<nscp::packet> &chunks) {
375        NSC_DEBUG_MSG_STD(_T("Connecting to: ") + utf8::cvt<std::wstring>(host + ":" + port));
376        boost::asio::io_service io_service;
377        nscp::client::socket socket(io_service, host, port);
378        socket.send(chunks, boost::posix_time::seconds(timeout));
379        return socket.recv(boost::posix_time::seconds(timeout));
380}
381
382NSC_WRAP_DLL();
383NSC_WRAPPERS_MAIN_DEF(NSCPClient);
384NSC_WRAPPERS_IGNORE_MSG_DEF();
385NSC_WRAPPERS_HANDLE_CMD_DEF();
386NSC_WRAPPERS_CLI_DEF();
387NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();
388
Note: See TracBrowser for help on using the repository browser.