summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDennis Ullrich <dennis.ullrich@plusserver.com>2025-11-27 16:24:56 +0100
committerDennis Ullrich <dennis.ullrich@plusserver.com>2025-11-27 16:43:56 +0100
commit052811414a7edcc6479895ddc169893e7740b28f (patch)
tree4e7f459a342d49752932eea4051c11ec63614b9b
parentcedfc166d42f4e89dddc1caa44e0655157d35a0a (diff)
downloadmonitoring-plugins-05281141.tar.gz
Refactor DNS flag handling: replace output pointer pattern with structured return types and add documentation.
This refactors all helper functions related to DNS flag extraction and validation: - Introduce a new `flag_list` struct used as unified return type for functions producing multiple output values - Replace all functions using output pointers (`char ***`, `size_t *`) with functions returning `flag_list` - Update callers in `main()` and the flag validation logic - Add documentation comments describing purpose, inputs and outputs for all new helper functions - Consolidate memory handling through a single `free_flag_list()` helper - Apply clang-format This brings more clarity, avoids hidden output and aligns with the review request for cleaner functions input/output.
-rw-r--r--plugins/check_dig.c274
1 files changed, 201 insertions, 73 deletions
diff --git a/plugins/check_dig.c b/plugins/check_dig.c
index b3f4c878..2db0f66b 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,7 +33,7 @@
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 <ctype.h>
@@ -58,10 +58,14 @@ void print_usage(void);
58static int verbose = 0; 58static int verbose = 0;
59 59
60/* helpers for flag parsing */ 60/* helpers for flag parsing */
61static bool parse_flags_line(const char *line, char ***out_flags, size_t *out_count); 61typedef struct {
62static void free_flags(char **flags, size_t count); 62 char **items;
63static bool list_contains(char **flags, size_t count, const char *needle); 63 size_t count;
64static void split_csv_trim(const char *csv, char ***out_items, size_t *out_count); 64} flag_list;
65static flag_list parse_flags_line(const char *line);
66static flag_list split_csv_trim(const char *csv);
67static bool flag_list_contains(const flag_list *list, const char *needle);
68static void free_flag_list(flag_list *list);
65 69
66int main(int argc, char **argv) { 70int main(int argc, char **argv) {
67 setlocale(LC_ALL, ""); 71 setlocale(LC_ALL, "");
@@ -108,10 +112,9 @@ int main(int argc, char **argv) {
108 output chld_out; 112 output chld_out;
109 output chld_err; 113 output chld_err;
110 char *msg = NULL; 114 char *msg = NULL;
111 char **dig_flags = NULL; 115 flag_list dig_flags = {.items = NULL, .count = 0};
112 size_t dig_flags_cnt = 0;
113
114 mp_state_enum result = STATE_UNKNOWN; 116 mp_state_enum result = STATE_UNKNOWN;
117
115 /* run the command */ 118 /* run the command */
116 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { 119 if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) {
117 result = STATE_WARNING; 120 result = STATE_WARNING;
@@ -121,16 +124,21 @@ int main(int argc, char **argv) {
121 /* extract ';; flags: ...' from stdout (first occurrence) */ 124 /* extract ';; flags: ...' from stdout (first occurrence) */
122 for (size_t i = 0; i < chld_out.lines; i++) { 125 for (size_t i = 0; i < chld_out.lines; i++) {
123 if (strstr(chld_out.line[i], "flags:")) { 126 if (strstr(chld_out.line[i], "flags:")) {
124 if (verbose) printf("Raw flags line: %s\n", chld_out.line[i]); 127 if (verbose) {
125 if (parse_flags_line(chld_out.line[i], &dig_flags, &dig_flags_cnt)) { 128 printf("Raw flags line: %s\n", chld_out.line[i]);
126 if (verbose) { 129 }
127 printf(_("Parsed flags:")); 130
128 for (size_t k = 0; k < dig_flags_cnt; k++) printf(" %s", dig_flags[k]); 131 dig_flags = parse_flags_line(chld_out.line[i]);
129 printf("\n"); 132
130 } 133 if (verbose && dig_flags.count > 0) {
131 } 134 printf(_("Parsed flags:"));
132 break; 135 for (size_t k = 0; k < dig_flags.count; k++) {
133 } 136 printf(" %s", dig_flags.items[k]);
137 }
138 printf("\n");
139 }
140 break;
141 }
134 } 142 }
135 143
136 for (size_t i = 0; i < chld_out.lines; i++) { 144 for (size_t i = 0; i < chld_out.lines; i++) {
@@ -200,47 +208,55 @@ int main(int argc, char **argv) {
200 } 208 }
201 209
202 /* Optional: evaluate dig flags only if -E/-X were provided */ 210 /* Optional: evaluate dig flags only if -E/-X were provided */
203 if ((config.require_flags && *config.require_flags) || (config.forbid_flags && *config.forbid_flags)) { 211 if ((config.require_flags && *config.require_flags) ||
204 if (dig_flags_cnt > 0) { 212 (config.forbid_flags && *config.forbid_flags)) {
213
214 if (dig_flags.count > 0) {
215
205 if (config.require_flags && *config.require_flags) { 216 if (config.require_flags && *config.require_flags) {
206 char **req = NULL; size_t reqn = 0; 217 flag_list req = split_csv_trim(config.require_flags);
207 split_csv_trim(config.require_flags, &req, &reqn); 218
208 for (size_t r = 0; r < reqn; r++) { 219 for (size_t r = 0; r < req.count; r++) {
209 if (!list_contains(dig_flags, dig_flags_cnt, req[r])) { 220 if (!flag_list_contains(&dig_flags, req.items[r])) {
210 result = STATE_CRITICAL; 221 result = STATE_CRITICAL;
211 if (!msg) { 222 if (!msg) {
212 xasprintf(&msg, _("Missing required DNS flag: %s"), req[r]); 223 xasprintf(&msg, _("Missing required DNS flag: %s"), req.items[r]);
213 } else { 224 } else {
214 char *newmsg = NULL; 225 char *newmsg = NULL;
215 xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg, req[r]); 226 xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg,
227 req.items[r]);
216 msg = newmsg; 228 msg = newmsg;
217 } 229 }
218 } 230 }
219 } 231 }
220 free_flags(req, reqn); 232
233 free_flag_list(&req);
221 } 234 }
235
222 if (config.forbid_flags && *config.forbid_flags) { 236 if (config.forbid_flags && *config.forbid_flags) {
223 char **bad = NULL; size_t badn = 0; 237 flag_list bad = split_csv_trim(config.forbid_flags);
224 split_csv_trim(config.forbid_flags, &bad, &badn); 238
225 for (size_t r = 0; r < badn; r++) { 239 for (size_t r = 0; r < bad.count; r++) {
226 if (list_contains(dig_flags, dig_flags_cnt, bad[r])) { 240 if (flag_list_contains(&dig_flags, bad.items[r])) {
227 result = STATE_CRITICAL; 241 result = STATE_CRITICAL;
228 if (!msg) { 242 if (!msg) {
229 xasprintf(&msg, _("Forbidden DNS flag present: %s"), bad[r]); 243 xasprintf(&msg, _("Forbidden DNS flag present: %s"), bad.items[r]);
230 } else { 244 } else {
231 char *newmsg = NULL; 245 char *newmsg = NULL;
232 xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg, bad[r]); 246 xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg,
247 bad.items[r]);
233 msg = newmsg; 248 msg = newmsg;
234 } 249 }
235 } 250 }
236 } 251 }
237 free_flags(bad, badn); 252
253 free_flag_list(&bad);
238 } 254 }
239 } 255 }
240 } 256 }
241 257
242 /* cleanup flags buffer */ 258 /* cleanup flags buffer */
243 free_flags(dig_flags, dig_flags_cnt); 259 free_flag_list(&dig_flags);
244 260
245 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, 261 printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time,
246 msg ? msg : _("Probably a non-existent host/domain"), 262 msg ? msg : _("Probably a non-existent host/domain"),
@@ -282,7 +298,8 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) {
282 298
283 int option = 0; 299 int option = 0;
284 while (true) { 300 while (true) {
285 int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option); 301 int option_index =
302 getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option);
286 303
287 if (option_index == -1 || option_index == EOF) { 304 if (option_index == -1 || option_index == EOF) {
288 break; 305 break;
@@ -444,69 +461,180 @@ void print_usage(void) {
444 461
445/* helpers */ 462/* helpers */
446 463
447static bool parse_flags_line(const char *line, char ***out_flags, size_t *out_count) { 464/**
448 if (!line || !out_flags || !out_count) return false; 465 * parse_flags_line - Parse a dig output line and extract DNS header flags.
449 *out_flags = NULL; *out_count = 0; 466 *
467 * Input:
468 * line - NUL terminated dig output line, e.g. ";; flags: qr rd ra; ..."
469 *
470 * Returns:
471 * flag_list where:
472 * - items: array of NUL terminated flag strings (heap allocated)
473 * - count: number of entries in items
474 * On parse failure or if no flags were found, count is 0 and items is NULL.
475 */
476static flag_list parse_flags_line(const char *line) {
477 flag_list result = {.items = NULL, .count = 0};
478
479 if (!line) {
480 return result;
481 }
450 482
483 /* Locate start of DNS header flags in dig output */
451 const char *p = strstr(line, "flags:"); 484 const char *p = strstr(line, "flags:");
452 if (!p) return false; 485 if (!p) {
453 p += 6; 486 return result;
487 }
488 p += 6; /* skip literal "flags:" */
489
490 /* Skip whitespace after "flags:" */
491 while (*p && isspace((unsigned char)*p)) {
492 p++;
493 }
454 494
455 while (*p && isspace((unsigned char)*p)) p++; 495 /* Flags are terminated by the next semicolon e.g. "qr rd ra;" */
456 const char *q = strchr(p, ';'); 496 const char *q = strchr(p, ';');
457 if (!q) return false; 497 if (!q) {
498 return result;
499 }
458 500
501 /* Extract substring containing the flag block */
459 size_t len = (size_t)(q - p); 502 size_t len = (size_t)(q - p);
460 if (len == 0) return false; 503 if (len == 0) {
504 return result;
505 }
461 506
462 char *buf = (char*)malloc(len + 1); 507 char *buf = (char *)malloc(len + 1);
463 if (!buf) return false; 508 if (!buf) {
464 memcpy(buf, p, len); buf[len] = '\0'; 509 return result;
510 }
511 memcpy(buf, p, len);
512 buf[len] = '\0';
465 513
466 char **arr = NULL; size_t cnt = 0; 514 /* Tokenize flags separated by whitespace */
515 char **arr = NULL;
516 size_t cnt = 0;
467 char *saveptr = NULL; 517 char *saveptr = NULL;
468 char *tok = strtok_r(buf, " \t", &saveptr); 518 char *tok = strtok_r(buf, " \t", &saveptr);
519
469 while (tok) { 520 while (tok) {
470 arr = (char**)realloc(arr, (cnt + 1) * sizeof(char*)); 521 /* Expand array for the next flag token */
522 char **tmp = (char **)realloc(arr, (cnt + 1) * sizeof(char *));
523 if (!tmp) {
524 /* On allocation failure keep what we have and return it */
525 break;
526 }
527 arr = tmp;
471 arr[cnt++] = strdup(tok); 528 arr[cnt++] = strdup(tok);
472 tok = strtok_r(NULL, " \t", &saveptr); 529 tok = strtok_r(NULL, " \t", &saveptr);
473 } 530 }
474 free(buf);
475 531
476 *out_flags = arr; 532 free(buf);
477 *out_count = cnt;
478 return (cnt > 0);
479}
480 533
481static void free_flags(char **flags, size_t count) { 534 result.items = arr;
482 if (!flags) return; 535 result.count = cnt;
483 for (size_t i = 0; i < count; i++) free(flags[i]); 536 return result;
484 free(flags);
485} 537}
486 538
487static bool list_contains(char **flags, size_t count, const char *needle) { 539/**
488 if (!needle || !*needle) return false; 540 * split_csv_trim - Split a comma separated string into trimmed tokens.
489 for (size_t i = 0; i < count; i++) { 541 *
490 if (strcasecmp(flags[i], needle) == 0) return true; 542 * Input:
543 * csv - NUL terminated string, e.g. "aa, qr , rd"
544 *
545 * Returns:
546 * flag_list where:
547 * - items: array of NUL terminated tokens (heap allocated, whitespace trimmed)
548 * - count: number of tokens
549 * On empty input, count is 0 and items is NULL
550 */
551static flag_list split_csv_trim(const char *csv) {
552 flag_list result = {.items = NULL, .count = 0};
553
554 if (!csv || !*csv) {
555 return result;
491 } 556 }
492 return false;
493}
494
495static void split_csv_trim(const char *csv, char ***out_items, size_t *out_count) {
496 *out_items = NULL; *out_count = 0;
497 if (!csv || !*csv) return;
498 557
499 char *tmp = strdup(csv); 558 char *tmp = strdup(csv);
559 if (!tmp) {
560 return result;
561 }
562
500 char *s = tmp; 563 char *s = tmp;
501 char *token = NULL; 564 char *token = NULL;
565
566 /* Split CSV by commas, trimming whitespace on each token */
502 while ((token = strsep(&s, ",")) != NULL) { 567 while ((token = strsep(&s, ",")) != NULL) {
503 while (*token && isspace((unsigned char)*token)) token++; 568 /* trim leading whitespace */
569 while (*token && isspace((unsigned char)*token)) {
570 token++;
571 }
572
573 /* trim trailing whitespace */
504 char *end = token + strlen(token); 574 char *end = token + strlen(token);
505 while (end > token && isspace((unsigned char)end[-1])) *--end = '\0'; 575 while (end > token && isspace((unsigned char)end[-1])) {
576 *--end = '\0';
577 }
578
506 if (*token) { 579 if (*token) {
507 *out_items = (char**)realloc(*out_items, (*out_count + 1) * sizeof(char*)); 580 /* Expand the items array and append the token */
508 (*out_items)[(*out_count)++] = strdup(token); 581 char **arr = (char **)realloc(result.items, (result.count + 1) * sizeof(char *));
582 if (!arr) {
583 /* Allocation failed, stop and return what we have */
584 break;
585 }
586 result.items = arr;
587 result.items[result.count++] = strdup(token);
509 } 588 }
510 } 589 }
590
511 free(tmp); 591 free(tmp);
592 return result;
593}
594
595/**
596 * flag_list_contains - Case-insensitive membership test in a flag_list.
597 *
598 * Input:
599 * list - pointer to a flag_list
600 * needle - NUL terminated string to search for
601 *
602 * Returns:
603 * true if needle is contained in list (strcasecmp)
604 * false otherwise
605 */
606static bool flag_list_contains(const flag_list *list, const char *needle) {
607 if (!list || !needle || !*needle) {
608 return false;
609 }
610
611 for (size_t i = 0; i < list->count; i++) {
612 if (strcasecmp(list->items[i], needle) == 0) {
613 return true;
614 }
615 }
616 return false;
617}
618
619/**
620 * free_flag_list - Release all heap allocations held by a flag_list.
621 *
622 * Input:
623 * list - pointer to a flag_list whose items were allocated by
624 * parse_flags_line() or split_csv_trim().
625 *
626 * After this call list->items is NULL and list->count is 0.
627 */
628static void free_flag_list(flag_list *list) {
629 if (!list || !list->items) {
630 return;
631 }
632
633 for (size_t i = 0; i < list->count; i++) {
634 free(list->items[i]);
635 }
636 free(list->items);
637
638 list->items = NULL;
639 list->count = 0;
512} 640}