summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/check_dig.c268
-rw-r--r--plugins/check_dig.d/config.h10
2 files changed, 273 insertions, 5 deletions
diff --git a/plugins/check_dig.c b/plugins/check_dig.c
index c27e5f13..9ea19e6a 100644
--- a/plugins/check_dig.c
+++ b/plugins/check_dig.c
@@ -3,7 +3,7 @@
3 * Monitoring check_dig plugin 3 * Monitoring check_dig plugin
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 2002-2024 Monitoring Plugins Development Team 6 * Copyright (c) 2002-2025 Monitoring Plugins Development Team
7 * 7 *
8 * Description: 8 * Description:
9 * 9 *
@@ -33,9 +33,10 @@
33 * because on some architectures those strings are in non-writable memory */ 33 * because on some architectures those strings are in non-writable memory */
34 34
35const char *progname = "check_dig"; 35const char *progname = "check_dig";
36const char *copyright = "2002-2024"; 36const char *copyright = "2002-2025";
37const char *email = "devel@monitoring-plugins.org"; 37const char *email = "devel@monitoring-plugins.org";
38 38
39#include <ctype.h>
39#include "common.h" 40#include "common.h"
40#include "netutils.h" 41#include "netutils.h"
41#include "utils.h" 42#include "utils.h"
@@ -56,6 +57,12 @@ void print_usage(void);
56 57
57static int verbose = 0; 58static int verbose = 0;
58 59
60/* helpers for flag parsing */
61static flag_list parse_flags_line(const char *line);
62static flag_list split_csv_trim(const char *csv);
63static bool flag_list_contains(const flag_list *list, const char *needle);
64static void free_flag_list(flag_list *list);
65
59int main(int argc, char **argv) { 66int main(int argc, char **argv) {
60 setlocale(LC_ALL, ""); 67 setlocale(LC_ALL, "");
61 bindtextdomain(PACKAGE, LOCALEDIR); 68 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -101,13 +108,35 @@ int main(int argc, char **argv) {
101 output chld_out; 108 output chld_out;
102 output chld_err; 109 output chld_err;
103 char *msg = NULL; 110 char *msg = NULL;
111 flag_list dig_flags = {.items = NULL, .count = 0};
104 mp_state_enum result = STATE_UNKNOWN; 112 mp_state_enum result = STATE_UNKNOWN;
113
105 /* run the command */ 114 /* run the command */
106 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 115 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
107 result = STATE_WARNING; 116 result = STATE_WARNING;
108 msg = (char *)_("dig returned an error status"); 117 msg = (char *)_("dig returned an error status");
109 } 118 }
110 119
120 /* extract ';; flags: ...' from stdout (first occurrence) */
121 for (size_t i = 0; i < chld_out.lines; i++) {
122 if (strstr(chld_out.line[i], "flags:")) {
123 if (verbose) {
124 printf("Raw flags line: %s\n", chld_out.line[i]);
125 }
126
127 dig_flags = parse_flags_line(chld_out.line[i]);
128
129 if (verbose && dig_flags.count > 0) {
130 printf(_("Parsed flags:"));
131 for (size_t k = 0; k < dig_flags.count; k++) {
132 printf(" %s", dig_flags.items[k]);
133 }
134 printf("\n");
135 }
136 break;
137 }
138 }
139
111 for (size_t i = 0; i < chld_out.lines; i++) { 140 for (size_t i = 0; i < chld_out.lines; i++) {
112 /* the server is responding, we just got the host name... */ 141 /* the server is responding, we just got the host name... */
113 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { 142 if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) {
@@ -174,6 +203,44 @@ int main(int argc, char **argv) {
174 result = STATE_WARNING; 203 result = STATE_WARNING;
175 } 204 }
176 205
206 /* Optional: evaluate dig flags only if -E/-X were provided */
207 if ((config.require_flags.count > 0) || (config.forbid_flags.count > 0)) {
208 if (dig_flags.count > 0) {
209 for (size_t r = 0; r < config.require_flags.count; r++) {
210 if (!flag_list_contains(&dig_flags, config.require_flags.items[r])) {
211 result = STATE_CRITICAL;
212 if (!msg) {
213 xasprintf(&msg, _("Missing required DNS flag: %s"),
214 config.require_flags.items[r]);
215 } else {
216 char *newmsg = NULL;
217 xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg,
218 config.require_flags.items[r]);
219 msg = newmsg;
220 }
221 }
222 }
223
224 for (size_t r = 0; r < config.forbid_flags.count; r++) {
225 if (flag_list_contains(&dig_flags, config.forbid_flags.items[r])) {
226 result = STATE_CRITICAL;
227 if (!msg) {
228 xasprintf(&msg, _("Forbidden DNS flag present: %s"),
229 config.forbid_flags.items[r]);
230 } else {
231 char *newmsg = NULL;
232 xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg,
233 config.forbid_flags.items[r]);
234 msg = newmsg;
235 }
236 }
237 }
238 }
239 }
240
241 /* cleanup flags buffer */
242 free_flag_list(&dig_flags);
243
177 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, 244 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
178 msg ? msg : _("Probably a non-existent host/domain"), 245 msg ? msg : _("Probably a non-existent host/domain"),
179 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), 246 fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED),
@@ -190,6 +257,8 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) {
190 {"critical", required_argument, 0, 'c'}, 257 {"critical", required_argument, 0, 'c'},
191 {"timeout", required_argument, 0, 't'}, 258 {"timeout", required_argument, 0, 't'},
192 {"dig-arguments", required_argument, 0, 'A'}, 259 {"dig-arguments", required_argument, 0, 'A'},
260 {"require-flags", required_argument, 0, 'E'},
261 {"forbid-flags", required_argument, 0, 'X'},
193 {"verbose", no_argument, 0, 'v'}, 262 {"verbose", no_argument, 0, 'v'},
194 {"version", no_argument, 0, 'V'}, 263 {"version", no_argument, 0, 'V'},
195 {"help", no_argument, 0, 'h'}, 264 {"help", no_argument, 0, 'h'},
@@ -212,7 +281,8 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) {
212 281
213 int option = 0; 282 int option = 0;
214 while (true) { 283 while (true) {
215 int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); 284 int option_index =
285 getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option);
216 286
217 if (option_index == -1 || option_index == EOF) { 287 if (option_index == -1 || option_index == EOF) {
218 break; 288 break;
@@ -263,6 +333,12 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) {
263 case 'A': /* dig arguments */ 333 case 'A': /* dig arguments */
264 result.config.dig_args = strdup(optarg); 334 result.config.dig_args = strdup(optarg);
265 break; 335 break;
336 case 'E': /* require flags */
337 result.config.require_flags = split_csv_trim(optarg);
338 break;
339 case 'X': /* forbid flags */
340 result.config.forbid_flags = split_csv_trim(optarg);
341 break;
266 case 'v': /* verbose */ 342 case 'v': /* verbose */
267 verbose++; 343 verbose++;
268 break; 344 break;
@@ -343,6 +419,10 @@ void print_help(void) {
343 printf(" %s\n", _("was in -l")); 419 printf(" %s\n", _("was in -l"));
344 printf(" %s\n", "-A, --dig-arguments=STRING"); 420 printf(" %s\n", "-A, --dig-arguments=STRING");
345 printf(" %s\n", _("Pass STRING as argument(s) to dig")); 421 printf(" %s\n", _("Pass STRING as argument(s) to dig"));
422 printf(" %s\n", "-E, --require-flags=LIST");
423 printf(" %s\n", _("Comma-separated dig flags that must be present (e.g. 'aa,qr')"));
424 printf(" %s\n", "-X, --forbid-flags=LIST");
425 printf(" %s\n", _("Comma-separated dig flags that must NOT be present"));
346 printf(UT_WARN_CRIT); 426 printf(UT_WARN_CRIT);
347 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 427 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
348 printf(UT_VERBOSE); 428 printf(UT_VERBOSE);
@@ -359,5 +439,185 @@ void print_usage(void) {
359 printf("%s\n", _("Usage:")); 439 printf("%s\n", _("Usage:"));
360 printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); 440 printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname);
361 printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); 441 printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n");
362 printf(" [-t <timeout>] [-a <expected answer address>] [-v]\n"); 442 printf(" [-t <timeout>] [-a <expected answer address>] [-E <flags>] [-X <flags>] [-v]\n");
443}
444
445/* helpers */
446
447/**
448 * parse_flags_line - Parse a dig output line and extract DNS header flags.
449 *
450 * Input:
451 * line - NUL terminated dig output line, e.g. ";; flags: qr rd ra; ..."
452 *
453 * Returns:
454 * flag_list where:
455 * - items: array of NUL terminated flag strings (heap allocated)
456 * - count: number of entries in items
457 * On parse failure or if no flags were found, count is 0 and items is NULL.
458 */
459static flag_list parse_flags_line(const char *line) {
460 flag_list result = {.items = NULL, .count = 0};
461
462 if (!line) {
463 return result;
464 }
465
466 /* Locate start of DNS header flags in dig output */
467 const char *p = strstr(line, "flags:");
468 if (!p) {
469 return result;
470 }
471 p += 6; /* skip literal "flags:" */
472
473 /* Skip whitespace after "flags:" */
474 while (*p && isspace((unsigned char)*p)) {
475 p++;
476 }
477
478 /* Flags are terminated by the next semicolon e.g. "qr rd ra;" */
479 const char *q = strchr(p, ';');
480 if (!q) {
481 return result;
482 }
483
484 /* Extract substring containing the flag block */
485 size_t len = (size_t)(q - p);
486 if (len == 0) {
487 return result;
488 }
489
490 char *buf = (char *)malloc(len + 1);
491 if (!buf) {
492 return result;
493 }
494 memcpy(buf, p, len);
495 buf[len] = '\0';
496
497 /* Tokenize flags separated by whitespace */
498 char **arr = NULL;
499 size_t cnt = 0;
500 char *saveptr = NULL;
501 char *tok = strtok_r(buf, " \t", &saveptr);
502
503 while (tok) {
504 /* Expand array for the next flag token */
505 char **tmp = (char **)realloc(arr, (cnt + 1) * sizeof(char *));
506 if (!tmp) {
507 /* On allocation failure keep what we have and return it */
508 break;
509 }
510 arr = tmp;
511 arr[cnt++] = strdup(tok);
512 tok = strtok_r(NULL, " \t", &saveptr);
513 }
514
515 free(buf);
516
517 result.items = arr;
518 result.count = cnt;
519 return result;
520}
521
522/**
523 * split_csv_trim - Split a comma separated string into trimmed tokens.
524 *
525 * Input:
526 * csv - NUL terminated string, e.g. "aa, qr , rd"
527 *
528 * Returns:
529 * flag_list where:
530 * - items: array of NUL terminated tokens (heap allocated, whitespace trimmed)
531 * - count: number of tokens
532 * On empty input, count is 0 and items is NULL
533 */
534static flag_list split_csv_trim(const char *csv) {
535 flag_list result = {.items = NULL, .count = 0};
536
537 if (!csv || !*csv) {
538 return result;
539 }
540
541 char *tmp = strdup(csv);
542 if (!tmp) {
543 return result;
544 }
545
546 char *s = tmp;
547 char *token = NULL;
548
549 /* Split CSV by commas, trimming whitespace on each token */
550 while ((token = strsep(&s, ",")) != NULL) {
551 /* trim leading whitespace */
552 while (*token && isspace((unsigned char)*token)) {
553 token++;
554 }
555
556 /* trim trailing whitespace */
557 char *end = token + strlen(token);
558 while (end > token && isspace((unsigned char)end[-1])) {
559 *--end = '\0';
560 }
561
562 if (*token) {
563 /* Expand the items array and append the token */
564 char **arr = (char **)realloc(result.items, (result.count + 1) * sizeof(char *));
565 if (!arr) {
566 /* Allocation failed, stop and return what we have */
567 break;
568 }
569 result.items = arr;
570 result.items[result.count++] = strdup(token);
571 }
572 }
573
574 free(tmp);
575 return result;
576}
577
578/**
579 * flag_list_contains - Case-insensitive membership test in a flag_list.
580 *
581 * Input:
582 * list - pointer to a flag_list
583 * needle - NUL terminated string to search for
584 *
585 * Returns:
586 * true if needle is contained in list (strcasecmp)
587 * false otherwise
588 */
589static bool flag_list_contains(const flag_list *list, const char *needle) {
590 if (!list || !needle || !*needle) {
591 return false;
592 }
593
594 for (size_t i = 0; i < list->count; i++) {
595 if (strcasecmp(list->items[i], needle) == 0) {
596 return true;
597 }
598 }
599 return false;
600}
601
602/**
603 * free_flag_list - Release all heap allocations held by a flag_list.
604 *
605 * Input:
606 * list - pointer to a flag_list whose items were allocated by
607 * parse_flags_line() or split_csv_trim().
608 *
609 * After this call list->items is NULL and list->count is 0.
610 */
611static void free_flag_list(flag_list *list) {
612 if (!list || !list->items) {
613 return;
614 }
615
616 for (size_t i = 0; i < list->count; i++) {
617 free(list->items[i]);
618 }
619 free(list->items);
620
621 list->items = NULL;
622 list->count = 0;
363} 623}
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h
index a570b633..dd1f58b5 100644
--- a/plugins/check_dig.d/config.h
+++ b/plugins/check_dig.d/config.h
@@ -8,6 +8,11 @@
8#define DEFAULT_TRIES 2 8#define DEFAULT_TRIES 2
9 9
10typedef struct { 10typedef struct {
11 char **items;
12 size_t count;
13} flag_list;
14
15typedef struct {
11 char *query_address; 16 char *query_address;
12 char *record_type; 17 char *record_type;
13 char *expected_address; 18 char *expected_address;
@@ -19,6 +24,8 @@ typedef struct {
19 24
20 double warning_interval; 25 double warning_interval;
21 double critical_interval; 26 double critical_interval;
27 flag_list require_flags;
28 flag_list forbid_flags;
22} check_dig_config; 29} check_dig_config;
23 30
24check_dig_config check_dig_config_init() { 31check_dig_config check_dig_config_init() {
@@ -34,7 +41,8 @@ check_dig_config check_dig_config_init() {
34 41
35 .warning_interval = UNDEFINED, 42 .warning_interval = UNDEFINED,
36 .critical_interval = UNDEFINED, 43 .critical_interval = UNDEFINED,
37 44 .require_flags = {.count = 0, .items = NULL},
45 .forbid_flags = {.count = 0, .items = NULL},
38 }; 46 };
39 return tmp; 47 return tmp;
40} 48}