]> granicus.if.org Git - icinga2/blob - plugins/check_ping.cpp
Fix string escaping in hpasm argument description
[icinga2] / plugins / check_ping.cpp
1 /******************************************************************************
2 * Icinga 2                                                                   *
3 * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
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 #ifndef WIN32_LEAN_AND_MEAN
21 #define WIN32_LEAN_AND_MEAN //else winsock will be included with windows.h and conflict with winsock2
22 #endif 
23
24 #include <winsock2.h>
25 #include <iphlpapi.h>
26 #include <icmpapi.h>
27 #include <Shlwapi.h>
28 #include <ws2ipdef.h>
29 #include <Mstcpip.h>
30 #include <Ws2tcpip.h>
31
32 #include <iostream>
33
34 #include "check_ping.h"
35
36 #define VERSION 1.0
37
38 namespace po = boost::program_options;
39
40 static BOOL debug = FALSE;
41
42 INT wmain(INT argc, WCHAR **argv)
43 {
44         po::variables_map vm;
45         printInfoStruct printInfo;
46         response response;
47
48         WSADATA dat;
49         WORD req = MAKEWORD(1, 1);
50
51         WSAStartup(req, &dat);
52
53         if (parseArguments(argc, argv, vm, printInfo) != -1)
54                 return 3;
55         if (printInfo.ipv4) {
56                 if (check_ping4(printInfo, response) != -1)
57                         return 3;
58         } else {
59                 if (check_ping6(printInfo, response) != -1)
60                         return 3;
61         }
62
63         WSACleanup();
64         return printOutput(printInfo, response);
65 }
66
67 INT parseArguments(INT ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
68 {
69         WCHAR namePath[MAX_PATH];
70         GetModuleFileName(NULL, namePath, MAX_PATH);
71         WCHAR *progName = PathFindFileName(namePath);
72
73         po::options_description desc;
74
75         desc.add_options()
76                 ("help,h", "Print usage message and exit")
77                 ("version,V", "Print version and exit")
78                 ("debug,d", "Verbose/Debug output")
79                 ("host,H", po::wvalue<std::wstring>()->required(), "Host ip to ping")
80                 (",4", "--Host is an ipv4 address (default)")
81                 (",6", "--Host is an ipv6 address")
82                 ("timeout,t", po::value<INT>(), "Specify timeout for requests in ms (default=1000)")
83                 ("packets,p", po::value<INT>(), "Declare ping count (default=5)")
84                 ("warning,w", po::wvalue<std::wstring>(), "Warning values: rtt,package loss")
85                 ("critical,c", po::wvalue<std::wstring>(), "Critical values: rtt,package loss")
86                 ;
87
88         po::basic_command_line_parser<WCHAR> parser(ac, av);
89
90         try {
91                 po::store(
92                         parser
93                         .options(desc)
94                         .style(
95                         po::command_line_style::unix_style |
96                         po::command_line_style::allow_long_disguise &
97                         ~po::command_line_style::allow_guessing
98                         )
99                         .run(),
100                         vm);
101                 vm.notify();
102         } catch (std::exception& e) {
103                 std::cout << e.what() << '\n' << desc << '\n';
104                 return 3;
105         }
106
107         if (vm.count("help")) {
108                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
109                 wprintf(
110                         L"%s is a simple program to ping an ip4 address.\n"
111                         L"You can use the following options to define its behaviour:\n\n", progName);
112                 std::cout << desc;
113                 wprintf(
114                         L"\nIt will take at least timeout times number of pings to run\n"
115                         L"Then it will output a string looking something like this:\n\n"
116                         L"\tPING WARNING RTA: 72ms Packet loss: 20% | ping=72ms;40;80;71;77 pl=20%;20;50;0;100\n\n"
117                         L"\"PING\" being the type of the check, \"WARNING\" the returned status\n"
118                         L"and \"RTA: 72ms Packet loss: 20%\" the relevant information.\n"
119                         L"The performance data is found behind the \"|\", in order:\n"
120                         L"returned value, warning threshold, critical threshold, minimal value and,\n"
121                         L"if applicable, the maximal value. \n\n"
122                         L"%s' exit codes denote the following:\n"
123                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
124                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
125                         L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
126                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
127                         L"Threshold syntax:\n\n"
128                         L"-w THRESHOLD\n"
129                         L"warn if threshold is broken, which means VALUE > THRESHOLD\n"
130                         L"(unless stated differently)\n\n"
131                         L"-w !THRESHOLD\n"
132                         L"inverts threshold check, VALUE < THRESHOLD (analogous to above)\n\n"
133                         L"-w [THR1-THR2]\n"
134                         L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
135                         L"-w ![THR1-THR2]\n"
136                         L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
137                         L"-w THRESHOLD%%\n"
138                         L"if the plugin accepts percentage based thresholds those will be used.\n"
139                         L"Does nothing if the plugin does not accept percentages, or only uses\n"
140                         L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
141                         L"to end with a percentage sign.\n\n"
142                         L"All of these options work with the critical threshold \"-c\" too."
143                         , progName);
144                 std::cout << '\n';
145                 return 0;
146         }
147
148         if (vm.count("version")) {
149                 std::cout << progName << " Version: " << VERSION << '\n';
150                 return 0;
151         }
152
153         if (vm.count("-4") && vm.count("-6")) {
154                 std::cout << "Conflicting options \"4\" and \"6\"" << '\n';
155                 return 3;
156         }
157     
158         if (vm.count("warning")) {
159                 std::vector<std::wstring> sVec = splitMultiOptions(vm["warning"].as<std::wstring>());
160                 if (sVec.size() != 2) {
161                         std::cout << "Wrong format for warning thresholds" << '\n';
162                         return 3;
163                 }
164                 try {
165                         printInfo.warn = threshold(*sVec.begin());
166                         printInfo.wpl = threshold(sVec.back());
167                         if (!printInfo.wpl.perc) {
168                                 std::cout << "Packet loss must be percentage" << '\n';
169                                 return 3;
170                         }
171                 } catch (std::invalid_argument& e) {
172                         std::cout << e.what() << '\n';
173                         return 3;
174                 }
175         }
176         if (vm.count("critical")) {
177                 std::vector<std::wstring> sVec = splitMultiOptions(vm["critical"].as<std::wstring>());
178                 if (sVec.size() != 2) {
179                         std::cout << "Wrong format for critical thresholds" << '\n';
180                         return 3;
181                 }
182                 try {
183                         printInfo.crit = threshold(*sVec.begin());
184                         printInfo.cpl = threshold(sVec.back());
185                         if (!printInfo.wpl.perc) {
186                                 std::cout << "Packet loss must be percentage" << '\n';
187                                 return 3;
188                         }
189                 } catch (std::invalid_argument& e) {
190                         std::cout << e.what() << '\n';
191                         return 3;
192                 }
193         }
194
195         if (vm.count("timeout"))
196                 printInfo.timeout = vm["timeout"].as<INT>();
197         if (vm.count("packets"))
198                 printInfo.num = vm["packets"].as<INT>();
199         if (vm.count("-6"))
200                 printInfo.ipv4 = FALSE;
201
202         printInfo.host = vm["host"].as<std::wstring>();
203
204         if (vm.count("debug"))
205                 debug = TRUE;
206
207         return -1;
208 }
209
210 INT printOutput(printInfoStruct& printInfo, response& response)
211 {
212         if (debug)
213                 std::wcout << L"Constructing output string" << '\n';
214
215         state state = OK;
216
217         double plp = ((double)response.dropped / printInfo.num) * 100.0;
218
219         if (printInfo.warn.rend(response.avg) || printInfo.wpl.rend(plp))
220                 state = WARNING;
221
222         if (printInfo.crit.rend(response.avg) || printInfo.cpl.rend(plp))
223                 state = CRITICAL;
224
225         std::wstringstream perf;
226         perf << L"rta=" << response.avg << L"ms;" << printInfo.warn.pString() << L";"
227                 << printInfo.crit.pString() << L";0;" << " pl=" << removeZero(plp) << "%;" 
228                 << printInfo.wpl.pString() << ";" << printInfo.cpl.pString() << ";0;100";
229
230         if (response.dropped == printInfo.num) {
231                 std::wcout << L"PING CRITICAL ALL CONNECTIONS DROPPED | " << perf.str() << '\n';
232                 return 3;
233         }
234
235         switch (state) {
236         case OK:
237                 std::wcout << L"PING OK RTA: " << response.avg << L"ms Packet loss: " << removeZero(plp) << "% | " << perf.str() << '\n';
238                 break;
239         case WARNING:
240                 std::wcout << L"PING WARNING RTA: " << response.avg << L"ms Packet loss: " << removeZero(plp) << "% | " << perf.str() << '\n';
241                 break;
242         case CRITICAL:
243                 std::wcout << L"PING CRITICAL RTA: " << response.avg << L"ms Packet loss: " << removeZero(plp) << "% | " << perf.str() << '\n';
244                 break;
245         }
246
247         return state;
248 }
249
250 INT check_ping4(const printInfoStruct& pi, response& response)
251 {
252         in_addr ipDest4;
253         HANDLE hIcmp;
254         DWORD dwRet = 0, dwRepSize = 0;
255         LPVOID repBuf = NULL;
256         UINT rtt = 0;
257         INT num = pi.num;
258         LARGE_INTEGER frequency, timer1, timer2;
259         LPCWSTR term;
260
261         if (debug)
262                 std::wcout << L"Parsing ip address" << '\n';
263
264         if (RtlIpv4StringToAddress(pi.host.c_str(), TRUE, &term, &ipDest4) == STATUS_INVALID_PARAMETER) {
265                 std::wcout << pi.host << " is not a valid ip address\n";
266                 return 3;
267         }
268
269         if (*term != L'\0') {
270                 std::wcout << pi.host << " is not a valid ip address\n";
271                 return 3;
272         }
273
274         if (debug)
275                 std::wcout << L"Creating Icmp File\n";
276
277         if ((hIcmp = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
278                 goto die;
279
280         dwRepSize = sizeof(ICMP_ECHO_REPLY) + 8;
281         repBuf = reinterpret_cast<VOID *>(new BYTE[dwRepSize]);
282
283         if (repBuf == NULL)
284                 goto die;
285
286         QueryPerformanceFrequency(&frequency);
287         do {
288                 QueryPerformanceCounter(&timer1);
289
290                 if (debug)
291                         std::wcout << L"Sending Icmp echo\n";
292
293                 if (!IcmpSendEcho2(hIcmp, NULL, NULL, NULL, ipDest4.S_un.S_addr,
294                         NULL, 0, NULL, repBuf, dwRepSize, pi.timeout)) {
295                         response.dropped++;
296                         if (debug)
297                                 std::wcout << L"Dropped: Response was 0" << '\n';
298                         continue;
299                 }
300
301                 if (debug)
302                         std::wcout << "Ping recieved" << '\n';
303
304                 PICMP_ECHO_REPLY pEchoReply = static_cast<PICMP_ECHO_REPLY>(repBuf);
305
306                 if (pEchoReply->Status != IP_SUCCESS) {
307                         response.dropped++;
308                         if (debug)
309                                 std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n';
310                         continue;
311                 }
312
313                 if (debug)
314                         std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n';
315
316                 rtt += pEchoReply->RoundTripTime;
317                 if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin)
318                         response.pMin = pEchoReply->RoundTripTime;
319                 else if (pEchoReply->RoundTripTime > response.pMax)
320                         response.pMax = pEchoReply->RoundTripTime;
321
322                 QueryPerformanceCounter(&timer2);
323                 if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout)
324                         Sleep(pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart));
325         } while (--num);
326
327         if (debug)
328                 std::wcout << L"All pings sent. Cleaning up and returning" << '\n';
329
330         if (hIcmp)
331                 IcmpCloseHandle(hIcmp);
332         if (repBuf)
333                 delete reinterpret_cast<VOID *>(repBuf);
334
335         response.avg = ((double)rtt / pi.num);
336
337         return -1;
338
339 die:
340         die();
341         if (hIcmp)
342                 IcmpCloseHandle(hIcmp);
343         if (repBuf)
344                 delete reinterpret_cast<VOID *>(repBuf);
345
346         return 3;
347 }
348
349 INT check_ping6(const printInfoStruct& pi, response& response)
350 {
351         sockaddr_in6 ipDest6, ipSource6;
352         IP_OPTION_INFORMATION ipInfo = { 30, 0, 0, 0, NULL };
353         DWORD dwRepSize = sizeof(ICMPV6_ECHO_REPLY) + 8;
354         LPVOID repBuf = reinterpret_cast<VOID *>(new BYTE[dwRepSize]);
355         HANDLE hIcmp = NULL;
356
357         LARGE_INTEGER frequency, timer1, timer2;
358         INT num = pi.num;
359         UINT rtt = 0;
360
361         if (debug)
362                 std::wcout << L"Parsing ip address" << '\n';
363
364         if (RtlIpv6StringToAddressEx(pi.host.c_str(), &ipDest6.sin6_addr, &ipDest6.sin6_scope_id, &ipDest6.sin6_port)) {
365                 std::wcout << pi.host << " is not a valid ipv6 address" << '\n';
366                 return 3;
367         }
368
369         ipDest6.sin6_family = AF_INET6;
370
371         ipSource6.sin6_addr = in6addr_any;
372         ipSource6.sin6_family = AF_INET6;
373         ipSource6.sin6_flowinfo = 0;
374         ipSource6.sin6_port = 0;
375
376         if (debug)
377                 std::wcout << L"Creating Icmp File" << '\n';
378
379         hIcmp = Icmp6CreateFile();
380         if (hIcmp == INVALID_HANDLE_VALUE) {
381                 goto die;
382         }
383
384         QueryPerformanceFrequency(&frequency);
385         do {
386                 QueryPerformanceCounter(&timer1);
387
388                 if (debug)
389                         std::wcout << L"Sending Icmp echo" << '\n';
390
391                 if (!Icmp6SendEcho2(hIcmp, NULL, NULL, NULL, &ipSource6, &ipDest6,
392                         NULL, 0, &ipInfo, repBuf, dwRepSize, pi.timeout)) {
393                         response.dropped++;
394                         if (debug)
395                                 std::wcout << L"Dropped: Response was 0" << '\n';
396                         continue;
397                 }
398
399                 if (debug)
400                         std::wcout << "Ping recieved" << '\n';
401
402                 Icmp6ParseReplies(repBuf, dwRepSize);
403
404                 ICMPV6_ECHO_REPLY *pEchoReply = static_cast<ICMPV6_ECHO_REPLY *>(repBuf);
405
406                 if (pEchoReply->Status != IP_SUCCESS) {
407                         response.dropped++;
408                         if (debug)
409                                 std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n';
410                         continue;
411                 }
412                 
413                 rtt += pEchoReply->RoundTripTime;
414
415                 if (debug)
416                         std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n';
417
418                 if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin)
419                         response.pMin = pEchoReply->RoundTripTime;
420                 else if (pEchoReply->RoundTripTime > response.pMax)
421                         response.pMax = pEchoReply->RoundTripTime;
422
423                 QueryPerformanceCounter(&timer2);
424                 if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout)
425                         Sleep(pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart));
426         } while (--num);
427
428         if (debug)
429                 std::wcout << L"All pings sent. Cleaning up and returning" << '\n';
430
431         if (hIcmp)
432                 IcmpCloseHandle(hIcmp);
433         if (repBuf)
434                 delete reinterpret_cast<VOID *>(repBuf);
435         response.avg = ((double)rtt / pi.num);
436
437         return -1;
438 die:
439         die(GetLastError());
440
441         if (hIcmp)
442                 IcmpCloseHandle(hIcmp);
443         if (repBuf)
444                 delete reinterpret_cast<VOID *>(repBuf);
445
446         return 3;
447 }