diff options
Diffstat (limited to 'plugins/check_dns.c')
-rw-r--r-- | plugins/check_dns.c | 165 |
1 files changed, 129 insertions, 36 deletions
diff --git a/plugins/check_dns.c b/plugins/check_dns.c index 48601f02..7ffce98b 100644 --- a/plugins/check_dns.c +++ b/plugins/check_dns.c | |||
@@ -41,7 +41,9 @@ const char *email = "devel@monitoring-plugins.org"; | |||
41 | 41 | ||
42 | int process_arguments (int, char **); | 42 | int process_arguments (int, char **); |
43 | int validate_arguments (void); | 43 | int validate_arguments (void); |
44 | int error_scan (char *); | 44 | int error_scan (char *, int *); |
45 | int ip_match_cidr(const char *, const char *); | ||
46 | unsigned long ip2long(const char *); | ||
45 | void print_help (void); | 47 | void print_help (void); |
46 | void print_usage (void); | 48 | void print_usage (void); |
47 | 49 | ||
@@ -52,8 +54,10 @@ char ptr_server[ADDRESS_LENGTH] = ""; | |||
52 | int verbose = FALSE; | 54 | int verbose = FALSE; |
53 | char **expected_address = NULL; | 55 | char **expected_address = NULL; |
54 | int expected_address_cnt = 0; | 56 | int expected_address_cnt = 0; |
57 | int expect_nxdomain = FALSE; | ||
55 | 58 | ||
56 | int expect_authority = FALSE; | 59 | int expect_authority = FALSE; |
60 | int all_match = FALSE; | ||
57 | thresholds *time_thresholds = NULL; | 61 | thresholds *time_thresholds = NULL; |
58 | 62 | ||
59 | static int | 63 | static int |
@@ -71,7 +75,7 @@ main (int argc, char **argv) | |||
71 | { | 75 | { |
72 | char *command_line = NULL; | 76 | char *command_line = NULL; |
73 | char input_buffer[MAX_INPUT_BUFFER]; | 77 | char input_buffer[MAX_INPUT_BUFFER]; |
74 | char *address = NULL; /* comma seperated str with addrs/ptrs (sorted) */ | 78 | char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ |
75 | char **addresses = NULL; | 79 | char **addresses = NULL; |
76 | int n_addresses = 0; | 80 | int n_addresses = 0; |
77 | char *msg = NULL; | 81 | char *msg = NULL; |
@@ -81,10 +85,10 @@ main (int argc, char **argv) | |||
81 | double elapsed_time; | 85 | double elapsed_time; |
82 | long microsec; | 86 | long microsec; |
83 | struct timeval tv; | 87 | struct timeval tv; |
84 | int multi_address; | ||
85 | int parse_address = FALSE; /* This flag scans for Address: but only after Name: */ | 88 | int parse_address = FALSE; /* This flag scans for Address: but only after Name: */ |
86 | output chld_out, chld_err; | 89 | output chld_out, chld_err; |
87 | size_t i; | 90 | size_t i; |
91 | int is_nxdomain = FALSE; | ||
88 | 92 | ||
89 | setlocale (LC_ALL, ""); | 93 | setlocale (LC_ALL, ""); |
90 | bindtextdomain (PACKAGE, LOCALEDIR); | 94 | bindtextdomain (PACKAGE, LOCALEDIR); |
@@ -127,7 +131,7 @@ main (int argc, char **argv) | |||
127 | if (verbose) | 131 | if (verbose) |
128 | puts(chld_out.line[i]); | 132 | puts(chld_out.line[i]); |
129 | 133 | ||
130 | if (strcasestr (chld_out.line[i], ".in-addr.arpa")) { | 134 | if (strcasestr (chld_out.line[i], ".in-addr.arpa") || strcasestr (chld_out.line[i], ".ip6.arpa")) { |
131 | if ((temp_buffer = strstr (chld_out.line[i], "name = "))) | 135 | if ((temp_buffer = strstr (chld_out.line[i], "name = "))) |
132 | addresses[n_addresses++] = strdup (temp_buffer + 7); | 136 | addresses[n_addresses++] = strdup (temp_buffer + 7); |
133 | else { | 137 | else { |
@@ -167,8 +171,8 @@ main (int argc, char **argv) | |||
167 | temp_buffer++; | 171 | temp_buffer++; |
168 | 172 | ||
169 | /* Strip leading spaces */ | 173 | /* Strip leading spaces */ |
170 | for (; *temp_buffer != '\0' && *temp_buffer == ' '; temp_buffer++) | 174 | while (*temp_buffer == ' ') |
171 | /* NOOP */; | 175 | temp_buffer++; |
172 | 176 | ||
173 | strip(temp_buffer); | 177 | strip(temp_buffer); |
174 | if (temp_buffer==NULL || strlen(temp_buffer)==0) { | 178 | if (temp_buffer==NULL || strlen(temp_buffer)==0) { |
@@ -184,7 +188,7 @@ main (int argc, char **argv) | |||
184 | } | 188 | } |
185 | 189 | ||
186 | 190 | ||
187 | result = error_scan (chld_out.line[i]); | 191 | result = error_scan (chld_out.line[i], &is_nxdomain); |
188 | if (result != STATE_OK) { | 192 | if (result != STATE_OK) { |
189 | msg = strchr (chld_out.line[i], ':'); | 193 | msg = strchr (chld_out.line[i], ':'); |
190 | if(msg) msg++; | 194 | if(msg) msg++; |
@@ -197,13 +201,20 @@ main (int argc, char **argv) | |||
197 | if (verbose) | 201 | if (verbose) |
198 | puts(chld_err.line[i]); | 202 | puts(chld_err.line[i]); |
199 | 203 | ||
200 | if (error_scan (chld_err.line[i]) != STATE_OK) { | 204 | if (error_scan (chld_err.line[i], &is_nxdomain) != STATE_OK) { |
201 | result = max_state (result, error_scan (chld_err.line[i])); | 205 | result = max_state (result, error_scan (chld_err.line[i], &is_nxdomain)); |
202 | msg = strchr(input_buffer, ':'); | 206 | msg = strchr(input_buffer, ':'); |
203 | if(msg) msg++; | 207 | if(msg) |
208 | msg++; | ||
209 | else | ||
210 | msg = input_buffer; | ||
204 | } | 211 | } |
205 | } | 212 | } |
206 | 213 | ||
214 | if (is_nxdomain && !expect_nxdomain) { | ||
215 | die (STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address); | ||
216 | } | ||
217 | |||
207 | if (addresses) { | 218 | if (addresses) { |
208 | int i,slen; | 219 | int i,slen; |
209 | char *adrp; | 220 | char *adrp; |
@@ -227,11 +238,27 @@ main (int argc, char **argv) | |||
227 | if (result == STATE_OK && expected_address_cnt > 0) { | 238 | if (result == STATE_OK && expected_address_cnt > 0) { |
228 | result = STATE_CRITICAL; | 239 | result = STATE_CRITICAL; |
229 | temp_buffer = ""; | 240 | temp_buffer = ""; |
241 | unsigned long expect_match = (1 << expected_address_cnt) - 1; | ||
242 | unsigned long addr_match = (1 << n_addresses) - 1; | ||
243 | |||
230 | for (i=0; i<expected_address_cnt; i++) { | 244 | for (i=0; i<expected_address_cnt; i++) { |
231 | /* check if we get a match and prepare an error string */ | 245 | int j; |
232 | if (strcmp(address, expected_address[i]) == 0) result = STATE_OK; | 246 | /* check if we get a match on 'raw' ip or cidr */ |
247 | for (j=0; j<n_addresses; j++) { | ||
248 | if ( strcmp(addresses[j], expected_address[i]) == 0 | ||
249 | || ip_match_cidr(addresses[j], expected_address[i]) ) { | ||
250 | result = STATE_OK; | ||
251 | addr_match &= ~(1 << j); | ||
252 | expect_match &= ~(1 << i); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /* prepare an error string */ | ||
233 | xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); | 257 | xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]); |
234 | } | 258 | } |
259 | /* check if expected_address must cover all in addresses and none may be missing */ | ||
260 | if (all_match && (expect_match != 0 || addr_match != 0)) | ||
261 | result = STATE_CRITICAL; | ||
235 | if (result == STATE_CRITICAL) { | 262 | if (result == STATE_CRITICAL) { |
236 | /* Strip off last semicolon... */ | 263 | /* Strip off last semicolon... */ |
237 | temp_buffer[strlen(temp_buffer)-2] = '\0'; | 264 | temp_buffer[strlen(temp_buffer)-2] = '\0'; |
@@ -239,6 +266,16 @@ main (int argc, char **argv) | |||
239 | } | 266 | } |
240 | } | 267 | } |
241 | 268 | ||
269 | if (expect_nxdomain) { | ||
270 | if (!is_nxdomain) { | ||
271 | result = STATE_CRITICAL; | ||
272 | xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address); | ||
273 | } else { | ||
274 | if (address != NULL) free(address); | ||
275 | address = "NXDOMAIN"; | ||
276 | } | ||
277 | } | ||
278 | |||
242 | /* check if authoritative */ | 279 | /* check if authoritative */ |
243 | if (result == STATE_OK && expect_authority && non_authoritative) { | 280 | if (result == STATE_OK && expect_authority && non_authoritative) { |
244 | result = STATE_CRITICAL; | 281 | result = STATE_CRITICAL; |
@@ -249,11 +286,6 @@ main (int argc, char **argv) | |||
249 | elapsed_time = (double)microsec / 1.0e6; | 286 | elapsed_time = (double)microsec / 1.0e6; |
250 | 287 | ||
251 | if (result == STATE_OK) { | 288 | if (result == STATE_OK) { |
252 | if (strchr (address, ',') == NULL) | ||
253 | multi_address = FALSE; | ||
254 | else | ||
255 | multi_address = TRUE; | ||
256 | |||
257 | result = get_status(elapsed_time, time_thresholds); | 289 | result = get_status(elapsed_time, time_thresholds); |
258 | if (result == STATE_OK) { | 290 | if (result == STATE_OK) { |
259 | printf ("DNS %s: ", _("OK")); | 291 | printf ("DNS %s: ", _("OK")); |
@@ -295,12 +327,43 @@ main (int argc, char **argv) | |||
295 | return result; | 327 | return result; |
296 | } | 328 | } |
297 | 329 | ||
330 | int | ||
331 | ip_match_cidr(const char *addr, const char *cidr_ro) | ||
332 | { | ||
333 | char *subnet, *mask_c, *cidr = strdup(cidr_ro); | ||
334 | int mask; | ||
335 | subnet = strtok(cidr, "/"); | ||
336 | mask_c = strtok(NULL, "\0"); | ||
337 | if (!subnet || !mask_c) | ||
338 | return FALSE; | ||
339 | mask = atoi(mask_c); | ||
340 | |||
341 | /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ | ||
342 | return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); | ||
343 | } | ||
298 | 344 | ||
345 | unsigned long | ||
346 | ip2long(const char* src) { | ||
347 | unsigned long ip[4]; | ||
348 | /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ | ||
349 | return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", | ||
350 | &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && | ||
351 | ip[0] < 256 && ip[1] < 256 && | ||
352 | ip[2] < 256 && ip[3] < 256) | ||
353 | ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] | ||
354 | : 0; | ||
355 | } | ||
299 | 356 | ||
300 | int | 357 | int |
301 | error_scan (char *input_buffer) | 358 | error_scan (char *input_buffer, int *is_nxdomain) |
302 | { | 359 | { |
303 | 360 | ||
361 | const int nxdomain = strstr (input_buffer, "Non-existent") || | ||
362 | strstr (input_buffer, "** server can't find") || | ||
363 | strstr (input_buffer, "** Can't find") || | ||
364 | strstr (input_buffer, "NXDOMAIN"); | ||
365 | if (nxdomain) *is_nxdomain = TRUE; | ||
366 | |||
304 | /* the DNS lookup timed out */ | 367 | /* the DNS lookup timed out */ |
305 | if (strstr (input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || | 368 | if (strstr (input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || |
306 | strstr (input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || | 369 | strstr (input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || |
@@ -310,6 +373,8 @@ error_scan (char *input_buffer) | |||
310 | /* DNS server is not running... */ | 373 | /* DNS server is not running... */ |
311 | else if (strstr (input_buffer, "No response from server")) | 374 | else if (strstr (input_buffer, "No response from server")) |
312 | die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); | 375 | die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); |
376 | else if (strstr (input_buffer, "no servers could be reached")) | ||
377 | die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server); | ||
313 | 378 | ||
314 | /* Host name is valid, but server doesn't have records... */ | 379 | /* Host name is valid, but server doesn't have records... */ |
315 | else if (strstr (input_buffer, "No records")) | 380 | else if (strstr (input_buffer, "No records")) |
@@ -317,7 +382,7 @@ error_scan (char *input_buffer) | |||
317 | 382 | ||
318 | /* Connection was refused */ | 383 | /* Connection was refused */ |
319 | else if (strstr (input_buffer, "Connection refused") || | 384 | else if (strstr (input_buffer, "Connection refused") || |
320 | strstr (input_buffer, "Couldn't find server") || | 385 | strstr (input_buffer, "Couldn't find server") || |
321 | strstr (input_buffer, "Refused") || | 386 | strstr (input_buffer, "Refused") || |
322 | (strstr (input_buffer, "** server can't find") && | 387 | (strstr (input_buffer, "** server can't find") && |
323 | strstr (input_buffer, ": REFUSED"))) | 388 | strstr (input_buffer, ": REFUSED"))) |
@@ -331,12 +396,6 @@ error_scan (char *input_buffer) | |||
331 | else if (strstr (input_buffer, "No information")) | 396 | else if (strstr (input_buffer, "No information")) |
332 | die (STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server); | 397 | die (STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server); |
333 | 398 | ||
334 | /* Host or domain name does not exist */ | ||
335 | else if (strstr (input_buffer, "Non-existent") || | ||
336 | strstr (input_buffer, "** server can't find") || | ||
337 | strstr (input_buffer,"NXDOMAIN")) | ||
338 | die (STATE_CRITICAL, _("Domain %s was not found by the server\n"), query_address); | ||
339 | |||
340 | /* Network is unreachable */ | 399 | /* Network is unreachable */ |
341 | else if (strstr (input_buffer, "Network is unreachable")) | 400 | else if (strstr (input_buffer, "Network is unreachable")) |
342 | die (STATE_CRITICAL, _("Network is unreachable\n")); | 401 | die (STATE_CRITICAL, _("Network is unreachable\n")); |
@@ -373,7 +432,9 @@ process_arguments (int argc, char **argv) | |||
373 | {"server", required_argument, 0, 's'}, | 432 | {"server", required_argument, 0, 's'}, |
374 | {"reverse-server", required_argument, 0, 'r'}, | 433 | {"reverse-server", required_argument, 0, 'r'}, |
375 | {"expected-address", required_argument, 0, 'a'}, | 434 | {"expected-address", required_argument, 0, 'a'}, |
435 | {"expect-nxdomain", no_argument, 0, 'n'}, | ||
376 | {"expect-authority", no_argument, 0, 'A'}, | 436 | {"expect-authority", no_argument, 0, 'A'}, |
437 | {"all", no_argument, 0, 'L'}, | ||
377 | {"warning", required_argument, 0, 'w'}, | 438 | {"warning", required_argument, 0, 'w'}, |
378 | {"critical", required_argument, 0, 'c'}, | 439 | {"critical", required_argument, 0, 'c'}, |
379 | {0, 0, 0, 0} | 440 | {0, 0, 0, 0} |
@@ -387,7 +448,7 @@ process_arguments (int argc, char **argv) | |||
387 | strcpy (argv[c], "-t"); | 448 | strcpy (argv[c], "-t"); |
388 | 449 | ||
389 | while (1) { | 450 | while (1) { |
390 | c = getopt_long (argc, argv, "hVvAt:H:s:r:a:w:c:", long_opts, &opt_index); | 451 | c = getopt_long (argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); |
391 | 452 | ||
392 | if (c == -1 || c == EOF) | 453 | if (c == -1 || c == EOF) |
393 | break; | 454 | break; |
@@ -428,13 +489,33 @@ process_arguments (int argc, char **argv) | |||
428 | case 'a': /* expected address */ | 489 | case 'a': /* expected address */ |
429 | if (strlen (optarg) >= ADDRESS_LENGTH) | 490 | if (strlen (optarg) >= ADDRESS_LENGTH) |
430 | die (STATE_UNKNOWN, _("Input buffer overflow\n")); | 491 | die (STATE_UNKNOWN, _("Input buffer overflow\n")); |
431 | expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); | 492 | if (strchr(optarg, ',') != NULL) { |
432 | expected_address[expected_address_cnt] = strdup(optarg); | 493 | char *comma = strchr(optarg, ','); |
433 | expected_address_cnt++; | 494 | while (comma != NULL) { |
495 | expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); | ||
496 | expected_address[expected_address_cnt] = strndup(optarg, comma - optarg); | ||
497 | expected_address_cnt++; | ||
498 | optarg = comma + 1; | ||
499 | comma = strchr(optarg, ','); | ||
500 | } | ||
501 | expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); | ||
502 | expected_address[expected_address_cnt] = strdup(optarg); | ||
503 | expected_address_cnt++; | ||
504 | } else { | ||
505 | expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); | ||
506 | expected_address[expected_address_cnt] = strdup(optarg); | ||
507 | expected_address_cnt++; | ||
508 | } | ||
509 | break; | ||
510 | case 'n': /* expect NXDOMAIN */ | ||
511 | expect_nxdomain = TRUE; | ||
434 | break; | 512 | break; |
435 | case 'A': /* expect authority */ | 513 | case 'A': /* expect authority */ |
436 | expect_authority = TRUE; | 514 | expect_authority = TRUE; |
437 | break; | 515 | break; |
516 | case 'L': /* all must match */ | ||
517 | all_match = TRUE; | ||
518 | break; | ||
438 | case 'w': | 519 | case 'w': |
439 | warning = optarg; | 520 | warning = optarg; |
440 | break; | 521 | break; |
@@ -470,8 +551,15 @@ process_arguments (int argc, char **argv) | |||
470 | int | 551 | int |
471 | validate_arguments () | 552 | validate_arguments () |
472 | { | 553 | { |
473 | if (query_address[0] == 0) | 554 | if (query_address[0] == 0) { |
555 | printf ("missing --host argument\n"); | ||
556 | return ERROR; | ||
557 | } | ||
558 | |||
559 | if (expected_address_cnt > 0 && expect_nxdomain) { | ||
560 | printf ("--expected-address and --expect-nxdomain cannot be combined\n"); | ||
474 | return ERROR; | 561 | return ERROR; |
562 | } | ||
475 | 563 | ||
476 | return OK; | 564 | return OK; |
477 | } | 565 | } |
@@ -500,17 +588,22 @@ print_help (void) | |||
500 | printf (" %s\n", _("The name or address you want to query")); | 588 | printf (" %s\n", _("The name or address you want to query")); |
501 | printf (" -s, --server=HOST\n"); | 589 | printf (" -s, --server=HOST\n"); |
502 | printf (" %s\n", _("Optional DNS server you want to use for the lookup")); | 590 | printf (" %s\n", _("Optional DNS server you want to use for the lookup")); |
503 | printf (" -a, --expected-address=IP-ADDRESS|HOST\n"); | 591 | printf (" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); |
504 | printf (" %s\n", _("Optional IP-ADDRESS you expect the DNS server to return. HOST must end with")); | 592 | printf (" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); |
505 | printf (" %s\n", _("a dot (.). This option can be repeated multiple times (Returns OK if any")); | 593 | printf (" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); |
506 | printf (" %s\n", _("value match). If multiple addresses are returned at once, you have to match")); | 594 | printf (" %s\n", _("value matches).")); |
507 | printf (" %s\n", _("the whole string of addresses separated with commas (sorted alphabetically).")); | 595 | printf (" -n, --expect-nxdomain\n"); |
596 | printf (" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); | ||
597 | printf (" %s\n", _("Cannot be used together with -a")); | ||
508 | printf (" -A, --expect-authority\n"); | 598 | printf (" -A, --expect-authority\n"); |
509 | printf (" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); | 599 | printf (" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); |
510 | printf (" -w, --warning=seconds\n"); | 600 | printf (" -w, --warning=seconds\n"); |
511 | printf (" %s\n", _("Return warning if elapsed time exceeds value. Default off")); | 601 | printf (" %s\n", _("Return warning if elapsed time exceeds value. Default off")); |
512 | printf (" -c, --critical=seconds\n"); | 602 | printf (" -c, --critical=seconds\n"); |
513 | printf (" %s\n", _("Return critical if elapsed time exceeds value. Default off")); | 603 | printf (" %s\n", _("Return critical if elapsed time exceeds value. Default off")); |
604 | printf (" -L, --all\n"); | ||
605 | printf (" %s\n", _("Return critical if the list of expected addresses does not match all addresses")); | ||
606 | printf (" %s\n", _("returned. Default off")); | ||
514 | 607 | ||
515 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | 608 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); |
516 | 609 | ||
@@ -522,5 +615,5 @@ void | |||
522 | print_usage (void) | 615 | print_usage (void) |
523 | { | 616 | { |
524 | printf ("%s\n", _("Usage:")); | 617 | printf ("%s\n", _("Usage:")); |
525 | printf ("%s -H host [-s server] [-a expected-address] [-A] [-t timeout] [-w warn] [-c crit]\n", progname); | 618 | printf ("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); |
526 | } | 619 | } |