source: nscp/trunk/NSClient++.cpp @ 4f2e807

Last change on this file since 4f2e807 was 4f2e807, checked in by Michael Medin <michael@…>, 8 years ago
  • Refactored out NSCLient Listener as a separate module
  • Added initial NRPE listener module (not yet implemented only a shell)
  • Changed Module API (Inject function has new syntax)
  • Added some tokenizer function to charEx
  • Added new wrapper function to inject Command
  • Minor changes in relation to refactor work
  • Property mode set to 100644
File size: 13.8 KB
Line 
1//////////////////////////////////////////////////////////////////////////
2// NSClient++ Base Service
3//
4// Copyright (c) 2004 MySolutions NORDIC (http://www.medin.name)
5//
6// Date: 2004-03-13
7// Author: Michael Medin (michael@medin.name)
8//
9// Part of this file is based on work by Bruno Vais (bvais@usa.net)
10//
11// This software is provided "AS IS", without a warranty of any kind.
12// You are free to use/modify this code but leave this header intact.
13//
14//////////////////////////////////////////////////////////////////////////
15#include "stdafx.h"
16#include <winsvc.h>
17#include "NSClient++.h"
18#include "Settings.h"
19#include <charEx.h>
20
21NSClient mainClient;    // Global core instance.
22bool g_bConsoleLog = false;
23//////////////////////////////////////////////////////////////////////////
24// Startup code
25
26/**
27 * Application startup point
28 *
29 * @param argc Argument count
30 * @param argv[] Argument array
31 * @param envp[] Environment array
32 * @return exit status
33 */
34int main(int argc, TCHAR* argv[], TCHAR* envp[])
35{
36        int nRetCode = 0;
37
38        if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
39                if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
40                        g_bConsoleLog = true;
41                        try {
42                                serviceControll::Install(SZSERVICENAME, SZSERVICEDISPLAYNAME, SZDEPENDENCIES);
43                        } catch (const serviceControll::SCException& e) {
44                                LOG_MESSAGE_STD("Service installation failed: " + e.error_);
45                                return -1;
46                        }
47                        LOG_MESSAGE("Service installed!");
48                } else if ( _stricmp( "uninstall", argv[1]+1 ) == 0 ) {
49                        g_bConsoleLog = true;
50                        try {
51                                serviceControll::Uninstall(SZSERVICENAME);
52                        } catch (const serviceControll::SCException& e) {
53                                LOG_MESSAGE_STD("Service deinstallation failed; " + e.error_);
54                                return -1;
55                        }
56                } else if ( _stricmp( "start", argv[1]+1 ) == 0 ) {
57                        g_bConsoleLog = true;
58                        serviceControll::Start(SZSERVICENAME);
59                } else if ( _stricmp( "stop", argv[1]+1 ) == 0 ) {
60                        g_bConsoleLog = true;
61                        serviceControll::Stop(SZSERVICENAME);
62                } else if ( _stricmp( "about", argv[1]+1 ) == 0 ) {
63                        g_bConsoleLog = true;
64                        LOG_MESSAGE(SZAPPNAME " (C) Michael Medin");
65                        LOG_MESSAGE("Version " SZVERSION);
66                } else if ( _stricmp( "version", argv[1]+1 ) == 0 ) {
67                        g_bConsoleLog = true;
68                        LOG_MESSAGE(SZAPPNAME " Version: " SZVERSION);
69                } else if ( _stricmp( "test", argv[1]+1 ) == 0 ) {
70                        g_bConsoleLog = true;
71                        mainClient.InitiateService();
72                        LOG_MESSAGE("Enter command to inject or exit to terminate...");
73                        std::string s = "";
74                        std::cin >> s;
75                        while (s != "exit") {
76//                              mainClient.inject(s);
77                                std::cin >> s;
78                        }
79                        mainClient.TerminateService();
80                        LOG_MESSAGE("DONE!");
81
82                        return 0;
83                } else {
84                        LOG_MESSAGE("Usage: -version, -about, -install, -uninstall, -start, -stop");
85                }
86                return nRetCode;
87        }
88        mainClient.StartServiceCtrlDispatcher();
89        return nRetCode;
90}
91
92//////////////////////////////////////////////////////////////////////////
93// Service functions
94
95/**
96 * Service control handler startup point.
97 * When the program is started as a service this will be the entry point.
98 */
99void NSClientT::InitiateService(void) {
100        Settings::getInstance()->setFile(getBasePath() + "NSC.ini");
101
102        SettingsT::sectionList list = Settings::getInstance()->getSection("modules");
103        for (SettingsT::sectionList::iterator it = list.begin(); it != list.end(); it++) {
104                try {
105                        LOG_DEBUG_STD("Loading: " + getBasePath() + "modules\\" + (*it));
106                        loadPlugin(getBasePath() + "modules\\" + (*it));
107                } catch(const NSPluginException& e) {
108                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
109                }
110        }
111}
112/**
113 * Service control handler termination point.
114 * When the program is stopped as a service this will be the "exit point".
115 */
116void NSClientT::TerminateService(void) {
117        try {
118                LOG_DEBUG("Socket closed, unloading plugins...");
119                mainClient.unloadPlugins();
120                LOG_DEBUG("Plugins unloaded...");
121        } catch(NSPluginException *e) {
122                std::cout << "Exception raised: " << e->error_ << " in module: " << e->file_ << std::endl;;
123        }
124        Settings::destroyInstance();
125}
126
127/**
128 * Forward this to the main service dispatcher helper class
129 * @param dwArgc
130 * @param *lpszArgv
131 */
132void WINAPI NSClientT::service_main_dispatch(DWORD dwArgc, LPTSTR *lpszArgv) {
133        mainClient.service_main(dwArgc, lpszArgv);
134}
135/**
136 * Forward this to the main service dispatcher helper class
137 * @param dwCtrlCode
138 */
139void WINAPI NSClientT::service_ctrl_dispatch(DWORD dwCtrlCode) {
140        mainClient.service_ctrl(dwCtrlCode);
141}
142
143//////////////////////////////////////////////////////////////////////////
144// Member functions
145
146
147/**
148 * Load a list of plug-ins
149 * @param plugins A list with plug-ins (DLL files) to load
150 */
151void NSClientT::loadPlugins(const std::list<std::string> plugins) {
152        MutexLock lock(pluginMutex);
153        if (!lock.hasMutex()) {
154                LOG_ERROR("FATAL ERROR: Could not get mutex.");
155                return;
156        }
157        std::list<std::string>::const_iterator it;
158        for (it = plugins.begin(); it != plugins.end(); ++it) {
159                loadPlugin(*it);
160        }
161}
162/**
163 * Unload all plug-ins (in reversed order)
164 */
165void NSClientT::unloadPlugins() {
166        MutexLock lock(pluginMutex,20000);
167        if (!lock.hasMutex()) {
168                LOG_ERROR("FATAL ERROR: Could not get mutex.");
169                return;
170        }
171        pluginList::reverse_iterator it;
172        for (it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
173                LOG_DEBUG_STD("Unloading plugin: " + (*it)->getName() + "...");
174                (*it)->unload();
175        }
176        {
177                MutexLock lock2(messageMutex,20000);
178                if (!lock2.hasMutex()) {
179                        LOG_ERROR("FATAL ERROR: Could not get mutex (we will now crash BTW).");
180                } else {
181                        messageHandlers_.clear();
182                }
183        }
184        commandHandlers_.clear();
185        for (it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
186                delete (*it);
187        }
188        plugins_.clear();
189}
190/**
191 * Load a single plug-in using a DLL filename
192 * @param file The DLL file
193 */
194void NSClientT::loadPlugin(const std::string file) {
195        addPlugin(new NSCPlugin(file));
196}
197/**
198 * Load and add a plugin to various internal structures
199 * @param *plugin The plug-ininstance to load. The pointer is managed by the
200 */
201void NSClientT::addPlugin(plugin_type plugin) {
202        MutexLock lock(pluginMutex);
203        if (!lock.hasMutex()) {
204                LOG_ERROR("FATAL ERROR: Could not get mutex.");
205                return;
206        }
207        plugin->load();
208        LOG_DEBUG_STD("Loading: " + plugin->getName());
209        // @todo Catch here and unload if we fail perhaps ?
210        plugins_.push_back(plugin);
211        if (plugin->hasCommandHandler())
212                commandHandlers_.push_back(plugin);
213        if (plugin->hasMessageHandler())
214                messageHandlers_.push_back(plugin);
215}
216/**
217 * Inject a command into the plug-in stack.
218 *
219 * @param buffer A command string to inject. This should be a unparsed command string such as command&arg1&arg2&arg...
220 * @return The result, empty string if no result
221 */
222int NSClientT::injectRAW(const char* command, const unsigned int argLen, char **argument, char *returnBuffer, unsigned int returnBufferLen) {
223        MutexLock lock(pluginMutex);
224
225        LOG_MESSAGE_STD("Injecting: " + command);
226
227        pluginList::const_iterator plit;
228        for (plit = commandHandlers_.begin(); plit != commandHandlers_.end(); ++plit) {
229                try {
230                        int c = (*plit)->handleCommand(command, argLen, argument, returnBuffer, returnBufferLen);
231                        if (c == NSCAPI::handled) {                                     // module handled the message "we are done..."
232                                LOG_DEBUG_STD("Injected Result: " +(std::string) returnBuffer);
233                                return c;
234                        } else if (c == NSCAPI::isfalse) {                      // Module ignored the message
235                                LOG_DEBUG("A module ignored this message");
236                        } else if (c == NSCAPI::invalidBufferLen) {     // Buffer is to small
237                                LOG_ERROR("Return buffer to small to handle this command.");
238                                return c;
239                        } else if (c == NSCAPI::isError) {                      // Error
240                                LOG_ERROR("An error occured while handling this command.");
241                                return c;
242                        } else {                                                                        // Something else went wrong...
243                                LOG_ERROR_STD("Unknown error from handleCommand: " + strEx::itos(c));
244                                return c;
245                        }
246                } catch(const NSPluginException& e) {
247                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
248                        return NSCAPI::isError;
249                }
250        }
251        LOG_MESSAGE_STD("No handler for command: " + command);
252        return NSCAPI::isfalse;
253}
254/**
255 * Helper function to return the current password (perhaps this should be static ?)
256 *
257 * @return The current password
258 */
259std::string NSClientT::getPassword() {
260        return Settings::getInstance()->getString("generic", "password", "");
261}
262/**
263 * Execute a command.
264 *
265 * @param password The password
266 * @param cmd The command
267 * @param args Arguments as a list<string>
268 * @return The result if any, empty string if no result.
269 *
270 * @todo Make an int return value to set critical/warning/ok/unknown status.
271 * ie. pair<int,string>
272 */
273std::string NSClientT::execute(std::string password, std::string cmd, std::list<std::string> args) {
274        MutexLock lock(pluginMutex);
275        if (!lock.hasMutex()) {
276                LOG_ERROR("FATAL ERROR: Could not execute command with a reasonable timeframe. (could not get mutex so core is most likely locked down).");
277                return "ERROR: Core was locked down";
278        }
279        static unsigned int bufferSize = 0;
280        if (bufferSize == 0)
281                bufferSize = static_cast<unsigned int>(Settings::getInstance()->getInt("main", "bufferSize", 4096));
282
283        if (password != getPassword())
284                return "ERROR: Authorization denied.";
285
286        std::string ret;
287        unsigned int len;
288        char **arguments = NSCHelper::list2arrayBuffer(args, len);
289        // Allocate return buffer
290        char* returnbuffer = new char[bufferSize+1];
291        pluginList::const_iterator plit;
292        for (plit = commandHandlers_.begin(); plit != commandHandlers_.end(); ++plit) {
293                try {
294                        int c = (*plit)->handleCommand(cmd.c_str(), len, arguments, returnbuffer, bufferSize);
295                        if (c == NSCAPI::handled) {                                     // module handled the message "we are done..."
296                                ret = returnbuffer;
297                                break;
298                        } else if (c == NSCAPI::isfalse) {                      // Module ignored the message
299                                LOG_DEBUG("A module ignored this message");
300                        } else if (c == NSCAPI::invalidBufferLen) {     // Buffer is to small
301                                LOG_ERROR("Return buffer to small, need to increase it in the ini file.");
302                        } else {                                                                        // Something else went wrong...
303                                LOG_ERROR_STD("Unknown error from handleCommand: " + strEx::itos(c));
304                        }
305                } catch(const NSPluginException& e) {
306                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
307                }
308        }
309        // delete buffers
310        delete [] returnbuffer;
311        NSCHelper::destroyArrayBuffer(arguments, len);
312        return ret;
313}
314/**
315 * Report a message to all logging enabled modules.
316 *
317 * @param msgType Message type
318 * @param file Filename generally __FILE__
319 * @param line  Line number, generally __LINE__
320 * @param message The message as a human readable string.
321 */
322void NSClientT::reportMessage(int msgType, const char* file, const int line, std::string message) {
323        MutexLock lock(messageMutex);
324        if (!lock.hasMutex()) {
325                std::cout << "Message was lost as the core was locked..." << std::endl;
326                std::cout << message << std::endl;
327                return;
328        }
329        if (g_bConsoleLog) {
330                std::cout << NSCHelper::translateMessageType(msgType) << " " << file << "(" << line << ") " << message << std::endl;
331        }
332        if (msgType == NSCAPI::debug) {
333                typedef enum status {unknown, debug, nodebug };
334                static status d = unknown;
335                if (d == unknown) {
336                        if (Settings::getInstance()->getInt("log", "debug", 0) == 1)
337                                d = debug;
338                        else
339                                d = nodebug;
340                }
341                if (d == nodebug)
342                        return;
343        }
344        pluginList::const_iterator plit;
345        for (plit = messageHandlers_.begin(); plit != messageHandlers_.end(); ++plit) {
346                try {
347                        (*plit)->handleMessage(msgType, file, line, message.c_str());
348                } catch(const NSPluginException& e) {
349                        // Here we are pretty much fucked! (as logging this might cause a loop :)
350                        std::cout << "Caught: " << e.error_ << " when trying to log a message..." << std::endl;
351                        std::cout << "This is *really really* bad, now the world is about to end..." << std::endl;
352                }
353        }
354}
355std::string NSClientT::getBasePath(void) {
356        MutexLock lock(pluginMutex);
357        if (!lock.hasMutex()) {
358                LOG_ERROR("FATAL ERROR: Could not get mutex.");
359                return "FATAL ERROR";
360        }
361        if (!basePath.empty())
362                return basePath;
363        char* buffer = new char[1024];
364        GetModuleFileName(NULL, buffer, 1023);
365        std::string path = buffer;
366        std::string::size_type pos = path.rfind('\\');
367        basePath = path.substr(0, pos) + "\\";
368        delete [] buffer;
369        Settings::getInstance()->setFile(basePath + "NSC.ini");
370        return basePath;
371}
372
373
374int NSAPIGetSettingsString(const char* section, const char* key, const char* defaultValue, char* buffer, unsigned int bufLen) {
375        return NSCHelper::wrapReturnString(buffer, bufLen, Settings::getInstance()->getString(section, key, defaultValue));
376}
377int NSAPIGetSettingsInt(const char* section, const char* key, int defaultValue) {
378        return Settings::getInstance()->getInt(section, key, defaultValue);
379}
380int NSAPIGetBasePath(char*buffer, unsigned int bufLen) {
381        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.getBasePath());
382}
383int NSAPIGetApplicationName(char*buffer, unsigned int bufLen) {
384        return NSCHelper::wrapReturnString(buffer, bufLen, SZAPPNAME);
385}
386int NSAPIGetApplicationVersionStr(char*buffer, unsigned int bufLen) {
387        return NSCHelper::wrapReturnString(buffer, bufLen, SZVERSION);
388}
389void NSAPIMessage(int msgType, const char* file, const int line, const char* message) {
390        mainClient.reportMessage(msgType, file, line, message);
391}
392void NSAPIStopServer(void) {
393        serviceControll::Stop(SZSERVICENAME);
394}
395int NSAPIInject(const char* command, const unsigned int argLen, char **argument, char *returnBuffer, unsigned int returnBufferLen) {
396        return mainClient.injectRAW(command, argLen, argument, returnBuffer, returnBufferLen);
397}
398
399LPVOID NSAPILoader(char*buffer) {
400        if (stricmp(buffer, "NSAPIGetApplicationName") == 0)
401                return &NSAPIGetApplicationName;
402        if (stricmp(buffer, "NSAPIGetApplicationVersionStr") == 0)
403                return &NSAPIGetApplicationVersionStr;
404        if (stricmp(buffer, "NSAPIGetSettingsString") == 0)
405                return &NSAPIGetSettingsString;
406        if (stricmp(buffer, "NSAPIGetSettingsInt") == 0)
407                return &NSAPIGetSettingsInt;
408        if (stricmp(buffer, "NSAPIMessage") == 0)
409                return &NSAPIMessage;
410        if (stricmp(buffer, "NSAPIStopServer") == 0)
411                return &NSAPIStopServer;
412        if (stricmp(buffer, "NSAPIInject") == 0)
413                return &NSAPIInject;
414        if (stricmp(buffer, "NSAPIGetBasePath") == 0)
415                return &NSAPIGetBasePath;
416        return NULL;
417}
Note: See TracBrowser for help on using the repository browser.