source: nscp/modules/NSClientListener/NSClientListener.cpp @ a34b229

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

2008-09-09 MickeM

  • Fixed issue with & and some commands via check_nt.
  • Fixed a crash on exit (which I added in Rc1).
  • Added 10 "bytes" the CPU buffer: (#174) + Added new option to [EventLog?] section buffer_size to change the size of the buffer used when scanning the evenlotg (defaults to 64k).
  • Fixed error handling in CHeckEventLog so errors are repoorted properly (#184)
  • Property mode set to 100644
File size: 13.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 "NSClientListener.h"
23#include <strEx.h>
24#include <time.h>
25#include <config.h>
26
27NSClientListener gNSClientListener;
28
29
30#define REQ_CLIENTVERSION       1       // Works fine!
31#define REQ_CPULOAD                     2       // Quirks
32#define REQ_UPTIME                      3       // Works fine!
33#define REQ_USEDDISKSPACE       4       // Works fine!
34#define REQ_SERVICESTATE        5       // Works fine!
35#define REQ_PROCSTATE           6       // Works fine!
36#define REQ_MEMUSE                      7       // Works fine!
37#define REQ_COUNTER                     8       // Works fine!
38#define REQ_FILEAGE                     9       // ... in the works ...
39//#define REQ_INSTANCES 10      // ! - not implemented Don't know how to use
40
41BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
42{
43        NSCModuleWrapper::wrapDllMain(hModule, ul_reason_for_call);
44        return TRUE;
45}
46
47NSClientListener::NSClientListener() {
48}
49NSClientListener::~NSClientListener() {
50}
51std::wstring getAllowedHosts() {
52        std::wstring ret = NSCModuleHelper::getSettingsString(NSCLIENT_SECTION_TITLE, MAIN_ALLOWED_HOSTS, _T(""));
53        if (ret.empty())
54                ret = NSCModuleHelper::getSettingsString(MAIN_SECTION_TITLE, MAIN_ALLOWED_HOSTS, MAIN_ALLOWED_HOSTS_DEFAULT);
55        return ret;
56}
57bool getCacheAllowedHosts() {
58        int val = NSCModuleHelper::getSettingsInt(NSCLIENT_SECTION_TITLE, MAIN_ALLOWED_HOSTS_CACHE, -1);
59        if (val == -1)
60                val = NSCModuleHelper::getSettingsInt(MAIN_SECTION_TITLE, MAIN_ALLOWED_HOSTS_CACHE, MAIN_ALLOWED_HOSTS_CACHE_DEFAULT);
61        return val==1?true:false;
62}
63
64bool NSClientListener::loadModule() {
65        allowedHosts.setAllowedHosts(strEx::splitEx(getAllowedHosts(), _T(",")), getCacheAllowedHosts());
66        unsigned short port = NSCModuleHelper::getSettingsInt(NSCLIENT_SECTION_TITLE, NSCLIENT_SETTINGS_PORT, NSCLIENT_SETTINGS_PORT_DEFAULT);
67        std::wstring host = NSCModuleHelper::getSettingsString(NSCLIENT_SECTION_TITLE, NSCLIENT_SETTINGS_BINDADDR, NSCLIENT_SETTINGS_BINDADDR_DEFAULT);
68        unsigned int backLog = NSCModuleHelper::getSettingsInt(NSCLIENT_SECTION_TITLE, NSCLIENT_SETTINGS_LISTENQUE, NSCLIENT_SETTINGS_LISTENQUE_DEFAULT);
69        socketTimeout_ = NSCModuleHelper::getSettingsInt(NSCLIENT_SECTION_TITLE, NSCLIENT_SETTINGS_READ_TIMEOUT, NSCLIENT_SETTINGS_READ_TIMEOUT_DEFAULT);
70        try {
71                socket.setHandler(this);
72                socket.StartListener(host, port, backLog);
73        } catch (simpleSocket::SocketException e) {
74                NSC_LOG_ERROR_STD(_T("Exception caught: ") + e.getMessage());
75                return false;
76        }
77        return true;
78}
79bool NSClientListener::unloadModule() {
80        try {
81                socket.removeHandler(this);
82                if (socket.hasListener())
83                        socket.StopListener();
84        } catch (simpleSocket::SocketException e) {
85                NSC_LOG_ERROR_STD(_T("Exception caught: ") + e.getMessage());
86                return false;
87        }
88        return true;
89}
90
91/**
92* Main command parser and delegator.
93* This also handles a lot of the simpler responses (though some are deferred to other helper functions)
94*
95#define REQ_CLIENTVERSION       1       // Works fine!
96#define REQ_CPULOAD             2       // Quirks
97- Needs settings for default buffer size (backlog) and possibly other things.
98- Buffer needs to be synced with the client (don't know the size of that)
99- I think the idea was that the buffer would recursive to make a smaller memory footprint.
100(ie. the first level buffer have samples from every second, next level samples from every minute, next level hours etc...)
101(and I don't know the status of this, doesn't seem to be that way)
102#define REQ_UPTIME                      3       // Works fine!
103#define REQ_USEDDISKSPACE       4       // Works fine!
104#define REQ_SERVICESTATE        5       // Works fine!
105#define REQ_PROCSTATE           6       // Works fine!
106#define REQ_MEMUSE                      7       // Works fine!
107//#define REQ_COUNTER           8       // ! - not implemented Have to look at this, if anyone has a sample let me know...
108//#define REQ_FILEAGE           9       // ! - not implemented Don't know how to use
109//#define REQ_INSTANCES 10      // ! - not implemented Don't know how to use
110*
111*/
112
113
114std::wstring getPassword() {
115        static std::wstring password = _T("");
116        if (password.empty()) {
117                password = NSCModuleHelper::getSettingsString(NSCLIENT_SECTION_TITLE, MAIN_OBFUSCATED_PASWD, MAIN_OBFUSCATED_PASWD_DEFAULT);
118                if (password.empty())
119                        password= NSCModuleHelper::getSettingsString(MAIN_SECTION_TITLE, MAIN_OBFUSCATED_PASWD, MAIN_OBFUSCATED_PASWD_DEFAULT);
120                if (!password.empty()) {
121                        password = NSCModuleHelper::Decrypt(password);
122                } else {
123                        password = NSCModuleHelper::getSettingsString(NSCLIENT_SECTION_TITLE, MAIN_SETTINGS_PWD, MAIN_SETTINGS_PWD_DEFAULT);
124                        if (password.empty())
125                                password = NSCModuleHelper::getSettingsString(MAIN_SECTION_TITLE, MAIN_SETTINGS_PWD, MAIN_SETTINGS_PWD_DEFAULT);
126                }
127        }
128        return password;
129}
130bool NSClientListener::isPasswordOk(std::wstring remotePassword)  {
131        std::wstring localPassword = getPassword();
132        if (localPassword == remotePassword) {
133                return true;
134        }
135        if ((remotePassword == _T("None")) && (localPassword.empty())) {
136                return true;
137        }
138        return false;
139}
140void split_to_list(std::list<std::wstring> &list, std::wstring str) {
141        strEx::splitList add = strEx::splitEx(str, _T("&"));
142        list.insert(list.begin(), add.begin(), add.end());
143}
144std::string NSClientListener::parseRequest(std::string str_buffer)  {
145        std::wstring buffer = strEx::string_to_wstring(str_buffer);
146        NSC_DEBUG_MSG_STD(_T("Data: ") + buffer);
147
148        std::wstring::size_type pos = buffer.find_first_of(_T("\n\r"));
149        if (pos != std::wstring::npos) {
150                std::wstring::size_type pos2 = buffer.find_first_not_of(_T("\n\r"), pos);
151                if (pos2 != std::wstring::npos) {
152                        std::wstring rest = buffer.substr(pos2);
153                        NSC_DEBUG_MSG_STD(_T("Ignoring data: ") + rest);
154                }
155                buffer = buffer.substr(0, pos);
156        }
157
158        strEx::token pwd = strEx::getToken(buffer, '&');
159        if (!isPasswordOk(pwd.first)) {
160                NSC_LOG_ERROR_STD(_T("Invalid password (") + pwd.first + _T(")."));
161                return "ERROR: Invalid password.";
162        }
163        if (pwd.second.empty())
164                return "ERROR: No command specified.";
165        strEx::token cmd = strEx::getToken(pwd.second, '&');
166        if (cmd.first.empty())
167                return "ERROR: No command specified.";
168
169        int c = _wtoi(cmd.first.c_str());
170
171        NSC_DEBUG_MSG_STD(_T("Data: ") + cmd.second);
172
173        std::list<std::wstring> args;
174
175        // prefix various commands
176        switch (c) {
177                case REQ_CPULOAD:
178                        cmd.first = _T("checkCPU");
179                        split_to_list(args, cmd.second);
180                        args.push_back(_T("nsclient"));
181                        break;
182                case REQ_UPTIME:
183                        cmd.first = _T("checkUpTime");
184                        args.push_back(_T("nsclient"));
185                        break;
186                case REQ_USEDDISKSPACE:
187                        cmd.first = _T("CheckDriveSize");
188                        split_to_list(args, cmd.second);
189                        args.push_back(_T("nsclient"));
190                        break;
191                case REQ_CLIENTVERSION:
192                        {
193                                std::wstring v = NSCModuleHelper::getSettingsString(NSCLIENT_SECTION_TITLE, NSCLIENT_SETTINGS_VERSION, NSCLIENT_SETTINGS_VERSION_DEFAULT);
194                                if (v == _T("auto"))
195                                        v = NSCModuleHelper::getApplicationName() + _T(" ") + NSCModuleHelper::getApplicationVersionString();
196                                return strEx::wstring_to_string(v);
197                        }
198                case REQ_SERVICESTATE:
199                        cmd.first = _T("checkServiceState");
200                        split_to_list(args, cmd.second);
201                        args.push_back(_T("nsclient"));
202                        break;
203                case REQ_PROCSTATE:
204                        cmd.first = _T("checkProcState");
205                        split_to_list(args, cmd.second);
206                        args.push_back(_T("nsclient"));
207                        break;
208                case REQ_MEMUSE:
209                        cmd.first = _T("checkMem");
210                        args.push_back(_T("nsclient"));
211                        break;
212                case REQ_COUNTER:
213                        cmd.first = _T("checkCounter");
214                        args.push_back(_T("Counter=") + cmd.second);
215                        args.push_back(_T("nsclient"));
216                        break;
217                case REQ_FILEAGE:
218                        cmd.first = _T("getFileAge");
219                        args.push_back(_T("path=") + cmd.second);
220                        break;
221                default:
222                        split_to_list(args, cmd.second);
223        }
224
225        std::wstring message, perf;
226        NSCAPI::nagiosReturn ret = NSCModuleHelper::InjectCommand(cmd.first.c_str(), args, message, perf);
227        if (!NSCHelper::isNagiosReturnCode(ret)) {
228                if (message.empty())
229                        return "ERROR: Could not complete the request check log file for more information.";
230                return "ERROR: " + strEx::wstring_to_string(message);
231        }
232        switch (c) {
233                case REQ_UPTIME:                // Some check_nt commands has no return code syntax
234                case REQ_MEMUSE:
235                case REQ_CPULOAD:
236                case REQ_CLIENTVERSION:
237                case REQ_USEDDISKSPACE:
238                case REQ_COUNTER:
239                case REQ_FILEAGE:
240                        return strEx::wstring_to_string(message);
241
242                case REQ_SERVICESTATE:  // Some check_nt commands return the return code (coded as a string)
243                case REQ_PROCSTATE:
244                        return strEx::wstring_to_string(strEx::itos(NSCHelper::nagios2int(ret)) + _T("& ") + message);
245
246                default:                                // "New" check_nscp also returns performance data
247                        if (perf.empty())
248                                return strEx::wstring_to_string(NSCHelper::translateReturn(ret) + _T("&") + message);
249                        return strEx::wstring_to_string(NSCHelper::translateReturn(ret) + _T("&") + message + _T("&") + perf);
250        }
251}
252
253void NSClientListener::onClose()
254{}
255
256void NSClientListener::sendTheResponse(simpleSocket::Socket *client, std::string response) {
257        client->send(response.c_str(), static_cast<int>(response.length()), 0);
258}
259
260void NSClientListener::retrivePacket(simpleSocket::Socket *client) {
261        simpleSocket::DataBuffer db;
262        unsigned int i;
263        unsigned int maxWait = socketTimeout_*10;
264        for (i=0;i<maxWait;i++) {
265                bool lastReadHasMore = false;
266                try {
267                        lastReadHasMore = client->readAll(db);
268                } catch (simpleSocket::SocketException e) {
269                        NSC_LOG_ERROR_STD(_T("Read on socket failed: ") + e.getMessage());
270                        client->close();
271                        return;
272                }
273                if (db.getLength() > 0) {
274                        unsigned long long pos = db.find('\n');
275                        if (pos==-1) {
276                                std::string incoming(db.getBuffer(), db.getLength());
277                                sendTheResponse(client, parseRequest(incoming));
278                                break;
279                        } else if (pos>0) {
280                                simpleSocket::DataBuffer buffer = db.unshift(static_cast<const unsigned int>(pos));
281                                std::string bstr(buffer.getBuffer(), buffer.getLength());
282                                db.nibble(1);
283                                std::string rstr(db.getBuffer(), db.getLength());
284                                std::string incoming(buffer.getBuffer(), buffer.getLength());
285                                sendTheResponse(client, parseRequest(incoming) + "\n");
286                        } else {
287                                db.nibble(1);
288                                NSC_LOG_ERROR_STD(_T("First char should (i think) not be a \\n :("));
289                        }
290                } else if (!lastReadHasMore) {
291                        client->close();
292                        return;
293                } else {
294                        Sleep(100);
295                }
296        }
297        if (i >= maxWait) {
298                NSC_LOG_ERROR_STD(_T("Timeout reading NS-client packet (increase socket_timeout)."));
299                client->close();
300                return;
301        }
302}
303
304
305void NSClientListener::onAccept(simpleSocket::Socket *client) {
306        if (!allowedHosts.inAllowedHosts(client->getAddr())) {
307                NSC_LOG_ERROR(_T("Unauthorized access from: ") + client->getAddrString());
308                client->close();
309                return;
310        }
311        //client->setNonBlock();
312        retrivePacket(client);
313
314
315
316//      client->readAll(db);
317//      if (db.getLength() > 0) {
318//              std::wstring incoming(db.getBuffer(), db.getLength());
319//              NSC_DEBUG_MSG_STD("Incoming data: " + incoming);
320//              std::wstring response = parseRequest(incoming);
321//              NSC_DEBUG_MSG("Outgoing data: " + response);
322//              client->send(response.c_str(), static_cast<int>(response.length()), 0);
323//      }
324        client->close();
325}
326
327NSC_WRAPPERS_MAIN_DEF(gNSClientListener);
328NSC_WRAPPERS_IGNORE_MSG_DEF();
329NSC_WRAPPERS_IGNORE_CMD_DEF();
330NSC_WRAPPERS_HANDLE_CONFIGURATION(gNSClientListener);
331
332
333MODULE_SETTINGS_START(NSClientListener, _T("NSClient Listener configuration"), _T("..."))
334
335PAGE(_T("NSClient Listener configuration"))
336
337ITEM_EDIT_TEXT(_T("port"), _T("This is the port the NSClientListener.dll will listen to."))
338ITEM_MAP_TO(_T("basic_ini_text_mapper"))
339OPTION(_T("section"), _T("NSClient"))
340OPTION(_T("key"), _T("port"))
341OPTION(_T("default"), _T("12489"))
342ITEM_END()
343
344PAGE_END()
345ADVANCED_PAGE(_T("Server settings"))
346
347ITEM_EDIT_TEXT(_T("password"), _T("Enter the password used by applications when queriying for data."))
348ITEM_MAP_TO(_T("basic_ini_text_mapper"))
349OPTION(_T("section"), _T("NSClient"))
350OPTION(_T("key"), _T("password"))
351OPTION(_T("default"), _T(""))
352ITEM_END()
353
354ITEM_EDIT_OPTIONAL_LIST(_T("Allow connection from:"), _T("This is the hosts that will be allowed to poll performance data from the server."))
355OPTION(_T("disabledCaption"), _T("Use global settings (defined previously)"))
356OPTION(_T("enabledCaption"), _T("Specify hosts for NSClient server"))
357OPTION(_T("listCaption"), _T("Add all IP addresses (not hosts) which should be able to connect:"))
358OPTION(_T("separator"), _T(","))
359OPTION(_T("disabled"), _T(""))
360ITEM_MAP_TO(_T("basic_ini_text_mapper"))
361OPTION(_T("section"), _T("NSClient"))
362OPTION(_T("key"), _T("allowed_hosts"))
363OPTION(_T("default"), _T(""))
364ITEM_END()
365
366PAGE_END()
367MODULE_SETTINGS_END()
Note: See TracBrowser for help on using the repository browser.