diff options
| author | Dennis Ullrich <dennis.ullrich@plusserver.com> | 2025-10-07 14:33:55 +0200 |
|---|---|---|
| committer | Dennis Ullrich <dennis.ullrich@plusserver.com> | 2025-10-07 14:34:50 +0200 |
| commit | 978343ed03d3327597fa3317901f1c1741a7e3c4 (patch) | |
| tree | f4ba9e7f2a4c2c2bfecf2f884b8ef2884bebd828 | |
| parent | ce10a6ff37119348503086686971eba1190a90a5 (diff) | |
| download | monitoring-plugins-978343ed.tar.gz | |
check_dig: Add feature to require or forbid dig DNS flags -E, -X.
Introduced helper functions for flag parsing.
-E, --require-flags=LIST
Comma-separated dig flags that must be present (e.g. 'aa,qr')
-X, --forbid-flags=LIST
Comma-separated dig flags that must NOT be present
| -rw-r--r-- | plugins/check_dig.c | 153 | ||||
| -rw-r--r-- | plugins/check_dig.d/config.h | 4 |
2 files changed, 155 insertions, 2 deletions
diff --git a/plugins/check_dig.c b/plugins/check_dig.c index c27e5f13..b3f4c878 100644 --- a/plugins/check_dig.c +++ b/plugins/check_dig.c | |||
| @@ -36,6 +36,7 @@ const char *progname = "check_dig"; | |||
| 36 | const char *copyright = "2002-2024"; | 36 | const char *copyright = "2002-2024"; |
| 37 | const char *email = "devel@monitoring-plugins.org"; | 37 | const 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 | ||
| 57 | static int verbose = 0; | 58 | static int verbose = 0; |
| 58 | 59 | ||
| 60 | /* helpers for flag parsing */ | ||
| 61 | static bool parse_flags_line(const char *line, char ***out_flags, size_t *out_count); | ||
| 62 | static void free_flags(char **flags, size_t count); | ||
| 63 | static bool list_contains(char **flags, size_t count, const char *needle); | ||
| 64 | static void split_csv_trim(const char *csv, char ***out_items, size_t *out_count); | ||
| 65 | |||
| 59 | int main(int argc, char **argv) { | 66 | int main(int argc, char **argv) { |
| 60 | setlocale(LC_ALL, ""); | 67 | setlocale(LC_ALL, ""); |
| 61 | bindtextdomain(PACKAGE, LOCALEDIR); | 68 | bindtextdomain(PACKAGE, LOCALEDIR); |
| @@ -101,6 +108,9 @@ 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 | char **dig_flags = NULL; | ||
| 112 | size_t dig_flags_cnt = 0; | ||
| 113 | |||
| 104 | mp_state_enum result = STATE_UNKNOWN; | 114 | mp_state_enum result = STATE_UNKNOWN; |
| 105 | /* run the command */ | 115 | /* run the command */ |
| 106 | if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { | 116 | if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { |
| @@ -108,6 +118,21 @@ int main(int argc, char **argv) { | |||
| 108 | msg = (char *)_("dig returned an error status"); | 118 | msg = (char *)_("dig returned an error status"); |
| 109 | } | 119 | } |
| 110 | 120 | ||
| 121 | /* extract ';; flags: ...' from stdout (first occurrence) */ | ||
| 122 | for (size_t i = 0; i < chld_out.lines; i++) { | ||
| 123 | if (strstr(chld_out.line[i], "flags:")) { | ||
| 124 | if (verbose) printf("Raw flags line: %s\n", chld_out.line[i]); | ||
| 125 | if (parse_flags_line(chld_out.line[i], &dig_flags, &dig_flags_cnt)) { | ||
| 126 | if (verbose) { | ||
| 127 | printf(_("Parsed flags:")); | ||
| 128 | for (size_t k = 0; k < dig_flags_cnt; k++) printf(" %s", dig_flags[k]); | ||
| 129 | printf("\n"); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 111 | for (size_t i = 0; i < chld_out.lines; i++) { | 136 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 112 | /* the server is responding, we just got the host name... */ | 137 | /* the server is responding, we just got the host name... */ |
| 113 | if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { | 138 | if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { |
| @@ -174,6 +199,49 @@ int main(int argc, char **argv) { | |||
| 174 | result = STATE_WARNING; | 199 | result = STATE_WARNING; |
| 175 | } | 200 | } |
| 176 | 201 | ||
| 202 | /* Optional: evaluate dig flags only if -E/-X were provided */ | ||
| 203 | if ((config.require_flags && *config.require_flags) || (config.forbid_flags && *config.forbid_flags)) { | ||
| 204 | if (dig_flags_cnt > 0) { | ||
| 205 | if (config.require_flags && *config.require_flags) { | ||
| 206 | char **req = NULL; size_t reqn = 0; | ||
| 207 | split_csv_trim(config.require_flags, &req, &reqn); | ||
| 208 | for (size_t r = 0; r < reqn; r++) { | ||
| 209 | if (!list_contains(dig_flags, dig_flags_cnt, req[r])) { | ||
| 210 | result = STATE_CRITICAL; | ||
| 211 | if (!msg) { | ||
| 212 | xasprintf(&msg, _("Missing required DNS flag: %s"), req[r]); | ||
| 213 | } else { | ||
| 214 | char *newmsg = NULL; | ||
| 215 | xasprintf(&newmsg, _("%s; missing required DNS flag: %s"), msg, req[r]); | ||
| 216 | msg = newmsg; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | free_flags(req, reqn); | ||
| 221 | } | ||
| 222 | if (config.forbid_flags && *config.forbid_flags) { | ||
| 223 | char **bad = NULL; size_t badn = 0; | ||
| 224 | split_csv_trim(config.forbid_flags, &bad, &badn); | ||
| 225 | for (size_t r = 0; r < badn; r++) { | ||
| 226 | if (list_contains(dig_flags, dig_flags_cnt, bad[r])) { | ||
| 227 | result = STATE_CRITICAL; | ||
| 228 | if (!msg) { | ||
| 229 | xasprintf(&msg, _("Forbidden DNS flag present: %s"), bad[r]); | ||
| 230 | } else { | ||
| 231 | char *newmsg = NULL; | ||
| 232 | xasprintf(&newmsg, _("%s; forbidden DNS flag present: %s"), msg, bad[r]); | ||
| 233 | msg = newmsg; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | free_flags(bad, badn); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | /* cleanup flags buffer */ | ||
| 243 | free_flags(dig_flags, dig_flags_cnt); | ||
| 244 | |||
| 177 | printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, | 245 | printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, |
| 178 | msg ? msg : _("Probably a non-existent host/domain"), | 246 | msg ? msg : _("Probably a non-existent host/domain"), |
| 179 | fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), | 247 | fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), |
| @@ -190,6 +258,8 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) { | |||
| 190 | {"critical", required_argument, 0, 'c'}, | 258 | {"critical", required_argument, 0, 'c'}, |
| 191 | {"timeout", required_argument, 0, 't'}, | 259 | {"timeout", required_argument, 0, 't'}, |
| 192 | {"dig-arguments", required_argument, 0, 'A'}, | 260 | {"dig-arguments", required_argument, 0, 'A'}, |
| 261 | {"require-flags", required_argument, 0, 'E'}, | ||
| 262 | {"forbid-flags", required_argument, 0, 'X'}, | ||
| 193 | {"verbose", no_argument, 0, 'v'}, | 263 | {"verbose", no_argument, 0, 'v'}, |
| 194 | {"version", no_argument, 0, 'V'}, | 264 | {"version", no_argument, 0, 'V'}, |
| 195 | {"help", no_argument, 0, 'h'}, | 265 | {"help", no_argument, 0, 'h'}, |
| @@ -212,7 +282,7 @@ check_dig_config_wrapper process_arguments(int argc, char **argv) { | |||
| 212 | 282 | ||
| 213 | int option = 0; | 283 | int option = 0; |
| 214 | while (true) { | 284 | while (true) { |
| 215 | int option_index = getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); | 285 | int option_index = 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 = strdup(optarg); | ||
| 338 | break; | ||
| 339 | case 'X': /* forbid flags */ | ||
| 340 | result.config.forbid_flags = strdup(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,74 @@ 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 | static bool parse_flags_line(const char *line, char ***out_flags, size_t *out_count) { | ||
| 448 | if (!line || !out_flags || !out_count) return false; | ||
| 449 | *out_flags = NULL; *out_count = 0; | ||
| 450 | |||
| 451 | const char *p = strstr(line, "flags:"); | ||
| 452 | if (!p) return false; | ||
| 453 | p += 6; | ||
| 454 | |||
| 455 | while (*p && isspace((unsigned char)*p)) p++; | ||
| 456 | const char *q = strchr(p, ';'); | ||
| 457 | if (!q) return false; | ||
| 458 | |||
| 459 | size_t len = (size_t)(q - p); | ||
| 460 | if (len == 0) return false; | ||
| 461 | |||
| 462 | char *buf = (char*)malloc(len + 1); | ||
| 463 | if (!buf) return false; | ||
| 464 | memcpy(buf, p, len); buf[len] = '\0'; | ||
| 465 | |||
| 466 | char **arr = NULL; size_t cnt = 0; | ||
| 467 | char *saveptr = NULL; | ||
| 468 | char *tok = strtok_r(buf, " \t", &saveptr); | ||
| 469 | while (tok) { | ||
| 470 | arr = (char**)realloc(arr, (cnt + 1) * sizeof(char*)); | ||
| 471 | arr[cnt++] = strdup(tok); | ||
| 472 | tok = strtok_r(NULL, " \t", &saveptr); | ||
| 473 | } | ||
| 474 | free(buf); | ||
| 475 | |||
| 476 | *out_flags = arr; | ||
| 477 | *out_count = cnt; | ||
| 478 | return (cnt > 0); | ||
| 479 | } | ||
| 480 | |||
| 481 | static void free_flags(char **flags, size_t count) { | ||
| 482 | if (!flags) return; | ||
| 483 | for (size_t i = 0; i < count; i++) free(flags[i]); | ||
| 484 | free(flags); | ||
| 485 | } | ||
| 486 | |||
| 487 | static bool list_contains(char **flags, size_t count, const char *needle) { | ||
| 488 | if (!needle || !*needle) return false; | ||
| 489 | for (size_t i = 0; i < count; i++) { | ||
| 490 | if (strcasecmp(flags[i], needle) == 0) return true; | ||
| 491 | } | ||
| 492 | return false; | ||
| 493 | } | ||
| 494 | |||
| 495 | static 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 | |||
| 499 | char *tmp = strdup(csv); | ||
| 500 | char *s = tmp; | ||
| 501 | char *token = NULL; | ||
| 502 | while ((token = strsep(&s, ",")) != NULL) { | ||
| 503 | while (*token && isspace((unsigned char)*token)) token++; | ||
| 504 | char *end = token + strlen(token); | ||
| 505 | while (end > token && isspace((unsigned char)end[-1])) *--end = '\0'; | ||
| 506 | if (*token) { | ||
| 507 | *out_items = (char**)realloc(*out_items, (*out_count + 1) * sizeof(char*)); | ||
| 508 | (*out_items)[(*out_count)++] = strdup(token); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | free(tmp); | ||
| 363 | } | 512 | } |
diff --git a/plugins/check_dig.d/config.h b/plugins/check_dig.d/config.h index a570b633..392848e5 100644 --- a/plugins/check_dig.d/config.h +++ b/plugins/check_dig.d/config.h | |||
| @@ -19,6 +19,8 @@ typedef struct { | |||
| 19 | 19 | ||
| 20 | double warning_interval; | 20 | double warning_interval; |
| 21 | double critical_interval; | 21 | double critical_interval; |
| 22 | char *require_flags; | ||
| 23 | char *forbid_flags; | ||
| 22 | } check_dig_config; | 24 | } check_dig_config; |
| 23 | 25 | ||
| 24 | check_dig_config check_dig_config_init() { | 26 | check_dig_config check_dig_config_init() { |
| @@ -34,6 +36,8 @@ check_dig_config check_dig_config_init() { | |||
| 34 | 36 | ||
| 35 | .warning_interval = UNDEFINED, | 37 | .warning_interval = UNDEFINED, |
| 36 | .critical_interval = UNDEFINED, | 38 | .critical_interval = UNDEFINED, |
| 39 | .require_flags = NULL, | ||
| 40 | .forbid_flags = NULL, | ||
| 37 | 41 | ||
| 38 | }; | 42 | }; |
| 39 | return tmp; | 43 | return tmp; |
