source: nscp/modules/NRPEClient/NRPEClient.cpp @ dcd90b2

0.4.00.4.10.4.2
Last change on this file since dcd90b2 was dcd90b2, checked in by Michael Medin <michael@…>, 3 years ago

Added back openssl now builds (and presumably the NRPEClient works with it haven't tested but will do after I get the NRPEListener up and running...)

  • Property mode set to 100644
File size: 19.4 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 "NRPEClient.h"
23#include <strEx.h>
24#include <time.h>
25#include <config.h>
26#include <msvc_wrappers.h>
27//#include <execute_process.hpp>
28#include <strEx.h>
29
30
31
32NRPEClient gNRPEClient;
33
34#ifdef WIN32
35BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
36{
37        NSCModuleWrapper::wrapDllMain(hModule, ul_reason_for_call);
38        return TRUE;
39}
40#endif
41
42NRPEClient::NRPEClient() : buffer_length_(0) {
43}
44
45NRPEClient::~NRPEClient() {
46}
47
48bool NRPEClient::loadModule(NSCAPI::moduleLoadMode mode) {
49        std::list<std::wstring> commands;
50        buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
51        try {
52                SETTINGS_REG_PATH(nrpe::CH_SECTION);
53                commands = NSCModuleHelper::getSettingsSection(setting_keys::nrpe::CH_SECTION_PATH);
54        } catch (NSCModuleHelper::NSCMHExcpetion &e) {
55                NSC_LOG_ERROR_STD(_T("Failed to register command: ") + e.msg_);
56        } catch (...) {
57                NSC_LOG_ERROR_STD(_T("Failed to register command."));
58        }
59        for (std::list<std::wstring>::const_iterator it = commands.begin(); it != commands.end(); ++it) {
60                NSC_DEBUG_MSG_STD(*it);
61                std::wstring s = NSCModuleHelper::getSettingsString(setting_keys::nrpe::CH_SECTION_PATH, (*it), _T(""));
62                if (s.empty()) {
63                        NSC_LOG_ERROR_STD(_T("Invalid NRPE-client entry: ") + (*it));
64                } else {
65                        addCommand((*it).c_str(), s);
66                }
67        }
68        return true;
69}
70
71void NRPEClient::add_options(po::options_description &desc, nrpe_connection_data command_data) {
72        desc.add_options()
73                ("help,h", "Show this help message.")
74                ("host,H", po::wvalue<std::wstring>(&command_data.host), "The address of the host running the NRPE daemon")
75                ("port,p", po::value<int>(&command_data.port), "The port on which the daemon is running (default=5666)")
76                ("command,c", po::wvalue<std::wstring>(&command_data.command), "The name of the command that the remote daemon should run")
77                ("timeout,t", po::value<int>(&command_data.timeout), "Number of seconds before connection times out (default=10)")
78                ("buffer-length,l", po::value<unsigned int>(&command_data.buffer_length), std::string("Length of payload (has to be same as on the server (default=" + to_string(buffer_length_) + ")").c_str())
79                ("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 plaintext.")
80                ("arguments,a", po::wvalue<std::vector<std::wstring> >(&command_data.argument_vector), "list of arguments")
81                ;
82}
83
84
85void NRPEClient::addCommand(strEx::blindstr key, std::wstring args) {
86        try {
87
88                NRPEClient::nrpe_connection_data command_data;
89                boost::program_options::variables_map vm;
90
91                po::options_description desc("Allowed options");
92                buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
93                add_options(desc, command_data);
94
95                po::positional_options_description p;
96                p.add("arguments", -1);
97
98                std::vector<std::wstring> list;
99                //explicit escaped_list_separator(Char e = '\\', Char c = ',',Char q = '\"')
100                boost::escaped_list_separator<wchar_t> sep(L'\\', L' ', L'\"');
101                typedef boost::tokenizer<boost::escaped_list_separator<wchar_t>,std::wstring::const_iterator, std::wstring > tokenizer_t;
102                tokenizer_t tok(args, sep);
103                for(tokenizer_t::iterator beg=tok.begin(); beg!=tok.end();++beg){
104                        std::wcout << *beg << std::endl;
105                        list.push_back(*beg);
106                }
107
108                po::wparsed_options parsed = po::basic_command_line_parser<wchar_t>(list).options(desc).positional(p).run();
109                po::store(parsed, vm);
110                po::notify(vm);
111                command_data.parse_arguments();
112
113                NSC_DEBUG_MSG_STD(_T("Added NRPE Client: ") + key.c_str() + _T(" = ") + command_data.toString());
114                commands[key] = command_data;
115        } catch (boost::program_options::validation_error &e) {
116                NSC_LOG_ERROR_STD(_T("Could not parse: ") + key.c_str() + strEx::string_to_wstring(e.what()));
117        } catch (...) {
118                NSC_LOG_ERROR_STD(_T("Could not parse: ") + key.c_str());
119        }
120}
121
122bool NRPEClient::unloadModule() {
123        return true;
124}
125
126bool NRPEClient::hasCommandHandler() {
127        return true;
128}
129bool NRPEClient::hasMessageHandler() {
130        return false;
131}
132NSCAPI::nagiosReturn NRPEClient::handleCommand(const strEx::blindstr command, const unsigned int argLen, TCHAR **char_args, std::wstring &message, std::wstring &perf)
133{
134        command_list::const_iterator cit = commands.find(command);
135        if (cit == commands.end())
136                return NSCAPI::returnIgnored;
137
138        std::wstring args = (*cit).second.arguments;
139        if (SETTINGS_GET_BOOL(nrpe::ALLOW_ARGS) == 1) {
140                arrayBuffer::arrayList arr = arrayBuffer::arrayBuffer2list(argLen, char_args);
141                arrayBuffer::arrayList::const_iterator cit2 = arr.begin();
142                int i=1;
143
144                for (;cit2!=arr.end();cit2++,i++) {
145                        if (SETTINGS_GET_INT(nrpe::ALLOW_NASTY) == 0) {
146                                if ((*cit2).find_first_of(NASTY_METACHARS) != std::wstring::npos) {
147                                        NSC_LOG_ERROR(_T("Request string contained illegal metachars!"));
148                                        return NSCAPI::returnIgnored;
149                                }
150                        }
151                        strEx::replace(args, _T("$ARG") + strEx::itos(i) + _T("$"), (*cit2));
152                }
153        }
154
155        NSC_DEBUG_MSG_STD(_T("Rewrote command arguments: ") + args);
156        nrpe_result_data r = execute_nrpe_command((*cit).second, args);
157        message = r.text;
158        return r.result;
159}
160
161int NRPEClient::commandLineExec(const unsigned int argLen, TCHAR** args) {
162        try {
163
164                NRPEClient::nrpe_connection_data command_data;
165                boost::program_options::variables_map vm;
166
167                po::options_description desc("Allowed options");
168                buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
169                add_options(desc, command_data);
170
171                po::positional_options_description p;
172                p.add("arguments", -1);
173                po::wparsed_options parsed = basic_command_line_parser_ex<wchar_t>(argLen, args).options(desc).positional(p).run();
174                po::store(parsed, vm);
175                po::notify(vm);
176                command_data.parse_arguments();
177
178                if (vm.count("help")) {
179                        std::cout << desc << "\n";
180                        return 1;
181                }
182                nrpe_result_data result = execute_nrpe_command(command_data, command_data.arguments);
183                std::wcout << result.text << std::endl;
184                return result.result;
185        } catch (boost::program_options::validation_error &e) {
186                std::cout << e.what() << std::endl;
187        } catch (...) {
188                std::cout << "Unknown exception parsing command line" << std::endl;
189        }
190        return NSCAPI::returnUNKNOWN;
191}
192NRPEClient::nrpe_result_data NRPEClient::execute_nrpe_command(nrpe_connection_data con, std::wstring arguments) {
193        try {
194                NRPEPacket packet;
195                if (!con.no_ssl) {
196#ifdef USE_SSL
197                        packet = send_ssl(con.host, con.port, con.timeout, NRPEPacket::make_request(con.get_cli(arguments), con.buffer_length));
198#else
199                        return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("SSL support not available (compiled without USE_SSL)!"));
200#endif
201                } else
202                        packet = send_nossl(con.host, con.port, con.timeout, NRPEPacket::make_request(con.get_cli(arguments), con.buffer_length));
203                return nrpe_result_data(packet.getResult(), packet.getPayload());
204        } catch (NRPEPacket::NRPEPacketException &e) {
205                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("NRPE Packet errro: ") + e.getMessage());
206        } catch (std::runtime_error &e) {
207                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("Socket error: ") + boost::lexical_cast<std::wstring>(e.what()));
208        } catch (...) {
209                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("Unknown error -- REPORT THIS!"));
210        }
211}
212/*
213NRPEPacket NRPEClient::send_ssl(std::wstring host, int port, int timeout, NRPEPacket packet)
214{
215#ifndef USE_SSL
216        return send_nossl(host, port, timeout, packet);
217#else
218       
219        simpleSSL::Socket socket(true);
220        socket.connect(host, port);
221        NSC_DEBUG_MSG_STD(_T(">>>length: ") + strEx::itos(packet.getBufferLength()));
222        socket.sendAll(packet.getBuffer(), packet.getBufferLength());
223        simpleSocket::DataBuffer buffer;
224        socket.readAll(buffer, packet.getBufferLength());
225        NSC_DEBUG_MSG_STD(_T("<<<length: ") + strEx::itos(buffer.getLength()));
226        packet.readFrom(buffer.getBuffer(), buffer.getLength());
227        return packet;
228       
229
230        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
231        ctx.use_tmp_dh_file("d:\\nrpe_512.pem");
232        ctx.set_verify_mode(boost::asio::ssl::context::verify_peer);
233        nrpe_ssl_socket socket(io_service, ctx, host, port);
234        //socket.
235
236#endif
237}
238*/
239using boost::asio::ip::tcp;
240
241void set_result(boost::optional<boost::system::error_code>* a, boost::system::error_code b)
242{
243        a->reset(b);
244}
245template <typename AsyncReadStream, typename RawSocket, typename MutableBufferSequence>
246void read_with_timeout(AsyncReadStream& sock, RawSocket& rawSocket, const MutableBufferSequence& buffers, boost::posix_time::time_duration duration)
247{
248        boost::optional<boost::system::error_code> timer_result;
249        boost::asio::deadline_timer timer(sock.io_service());
250        timer.expires_from_now(duration);
251        timer.async_wait(boost::bind(set_result, &timer_result, _1));
252
253        boost::optional<boost::system::error_code> read_result;
254        async_read(sock, buffers, boost::bind(set_result, &read_result, _1));
255
256        sock.io_service().reset();
257        while (sock.io_service().run_one())
258        {
259                if (read_result)
260                        timer.cancel();
261                else if (timer_result)
262                        rawSocket.close();
263        }
264
265        if (*read_result)
266                throw boost::system::system_error(*read_result);
267}
268
269template <typename AsyncWriteStream, typename RawSocket, typename MutableBufferSequence>
270void write_with_timeout(AsyncWriteStream& sock, RawSocket& rawSocket, const MutableBufferSequence& buffers, boost::posix_time::time_duration duration)
271{
272        boost::optional<boost::system::error_code> timer_result;
273        boost::asio::deadline_timer timer(sock.io_service());
274        timer.expires_from_now(duration);
275        timer.async_wait(boost::bind(set_result, &timer_result, _1));
276
277        boost::optional<boost::system::error_code> read_result;
278        async_write(sock, buffers, boost::bind(set_result, &read_result, _1));
279
280        sock.io_service().reset();
281        while (sock.io_service().run_one())
282        {
283                if (read_result)
284                        timer.cancel();
285                else if (timer_result)
286                        rawSocket.close();
287        }
288
289        if (*read_result)
290                throw boost::system::system_error(*read_result);
291}
292
293class nrpe_socket : public boost::noncopyable {
294public:
295        tcp::socket socket_;
296
297public:
298        nrpe_socket(boost::asio::io_service &io_service, std::wstring host, int port) : socket_(io_service) {
299                NSC_LOG_CRITICAL(_T("Looking up..."));
300                tcp::resolver resolver(io_service);
301                tcp::resolver::query query(to_string(host), to_string(port));
302                //tcp::resolver::query query("www.medin.name", "80");
303                //tcp::resolver::query query("test_server", "80");
304
305                tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
306                tcp::resolver::iterator end;
307
308                NSC_LOG_CRITICAL(_T("connecting..."));
309                boost::system::error_code error = boost::asio::error::host_not_found;
310                while (error && endpoint_iterator != end)
311                {
312                        tcp::resolver::endpoint_type ep = *endpoint_iterator;
313                        NSC_DEBUG_MSG_STD(_T("Attempting to connect to: ") + to_wstring(ep.address().to_string()) + _T(":") + to_wstring(ep.port()));
314                        socket_.close();
315                        socket_.connect(*endpoint_iterator++, error);
316                }
317                NSC_LOG_CRITICAL(_T("Connected..."));
318                if (error)
319                        throw boost::system::system_error(error);
320        }
321        ~nrpe_socket() {
322                socket_.close();
323        }
324
325        void send(NRPEPacket &packet, boost::posix_time::seconds timeout) {
326                std::vector<char> buf(packet.getBufferLength());
327                write_with_timeout(socket_, socket_, boost::asio::buffer(packet.getBuffer(), packet.getBufferLength()), timeout);
328        }
329        NRPEPacket recv(const NRPEPacket &packet, boost::posix_time::seconds timeout) {
330                std::vector<char> buf(packet.getBufferLength());
331                read_with_timeout(socket_, socket_, boost::asio::buffer(buf), timeout);
332                return NRPEPacket(&buf[0], buf.size(), packet.getInternalBufferLength());
333        }
334};
335#ifdef USE_SSL
336
337class nrpe_ssl_socket {
338
339private:
340        boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
341public:
342        nrpe_ssl_socket(boost::asio::io_service &io_service, boost::asio::ssl::context &ctx, std::wstring host, int port) : socket_(io_service, ctx) {
343                NSC_LOG_CRITICAL(_T("Looking up..."));
344                tcp::resolver resolver(io_service);
345                //tcp::resolver::query query(to_string(host), to_string(port));
346                tcp::resolver::query query("www.medin.name", "80");
347                //tcp::resolver::query query("test_server", "80");
348
349                tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
350                tcp::resolver::iterator end;
351
352                boost::system::error_code error = boost::asio::error::host_not_found;
353                NSC_LOG_CRITICAL(_T("Connecting..."));
354                while (error && endpoint_iterator != end)
355                {
356                        tcp::resolver::endpoint_type ep = *endpoint_iterator;
357                        NSC_DEBUG_MSG_STD(_T("Attempting to connect to: ") + to_wstring(ep.address().to_string()) + _T(":") + to_wstring(ep.port()));
358                        socket_.lowest_layer().close();
359                        socket_.lowest_layer().connect(*endpoint_iterator++, error);
360                }
361                if (error)
362                        throw boost::system::system_error(error);
363                NSC_LOG_CRITICAL(_T("Connected..."));
364
365                NSC_LOG_CRITICAL(_T("Handshaking..."));
366                //socket_.handshake(boost::asio::ssl::stream_base::client);
367                socket_.handshake(boost::asio::ssl::stream_base::client);
368                NSC_LOG_CRITICAL(_T("Handshook...") + strEx::itos(error.value()));
369
370        }
371
372        void send(NRPEPacket &packet, boost::posix_time::seconds timeout) {
373                NSC_LOG_CRITICAL(_T("Writing..."));
374                std::vector<char> buf(packet.getBufferLength());
375                write_with_timeout(socket_, socket_.lowest_layer(), boost::asio::buffer(packet.getBuffer(), packet.getBufferLength()), timeout);
376                NSC_LOG_CRITICAL(_T("Written..."));
377        }
378        NRPEPacket recv(const NRPEPacket &packet, boost::posix_time::seconds timeout) {
379                NSC_LOG_CRITICAL(_T("Reading..."));
380                std::vector<char> buf(packet.getBufferLength());
381                read_with_timeout(socket_, socket_.lowest_layer(), boost::asio::buffer(buf), timeout);
382                return NRPEPacket(&buf[0], buf.size(), packet.getInternalBufferLength());
383                NSC_LOG_CRITICAL(_T("Read..."));
384        }
385};
386NRPEPacket NRPEClient::send_ssl(std::wstring host, int port, int timeout, NRPEPacket packet)
387{
388        boost::asio::io_service io_service;
389        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
390        SSL_CTX_set_cipher_list(ctx.impl(), "ADH");
391        ctx.use_tmp_dh_file("d:\\nrpe_512.pem");
392        ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
393        nrpe_ssl_socket socket(io_service, ctx, host, port);
394        socket.send(packet, boost::posix_time::seconds(timeout));
395        return socket.recv(packet, boost::posix_time::seconds(timeout));
396}
397#endif
398
399NRPEPacket NRPEClient::send_nossl(std::wstring host, int port, int timeout, NRPEPacket packet)
400{
401        boost::asio::io_service io_service;
402        nrpe_socket socket(io_service, host, port);
403        socket.send(packet, boost::posix_time::seconds(timeout));
404        return socket.recv(packet, boost::posix_time::seconds(timeout));
405}
406
407/*
408NRPEPacket NRPEClient::send_nossl(std::wstring host, int port, int timeout, NRPEPacket packet)
409{
410        unsigned char dh512_p[] = {
411                0xCF, 0xFF, 0x65, 0xC2, 0xC8, 0xB4, 0xD2, 0x68, 0x8C, 0xC1, 0x80, 0xB1,
412                0x7B, 0xD6, 0xE8, 0xB3, 0x62, 0x59, 0x62, 0xED, 0xA7, 0x45, 0x6A, 0xF8,
413                0xE9, 0xD8, 0xBE, 0x3F, 0x38, 0x42, 0x5F, 0xB2, 0xA5, 0x36, 0x03, 0xD3,
414                0x06, 0x27, 0x81, 0xC8, 0x9B, 0x88, 0x50, 0x3B, 0x82, 0x3D, 0x31, 0x45,
415                0x2C, 0xB4, 0xC5, 0xA5, 0xBE, 0x6A, 0xE3, 0x2E, 0xA6, 0x86, 0xFD, 0x6A,
416                0x7E, 0x1E, 0x6A, 0x73,
417        };
418        unsigned char dh512_g[] = { 0x02, };
419
420        DH *dh_2 = DH_new();
421        dh_2->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
422        dh_2->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
423
424        FILE *outfile = fopen("d:\\nrpe_512.pem", "w");
425        PEM_write_DHparams(outfile, dh_2);
426        PEM_write_DHparams(stdout, dh_2);
427        fclose(outfile);
428
429        nrpe_socket socket(host, port);
430        socket.send(packet, boost::posix_time::seconds(timeout));
431        return socket.recv(packet, boost::posix_time::seconds(timeout));
432}
433*/
434
435
436
437
438
439NSC_WRAPPERS_MAIN_DEF(gNRPEClient);
440NSC_WRAPPERS_IGNORE_MSG_DEF();
441NSC_WRAPPERS_HANDLE_CMD_DEF(gNRPEClient);
442NSC_WRAPPERS_HANDLE_CONFIGURATION(gNRPEClient);
443NSC_WRAPPERS_CLI_DEF(gNRPEClient);
444
445
446MODULE_SETTINGS_START(NRPEClient, _T("NRPE Listener configuration"), _T("..."))
447
448PAGE(_T("NRPE Listsner configuration"))
449
450ITEM_EDIT_TEXT(_T("port"), _T("This is the port the NRPEClient.dll will listen to."))
451ITEM_MAP_TO(_T("basic_ini_text_mapper"))
452OPTION(_T("section"), _T("NRPE"))
453OPTION(_T("key"), _T("port"))
454OPTION(_T("default"), _T("5666"))
455ITEM_END()
456
457ITEM_CHECK_BOOL(_T("allow_arguments"), _T("This option determines whether or not the NRPE daemon will allow clients to specify arguments to commands that are executed."))
458ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
459OPTION(_T("section"), _T("NRPE"))
460OPTION(_T("key"), _T("allow_arguments"))
461OPTION(_T("default"), _T("false"))
462OPTION(_T("true_value"), _T("1"))
463OPTION(_T("false_value"), _T("0"))
464ITEM_END()
465
466ITEM_CHECK_BOOL(_T("allow_nasty_meta_chars"), _T("This might have security implications (depending on what you do with the options)"))
467ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
468OPTION(_T("section"), _T("NRPE"))
469OPTION(_T("key"), _T("allow_nasty_meta_chars"))
470OPTION(_T("default"), _T("false"))
471OPTION(_T("true_value"), _T("1"))
472OPTION(_T("false_value"), _T("0"))
473ITEM_END()
474
475ITEM_CHECK_BOOL(_T("use_ssl"), _T("This option will enable SSL encryption on the NRPE data socket (this increases security somwhat."))
476ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
477OPTION(_T("section"), _T("NRPE"))
478OPTION(_T("key"), _T("use_ssl"))
479OPTION(_T("default"), _T("true"))
480OPTION(_T("true_value"), _T("1"))
481OPTION(_T("false_value"), _T("0"))
482ITEM_END()
483
484PAGE_END()
485ADVANCED_PAGE(_T("Access configuration"))
486
487ITEM_EDIT_OPTIONAL_LIST(_T("Allow connection from:"), _T("This is the hosts that will be allowed to poll performance data from the NRPE server."))
488OPTION(_T("disabledCaption"), _T("Use global settings (defined previously)"))
489OPTION(_T("enabledCaption"), _T("Specify hosts for NRPE server"))
490OPTION(_T("listCaption"), _T("Add all IP addresses (not hosts) which should be able to connect:"))
491OPTION(_T("separator"), _T(","))
492OPTION(_T("disabled"), _T(""))
493ITEM_MAP_TO(_T("basic_ini_text_mapper"))
494OPTION(_T("section"), _T("NRPE"))
495OPTION(_T("key"), _T("allowed_hosts"))
496OPTION(_T("default"), _T(""))
497ITEM_END()
498
499PAGE_END()
500MODULE_SETTINGS_END()
Note: See TracBrowser for help on using the repository browser.