source: nscp/modules/NSCPClient/NSCPClient.cpp @ f33c12f

0.4.00.4.10.4.2
Last change on this file since f33c12f was f33c12f, checked in by Michael Medin <michael@…>, 18 months ago
  • Changed so Client interfaces no longer use "simple" interface meaning correct targets are now propagated (ie. calling NRPE -> NSCA will get correct host set automatically) (havent tested this yet, but now it builds at least, will test to night)
  • Property mode set to 100644
File size: 17.5 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
34namespace sh = nscapi::settings_helper;
35
36/**
37 * Default c-tor
38 * @return
39 */
40NSCPClient::NSCPClient() {}
41
42/**
43 * Default d-tor
44 * @return
45 */
46NSCPClient::~NSCPClient() {}
47
48/**
49 * Load (initiate) module.
50 * Start the background collector thread and let it run until unloadModule() is called.
51 * @return true
52 */
53bool NSCPClient::loadModule() {
54        return false;
55}
56
57bool NSCPClient::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
58        std::map<std::wstring,std::wstring> commands;
59
60        std::wstring certificate;
61        unsigned int timeout = 30, buffer_length = 1024;
62        bool use_ssl = true;
63        try {
64
65                register_command(_T("query_nscp"), _T("Submit a query to a remote host via NSCP"));
66                register_command(_T("submit_nscp"), _T("Submit a query to a remote host via NSCP"));
67                register_command(_T("exec_nscp"), _T("Execute remote command on a remote host via NSCP"));
68
69                sh::settings_registry settings(get_settings_proxy());
70                settings.set_alias(_T("NSCP"), alias, _T("client"));
71                target_path = settings.alias().get_settings_path(_T("targets"));
72
73                settings.alias().add_path_to_settings()
74                        (_T("NSCP CLIENT SECTION"), _T("Section for NSCP active/passive check module."))
75
76                        (_T("handlers"), sh::fun_values_path(boost::bind(&NSCPClient::add_command, this, _1, _2)),
77                        _T("CLIENT HANDLER SECTION"), _T(""))
78
79                        (_T("targets"), sh::fun_values_path(boost::bind(&NSCPClient::add_target, this, _1, _2)),
80                        _T("REMOTE TARGET DEFINITIONS"), _T(""))
81
82                        ;
83
84                settings.alias().add_key_to_settings()
85                        (_T("channel"), sh::wstring_key(&channel_, _T("NSCP")),
86                        _T("CHANNEL"), _T("The channel to listen to."))
87
88                        ;
89
90                settings.alias().add_key_to_settings(_T("targets/default"))
91
92                        (_T("timeout"), sh::uint_key(&timeout, 30),
93                        _T("TIMEOUT"), _T("Timeout when reading/writing packets to/from sockets."))
94
95                        (_T("use ssl"), sh::bool_key(&use_ssl, true),
96                        _T("ENABLE SSL ENCRYPTION"), _T("This option controls if SSL should be enabled."))
97
98                        (_T("certificate"), sh::wpath_key(&certificate, _T("${certificate-path}/nrpe_dh_512.pem")),
99                        _T("SSL CERTIFICATE"), _T(""))
100
101                        (_T("payload length"),  sh::uint_key(&buffer_length, 1024),
102                        _T("PAYLOAD LENGTH"), _T("Length of payload to/from the NRPE agent. This is a hard specific value so you have to \"configure\" (read recompile) your NRPE agent to use the same value for it to work."))
103                        ;
104
105                settings.register_all();
106                settings.notify();
107
108                get_core()->registerSubmissionListener(get_id(), channel_);
109
110                if (!targets.has_target(_T("default"))) {
111                        add_target(_T("default"), _T("default"));
112                        targets.rebuild();
113                }
114                nscapi::target_handler::optarget t = targets.find_target(_T("default"));
115                if (t) {
116                        if (!t->has_option("certificate"))
117                                t->options[_T("certificate")] = certificate;
118                        if (!t->has_option("timeout"))
119                                t->options[_T("timeout")] = strEx::itos(timeout);
120                        if (!t->has_option("payload length"))
121                                t->options[_T("payload length")] = strEx::itos(buffer_length);
122                        if (!t->has_option("ssl"))
123                                t->options[_T("ssl")] = use_ssl?_T("true"):_T("false");
124                        targets.add(*t);
125                } else {
126                        NSC_LOG_ERROR(_T("Default target not found!"));
127                }
128
129        } catch (nscapi::nscapi_exception &e) {
130                NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what()));
131                return false;
132        } catch (std::exception &e) {
133                NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what()));
134                return false;
135        } catch (...) {
136                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
137                return false;
138        }
139        return true;
140}
141std::string get_command(std::string alias, std::string command = "") {
142        if (!alias.empty())
143                return alias;
144        if (!command.empty())
145                return command;
146        return "_NRPE_CHECK";
147}
148
149//////////////////////////////////////////////////////////////////////////
150// Settings helpers
151//
152
153void NSCPClient::add_target(std::wstring key, std::wstring arg) {
154        try {
155                nscapi::target_handler::target t = targets.add(get_settings_proxy(), target_path , key, arg);
156                if (t.has_option(_T("certificate"))) {
157                        boost::filesystem::wpath p = t.options[_T("certificate")];
158                        if (!boost::filesystem::is_regular(p)) {
159                                p = get_core()->getBasePath() / p;
160                                t.options[_T("certificate")] = utf8::cvt<std::wstring>(p.string());
161                                targets.add(t);
162                        }
163                        if (boost::filesystem::is_regular(p)) {
164                                NSC_DEBUG_MSG_STD(_T("Using certificate: ") + p.string());
165                        } else {
166                                NSC_LOG_ERROR_STD(_T("Certificate not found: ") + p.string());
167                        }
168                }
169        } catch (...) {
170                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key);
171        }
172}
173
174void NSCPClient::add_command(std::wstring name, std::wstring args) {
175        try {
176                std::wstring key = commands.add_command(name, args);
177                if (!key.empty())
178                        register_command(key.c_str(), _T("NSCP relay for: ") + name);
179        } catch (boost::program_options::validation_error &e) {
180                NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what()));
181        } catch (...) {
182                NSC_LOG_ERROR_STD(_T("Could not add command ") + name);
183        }
184}
185
186/**
187 * Unload (terminate) module.
188 * Attempt to stop the background processing thread.
189 * @return true if successfully, false if not (if not things might be bad)
190 */
191bool NSCPClient::unloadModule() {
192        return true;
193}
194
195NSCAPI::nagiosReturn NSCPClient::handleRAWCommand(const wchar_t* char_command, const std::string &request, std::string &result) {
196        nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_query_request(char_command, request);
197        std::wstring cmd = client::command_line_parser::parse_command(data.command, _T("syslog"));
198        client::configuration config;
199        setup(config);
200        if (!client::command_line_parser::is_command(cmd))
201                return client::command_line_parser::do_execute_command_as_query(config, cmd, data.args, result);
202        return commands.exec_simple(config, data.target, char_command, data.args, result);
203}
204
205NSCAPI::nagiosReturn NSCPClient::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &result) {
206        nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_exec_request(char_command, request);
207        std::wstring cmd = client::command_line_parser::parse_command(char_command, _T("syslog"));
208        if (!client::command_line_parser::is_command(cmd))
209                return NSCAPI::returnIgnored;
210        client::configuration config;
211        setup(config);
212        return client::command_line_parser::do_execute_command_as_exec(config, cmd, data.args, result);
213}
214
215NSCAPI::nagiosReturn NSCPClient::handleRAWNotification(const wchar_t* channel, std::string request, std::string &result) {
216        client::configuration config;
217        setup(config);
218        return client::command_line_parser::do_relay_submit(config, request, result);
219}
220
221//////////////////////////////////////////////////////////////////////////
222// Parser setup/Helpers
223//
224
225void NSCPClient::add_local_options(po::options_description &desc, client::configuration::data_type data) {
226        desc.add_options()
227                ("certificate,c", po::value<std::string>()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "certificate", _1)),
228                        "Length of payload (has to be same as on the server)")
229/*
230                ("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.")
231
232                ("cert,c", po::value<std::wstring>(&command_data.cert)->default_value(cert_), "Certificate to use.")
233                */
234                ;
235}
236
237void NSCPClient::setup(client::configuration &config) {
238        boost::shared_ptr<clp_handler_impl> handler = boost::shared_ptr<clp_handler_impl>(new clp_handler_impl(this));
239        add_local_options(config.local, config.data);
240
241        net::wurl url;
242        url.protocol = _T("nscp");
243        url.port = 5668;
244        nscapi::target_handler::optarget opt = targets.find_target(_T("default"));
245        if (opt) {
246                nscapi::target_handler::target t = *opt;
247                url.host = t.host;
248                if (t.has_option("port")) {
249                        try {
250                                url.port = strEx::stoi(t.options[_T("port")]);
251                        } catch (...) {}
252                }
253                std::string keys[] = {"certificate", "timeout", "payload length", "ssl"};
254                BOOST_FOREACH(std::string s, keys) {
255                        config.data->recipient.data[s] = utf8::cvt<std::string>(t.options[utf8::cvt<std::wstring>(s)]);
256                }
257        }
258        config.data->recipient.id = "default";
259        config.data->recipient.address = utf8::cvt<std::string>(url.to_string());
260        config.data->host_self.id = "self";
261        //config.data->host_self.host = hostname_;
262
263        config.target_lookup = handler;
264        config.handler = handler;
265}
266
267NSCPClient::connection_data NSCPClient::parse_header(const ::Plugin::Common_Header &header) {
268        nscapi::functions::destination_container recipient;
269        nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true);
270        return connection_data(recipient);
271}
272
273//////////////////////////////////////////////////////////////////////////
274// Parser implementations
275//
276
277std::string gather_and_log_errors(std::string  &payload) {
278        NSCPIPC::ErrorMessage message;
279        message.ParseFromString(payload);
280        std::string ret;
281        for (int i=0;i<message.error_size();i++) {
282                ret += message.error(i).message();
283                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::cvt<std::wstring>(message.error(i).message()));
284        }
285        return ret;
286}
287int NSCPClient::clp_handler_impl::query(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
288        int ret = NSCAPI::returnUNKNOWN;
289        Plugin::QueryRequestMessage request_message;
290        request_message.ParseFromString(request);
291        connection_data con = parse_header(*header);
292
293        Plugin::QueryResponseMessage response_message;
294        nscapi::functions::make_return_header(response_message.mutable_header(), *header);
295
296        std::list<nscp::packet> chunks;
297        chunks.push_back(nscp::factory::create_envelope_request(1));
298        chunks.push_back(nscp::factory::create_payload(nscp::data::command_request, request, 0));
299        chunks = instance->send(con, chunks);
300        BOOST_FOREACH(nscp::packet &chunk, chunks) {
301                if (nscp::checks::is_query_response(chunk)) {
302                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
303                } else if (nscp::checks::is_error(chunk)) {
304                        std::string error = gather_and_log_errors(chunk.payload);
305                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
306                        ret = NSCAPI::returnUNKNOWN;
307                } else {
308                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
309                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
310                        ret = NSCAPI::returnUNKNOWN;
311                }
312        }
313        response_message.SerializeToString(&reply);
314        return ret;
315}
316
317int NSCPClient::clp_handler_impl::submit(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
318        int ret = NSCAPI::returnUNKNOWN;
319        Plugin::SubmitRequestMessage request_message;
320        request_message.ParseFromString(request);
321        connection_data con = parse_header(*header);
322        Plugin::SubmitResponseMessage response_message;
323        nscapi::functions::make_return_header(response_message.mutable_header(), *header);
324
325        std::list<nscp::packet> chunks;
326        chunks.push_back(nscp::factory::create_payload(nscp::data::command_response, request, 0));
327        chunks = instance->send(con, chunks);
328        BOOST_FOREACH(nscp::packet &chunk, chunks) {
329                if (nscp::checks::is_submit_response(chunk)) {
330                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
331                } else if (nscp::checks::is_error(chunk)) {
332                        std::string error = gather_and_log_errors(chunk.payload);
333                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
334                        ret = NSCAPI::returnUNKNOWN;
335                } else {
336                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
337                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
338                        ret = NSCAPI::returnUNKNOWN;
339                }
340        }
341        response_message.SerializeToString(&reply);
342        return ret;
343}
344
345int NSCPClient::clp_handler_impl::exec(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
346        int ret = NSCAPI::returnOK;
347        Plugin::ExecuteRequestMessage request_message;
348        request_message.ParseFromString(request);
349        connection_data con = parse_header(*header);
350
351        Plugin::ExecuteResponseMessage response_message;
352        nscapi::functions::make_return_header(response_message.mutable_header(), *header);
353
354        std::list<nscp::packet> chunks;
355        chunks.push_back(nscp::factory::create_envelope_request(1));
356        chunks.push_back(nscp::factory::create_payload(nscp::data::exec_request, request, 0));
357        chunks = instance->send(con, chunks);
358        BOOST_FOREACH(nscp::packet &chunk, chunks) {
359                if (nscp::checks::is_exec_response(chunk)) {
360                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
361                } else if (nscp::checks::is_error(chunk)) {
362                        std::string error = gather_and_log_errors(chunk.payload);
363                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
364                        ret = NSCAPI::returnUNKNOWN;
365                } else {
366                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
367                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
368                        ret = NSCAPI::returnUNKNOWN;
369                }
370        }
371        response_message.SerializeToString(&reply);
372        return ret;
373}
374
375//////////////////////////////////////////////////////////////////////////
376// Protocol implementations
377//
378
379std::list<nscp::packet> NSCPClient::send(connection_data con, std::list<nscp::packet> &chunks) {
380        NSC_DEBUG_MSG_STD(_T("NRPE Connection details: ") + con.to_wstring());
381        chunks.push_front(nscp::factory::create_envelope_request(1));
382        std::list<nscp::packet> tmp, result;
383        if (con.use_ssl) {
384#ifdef USE_SSL
385                tmp = send_ssl(con.host, con.port, con.cert, con.timeout, chunks);
386#else
387                NSC_LOG_ERROR_STD(_T("SSL not avalible (not compiled with USE_SSL)"));
388                result.push_back(nscp::factory::create_error(_T("SSL support not available (compiled without USE_SSL)!")));
389#endif
390        } else {
391                tmp = send_nossl(con.host, con.port, con.timeout, chunks);
392        }
393        BOOST_FOREACH(nscp::packet &p, tmp) {
394                if (nscp::checks::is_envelope_response(p)) {
395                } else {
396                        result.push_back(p);
397                }
398        }
399        return result;
400}
401
402#ifdef USE_SSL
403std::list<nscp::packet> NSCPClient::send_ssl(std::string host, std::string port, std::wstring cert, int timeout, const std::list<nscp::packet> &chunks) {
404        NSC_DEBUG_MSG_STD(_T("Connecting SSL to: ") + utf8::cvt<std::wstring>(host + ":" + port));
405        boost::asio::io_service io_service;
406        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
407        SSL_CTX_set_cipher_list(ctx.impl(), "ADH");
408        ctx.use_tmp_dh_file(to_string(cert));
409        ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
410        nscp::client::ssl_socket socket(io_service, ctx, host, port);
411        socket.send(chunks, boost::posix_time::seconds(timeout));
412        return socket.recv(boost::posix_time::seconds(timeout));
413}
414#endif
415
416std::list<nscp::packet> NSCPClient::send_nossl(std::string host, std::string port, int timeout, const std::list<nscp::packet> &chunks) {
417        NSC_DEBUG_MSG_STD(_T("Connecting to: ") + utf8::cvt<std::wstring>(host + ":" + port));
418        boost::asio::io_service io_service;
419        nscp::client::socket socket(io_service, host, port);
420        socket.send(chunks, boost::posix_time::seconds(timeout));
421        return socket.recv(boost::posix_time::seconds(timeout));
422}
423
424NSC_WRAP_DLL();
425NSC_WRAPPERS_MAIN_DEF(NSCPClient);
426NSC_WRAPPERS_IGNORE_MSG_DEF();
427NSC_WRAPPERS_HANDLE_CMD_DEF();
428NSC_WRAPPERS_CLI_DEF();
429NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();
430
Note: See TracBrowser for help on using the repository browser.