diff options
Diffstat (limited to 'plugins/check_dig.c')
| -rw-r--r-- | plugins/check_dig.c | 919 |
1 files changed, 581 insertions, 338 deletions
diff --git a/plugins/check_dig.c b/plugins/check_dig.c index be7a6101..9ea19e6a 100644 --- a/plugins/check_dig.c +++ b/plugins/check_dig.c | |||
| @@ -1,30 +1,30 @@ | |||
| 1 | /***************************************************************************** | 1 | /***************************************************************************** |
| 2 | * | 2 | * |
| 3 | * Monitoring check_dig plugin | 3 | * Monitoring check_dig plugin |
| 4 | * | 4 | * |
| 5 | * License: GPL | 5 | * License: GPL |
| 6 | * Copyright (c) 2002-2008 Monitoring Plugins Development Team | 6 | * Copyright (c) 2002-2025 Monitoring Plugins Development Team |
| 7 | * | 7 | * |
| 8 | * Description: | 8 | * Description: |
| 9 | * | 9 | * |
| 10 | * This file contains the check_dig plugin | 10 | * This file contains the check_dig plugin |
| 11 | * | 11 | * |
| 12 | * | 12 | * |
| 13 | * This program is free software: you can redistribute it and/or modify | 13 | * This program is free software: you can redistribute it and/or modify |
| 14 | * it under the terms of the GNU General Public License as published by | 14 | * it under the terms of the GNU General Public License as published by |
| 15 | * the Free Software Foundation, either version 3 of the License, or | 15 | * the Free Software Foundation, either version 3 of the License, or |
| 16 | * (at your option) any later version. | 16 | * (at your option) any later version. |
| 17 | * | 17 | * |
| 18 | * This program is distributed in the hope that it will be useful, | 18 | * This program is distributed in the hope that it will be useful, |
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | * GNU General Public License for more details. | 21 | * GNU General Public License for more details. |
| 22 | * | 22 | * |
| 23 | * You should have received a copy of the GNU General Public License | 23 | * You should have received a copy of the GNU General Public License |
| 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 24 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 25 | * | 25 | * |
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | /* Hackers note: | 29 | /* Hackers note: |
| 30 | * There are typecasts to (char *) from _("foo bar") in this file. | 30 | * There are typecasts to (char *) from _("foo bar") in this file. |
| @@ -33,348 +33,591 @@ | |||
| 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 | ||
| 35 | const char *progname = "check_dig"; | 35 | const char *progname = "check_dig"; |
| 36 | const char *copyright = "2002-2008"; | 36 | const char *copyright = "2002-2025"; |
| 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" |
| 42 | #include "runcmd.h" | 43 | #include "runcmd.h" |
| 43 | 44 | ||
| 44 | int process_arguments (int, char **); | 45 | #include "check_dig.d/config.h" |
| 45 | int validate_arguments (void); | 46 | #include "states.h" |
| 46 | void print_help (void); | 47 | |
| 47 | void print_usage (void); | 48 | typedef struct { |
| 48 | 49 | int errorcode; | |
| 49 | #define UNDEFINED 0 | 50 | check_dig_config config; |
| 50 | #define DEFAULT_PORT 53 | 51 | } check_dig_config_wrapper; |
| 51 | #define DEFAULT_TRIES 2 | 52 | static check_dig_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 52 | 53 | static check_dig_config_wrapper validate_arguments(check_dig_config_wrapper /*config_wrapper*/); | |
| 53 | char *query_address = NULL; | 54 | |
| 54 | char *record_type = "A"; | 55 | static void print_help(void); |
| 55 | char *expected_address = NULL; | 56 | void print_usage(void); |
| 56 | char *dns_server = NULL; | 57 | |
| 57 | char *dig_args = ""; | 58 | static int verbose = 0; |
| 58 | char *query_transport = ""; | 59 | |
| 59 | bool verbose = false; | 60 | /* helpers for flag parsing */ |
| 60 | int server_port = DEFAULT_PORT; | 61 | static flag_list parse_flags_line(const char *line); |
| 61 | int number_tries = DEFAULT_TRIES; | 62 | static flag_list split_csv_trim(const char *csv); |
| 62 | double warning_interval = UNDEFINED; | 63 | static bool flag_list_contains(const flag_list *list, const char *needle); |
| 63 | double critical_interval = UNDEFINED; | 64 | static void free_flag_list(flag_list *list); |
| 64 | struct timeval tv; | 65 | |
| 65 | 66 | int main(int argc, char **argv) { | |
| 66 | int | 67 | setlocale(LC_ALL, ""); |
| 67 | main (int argc, char **argv) | 68 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 68 | { | 69 | textdomain(PACKAGE); |
| 69 | char *command_line; | 70 | |
| 70 | output chld_out, chld_err; | 71 | /* Set signal handling and alarm */ |
| 71 | char *msg = NULL; | 72 | if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { |
| 72 | size_t i; | 73 | usage_va(_("Cannot catch SIGALRM")); |
| 73 | char *t; | 74 | } |
| 74 | long microsec; | 75 | |
| 75 | double elapsed_time; | 76 | /* Parse extra opts if any */ |
| 76 | int result = STATE_UNKNOWN; | 77 | argv = np_extra_opts(&argc, argv, progname); |
| 77 | int timeout_interval_dig; | 78 | |
| 78 | 79 | check_dig_config_wrapper tmp_config = process_arguments(argc, argv); | |
| 79 | setlocale (LC_ALL, ""); | 80 | if (tmp_config.errorcode == ERROR) { |
| 80 | bindtextdomain (PACKAGE, LOCALEDIR); | 81 | usage_va(_("Could not parse arguments")); |
| 81 | textdomain (PACKAGE); | 82 | } |
| 82 | 83 | ||
| 83 | /* Set signal handling and alarm */ | 84 | const check_dig_config config = tmp_config.config; |
| 84 | if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) | 85 | |
| 85 | usage_va(_("Cannot catch SIGALRM")); | 86 | /* dig applies the timeout to each try, so we need to work around this */ |
| 86 | 87 | int timeout_interval_dig = ((int)timeout_interval / config.number_tries) + config.number_tries; | |
| 87 | /* Parse extra opts if any */ | 88 | |
| 88 | argv=np_extra_opts (&argc, argv, progname); | 89 | char *command_line; |
| 89 | 90 | /* get the command to run */ | |
| 90 | if (process_arguments (argc, argv) == ERROR) | 91 | xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, |
| 91 | usage_va(_("Could not parse arguments")); | 92 | config.dig_args, config.query_transport, config.server_port, config.dns_server, |
| 92 | 93 | config.query_address, config.record_type, config.number_tries, timeout_interval_dig); | |
| 93 | /* dig applies the timeout to each try, so we need to work around this */ | 94 | |
| 94 | timeout_interval_dig = timeout_interval / number_tries + number_tries; | 95 | alarm(timeout_interval); |
| 95 | 96 | struct timeval start_time; | |
| 96 | /* get the command to run */ | 97 | gettimeofday(&start_time, NULL); |
| 97 | xasprintf (&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", | 98 | |
| 98 | PATH_TO_DIG, dig_args, query_transport, server_port, dns_server, query_address, record_type, number_tries, timeout_interval_dig); | 99 | if (verbose) { |
| 99 | 100 | printf("%s\n", command_line); | |
| 100 | alarm (timeout_interval); | 101 | if (config.expected_address != NULL) { |
| 101 | gettimeofday (&tv, NULL); | 102 | printf(_("Looking for: '%s'\n"), config.expected_address); |
| 102 | 103 | } else { | |
| 103 | if (verbose) { | 104 | printf(_("Looking for: '%s'\n"), config.query_address); |
| 104 | printf ("%s\n", command_line); | 105 | } |
| 105 | if(expected_address != NULL) { | 106 | } |
| 106 | printf (_("Looking for: '%s'\n"), expected_address); | 107 | |
| 107 | } else { | 108 | output chld_out; |
| 108 | printf (_("Looking for: '%s'\n"), query_address); | 109 | output chld_err; |
| 109 | } | 110 | char *msg = NULL; |
| 110 | } | 111 | flag_list dig_flags = {.items = NULL, .count = 0}; |
| 111 | 112 | mp_state_enum result = STATE_UNKNOWN; | |
| 112 | /* run the command */ | 113 | |
| 113 | if(np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { | 114 | /* run the command */ |
| 114 | result = STATE_WARNING; | 115 | if (np_runcmd(command_line, &chld_out, &chld_err, 0) != 0) { |
| 115 | msg = (char *)_("dig returned an error status"); | 116 | result = STATE_WARNING; |
| 116 | } | 117 | msg = (char *)_("dig returned an error status"); |
| 117 | 118 | } | |
| 118 | for(i = 0; i < chld_out.lines; i++) { | 119 | |
| 119 | /* the server is responding, we just got the host name... */ | 120 | /* extract ';; flags: ...' from stdout (first occurrence) */ |
| 120 | if (strstr (chld_out.line[i], ";; ANSWER SECTION:")) { | 121 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 121 | 122 | if (strstr(chld_out.line[i], "flags:")) { | |
| 122 | /* loop through the whole 'ANSWER SECTION' */ | 123 | if (verbose) { |
| 123 | for(; i < chld_out.lines; i++) { | 124 | printf("Raw flags line: %s\n", chld_out.line[i]); |
| 124 | /* get the host address */ | 125 | } |
| 125 | if (verbose) | 126 | |
| 126 | printf ("%s\n", chld_out.line[i]); | 127 | dig_flags = parse_flags_line(chld_out.line[i]); |
| 127 | 128 | ||
| 128 | if (strcasestr (chld_out.line[i], (expected_address == NULL ? query_address : expected_address)) != NULL) { | 129 | if (verbose && dig_flags.count > 0) { |
| 129 | msg = chld_out.line[i]; | 130 | printf(_("Parsed flags:")); |
| 130 | result = STATE_OK; | 131 | for (size_t k = 0; k < dig_flags.count; k++) { |
| 131 | 132 | printf(" %s", dig_flags.items[k]); | |
| 132 | /* Translate output TAB -> SPACE */ | 133 | } |
| 133 | t = msg; | 134 | printf("\n"); |
| 134 | while ((t = strchr(t, '\t')) != NULL) *t = ' '; | 135 | } |
| 135 | break; | 136 | break; |
| 136 | } | 137 | } |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 139 | if (result == STATE_UNKNOWN) { | 140 | for (size_t i = 0; i < chld_out.lines; i++) { |
| 140 | msg = (char *)_("Server not found in ANSWER SECTION"); | 141 | /* the server is responding, we just got the host name... */ |
| 141 | result = STATE_WARNING; | 142 | if (strstr(chld_out.line[i], ";; ANSWER SECTION:")) { |
| 142 | } | 143 | |
| 143 | 144 | /* loop through the whole 'ANSWER SECTION' */ | |
| 144 | /* we found the answer section, so break out of the loop */ | 145 | for (; i < chld_out.lines; i++) { |
| 145 | break; | 146 | /* get the host address */ |
| 146 | } | 147 | if (verbose) { |
| 147 | } | 148 | printf("%s\n", chld_out.line[i]); |
| 148 | 149 | } | |
| 149 | if (result == STATE_UNKNOWN) { | 150 | |
| 150 | msg = (char *)_("No ANSWER SECTION found"); | 151 | if (strcasestr(chld_out.line[i], (config.expected_address == NULL |
| 151 | result = STATE_CRITICAL; | 152 | ? config.query_address |
| 152 | } | 153 | : config.expected_address)) != NULL) { |
| 153 | 154 | msg = chld_out.line[i]; | |
| 154 | /* If we get anything on STDERR, at least set warning */ | 155 | result = STATE_OK; |
| 155 | if(chld_err.buflen > 0) { | 156 | |
| 156 | result = max_state(result, STATE_WARNING); | 157 | /* Translate output TAB -> SPACE */ |
| 157 | if(!msg) for(i = 0; i < chld_err.lines; i++) { | 158 | char *temp = msg; |
| 158 | msg = strchr(chld_err.line[0], ':'); | 159 | while ((temp = strchr(temp, '\t')) != NULL) { |
| 159 | if(msg) { | 160 | *temp = ' '; |
| 160 | msg++; | 161 | } |
| 161 | break; | 162 | break; |
| 162 | } | 163 | } |
| 163 | } | 164 | } |
| 164 | } | 165 | |
| 165 | 166 | if (result == STATE_UNKNOWN) { | |
| 166 | microsec = deltime (tv); | 167 | msg = (char *)_("Server not found in ANSWER SECTION"); |
| 167 | elapsed_time = (double)microsec / 1.0e6; | 168 | result = STATE_WARNING; |
| 168 | 169 | } | |
| 169 | if (critical_interval > UNDEFINED && elapsed_time > critical_interval) | 170 | |
| 170 | result = STATE_CRITICAL; | 171 | /* we found the answer section, so break out of the loop */ |
| 171 | 172 | break; | |
| 172 | else if (warning_interval > UNDEFINED && elapsed_time > warning_interval) | 173 | } |
| 173 | result = STATE_WARNING; | 174 | } |
| 174 | 175 | ||
| 175 | printf ("DNS %s - %.3f seconds response time (%s)|%s\n", | 176 | if (result == STATE_UNKNOWN) { |
| 176 | state_text (result), elapsed_time, | 177 | msg = (char *)_("No ANSWER SECTION found"); |
| 177 | msg ? msg : _("Probably a non-existent host/domain"), | 178 | result = STATE_CRITICAL; |
| 178 | fperfdata("time", elapsed_time, "s", | 179 | } |
| 179 | (warning_interval>UNDEFINED ? true:false), | 180 | |
| 180 | warning_interval, | 181 | /* If we get anything on STDERR, at least set warning */ |
| 181 | (critical_interval>UNDEFINED ? true:false), | 182 | if (chld_err.buflen > 0) { |
| 182 | critical_interval, | 183 | result = max_state(result, STATE_WARNING); |
| 183 | true, 0, false, 0)); | 184 | if (!msg) { |
| 184 | return result; | 185 | for (size_t i = 0; i < chld_err.lines; i++) { |
| 186 | msg = strchr(chld_err.line[0], ':'); | ||
| 187 | if (msg) { | ||
| 188 | msg++; | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | long microsec = deltime(start_time); | ||
| 196 | double elapsed_time = (double)microsec / 1.0e6; | ||
| 197 | |||
| 198 | if (config.critical_interval > UNDEFINED && elapsed_time > config.critical_interval) { | ||
| 199 | result = STATE_CRITICAL; | ||
| 200 | } | ||
| 201 | |||
| 202 | else if (config.warning_interval > UNDEFINED && elapsed_time > config.warning_interval) { | ||
| 203 | result = STATE_WARNING; | ||
| 204 | } | ||
| 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 | |||
| 244 | printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, | ||
| 245 | msg ? msg : _("Probably a non-existent host/domain"), | ||
| 246 | fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), | ||
| 247 | config.warning_interval, (config.critical_interval > UNDEFINED), | ||
| 248 | config.critical_interval, true, 0, false, 0)); | ||
| 249 | exit(result); | ||
| 185 | } | 250 | } |
| 186 | 251 | ||
| 187 | |||
| 188 | |||
| 189 | /* process command-line arguments */ | 252 | /* process command-line arguments */ |
| 190 | int | 253 | check_dig_config_wrapper process_arguments(int argc, char **argv) { |
| 191 | process_arguments (int argc, char **argv) | 254 | static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, |
| 192 | { | 255 | {"query_address", required_argument, 0, 'l'}, |
| 193 | int c; | 256 | {"warning", required_argument, 0, 'w'}, |
| 194 | 257 | {"critical", required_argument, 0, 'c'}, | |
| 195 | int option = 0; | 258 | {"timeout", required_argument, 0, 't'}, |
| 196 | static struct option longopts[] = { | 259 | {"dig-arguments", required_argument, 0, 'A'}, |
| 197 | {"hostname", required_argument, 0, 'H'}, | 260 | {"require-flags", required_argument, 0, 'E'}, |
| 198 | {"query_address", required_argument, 0, 'l'}, | 261 | {"forbid-flags", required_argument, 0, 'X'}, |
| 199 | {"warning", required_argument, 0, 'w'}, | 262 | {"verbose", no_argument, 0, 'v'}, |
| 200 | {"critical", required_argument, 0, 'c'}, | 263 | {"version", no_argument, 0, 'V'}, |
| 201 | {"timeout", required_argument, 0, 't'}, | 264 | {"help", no_argument, 0, 'h'}, |
| 202 | {"dig-arguments", required_argument, 0, 'A'}, | 265 | {"record_type", required_argument, 0, 'T'}, |
| 203 | {"verbose", no_argument, 0, 'v'}, | 266 | {"expected_address", required_argument, 0, 'a'}, |
| 204 | {"version", no_argument, 0, 'V'}, | 267 | {"port", required_argument, 0, 'p'}, |
| 205 | {"help", no_argument, 0, 'h'}, | 268 | {"use-ipv4", no_argument, 0, '4'}, |
| 206 | {"record_type", required_argument, 0, 'T'}, | 269 | {"use-ipv6", no_argument, 0, '6'}, |
| 207 | {"expected_address", required_argument, 0, 'a'}, | 270 | {0, 0, 0, 0}}; |
| 208 | {"port", required_argument, 0, 'p'}, | 271 | |
| 209 | {"use-ipv4", no_argument, 0, '4'}, | 272 | check_dig_config_wrapper result = { |
| 210 | {"use-ipv6", no_argument, 0, '6'}, | 273 | .errorcode = OK, |
| 211 | {0, 0, 0, 0} | 274 | .config = check_dig_config_init(), |
| 212 | }; | 275 | }; |
| 213 | 276 | ||
| 214 | if (argc < 2) | 277 | if (argc < 2) { |
| 215 | return ERROR; | 278 | result.errorcode = ERROR; |
| 216 | 279 | return result; | |
| 217 | while (1) { | 280 | } |
| 218 | c = getopt_long (argc, argv, "hVvt:l:H:w:c:T:p:a:A:46", longopts, &option); | 281 | |
| 219 | 282 | int option = 0; | |
| 220 | if (c == -1 || c == EOF) | 283 | while (true) { |
| 221 | break; | 284 | int option_index = |
| 222 | 285 | getopt_long(argc, argv, "hVvt:l:H:w:c:T:p:a:A:E:X:46", longopts, &option); | |
| 223 | switch (c) { | 286 | |
| 224 | case 'h': /* help */ | 287 | if (option_index == -1 || option_index == EOF) { |
| 225 | print_help (); | 288 | break; |
| 226 | exit (STATE_UNKNOWN); | 289 | } |
| 227 | case 'V': /* version */ | 290 | |
| 228 | print_revision (progname, NP_VERSION); | 291 | switch (option_index) { |
| 229 | exit (STATE_UNKNOWN); | 292 | case 'h': /* help */ |
| 230 | case 'H': /* hostname */ | 293 | print_help(); |
| 231 | host_or_die(optarg); | 294 | exit(STATE_UNKNOWN); |
| 232 | dns_server = optarg; | 295 | case 'V': /* version */ |
| 233 | break; | 296 | print_revision(progname, NP_VERSION); |
| 234 | case 'p': /* server port */ | 297 | exit(STATE_UNKNOWN); |
| 235 | if (is_intpos (optarg)) { | 298 | case 'H': /* hostname */ |
| 236 | server_port = atoi (optarg); | 299 | host_or_die(optarg); |
| 237 | } | 300 | result.config.dns_server = optarg; |
| 238 | else { | 301 | break; |
| 239 | usage_va(_("Port must be a positive integer - %s"), optarg); | 302 | case 'p': /* server port */ |
| 240 | } | 303 | if (is_intpos(optarg)) { |
| 241 | break; | 304 | result.config.server_port = atoi(optarg); |
| 242 | case 'l': /* address to lookup */ | 305 | } else { |
| 243 | query_address = optarg; | 306 | usage_va(_("Port must be a positive integer - %s"), optarg); |
| 244 | break; | 307 | } |
| 245 | case 'w': /* warning */ | 308 | break; |
| 246 | if (is_nonnegative (optarg)) { | 309 | case 'l': /* address to lookup */ |
| 247 | warning_interval = strtod (optarg, NULL); | 310 | result.config.query_address = optarg; |
| 248 | } | 311 | break; |
| 249 | else { | 312 | case 'w': /* warning */ |
| 250 | usage_va(_("Warning interval must be a positive integer - %s"), optarg); | 313 | if (is_nonnegative(optarg)) { |
| 251 | } | 314 | result.config.warning_interval = strtod(optarg, NULL); |
| 252 | break; | 315 | } else { |
| 253 | case 'c': /* critical */ | 316 | usage_va(_("Warning interval must be a positive integer - %s"), optarg); |
| 254 | if (is_nonnegative (optarg)) { | 317 | } |
| 255 | critical_interval = strtod (optarg, NULL); | 318 | break; |
| 256 | } | 319 | case 'c': /* critical */ |
| 257 | else { | 320 | if (is_nonnegative(optarg)) { |
| 258 | usage_va(_("Critical interval must be a positive integer - %s"), optarg); | 321 | result.config.critical_interval = strtod(optarg, NULL); |
| 259 | } | 322 | } else { |
| 260 | break; | 323 | usage_va(_("Critical interval must be a positive integer - %s"), optarg); |
| 261 | case 't': /* timeout */ | 324 | } |
| 262 | if (is_intnonneg (optarg)) { | 325 | break; |
| 263 | timeout_interval = atoi (optarg); | 326 | case 't': /* timeout */ |
| 264 | } | 327 | if (is_intnonneg(optarg)) { |
| 265 | else { | 328 | timeout_interval = atoi(optarg); |
| 266 | usage_va(_("Timeout interval must be a positive integer - %s"), optarg); | 329 | } else { |
| 267 | } | 330 | usage_va(_("Timeout interval must be a positive integer - %s"), optarg); |
| 268 | break; | 331 | } |
| 269 | case 'A': /* dig arguments */ | 332 | break; |
| 270 | dig_args = strdup(optarg); | 333 | case 'A': /* dig arguments */ |
| 271 | break; | 334 | result.config.dig_args = strdup(optarg); |
| 272 | case 'v': /* verbose */ | 335 | break; |
| 273 | verbose = true; | 336 | case 'E': /* require flags */ |
| 274 | break; | 337 | result.config.require_flags = split_csv_trim(optarg); |
| 275 | case 'T': | 338 | break; |
| 276 | record_type = optarg; | 339 | case 'X': /* forbid flags */ |
| 277 | break; | 340 | result.config.forbid_flags = split_csv_trim(optarg); |
| 278 | case 'a': | 341 | break; |
| 279 | expected_address = optarg; | 342 | case 'v': /* verbose */ |
| 280 | break; | 343 | verbose++; |
| 281 | case '4': | 344 | break; |
| 282 | query_transport = "-4"; | 345 | case 'T': |
| 283 | break; | 346 | result.config.record_type = optarg; |
| 284 | case '6': | 347 | break; |
| 285 | query_transport = "-6"; | 348 | case 'a': |
| 286 | break; | 349 | result.config.expected_address = optarg; |
| 287 | default: /* usage5 */ | 350 | break; |
| 288 | usage5(); | 351 | case '4': |
| 289 | } | 352 | result.config.query_transport = "-4"; |
| 290 | } | 353 | break; |
| 291 | 354 | case '6': | |
| 292 | c = optind; | 355 | result.config.query_transport = "-6"; |
| 293 | if (dns_server == NULL) { | 356 | break; |
| 294 | if (c < argc) { | 357 | default: /* usage5 */ |
| 295 | host_or_die(argv[c]); | 358 | usage5(); |
| 296 | dns_server = argv[c]; | 359 | } |
| 297 | } | 360 | } |
| 298 | else { | 361 | |
| 299 | if (strcmp(query_transport,"-6") == 0) | 362 | int index = optind; |
| 300 | dns_server = strdup("::1"); | 363 | if (result.config.dns_server == NULL) { |
| 301 | else | 364 | if (index < argc) { |
| 302 | dns_server = strdup ("127.0.0.1"); | 365 | host_or_die(argv[index]); |
| 303 | } | 366 | result.config.dns_server = argv[index]; |
| 304 | } | 367 | } else { |
| 305 | 368 | if (strcmp(result.config.query_transport, "-6") == 0) { | |
| 306 | return validate_arguments (); | 369 | result.config.dns_server = strdup("::1"); |
| 370 | } else { | ||
| 371 | result.config.dns_server = strdup("127.0.0.1"); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | return validate_arguments(result); | ||
| 307 | } | 377 | } |
| 308 | 378 | ||
| 309 | 379 | check_dig_config_wrapper validate_arguments(check_dig_config_wrapper config_wrapper) { | |
| 310 | 380 | if (config_wrapper.config.query_address == NULL) { | |
| 311 | int | 381 | config_wrapper.errorcode = ERROR; |
| 312 | validate_arguments (void) | 382 | } |
| 313 | { | 383 | return config_wrapper; |
| 314 | if (query_address != NULL) | ||
| 315 | return OK; | ||
| 316 | else | ||
| 317 | return ERROR; | ||
| 318 | } | 384 | } |
| 319 | 385 | ||
| 386 | void print_help(void) { | ||
| 387 | char *myport; | ||
| 320 | 388 | ||
| 389 | xasprintf(&myport, "%d", DEFAULT_PORT); | ||
| 321 | 390 | ||
| 322 | void | 391 | print_revision(progname, NP_VERSION); |
| 323 | print_help (void) | ||
| 324 | { | ||
| 325 | char *myport; | ||
| 326 | 392 | ||
| 327 | xasprintf (&myport, "%d", DEFAULT_PORT); | 393 | printf("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); |
| 394 | printf(COPYRIGHT, copyright, email); | ||
| 328 | 395 | ||
| 329 | print_revision (progname, NP_VERSION); | 396 | printf(_("This plugin tests the DNS service on the specified host using dig")); |
| 330 | 397 | ||
| 331 | printf ("Copyright (c) 2000 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"); | 398 | printf("\n\n"); |
| 332 | printf (COPYRIGHT, copyright, email); | ||
| 333 | 399 | ||
| 334 | printf (_("This plugin tests the DNS service on the specified host using dig")); | 400 | print_usage(); |
| 335 | 401 | ||
| 336 | printf ("\n\n"); | 402 | printf(UT_HELP_VRSN); |
| 337 | 403 | ||
| 338 | print_usage (); | 404 | printf(UT_EXTRA_OPTS); |
| 339 | 405 | ||
| 340 | printf (UT_HELP_VRSN); | 406 | printf(UT_HOST_PORT, 'p', myport); |
| 341 | 407 | ||
| 342 | printf (UT_EXTRA_OPTS); | 408 | printf(" %s\n", "-4, --use-ipv4"); |
| 409 | printf(" %s\n", _("Force dig to only use IPv4 query transport")); | ||
| 410 | printf(" %s\n", "-6, --use-ipv6"); | ||
| 411 | printf(" %s\n", _("Force dig to only use IPv6 query transport")); | ||
| 412 | printf(" %s\n", "-l, --query_address=STRING"); | ||
| 413 | printf(" %s\n", _("Machine name to lookup")); | ||
| 414 | printf(" %s\n", "-T, --record_type=STRING"); | ||
| 415 | printf(" %s\n", _("Record type to lookup (default: A)")); | ||
| 416 | printf(" %s\n", "-a, --expected_address=STRING"); | ||
| 417 | printf(" %s\n", | ||
| 418 | _("An address expected to be in the answer section. If not set, uses whatever")); | ||
| 419 | printf(" %s\n", _("was in -l")); | ||
| 420 | printf(" %s\n", "-A, --dig-arguments=STRING"); | ||
| 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")); | ||
| 426 | printf(UT_WARN_CRIT); | ||
| 427 | printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 428 | printf(UT_VERBOSE); | ||
| 343 | 429 | ||
| 344 | printf (UT_HOST_PORT, 'p', myport); | 430 | printf("\n"); |
| 431 | printf("%s\n", _("Examples:")); | ||
| 432 | printf(" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\""); | ||
| 433 | printf(" %s\n", "This will send a tcp query to DNSSERVER for www.example.com"); | ||
| 345 | 434 | ||
| 346 | printf (" %s\n","-4, --use-ipv4"); | 435 | printf(UT_SUPPORT); |
| 347 | printf (" %s\n",_("Force dig to only use IPv4 query transport")); | 436 | } |
| 348 | printf (" %s\n","-6, --use-ipv6"); | ||
| 349 | printf (" %s\n",_("Force dig to only use IPv6 query transport")); | ||
| 350 | printf (" %s\n","-l, --query_address=STRING"); | ||
| 351 | printf (" %s\n",_("Machine name to lookup")); | ||
| 352 | printf (" %s\n","-T, --record_type=STRING"); | ||
| 353 | printf (" %s\n",_("Record type to lookup (default: A)")); | ||
| 354 | printf (" %s\n","-a, --expected_address=STRING"); | ||
| 355 | printf (" %s\n",_("An address expected to be in the answer section. If not set, uses whatever")); | ||
| 356 | printf (" %s\n",_("was in -l")); | ||
| 357 | printf (" %s\n","-A, --dig-arguments=STRING"); | ||
| 358 | printf (" %s\n",_("Pass STRING as argument(s) to dig")); | ||
| 359 | printf (UT_WARN_CRIT); | ||
| 360 | printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
| 361 | printf (UT_VERBOSE); | ||
| 362 | 437 | ||
| 363 | printf ("\n"); | 438 | void print_usage(void) { |
| 364 | printf ("%s\n", _("Examples:")); | 439 | printf("%s\n", _("Usage:")); |
| 365 | printf (" %s\n", "check_dig -H DNSSERVER -l www.example.com -A \"+tcp\""); | 440 | printf("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); |
| 366 | printf (" %s\n", "This will send a tcp query to DNSSERVER for www.example.com"); | 441 | printf(" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); |
| 442 | printf(" [-t <timeout>] [-a <expected answer address>] [-E <flags>] [-X <flags>] [-v]\n"); | ||
| 443 | } | ||
| 367 | 444 | ||
| 368 | printf (UT_SUPPORT); | 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 | */ | ||
| 459 | static 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; | ||
| 369 | } | 520 | } |
| 370 | 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 | */ | ||
| 534 | static 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 | } | ||
| 371 | 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 | */ | ||
| 589 | static 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 | } | ||
| 372 | 601 | ||
| 373 | void | 602 | /** |
| 374 | print_usage (void) | 603 | * free_flag_list - Release all heap allocations held by a flag_list. |
| 375 | { | 604 | * |
| 376 | printf ("%s\n", _("Usage:")); | 605 | * Input: |
| 377 | printf ("%s -l <query_address> [-H <host>] [-p <server port>]\n", progname); | 606 | * list - pointer to a flag_list whose items were allocated by |
| 378 | printf (" [-T <query type>] [-w <warning interval>] [-c <critical interval>]\n"); | 607 | * parse_flags_line() or split_csv_trim(). |
| 379 | printf (" [-t <timeout>] [-a <expected answer address>] [-v]\n"); | 608 | * |
| 609 | * After this call list->items is NULL and list->count is 0. | ||
| 610 | */ | ||
| 611 | static 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; | ||
| 380 | } | 623 | } |
