summaryrefslogtreecommitdiffstats
path: root/plugins/check_dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_dns.c')
-rw-r--r--plugins/check_dns.c167
1 files changed, 130 insertions, 37 deletions
diff --git a/plugins/check_dns.c b/plugins/check_dns.c
index 2212122..9de6caf 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -41,7 +41,9 @@ const char *email = "devel@monitoring-plugins.org";
41 41
42int process_arguments (int, char **); 42int process_arguments (int, char **);
43int validate_arguments (void); 43int validate_arguments (void);
44int error_scan (char *); 44int error_scan (char *, int *);
45int ip_match_cidr(const char *, const char *);
46unsigned long ip2long(const char *);
45void print_help (void); 47void print_help (void);
46void print_usage (void); 48void print_usage (void);
47 49
@@ -52,8 +54,10 @@ char ptr_server[ADDRESS_LENGTH] = "";
52int verbose = FALSE; 54int verbose = FALSE;
53char **expected_address = NULL; 55char **expected_address = NULL;
54int expected_address_cnt = 0; 56int expected_address_cnt = 0;
57int expect_nxdomain = FALSE;
55 58
56int expect_authority = FALSE; 59int expect_authority = FALSE;
60int all_match = FALSE;
57thresholds *time_thresholds = NULL; 61thresholds *time_thresholds = NULL;
58 62
59static int 63static int
@@ -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 (strstr (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
330int
331ip_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
345unsigned long
346ip2long(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
300int 357int
301error_scan (char *input_buffer) 358error_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;
@@ -395,10 +456,10 @@ process_arguments (int argc, char **argv)
395 switch (c) { 456 switch (c) {
396 case 'h': /* help */ 457 case 'h': /* help */
397 print_help (); 458 print_help ();
398 exit (STATE_OK); 459 exit (STATE_UNKNOWN);
399 case 'V': /* version */ 460 case 'V': /* version */
400 print_revision (progname, NP_VERSION); 461 print_revision (progname, NP_VERSION);
401 exit (STATE_OK); 462 exit (STATE_UNKNOWN);
402 case 'v': /* version */ 463 case 'v': /* version */
403 verbose = TRUE; 464 verbose = TRUE;
404 break; 465 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)
470int 551int
471validate_arguments () 552validate_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
522print_usage (void) 615print_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}