]> granicus.if.org Git - icinga2/blob - plugins/check_network.cpp
Move new password functions into tlsutility
[icinga2] / plugins / check_network.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #define WIN32_LEAN_AND_MEAN
21
22 #include <Windows.h>
23 #include <Pdh.h>
24 #include <Shlwapi.h>
25 #include <iostream>
26 #include <pdhmsg.h>
27 #include <WinSock2.h>
28 #include <map>
29
30 #include <IPHlpApi.h>
31
32 #include "check_network.h"
33 #include "boost/algorithm/string/replace.hpp"
34
35 #define VERSION 1.2
36
37 namespace po = boost::program_options;
38
39 static BOOL debug = FALSE;
40 static BOOL noisatap = FALSE;
41
42 INT wmain(INT argc, WCHAR **argv) 
43 {
44         std::vector<nInterface> vInterfaces;
45         std::map<std::wstring, std::wstring> mapNames;
46         printInfoStruct printInfo{};
47         po::variables_map vm;
48
49         INT ret = parseArguments(argc, argv, vm, printInfo);
50
51         if (ret != -1)
52                 return ret;
53
54         if (!mapSystemNamesToFamiliarNames(mapNames))
55                 return 3;
56
57         ret = check_network(vInterfaces);
58         if (ret != -1)
59                 return ret;
60                 
61         return printOutput(printInfo, vInterfaces, mapNames);
62 }
63
64 INT parseArguments(INT ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) 
65 {
66         WCHAR namePath[MAX_PATH];
67         GetModuleFileName(NULL, namePath, MAX_PATH);
68         WCHAR *progName = PathFindFileName(namePath);
69
70         po::options_description desc("Options");
71
72         desc.add_options()
73                 ("help,h", "print usage and exit")
74                 ("version,V", "print version and exit")
75                 ("debug,d", "Verbose/Debug output")
76                 ("noisatap,n", "Don't show ISATAP interfaces in output")
77                 ("warning,w", po::wvalue<std::wstring>(), "warning value")
78                 ("critical,c", po::wvalue<std::wstring>(), "critical value")
79                 ;
80
81         po::basic_command_line_parser<WCHAR> parser(ac, av);
82
83         try {
84                 po::store(
85                         parser
86                         .options(desc)
87                         .style(
88                         po::command_line_style::unix_style |
89                         po::command_line_style::allow_long_disguise)
90                         .run(),
91                         vm);
92                 vm.notify();
93         } catch (std::exception& e) {
94                 std::cout << e.what() << '\n' << desc << '\n';
95                 return 3;
96         }
97
98         if (vm.count("help")) {
99                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
100                 wprintf(
101                         L"%s is a simple program to check a machines network performance.\n"
102                         L"You can use the following options to define its behaviour:\n\n", progName);
103                 std::cout << desc;
104                 wprintf(
105                         L"\nIt will then output a string looking something like this:\n\n"
106                         L"\tNETWORK WARNING 1131B/s | network=1131B;1000;7000;0\n\n"
107                         L"\"NETWORK\" being the type of the check, \"WARNING\" the returned status\n"
108                         L"and \"1131B/s\" is the returned value.\n"
109                         L"The performance data is found behind the \"|\", in order:\n"
110                         L"returned value, warning threshold, critical threshold, minimal value and,\n"
111                         L"if applicable, the maximal value. Performance data will only be displayed when\n"
112                         L"you set at least one threshold\n\n"
113                         L"This program will also print out additional performance data interface\n"
114                         L"by interface\n\n"
115                         L"%s' exit codes denote the following:\n"
116                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
117                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
118                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
119                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
120                         L"Threshold syntax:\n\n"
121                         L"-w THRESHOLD\n"
122                         L"warn if threshold is broken, which means VALUE > THRESHOLD\n"
123                         L"(unless stated differently)\n\n"
124                         L"-w !THRESHOLD\n"
125                         L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n"
126                         L"-w [THR1-THR2]\n"
127                         L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
128                         L"-w ![THR1-THR2]\n"
129                         L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
130                         L"-w THRESHOLD%%\n"
131                         L"if the plugin accepts percentage based thresholds those will be used.\n"
132                         L"Does nothing if the plugin does not accept percentages, or only uses\n"
133                         L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
134                         L"to end with a percentage sign.\n\n"
135                         L"All of these options work with the critical threshold \"-c\" too."
136                         , progName);
137                 std::cout << '\n';
138                 return 0;
139         }
140
141         if (vm.count("version"))
142                 std::cout << "Version: " << VERSION << '\n';
143
144         if (vm.count("warning")) {
145                 try {
146                         printInfo.warn = threshold(vm["warning"].as<std::wstring>());
147                 } catch (std::invalid_argument& e) {
148                         std::cout << e.what() << '\n';
149                         return 3;
150                 }
151         }
152         if (vm.count("critical")) {
153                 try {
154                         printInfo.crit = threshold(vm["critical"].as<std::wstring>());
155                 } catch (std::invalid_argument& e) {
156                         std::cout << e.what() << '\n';
157                         return 3;
158                 }
159         }
160         
161         if (vm.count("debug"))
162                 debug = TRUE;
163
164         if (vm.count("noisatap"))
165                 noisatap = TRUE;
166
167         return -1;
168 }
169
170 INT printOutput(printInfoStruct& printInfo, CONST std::vector<nInterface>& vInterfaces, CONST std::map<std::wstring, std::wstring>& mapNames)
171 {
172         if (debug)
173                 std::wcout << L"Constructing output string" << '\n';
174
175         long tIn = 0, tOut = 0;
176         std::wstringstream tss, perfDataFirst;
177         state state = OK;
178
179         std::map<std::wstring, std::wstring>::const_iterator mapIt;
180         std::wstring wsFriendlyName;
181
182         for (std::vector<nInterface>::const_iterator it = vInterfaces.begin(); it != vInterfaces.end(); ++it) {
183                 tIn += it->BytesInSec;
184                 tOut += it->BytesOutSec;
185                 if (debug)
186                         std::wcout << "Getting friendly name of " << it->name << '\n';
187                 mapIt = mapNames.find(it->name);
188                 if (mapIt != mapNames.end()) {
189                         if (debug)
190                                 std::wcout << "\tIs " << mapIt->second << '\n';
191                         wsFriendlyName = mapIt->second;
192                 } else {
193                         if (debug)
194                                 std::wcout << "\tNo friendly name found, using adapter name\n";
195                         wsFriendlyName = it->name;
196                 }
197                 if(wsFriendlyName.find(L"isatap") != std::wstring::npos && noisatap) {
198                         if (debug)
199                                 std::wcout << "\tSkipping isatap interface " << wsFriendlyName << "\n";
200                         continue;
201                 }
202                 else
203                 {
204                         boost::algorithm::replace_all(wsFriendlyName, "'", "''");
205                         tss << L"\'" << wsFriendlyName << L"_in\'=" << it->BytesInSec << L"B \'" << wsFriendlyName << L"_out\'=" << it->BytesOutSec << L"B ";
206                 }
207         }
208
209         if (printInfo.warn.rend(tIn + tOut))
210                 state = WARNING;
211         if (printInfo.crit.rend(tIn + tOut))
212                 state = CRITICAL;
213
214         perfDataFirst << L"network=" << tIn + tOut << L"B;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; ";
215
216         switch (state) {
217         case OK:
218                 std::wcout << L"NETWORK OK " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << '\n';
219                 break;
220         case WARNING:
221                 std::wcout << L"NETWORK WARNING " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << '\n';
222                 break;
223         case CRITICAL:
224                 std::wcout << L"NETWORK CRITICAL " << tIn + tOut << L"B/s | " << perfDataFirst.str() << tss.str() << '\n';
225                 break;
226         }
227
228         return state;
229 }
230
231 INT check_network(std::vector <nInterface>& vInterfaces) 
232 {
233         CONST WCHAR *perfIn = L"\\Network Interface(*)\\Bytes Received/sec";
234         CONST WCHAR *perfOut = L"\\Network Interface(*)\\Bytes Sent/sec";
235
236         PDH_HQUERY phQuery = NULL;
237         PDH_HCOUNTER phCounterIn, phCounterOut;
238         DWORD dwBufferSizeIn = 0, dwBufferSizeOut = 0, dwItemCount = 0;
239         PDH_FMT_COUNTERVALUE_ITEM *pDisplayValuesIn = NULL, *pDisplayValuesOut = NULL;
240         PDH_STATUS err;
241
242         if (debug)
243                 std::wcout << L"Creating Query and adding counters" << '\n';
244
245         err = PdhOpenQuery(NULL, NULL, &phQuery);
246         if (!SUCCEEDED(err))
247                 goto die;
248
249         err = PdhAddEnglishCounter(phQuery, perfIn, NULL, &phCounterIn);
250         if (!SUCCEEDED(err)) 
251                 goto die;
252         
253         err = PdhAddEnglishCounter(phQuery, perfOut, NULL, &phCounterOut);
254         if (!SUCCEEDED(err)) 
255                 goto die;
256         
257         if (debug)
258                 std::wcout << L"Collecting first batch of query data" << '\n';
259
260         err = PdhCollectQueryData(phQuery);
261         if (!SUCCEEDED(err))
262                 goto die;
263
264         if (debug)
265                 std::wcout << L"Sleep for one second" << '\n';
266
267         Sleep(1000);
268
269         if (debug)
270                 std::wcout << L"Collecting second batch of query data" << '\n';
271
272         err = PdhCollectQueryData(phQuery);
273         if (!SUCCEEDED(err))
274                 goto die;
275
276         if (debug)
277                 std::wcout << L"Creating formatted counter arrays" << '\n';
278         
279         err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn);
280         if (err == PDH_MORE_DATA || SUCCEEDED(err))
281                 pDisplayValuesIn = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]);
282         else
283                 goto die;
284         
285         err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut);
286         if (err == PDH_MORE_DATA || SUCCEEDED(err))
287                 pDisplayValuesOut = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwItemCount*dwBufferSizeIn]);
288         else
289                 goto die;
290
291         err = PdhGetFormattedCounterArray(phCounterIn, PDH_FMT_LONG, &dwBufferSizeIn, &dwItemCount, pDisplayValuesIn);
292         if (!SUCCEEDED(err))
293                 goto die;
294
295         err = PdhGetFormattedCounterArray(phCounterOut, PDH_FMT_LONG, &dwBufferSizeOut, &dwItemCount, pDisplayValuesOut);
296         if (!SUCCEEDED(err))
297                 goto die;
298
299         if (debug)
300                 std::wcout << L"Going over counter array" << '\n';
301
302         for (DWORD i = 0; i < dwItemCount; i++) {
303                 nInterface *iface = new nInterface(std::wstring(pDisplayValuesIn[i].szName));
304                 iface->BytesInSec = pDisplayValuesIn[i].FmtValue.longValue;
305                 iface->BytesOutSec = pDisplayValuesOut[i].FmtValue.longValue;
306                 vInterfaces.push_back(*iface);
307                 if (debug)
308                         std::wcout << L"Collected interface " << pDisplayValuesIn[i].szName << '\n';
309         }
310         if (debug)
311                 std::wcout << L"Finished collection. Cleaning up and returning" << '\n';
312
313         if (phQuery)
314                 PdhCloseQuery(phQuery);
315         if (pDisplayValuesIn)
316                 delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesIn);
317         if (pDisplayValuesOut)
318                 delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesOut);
319         return -1;
320 die:
321         die(err);
322         if (phQuery)
323                 PdhCloseQuery(phQuery);
324         if (pDisplayValuesIn)
325                 delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesIn);
326         if (pDisplayValuesOut)
327                 delete reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(pDisplayValuesOut);
328         return 3;
329 }
330
331 BOOL mapSystemNamesToFamiliarNames(std::map<std::wstring, std::wstring>& mapNames)
332 {
333         DWORD dwSize = 0, dwRetVal = 0;
334
335         ULONG family = AF_UNSPEC, flags = GAA_FLAG_INCLUDE_PREFIX,
336                 outBufLen = 0, Iterations = 0;
337         LPVOID lpMsgBuf = NULL;
338
339         PIP_ADAPTER_ADDRESSES pAddresses = NULL;
340         PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
341         /*
342         PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
343         PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
344         PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
345         PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsServer = NULL;
346         PIP_ADAPTER_PREFIX pPrefix = NULL;
347         */
348         outBufLen = 15000; //15KB as suggestet by msdn of GetAdaptersAddresses
349
350         if (debug)
351                 std::wcout << "Mapping adapter system names to friendly names\n";
352
353         do {
354                 pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(new BYTE[outBufLen]);
355
356                 dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
357
358                 if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
359                         delete[]pAddresses;
360                         pAddresses = NULL;
361                 } else
362                         break;
363         } while (++Iterations < 3);
364
365         if (dwRetVal != NO_ERROR) {
366                 std::wcout << "Failed to collect friendly adapter names\n";
367                 delete[]pAddresses;
368                 return FALSE;
369         }
370
371         pCurrAddresses = pAddresses;
372         std::wstringstream wssAdapterName;
373         std::wstringstream wssFriendlyName;
374         for (pCurrAddresses = pAddresses; pCurrAddresses; pCurrAddresses = pCurrAddresses->Next) {
375                 wssAdapterName.str(std::wstring());
376                 wssFriendlyName.str(std::wstring());
377                 wssAdapterName << pCurrAddresses->Description;
378                 wssFriendlyName << pCurrAddresses->FriendlyName;
379                 if (debug)
380                         std::wcout << "Got: " << wssAdapterName.str() << " -- " << wssFriendlyName.str() << '\n';
381
382                 mapNames.insert(std::pair<std::wstring, std::wstring>(wssAdapterName.str(), wssFriendlyName.str()));
383         }
384
385         delete[]pAddresses;
386         return TRUE;
387 }