source: nscp/modules/NSCPClient/NSCPClient.cpp @ 465866c

0.4.10.4.2
Last change on this file since 465866c was 465866c, checked in by Michael Medin <michael@…>, 12 months ago

2012-06-05 MickeM

  • Tweaked all servers to use the new internals and added first testcase for NSCP socket

2012-05-24 MickeM

  • Reworked real time event log support to be a lot more flexible You can now specify all options on a "filter" level.
  • WARNING* Old syntax NOT supported (and will not upgrade) but hopefully not to many will be affected.
  • Added support for ipv6 allowed hosts validation

2012-05-21 MickeM

  • Sofia Born (My second daughter)
  • Property mode set to 100644
File size: 15.2 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
24#include <time.h>
25#include <strEx.h>
26
27#include <protobuf/plugin.pb.h>
28
29#include <settings/client/settings_client.hpp>
30#include <nscapi/nscapi_protobuf_functions.hpp>
31
32namespace sh = nscapi::settings_helper;
33
34const std::wstring NSCPClient::command_prefix = _T("nscp");
35/**
36 * Default c-tor
37 * @return
38 */
39NSCPClient::NSCPClient() {}
40
41/**
42 * Default d-tor
43 * @return
44 */
45NSCPClient::~NSCPClient() {}
46
47/**
48 * Load (initiate) module.
49 * Start the background collector thread and let it run until unloadModule() is called.
50 * @return true
51 */
52bool NSCPClient::loadModule() {
53        return false;
54}
55
56bool NSCPClient::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
57        std::map<std::wstring,std::wstring> commands;
58
59        try {
60
61
62                sh::settings_registry settings(get_settings_proxy());
63                settings.set_alias(_T("nscp"), alias, _T("client"));
64                target_path = settings.alias().get_settings_path(_T("targets"));
65
66                settings.alias().add_path_to_settings()
67                        (_T("NSCP CLIENT SECTION"), _T("Section for NSCP active/passive check module."))
68
69                        (_T("handlers"), sh::fun_values_path(boost::bind(&NSCPClient::add_command, this, _1, _2)),
70                        _T("CLIENT HANDLER SECTION"), _T(""))
71
72                        (_T("targets"), sh::fun_values_path(boost::bind(&NSCPClient::add_target, this, _1, _2)),
73                        _T("REMOTE TARGET DEFINITIONS"), _T(""))
74                        ;
75
76                settings.alias().add_key_to_settings()
77                        (_T("channel"), sh::wstring_key(&channel_, _T("NSCP")),
78                        _T("CHANNEL"), _T("The channel to listen to."))
79
80                        ;
81
82                settings.register_all();
83                settings.notify();
84
85                targets.add_missing(get_settings_proxy(), target_path, _T("default"), _T(""), true);
86
87
88                get_core()->registerSubmissionListener(get_id(), channel_);
89                register_command(_T("nscp_query"), _T("Submit a query to a remote host via NSCP"));
90                register_command(_T("nscp_forward"), _T("Forward query to remote NSCP host"));
91                register_command(_T("nscp_submit"), _T("Submit a query to a remote host via NSCP"));
92                register_command(_T("nscp_exec"), _T("Execute remote command on a remote host via NSCP"));
93                register_command(_T("nscp_help"), _T("Help on using NSCP Client"));
94        } catch (nscapi::nscapi_exception &e) {
95                NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what()));
96                return false;
97        } catch (std::exception &e) {
98                NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what()));
99                return false;
100        } catch (...) {
101                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
102                return false;
103        }
104        return true;
105}
106std::string get_command(std::string alias, std::string command = "") {
107        if (!alias.empty())
108                return alias;
109        if (!command.empty())
110                return command;
111        return "_NRPE_CHECK";
112}
113
114//////////////////////////////////////////////////////////////////////////
115// Settings helpers
116//
117
118void NSCPClient::add_target(std::wstring key, std::wstring arg) {
119        try {
120                targets.add(get_settings_proxy(), target_path , key, arg);
121        } catch (const std::exception &e) {
122                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key + _T(", ") + utf8::to_unicode(e.what()));
123        } catch (...) {
124                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key);
125        }
126}
127
128void NSCPClient::add_command(std::wstring name, std::wstring args) {
129        try {
130                std::wstring key = commands.add_command(name, args);
131                if (!key.empty())
132                        register_command(key.c_str(), _T("NSCP relay for: ") + name);
133        } catch (boost::program_options::validation_error &e) {
134                NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what()));
135        } catch (...) {
136                NSC_LOG_ERROR_STD(_T("Could not add command ") + name);
137        }
138}
139
140/**
141 * Unload (terminate) module.
142 * Attempt to stop the background processing thread.
143 * @return true if successfully, false if not (if not things might be bad)
144 */
145bool NSCPClient::unloadModule() {
146        return true;
147}
148
149NSCAPI::nagiosReturn NSCPClient::handleRAWCommand(const wchar_t* char_command, const std::string &request, std::string &result) {
150        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
151
152        Plugin::QueryRequestMessage message;
153        message.ParseFromString(request);
154
155        client::configuration config;
156        setup(config, message.header());
157
158        return commands.process_query(cmd, config, message, result);
159}
160
161NSCAPI::nagiosReturn NSCPClient::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &result) {
162        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
163
164        Plugin::ExecuteRequestMessage message;
165        message.ParseFromString(request);
166
167        client::configuration config;
168        setup(config, message.header());
169
170        return commands.process_exec(cmd, config, message, result);
171}
172
173NSCAPI::nagiosReturn NSCPClient::handleRAWNotification(const wchar_t* channel, std::string request, std::string &result) {
174        Plugin::SubmitRequestMessage message;
175        message.ParseFromString(request);
176
177        client::configuration config;
178        setup(config, message.header());
179
180        return client::command_line_parser::do_relay_submit(config, message, result);
181}
182
183//////////////////////////////////////////////////////////////////////////
184// Parser setup/Helpers
185//
186
187void NSCPClient::add_local_options(po::options_description &desc, client::configuration::data_type data) {
188        desc.add_options()
189                ("certificate,c", po::value<std::string>()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "certificate", _1)),
190                "Length of payload (has to be same as on the server)")
191
192                ("no-ssl,n", po::value<bool>()->zero_tokens()->default_value(false)->notifier(boost::bind(&nscapi::functions::destination_container::set_bool_data, &data->recipient, "no ssl", _1)),
193                "Do not initial an ssl handshake with the server, talk in plaintext.")
194                ;
195}
196
197void NSCPClient::setup(client::configuration &config, const ::Plugin::Common_Header& header) {
198        boost::shared_ptr<clp_handler_impl> handler = boost::shared_ptr<clp_handler_impl>(new clp_handler_impl(this));
199        add_local_options(config.local, config.data);
200
201        config.data->recipient.id = header.recipient_id();
202        std::wstring recipient = utf8::cvt<std::wstring>(config.data->recipient.id);
203        if (!targets.has_object(recipient)) {
204                recipient = _T("default");
205        }
206        nscapi::targets::optional_target_object opt = targets.find_object(recipient);
207
208        if (opt) {
209                nscapi::targets::target_object t = *opt;
210                nscapi::functions::destination_container def = t.to_destination_container();
211                config.data->recipient.apply(def);
212        }
213        config.data->host_self.id = "self";
214        //config.data->host_self.host = hostname_;
215
216        config.target_lookup = handler;
217        config.handler = handler;
218}
219
220NSCPClient::connection_data NSCPClient::parse_header(const ::Plugin::Common_Header &header, client::configuration::data_type data) {
221        nscapi::functions::destination_container recipient;
222        nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true);
223        return connection_data(recipient, data->recipient);
224}
225
226//////////////////////////////////////////////////////////////////////////
227// Parser implementations
228//
229
230std::string gather_and_log_errors(std::string  &payload) {
231        NSCPIPC::ErrorMessage message;
232        message.ParseFromString(payload);
233        std::string ret;
234        for (int i=0;i<message.error_size();i++) {
235                ret += message.error(i).message();
236                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::cvt<std::wstring>(message.error(i).message()));
237        }
238        return ret;
239}
240int NSCPClient::clp_handler_impl::query(client::configuration::data_type data, const Plugin::QueryRequestMessage &request_message, std::string &reply) {
241        const ::Plugin::Common_Header& request_header = request_message.header();
242        int ret = NSCAPI::returnUNKNOWN;
243        connection_data con = parse_header(request_header, data);
244
245        Plugin::QueryResponseMessage response_message;
246        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
247
248        std::list<nscp::packet> chunks;
249        chunks.push_back(nscp::factory::create_payload(nscp::data::command_request, request_message.SerializeAsString(), 0));
250        chunks = instance->send(con, chunks);
251        BOOST_FOREACH(nscp::packet &chunk, chunks) {
252                if (nscp::checks::is_query_response(chunk)) {
253                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
254                } else if (nscp::checks::is_error(chunk)) {
255                        std::string error = gather_and_log_errors(chunk.payload);
256                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
257                        ret = NSCAPI::returnUNKNOWN;
258                } else {
259                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
260                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
261                        ret = NSCAPI::returnUNKNOWN;
262                }
263        }
264        response_message.SerializeToString(&reply);
265        return ret;
266}
267
268int NSCPClient::clp_handler_impl::submit(client::configuration::data_type data, const Plugin::SubmitRequestMessage &request_message, std::string &reply) {
269        const ::Plugin::Common_Header& request_header = request_message.header();
270        int ret = NSCAPI::returnUNKNOWN;
271        connection_data con = parse_header(request_header, data);
272        Plugin::SubmitResponseMessage response_message;
273        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
274
275        std::list<nscp::packet> chunks;
276        chunks.push_back(nscp::factory::create_payload(nscp::data::command_response, request_message.SerializeAsString(), 0));
277        chunks = instance->send(con, chunks);
278        BOOST_FOREACH(nscp::packet &chunk, chunks) {
279                if (nscp::checks::is_submit_response(chunk)) {
280                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
281                } else if (nscp::checks::is_error(chunk)) {
282                        std::string error = gather_and_log_errors(chunk.payload);
283                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
284                        ret = NSCAPI::returnUNKNOWN;
285                } else {
286                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
287                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
288                        ret = NSCAPI::returnUNKNOWN;
289                }
290        }
291        response_message.SerializeToString(&reply);
292        return ret;
293}
294
295int NSCPClient::clp_handler_impl::exec(client::configuration::data_type data, const Plugin::ExecuteRequestMessage &request_message, std::string &reply) {
296        const ::Plugin::Common_Header& request_header = request_message.header();
297        int ret = NSCAPI::returnOK;
298        connection_data con = parse_header(request_header, data);
299
300        Plugin::ExecuteResponseMessage response_message;
301        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
302
303        std::list<nscp::packet> chunks;
304        chunks.push_back(nscp::factory::create_payload(nscp::data::exec_request, request_message.SerializeAsString(), 0));
305        chunks = instance->send(con, chunks);
306        BOOST_FOREACH(nscp::packet &chunk, chunks) {
307                if (nscp::checks::is_exec_response(chunk)) {
308                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
309                } else if (nscp::checks::is_error(chunk)) {
310                        std::string error = gather_and_log_errors(chunk.payload);
311                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
312                        ret = NSCAPI::returnUNKNOWN;
313                } else {
314                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
315                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
316                        ret = NSCAPI::returnUNKNOWN;
317                }
318        }
319        response_message.SerializeToString(&reply);
320        return ret;
321}
322
323//////////////////////////////////////////////////////////////////////////
324// Protocol implementations
325//
326struct client_handler : public socket_helpers::client::client_handler {
327        client_handler(NSCPClient::connection_data &con)
328                : socket_helpers::client::client_handler(con.host, con.port, con.timeout, con.use_ssl, con.cert)
329        {
330
331        }
332        void log_debug(std::string file, int line, std::string msg) const {
333                if (GET_CORE()->should_log(NSCAPI::log_level::debug)) {
334                        GET_CORE()->log(NSCAPI::log_level::debug, file, line, utf8::to_unicode(msg));
335                }
336        }
337        void log_error(std::string file, int line, std::string msg) const {
338                if (GET_CORE()->should_log(NSCAPI::log_level::error)) {
339                        GET_CORE()->log(NSCAPI::log_level::error, file, line, utf8::to_unicode(msg));
340                }
341        }
342};
343
344std::list<nscp::packet> NSCPClient::send(connection_data con, std::list<nscp::packet> &chunks) {
345        std::list<nscp::packet> response;
346        try {
347                NSC_DEBUG_MSG_STD(_T("NSCP Connection details: ") + con.to_wstring());
348                //NSC_DEBUG_MSG_STD(_T("NSCP data: ") + utf8::cvt<std::wstring>(data));
349                if (con.use_ssl) {
350#ifndef USE_SSL
351                        NSC_LOG_ERROR_STD(_T("SSL not avalible (compiled without USE_SSL)"));
352                        return response;
353#endif
354                }
355                socket_helpers::client::client<nscp::client::protocol> client(boost::shared_ptr<client_handler>(new client_handler(con)));
356                client.connect();
357                BOOST_FOREACH(nscp::packet packet, chunks) {
358                        response.push_back(client.process_request(packet));
359                }
360                client.shutdown();
361                return response;
362        } catch (std::runtime_error &e) {
363                NSC_LOG_ERROR_STD(_T("Socket error: ") + utf8::to_unicode(e.what()));
364                return response;
365        } catch (std::exception &e) {
366                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::to_unicode(e.what()));
367                return response;
368        } catch (...) {
369                return response;
370        }
371}
372
373NSC_WRAP_DLL();
374NSC_WRAPPERS_MAIN_DEF(NSCPClient);
375NSC_WRAPPERS_IGNORE_MSG_DEF();
376NSC_WRAPPERS_HANDLE_CMD_DEF();
377NSC_WRAPPERS_CLI_DEF();
378NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();
379
Note: See TracBrowser for help on using the repository browser.