/************************************************************************** * Copyright (C) 2004-2007 by Michael Medin * * * * This code is part of NSClient++ - http://trac.nakednuns.org/nscp * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "stdafx.h" #include "NSCAClient.h" #include #include #include #include #include #include namespace sh = nscapi::settings_helper; /** * Default c-tor * @return */ NSCAAgent::NSCAAgent() {} /** * Default d-tor * @return */ NSCAAgent::~NSCAAgent() {} /** * Load (initiate) module. * Start the background collector thread and let it run until unloadModule() is called. * @return true */ bool NSCAAgent::loadModule() { return false; } /* DEFINE_SETTING_S(REPORT_MODE, NSCA_SERVER_SECTION, "report", "all"); DESCRIBE_SETTING(REPORT_MODE, "REPORT MODE", "What to report to the server (any of the following: all, critical, warning, unknown, ok)"); */ bool NSCAAgent::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) { std::wstring encryption, password, nscahost; std::string delay; unsigned int timeout = 30, payload_length = 512, nscaport = 5666; try { sh::settings_registry settings(get_settings_proxy()); settings.set_alias(_T("NSCA"), alias, _T("client")); target_path = settings.alias().get_settings_path(_T("targets")); settings.alias().add_path_to_settings() (_T("NSCA CLIENT SECTION"), _T("Section for NSCA passive check module.")) (_T("handlers"), sh::fun_values_path(boost::bind(&NSCAAgent::add_command, this, _1, _2)), _T("CLIENT HANDLER SECTION"), _T("")) (_T("targets"), sh::fun_values_path(boost::bind(&NSCAAgent::add_target, this, _1, _2)), _T("REMOTE TARGET DEFINITIONS"), _T("")) ; settings.alias().add_key_to_settings() (_T("hostname"), sh::string_key(&hostname_), _T("HOSTNAME"), _T("The host name of this host if set to blank (default) the windows name of the computer will be used.")) (_T("channel"), sh::wstring_key(&channel_, _T("NSCA")), _T("CHANNEL"), _T("The channel to listen to.")) (_T("delay"), sh::string_fun_key(boost::bind(&NSCAAgent::set_delay, this, _1), _T("0")), _T("DELAY"), _T("")) ; settings.alias().add_path_to_settings(_T("server")) (_T("NSCA SERVER"), _T("Configure the NSCA server to report to.")) ; settings.alias(_T("/targets/default")).add_key_to_settings() (_T("timeout"), sh::uint_key(&timeout, 30), _T("TIMEOUT"), _T("Timeout when reading packets on incoming sockets. If the data has not arrived withint this time we will bail out.")) (_T("host"), sh::wstring_key(&nscahost), _T("NSCA HOST"), _T("The NSCA server to report results to.")) (_T("port"), sh::uint_key(&nscaport, 5667), _T("NSCA PORT"), _T("The NSCA server port")) (_T("encryption"), sh::wstring_key(&encryption, _T("aes")), _T("ENCRYPTION METHOD"), _T("Number corresponding to the various encryption algorithms (see the wiki). Has to be the same as the server or it wont work at all.")) (_T("password"), sh::wstring_key(&password), _T("PASSWORD"), _T("The password to use. Again has to be the same as the server or it wont work at all.")) (_T("payload length"), sh::uint_key(&payload_length, 512), _T("PAYLOAD LENGTH"), _T("Length of payload to/from the NSCA agent. This is a hard specific value so you have to \"configure\" (read recompile) your NSCA agent to use the same value for it to work.")) (_T("time offset"), sh::string_key(&delay, "0"), _T("TIME OFFSET"), _T("Time offset.")) ; settings.register_all(); settings.notify(); get_core()->registerSubmissionListener(get_id(), channel_); if (!targets.has_target(_T("default"))) { add_target(_T("default"), _T("default")); targets.rebuild(); } nscapi::target_handler::optarget t = targets.find_target(_T("default")); if (t) { if (!t->has_option("encryption")) t->options[_T("encryption")] = encryption; if (!t->has_option("timeout")) t->options[_T("timeout")] = strEx::itos(timeout); if (!t->has_option("payload length")) t->options[_T("payload length")] = strEx::itos(payload_length); if (!t->has_option("time offset")) t->options[_T("time offset")] = utf8::cvt(delay); if (!t->has_option("password")) t->options[_T("password")] = password; if (!t->address.empty()) t->address = _T("nsca://") + nscahost + _T(":") + strEx::itos(nscaport); targets.add(*t); } else { NSC_LOG_ERROR(_T("Default target not found!")); } } catch (nscapi::nscapi_exception &e) { NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what())); return false; } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what())); return false; } catch (...) { NSC_LOG_ERROR_STD(_T("Exception caught: ")); return false; } return true; } std::wstring NSCAAgent::getCryptos() { std::wstring ret = _T("{"); for (int i=0;i"); else name = utf8::to_unicode(core->getName()); } catch (nsca::nsca_encrypt::encryption_exception &e) { name = utf8::to_unicode(e.what()); } if (ret.size() > 1) ret += _T(", "); ret += strEx::itos(i) + _T("=") + name; } } return ret + _T("}"); } std::string get_command(std::string alias, std::string command = "") { if (!alias.empty()) return alias; if (!command.empty()) return command; return "host_check"; } ////////////////////////////////////////////////////////////////////////// // Settings helpers // void NSCAAgent::add_target(std::wstring key, std::wstring arg) { try { targets.add(get_settings_proxy(), target_path , key, arg); } catch (...) { NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key); } } void NSCAAgent::add_command(std::wstring name, std::wstring args) { try { std::wstring key = commands.add_command(name, args); if (!key.empty()) register_command(key.c_str(), _T("NSCA relay for: ") + name); } catch (boost::program_options::validation_error &e) { NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what())); } catch (...) { NSC_LOG_ERROR_STD(_T("Could not add command ") + name); } } /** * Unload (terminate) module. * Attempt to stop the background processing thread. * @return true if successfully, false if not (if not things might be bad) */ bool NSCAAgent::unloadModule() { return true; } NSCAPI::nagiosReturn NSCAAgent::handleCommand(const std::wstring &target, const std::wstring &command, std::list &arguments, std::wstring &message, std::wstring &perf) { std::wstring cmd = client::command_line_parser::parse_command(command, _T("nsca")); client::configuration config; setup(config); if (cmd == _T("query")) return client::command_line_parser::query(config, cmd, arguments, message, perf); if (cmd == _T("submit")) { boost::tuple result = client::command_line_parser::simple_submit(config, cmd, arguments); message = result.get<1>(); return result.get<0>(); } if (cmd == _T("exec")) { return client::command_line_parser::exec(config, cmd, arguments, message); } return commands.exec_simple(config, target, command, arguments, message, perf); } int NSCAAgent::commandLineExec(const std::wstring &command, std::list &arguments, std::wstring &result) { std::wstring cmd = client::command_line_parser::parse_command(command, _T("nsca")); if (!client::command_line_parser::is_command(cmd)) return NSCAPI::returnIgnored; client::configuration config; setup(config); return client::command_line_parser::commandLineExec(config, cmd, arguments, result); } NSCAPI::nagiosReturn NSCAAgent::handleRAWNotification(const wchar_t* channel, std::string request, std::string &response) { try { client::configuration config; setup(config); if (!client::command_line_parser::relay_submit(config, request, response)) { NSC_LOG_ERROR_STD(_T("Failed to submit message...")); return NSCAPI::hasFailed; } return NSCAPI::isSuccess; } catch (nsca::nsca_encrypt::encryption_exception &e) { NSC_LOG_ERROR_STD(_T("Failed to encrypt data: ") + utf8::to_unicode(e.what())); return NSCAPI::hasFailed; } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Failed to send data: ") + utf8::to_unicode(e.what())); return NSCAPI::hasFailed; } catch (...) { NSC_LOG_ERROR_STD(_T("Failed to send data: UNKNOWN")); return NSCAPI::hasFailed; } } ////////////////////////////////////////////////////////////////////////// // Parser setup/Helpers // void NSCAAgent::add_local_options(po::options_description &desc, client::configuration::data_type data) { desc.add_options() ("encryption,e", po::value()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "encryption", _1)), "Length of payload (has to be same as on the server)") ("buffer-length,l", po::value()->notifier(boost::bind(&nscapi::functions::destination_container::set_int_data, &data->recipient, "payload length", _1)), "Length of payload (has to be same as on the server)") ("timeout", po::value()->notifier(boost::bind(&nscapi::functions::destination_container::set_int_data, &data->recipient, "timeout", _1)), "") ("password", po::value()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "password", _1)), "Password") ("time-offset", po::value()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "time offset", _1)), "") ; } void NSCAAgent::setup(client::configuration &config) { boost::shared_ptr handler = boost::shared_ptr(new clp_handler_impl(this)); add_local_options(config.local, config.data); net::wurl url; url.protocol = _T("nsca"); url.port = 5667; nscapi::target_handler::optarget opt = targets.find_target(_T("default")); if (opt) { nscapi::target_handler::target t = *opt; url.host = t.host; if (t.has_option("port")) { try { url.port = strEx::stoi(t.options[_T("port")]); } catch (...) {} } std::string keys[] = {"encryption", "timeout", "payload length", "password", "time offset"}; BOOST_FOREACH(std::string s, keys) { config.data->recipient.data[s] = utf8::cvt(t.options[utf8::cvt(s)]); } } config.data->recipient.id = "default"; config.data->recipient.address = utf8::cvt(url.to_string()); config.data->host_self.id = "self"; config.data->host_self.host = hostname_; config.target_lookup = handler; config.handler = handler; } NSCAAgent::connection_data NSCAAgent::parse_header(const ::Plugin::Common_Header &header) { nscapi::functions::destination_container recipient, sender; nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true); nscapi::functions::parse_destination(header, header.sender_id(), sender, true); return connection_data(recipient, sender); } ////////////////////////////////////////////////////////////////////////// // Parser implementations // int NSCAAgent::clp_handler_impl::query(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) { NSCAPI::nagiosReturn ret = NSCAPI::returnOK; try { Plugin::QueryRequestMessage request_message; request_message.ParseFromString(request); connection_data con = parse_header(*header); std::list list; for (int i=0;i < request_message.payload_size(); ++i) { nsca::packet packet(con.sender_hostname, con.buffer_length, con.time_delta); nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_query_request(request_message.payload(i)); packet.code = 0; packet.result = utf8::cvt(data.command); list.push_back(packet); } boost::tuple ret = instance->send(con, list); nscapi::functions::create_simple_query_response(_T("UNKNOWN"), ret.get<0>(), ret.get<1>(), _T(""), reply); return NSCAPI::isSuccess; } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what())); nscapi::functions::create_simple_query_response(_T("command"), NSCAPI::returnUNKNOWN, _T("Exception: ") + utf8::to_unicode(e.what()), _T(""), reply); return NSCAPI::returnUNKNOWN; } } int NSCAAgent::clp_handler_impl::submit(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) { std::wstring channel; try { Plugin::SubmitRequestMessage message; message.ParseFromString(request); connection_data con = parse_header(*header); channel = utf8::cvt(message.channel()); std::list list; for (int i=0;i < message.payload_size(); ++i) { nsca::packet packet(con.sender_hostname, con.buffer_length, con.time_delta); std::wstring alias, msg; packet.code = nscapi::functions::parse_simple_submit_request_payload(message.payload(i), alias, msg); if (alias != _T("host_check")) packet.service = utf8::cvt(alias); packet.result = utf8::cvt(msg); list.push_back(packet); } boost::tuple ret = instance->send(con, list); nscapi::functions::create_simple_submit_response(channel, _T("UNKNOWN"), ret.get<0>(), ret.get<1>(), reply); return NSCAPI::isSuccess; } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what())); nscapi::functions::create_simple_submit_response(channel, _T("UNKNOWN"), NSCAPI::returnUNKNOWN, utf8::to_unicode(e.what()), reply); return NSCAPI::hasFailed; } } int NSCAAgent::clp_handler_impl::exec(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) { NSCAPI::nagiosReturn ret = NSCAPI::returnOK; try { Plugin::ExecuteRequestMessage request_message; request_message.ParseFromString(request); connection_data con = parse_header(*header); std::list list; for (int i=0;i < request_message.payload_size(); ++i) { nsca::packet packet(con.sender_hostname, con.buffer_length, con.time_delta); nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_exec_request_payload(request_message.payload(i)); packet.code = 0; if (data.command != _T("host_check")) packet.service = utf8::cvt(data.command); //packet.result = data.; list.push_back(packet); } boost::tuple ret = instance->send(con, list); nscapi::functions::create_simple_exec_response(_T("UNKNOWN"), ret.get<0>(), ret.get<1>(), reply); return NSCAPI::isSuccess; } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what())); nscapi::functions::create_simple_exec_response(_T("command"), NSCAPI::returnUNKNOWN, _T("Exception: ") + utf8::to_unicode(e.what()), reply); return NSCAPI::hasFailed; } } ////////////////////////////////////////////////////////////////////////// // Protocol implementations boost::tuple NSCAAgent::send(connection_data data, const std::list packets) { try { NSC_DEBUG_MSG_STD(_T("Connection details: ") + data.to_wstring()); boost::asio::io_service io_service; nsca::socket socket(io_service); socket.connect(data.host, data.port); if (!socket.recv_iv(data.password, data.get_encryption(), boost::posix_time::seconds(data.timeout<5?30:data.timeout))) { NSC_LOG_ERROR_STD(_T("Failed to read iv")); return NSCAPI::hasFailed; } NSC_DEBUG_MSG_STD(_T("Got IV sending data: ") + strEx::itos(packets.size())); BOOST_FOREACH(const nsca::packet &packet, packets) { NSC_DEBUG_MSG_STD(_T("Sending: ") + utf8::cvt(packet.to_string())); socket.send_nsca(packet, boost::posix_time::seconds(data.timeout)); } return NSCAPI::isSuccess; } catch (nsca::nsca_encrypt::encryption_exception &e) { NSC_LOG_ERROR_STD(_T("NSCA Error: ") + utf8::to_unicode(e.what())); return boost::tie(NSCAPI::returnUNKNOWN, _T("NSCA error: ") + utf8::to_unicode(e.what())); } catch (std::runtime_error &e) { NSC_LOG_ERROR_STD(_T("Socket error: ") + utf8::to_unicode(e.what())); return boost::tie(NSCAPI::returnUNKNOWN, _T("Socket error: ") + utf8::to_unicode(e.what())); } catch (std::exception &e) { NSC_LOG_ERROR_STD(_T("Error: ") + utf8::to_unicode(e.what())); return boost::tie(NSCAPI::returnUNKNOWN, _T("Error: ") + utf8::to_unicode(e.what())); } catch (...) { return boost::tie(NSCAPI::returnUNKNOWN, _T("Unknown error -- REPORT THIS!")); } } NSC_WRAP_DLL(); NSC_WRAPPERS_MAIN_DEF(NSCAAgent); NSC_WRAPPERS_IGNORE_MSG_DEF(); NSC_WRAPPERS_HANDLE_CMD_DEF(); NSC_WRAPPERS_CLI_DEF(); NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();