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.c1101
1 files changed, 528 insertions, 573 deletions
diff --git a/plugins/check_dns.c b/plugins/check_dns.c
index 468bc958..e1e7c00e 100644
--- a/plugins/check_dns.c
+++ b/plugins/check_dns.c
@@ -1,36 +1,36 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_dns plugin 3 * Monitoring check_dns plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2000-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the check_dns plugin 10 * This file contains the check_dns plugin
11* 11 *
12* LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which 12 * LIMITATION: nslookup on Solaris 7 can return output over 2 lines, which
13* will not be picked up by this plugin 13 * will not be picked up by this plugin
14* 14 *
15* 15 *
16* This program is free software: you can redistribute it and/or modify 16 * This program is free software: you can redistribute it and/or modify
17* it under the terms of the GNU General Public License as published by 17 * it under the terms of the GNU General Public License as published by
18* the Free Software Foundation, either version 3 of the License, or 18 * the Free Software Foundation, either version 3 of the License, or
19* (at your option) any later version. 19 * (at your option) any later version.
20* 20 *
21* This program is distributed in the hope that it will be useful, 21 * This program is distributed in the hope that it will be useful,
22* but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24* GNU General Public License for more details. 24 * GNU General Public License for more details.
25* 25 *
26* You should have received a copy of the GNU General Public License 26 * You should have received a copy of the GNU General Public License
27* along with this program. If not, see <http://www.gnu.org/licenses/>. 27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28* 28 *
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_dns"; 32const char *progname = "check_dns";
33const char *copyright = "2000-2008"; 33const char *copyright = "2000-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "common.h" 36#include "common.h"
@@ -39,579 +39,534 @@ const char *email = "devel@monitoring-plugins.org";
39#include "netutils.h" 39#include "netutils.h"
40#include "runcmd.h" 40#include "runcmd.h"
41 41
42int process_arguments (int, char **); 42static int process_arguments(int /*argc*/, char ** /*argv*/);
43int validate_arguments (void); 43static int validate_arguments(void);
44int error_scan (char *, bool *); 44static int error_scan(char * /*input_buffer*/, bool *);
45bool ip_match_cidr(const char *, const char *); 45static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/);
46unsigned long ip2long(const char *); 46static unsigned long ip2long(const char * /*src*/);
47void print_help (void); 47static void print_help(void);
48void print_usage (void); 48void print_usage(void);
49 49
50#define ADDRESS_LENGTH 256 50#define ADDRESS_LENGTH 256
51char query_address[ADDRESS_LENGTH] = ""; 51static char query_address[ADDRESS_LENGTH] = "";
52char dns_server[ADDRESS_LENGTH] = ""; 52static char dns_server[ADDRESS_LENGTH] = "";
53char ptr_server[ADDRESS_LENGTH] = ""; 53static char ptr_server[ADDRESS_LENGTH] = "";
54bool verbose = false; 54static bool verbose = false;
55char **expected_address = NULL; 55static char **expected_address = NULL;
56int expected_address_cnt = 0; 56static int expected_address_cnt = 0;
57bool expect_nxdomain = false; 57static bool expect_nxdomain = false;
58 58
59bool expect_authority = false; 59static bool expect_authority = false;
60bool all_match = false; 60static bool all_match = false;
61thresholds *time_thresholds = NULL; 61static thresholds *time_thresholds = NULL;
62 62
63static int 63static int qstrcmp(const void *p1, const void *p2) {
64qstrcmp(const void *p1, const void *p2)
65{
66 /* The actual arguments to this function are "pointers to 64 /* The actual arguments to this function are "pointers to
67 pointers to char", but strcmp() arguments are "pointers 65 pointers to char", but strcmp() arguments are "pointers
68 to char", hence the following cast plus dereference */ 66 to char", hence the following cast plus dereference */
69 return strcmp(* (char * const *) p1, * (char * const *) p2); 67 return strcmp(*(char *const *)p1, *(char *const *)p2);
70} 68}
71 69
70int main(int argc, char **argv) {
71 char *command_line = NULL;
72 char input_buffer[MAX_INPUT_BUFFER];
73 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */
74 char **addresses = NULL;
75 int n_addresses = 0;
76 char *msg = NULL;
77 char *temp_buffer = NULL;
78 bool non_authoritative = false;
79 int result = STATE_UNKNOWN;
80 double elapsed_time;
81 long microsec;
82 struct timeval tv;
83 bool parse_address = false; /* This flag scans for Address: but only after Name: */
84 output chld_out;
85 output chld_err;
86 bool is_nxdomain = false;
87
88 setlocale(LC_ALL, "");
89 bindtextdomain(PACKAGE, LOCALEDIR);
90 textdomain(PACKAGE);
91
92 /* Set signal handling and alarm */
93 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
94 usage_va(_("Cannot catch SIGALRM"));
95 }
96
97 /* Parse extra opts if any */
98 argv = np_extra_opts(&argc, argv, progname);
99
100 if (process_arguments(argc, argv) == ERROR) {
101 usage_va(_("Could not parse arguments"));
102 }
103
104 /* get the command to run */
105 xasprintf(&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server);
106
107 alarm(timeout_interval);
108 gettimeofday(&tv, NULL);
109
110 if (verbose)
111 printf("%s\n", command_line);
112
113 /* run the command */
114 if ((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) {
115 msg = (char *)_("nslookup returned an error status");
116 result = STATE_WARNING;
117 }
118
119 /* scan stdout */
120 for (size_t i = 0; i < chld_out.lines; i++) {
121 if (addresses == NULL)
122 addresses = malloc(sizeof(*addresses) * 10);
123 else if (!(n_addresses % 10))
124 addresses = realloc(addresses, sizeof(*addresses) * (n_addresses + 10));
125
126 if (verbose)
127 puts(chld_out.line[i]);
128
129 if (strcasestr(chld_out.line[i], ".in-addr.arpa") || strcasestr(chld_out.line[i], ".ip6.arpa")) {
130 if ((temp_buffer = strstr(chld_out.line[i], "name = ")))
131 addresses[n_addresses++] = strdup(temp_buffer + 7);
132 else {
133 msg = (char *)_("Warning plugin error");
134 result = STATE_WARNING;
135 }
136 }
137
138 /* bug ID: 2946553 - Older versions of bind will use all available dns
139 servers, we have to match the one specified */
140 if (strstr(chld_out.line[i], "Server:") && strlen(dns_server) > 0) {
141 temp_buffer = strchr(chld_out.line[i], ':');
142 temp_buffer++;
143
144 /* Strip leading tabs */
145 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++)
146 /* NOOP */;
147
148 strip(temp_buffer);
149 if (temp_buffer == NULL || strlen(temp_buffer) == 0) {
150 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), NSLOOKUP_COMMAND);
151 }
152
153 if (strcmp(temp_buffer, dns_server) != 0) {
154 die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server);
155 }
156 }
157
158 /* the server is responding, we just got the host name... */
159 if (strstr(chld_out.line[i], "Name:"))
160 parse_address = true;
161 else if (parse_address && (strstr(chld_out.line[i], "Address:") || strstr(chld_out.line[i], "Addresses:"))) {
162 temp_buffer = index(chld_out.line[i], ':');
163 temp_buffer++;
164
165 /* Strip leading spaces */
166 while (*temp_buffer == ' ')
167 temp_buffer++;
168
169 strip(temp_buffer);
170 if (temp_buffer == NULL || strlen(temp_buffer) == 0) {
171 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), NSLOOKUP_COMMAND);
172 }
173
174 addresses[n_addresses++] = strdup(temp_buffer);
175 } else if (strstr(chld_out.line[i], _("Non-authoritative answer:"))) {
176 non_authoritative = true;
177 }
178
179 result = error_scan(chld_out.line[i], &is_nxdomain);
180 if (result != STATE_OK) {
181 msg = strchr(chld_out.line[i], ':');
182 if (msg)
183 msg++;
184 break;
185 }
186 }
187
188 /* scan stderr */
189 for (size_t i = 0; i < chld_err.lines; i++) {
190 if (verbose)
191 puts(chld_err.line[i]);
192
193 if (error_scan(chld_err.line[i], &is_nxdomain) != STATE_OK) {
194 result = max_state(result, error_scan(chld_err.line[i], &is_nxdomain));
195 msg = strchr(input_buffer, ':');
196 if (msg)
197 msg++;
198 else
199 msg = input_buffer;
200 }
201 }
202
203 if (is_nxdomain && !expect_nxdomain) {
204 die(STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address);
205 }
72 206
73int 207 if (addresses) {
74main (int argc, char **argv) 208 int i;
75{ 209 int slen;
76 char *command_line = NULL; 210 char *adrp;
77 char input_buffer[MAX_INPUT_BUFFER]; 211 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp);
78 char *address = NULL; /* comma separated str with addrs/ptrs (sorted) */ 212 for (i = 0, slen = 1; i < n_addresses; i++) {
79 char **addresses = NULL; 213 slen += strlen(addresses[i]) + 1;
80 int n_addresses = 0; 214 }
81 char *msg = NULL; 215 adrp = address = malloc(slen);
82 char *temp_buffer = NULL; 216 for (i = 0; i < n_addresses; i++) {
83 bool non_authoritative = false; 217 if (i)
84 int result = STATE_UNKNOWN; 218 *adrp++ = ',';
85 double elapsed_time; 219 strcpy(adrp, addresses[i]);
86 long microsec; 220 adrp += strlen(addresses[i]);
87 struct timeval tv; 221 }
88 bool parse_address = false; /* This flag scans for Address: but only after Name: */ 222 *adrp = 0;
89 output chld_out, chld_err; 223 } else
90 bool is_nxdomain = false; 224 die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), NSLOOKUP_COMMAND);
91 225
92 setlocale (LC_ALL, ""); 226 /* compare to expected address */
93 bindtextdomain (PACKAGE, LOCALEDIR); 227 if (result == STATE_OK && expected_address_cnt > 0) {
94 textdomain (PACKAGE); 228 result = STATE_CRITICAL;
95 229 temp_buffer = "";
96 /* Set signal handling and alarm */ 230 unsigned long expect_match = (1 << expected_address_cnt) - 1;
97 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 231 unsigned long addr_match = (1 << n_addresses) - 1;
98 usage_va(_("Cannot catch SIGALRM")); 232
99 } 233 for (int i = 0; i < expected_address_cnt; i++) {
100 234 int j;
101 /* Parse extra opts if any */ 235 /* check if we get a match on 'raw' ip or cidr */
102 argv=np_extra_opts (&argc, argv, progname); 236 for (j = 0; j < n_addresses; j++) {
103 237 if (strcmp(addresses[j], expected_address[i]) == 0 || ip_match_cidr(addresses[j], expected_address[i])) {
104 if (process_arguments (argc, argv) == ERROR) { 238 result = STATE_OK;
105 usage_va(_("Could not parse arguments")); 239 addr_match &= ~(1 << j);
106 } 240 expect_match &= ~(1 << i);
107 241 }
108 /* get the command to run */ 242 }
109 xasprintf (&command_line, "%s %s %s", NSLOOKUP_COMMAND, query_address, dns_server); 243
110 244 /* prepare an error string */
111 alarm (timeout_interval); 245 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]);
112 gettimeofday (&tv, NULL); 246 }
113 247 /* check if expected_address must cover all in addresses and none may be missing */
114 if (verbose) 248 if (all_match && (expect_match != 0 || addr_match != 0))
115 printf ("%s\n", command_line); 249 result = STATE_CRITICAL;
116 250 if (result == STATE_CRITICAL) {
117 /* run the command */ 251 /* Strip off last semicolon... */
118 if((np_runcmd(command_line, &chld_out, &chld_err, 0)) != 0) { 252 temp_buffer[strlen(temp_buffer) - 2] = '\0';
119 msg = (char *)_("nslookup returned an error status"); 253 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address);
120 result = STATE_WARNING; 254 }
121 } 255 }
122 256
123 /* scan stdout */ 257 if (expect_nxdomain) {
124 for(size_t i = 0; i < chld_out.lines; i++) { 258 if (!is_nxdomain) {
125 if (addresses == NULL) 259 result = STATE_CRITICAL;
126 addresses = malloc(sizeof(*addresses)*10); 260 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address);
127 else if (!(n_addresses % 10)) 261 } else {
128 addresses = realloc(addresses,sizeof(*addresses) * (n_addresses + 10)); 262 if (address != NULL)
129 263 free(address);
130 if (verbose) 264 address = "NXDOMAIN";
131 puts(chld_out.line[i]); 265 }
132 266 }
133 if (strcasestr (chld_out.line[i], ".in-addr.arpa") || strcasestr (chld_out.line[i], ".ip6.arpa")) { 267
134 if ((temp_buffer = strstr (chld_out.line[i], "name = "))) 268 /* check if authoritative */
135 addresses[n_addresses++] = strdup (temp_buffer + 7); 269 if (result == STATE_OK && expect_authority && non_authoritative) {
136 else { 270 result = STATE_CRITICAL;
137 msg = (char *)_("Warning plugin error"); 271 xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address);
138 result = STATE_WARNING; 272 }
139 } 273
140 } 274 microsec = deltime(tv);
141 275 elapsed_time = (double)microsec / 1.0e6;
142 /* bug ID: 2946553 - Older versions of bind will use all available dns 276
143 servers, we have to match the one specified */ 277 if (result == STATE_OK) {
144 if (strstr (chld_out.line[i], "Server:") && strlen(dns_server) > 0) { 278 result = get_status(elapsed_time, time_thresholds);
145 temp_buffer = strchr (chld_out.line[i], ':'); 279 if (result == STATE_OK) {
146 temp_buffer++; 280 printf("DNS %s: ", _("OK"));
147 281 } else if (result == STATE_WARNING) {
148 /* Strip leading tabs */ 282 printf("DNS %s: ", _("WARNING"));
149 for (; *temp_buffer != '\0' && *temp_buffer == '\t'; temp_buffer++) 283 } else if (result == STATE_CRITICAL) {
150 /* NOOP */; 284 printf("DNS %s: ", _("CRITICAL"));
151 285 }
152 strip(temp_buffer); 286 printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time);
153 if (temp_buffer==NULL || strlen(temp_buffer)==0) { 287 printf(_(". %s returns %s"), query_address, address);
154 die (STATE_CRITICAL, 288 if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) {
155 _("DNS CRITICAL - '%s' returned empty server string\n"), 289 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, true, time_thresholds->critical->end,
156 NSLOOKUP_COMMAND); 290 true, 0, false, 0));
157 } 291 } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) {
158 292 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, time_thresholds->critical->end, true, 0, false, 0));
159 if (strcmp(temp_buffer, dns_server) != 0) { 293 } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) {
160 die (STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), dns_server); 294 printf("|%s\n", fperfdata("time", elapsed_time, "s", true, time_thresholds->warning->end, false, 0, true, 0, false, 0));
161 } 295 } else
162 } 296 printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0));
163 297 } else if (result == STATE_WARNING)
164 /* the server is responding, we just got the host name... */ 298 printf(_("DNS WARNING - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
165 if (strstr (chld_out.line[i], "Name:")) 299 else if (result == STATE_CRITICAL)
166 parse_address = true; 300 printf(_("DNS CRITICAL - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
167 else if (parse_address && (strstr (chld_out.line[i], "Address:") || 301 else
168 strstr (chld_out.line[i], "Addresses:"))) { 302 printf(_("DNS UNKNOWN - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg);
169 temp_buffer = index (chld_out.line[i], ':'); 303
170 temp_buffer++; 304 return result;
171
172 /* Strip leading spaces */
173 while (*temp_buffer == ' ')
174 temp_buffer++;
175
176 strip(temp_buffer);
177 if (temp_buffer==NULL || strlen(temp_buffer)==0) {
178 die (STATE_CRITICAL,
179 _("DNS CRITICAL - '%s' returned empty host name string\n"),
180 NSLOOKUP_COMMAND);
181 }
182
183 addresses[n_addresses++] = strdup(temp_buffer);
184 }
185 else if (strstr (chld_out.line[i], _("Non-authoritative answer:"))) {
186 non_authoritative = true;
187 }
188
189
190 result = error_scan (chld_out.line[i], &is_nxdomain);
191 if (result != STATE_OK) {
192 msg = strchr (chld_out.line[i], ':');
193 if(msg) msg++;
194 break;
195 }
196 }
197
198 /* scan stderr */
199 for(size_t i = 0; i < chld_err.lines; i++) {
200 if (verbose)
201 puts(chld_err.line[i]);
202
203 if (error_scan (chld_err.line[i], &is_nxdomain) != STATE_OK) {
204 result = max_state (result, error_scan (chld_err.line[i], &is_nxdomain));
205 msg = strchr(input_buffer, ':');
206 if(msg)
207 msg++;
208 else
209 msg = input_buffer;
210 }
211 }
212
213 if (is_nxdomain && !expect_nxdomain) {
214 die (STATE_CRITICAL, _("Domain '%s' was not found by the server\n"), query_address);
215 }
216
217 if (addresses) {
218 int i,slen;
219 char *adrp;
220 qsort(addresses, n_addresses, sizeof(*addresses), qstrcmp);
221 for(i=0, slen=1; i < n_addresses; i++) {
222 slen += strlen(addresses[i])+1;
223 }
224 adrp = address = malloc(slen);
225 for(i=0; i < n_addresses; i++) {
226 if (i) *adrp++ = ',';
227 strcpy(adrp, addresses[i]);
228 adrp += strlen(addresses[i]);
229 }
230 *adrp = 0;
231 } else
232 die (STATE_CRITICAL,
233 _("DNS CRITICAL - '%s' msg parsing exited with no address\n"),
234 NSLOOKUP_COMMAND);
235
236 /* compare to expected address */
237 if (result == STATE_OK && expected_address_cnt > 0) {
238 result = STATE_CRITICAL;
239 temp_buffer = "";
240 unsigned long expect_match = (1 << expected_address_cnt) - 1;
241 unsigned long addr_match = (1 << n_addresses) - 1;
242
243 for (int i=0; i<expected_address_cnt; i++) {
244 int j;
245 /* check if we get a match on 'raw' ip or cidr */
246 for (j=0; j<n_addresses; j++) {
247 if ( strcmp(addresses[j], expected_address[i]) == 0
248 || ip_match_cidr(addresses[j], expected_address[i]) ) {
249 result = STATE_OK;
250 addr_match &= ~(1 << j);
251 expect_match &= ~(1 << i);
252 }
253 }
254
255 /* prepare an error string */
256 xasprintf(&temp_buffer, "%s%s; ", temp_buffer, expected_address[i]);
257 }
258 /* check if expected_address must cover all in addresses and none may be missing */
259 if (all_match && (expect_match != 0 || addr_match != 0))
260 result = STATE_CRITICAL;
261 if (result == STATE_CRITICAL) {
262 /* Strip off last semicolon... */
263 temp_buffer[strlen(temp_buffer)-2] = '\0';
264 xasprintf(&msg, _("expected '%s' but got '%s'"), temp_buffer, address);
265 }
266 }
267
268 if (expect_nxdomain) {
269 if (!is_nxdomain) {
270 result = STATE_CRITICAL;
271 xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), query_address, address);
272 } else {
273 if (address != NULL) free(address);
274 address = "NXDOMAIN";
275 }
276 }
277
278 /* check if authoritative */
279 if (result == STATE_OK && expect_authority && non_authoritative) {
280 result = STATE_CRITICAL;
281 xasprintf(&msg, _("server %s is not authoritative for %s"), dns_server, query_address);
282 }
283
284 microsec = deltime (tv);
285 elapsed_time = (double)microsec / 1.0e6;
286
287 if (result == STATE_OK) {
288 result = get_status(elapsed_time, time_thresholds);
289 if (result == STATE_OK) {
290 printf ("DNS %s: ", _("OK"));
291 } else if (result == STATE_WARNING) {
292 printf ("DNS %s: ", _("WARNING"));
293 } else if (result == STATE_CRITICAL) {
294 printf ("DNS %s: ", _("CRITICAL"));
295 }
296 printf (ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time);
297 printf (_(". %s returns %s"), query_address, address);
298 if ((time_thresholds->warning != NULL) && (time_thresholds->critical != NULL)) {
299 printf ("|%s\n", fperfdata ("time", elapsed_time, "s",
300 true, time_thresholds->warning->end,
301 true, time_thresholds->critical->end,
302 true, 0, false, 0));
303 } else if ((time_thresholds->warning == NULL) && (time_thresholds->critical != NULL)) {
304 printf ("|%s\n", fperfdata ("time", elapsed_time, "s",
305 false, 0,
306 true, time_thresholds->critical->end,
307 true, 0, false, 0));
308 } else if ((time_thresholds->warning != NULL) && (time_thresholds->critical == NULL)) {
309 printf ("|%s\n", fperfdata ("time", elapsed_time, "s",
310 true, time_thresholds->warning->end,
311 false, 0,
312 true, 0, false, 0));
313 } else
314 printf ("|%s\n", fperfdata ("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0));
315 }
316 else if (result == STATE_WARNING)
317 printf (_("DNS WARNING - %s\n"),
318 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg);
319 else if (result == STATE_CRITICAL)
320 printf (_("DNS CRITICAL - %s\n"),
321 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg);
322 else
323 printf (_("DNS UNKNOWN - %s\n"),
324 !strcmp (msg, "") ? _(" Probably a non-existent host/domain") : msg);
325
326 return result;
327} 305}
328 306
329bool ip_match_cidr(const char *addr, const char *cidr_ro) { 307bool ip_match_cidr(const char *addr, const char *cidr_ro) {
330 char *subnet, *mask_c, *cidr = strdup(cidr_ro); 308 char *subnet;
331 int mask; 309 char *mask_c;
332 subnet = strtok(cidr, "/"); 310 char *cidr = strdup(cidr_ro);
333 mask_c = strtok(NULL, "\0"); 311 int mask;
334 if (!subnet || !mask_c) { 312 subnet = strtok(cidr, "/");
335 return false; 313 mask_c = strtok(NULL, "\0");
314 if (!subnet || !mask_c) {
315 return false;
336 } 316 }
337 mask = atoi(mask_c); 317 mask = atoi(mask_c);
338 318
339 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ 319 /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */
340 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); 320 return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask);
341} 321}
342 322
343unsigned long 323unsigned long ip2long(const char *src) {
344ip2long(const char* src) { 324 unsigned long ip[4];
345 unsigned long ip[4]; 325 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */
346 /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ 326 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && ip[0] < 256 && ip[1] < 256 && ip[2] < 256 &&
347 return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", 327 ip[3] < 256)
348 &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && 328 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
349 ip[0] < 256 && ip[1] < 256 && 329 : 0;
350 ip[2] < 256 && ip[3] < 256)
351 ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]
352 : 0;
353} 330}
354 331
355int 332int error_scan(char *input_buffer, bool *is_nxdomain) {
356error_scan (char *input_buffer, bool *is_nxdomain)
357{
358
359 const int nxdomain = strstr (input_buffer, "Non-existent") ||
360 strstr (input_buffer, "** server can't find") ||
361 strstr (input_buffer, "** Can't find") ||
362 strstr (input_buffer, "NXDOMAIN");
363 if (nxdomain) *is_nxdomain = true;
364
365 /* the DNS lookup timed out */
366 if (strstr (input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) ||
367 strstr (input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
368 strstr (input_buffer, _("the `-sil[ent]' option to prevent this message from appearing.")))
369 return STATE_OK;
370
371 /* DNS server is not running... */
372 else if (strstr (input_buffer, "No response from server"))
373 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
374 else if (strstr (input_buffer, "no servers could be reached"))
375 die (STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
376
377 /* Host name is valid, but server doesn't have records... */
378 else if (strstr (input_buffer, "No records"))
379 die (STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
380
381 /* Connection was refused */
382 else if (strstr (input_buffer, "Connection refused") ||
383 strstr (input_buffer, "Couldn't find server") ||
384 strstr (input_buffer, "Refused") ||
385 (strstr (input_buffer, "** server can't find") &&
386 strstr (input_buffer, ": REFUSED")))
387 die (STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
388
389 /* Query refused (usually by an ACL in the namserver) */
390 else if (strstr (input_buffer, "Query refused"))
391 die (STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
392
393 /* No information (e.g. nameserver IP has two PTR records) */
394 else if (strstr (input_buffer, "No information"))
395 die (STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
396
397 /* Network is unreachable */
398 else if (strstr (input_buffer, "Network is unreachable"))
399 die (STATE_CRITICAL, _("Network is unreachable\n"));
400
401 /* Internal server failure */
402 else if (strstr (input_buffer, "Server failure"))
403 die (STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
404
405 /* Request error or the DNS lookup timed out */
406 else if (strstr (input_buffer, "Format error") ||
407 strstr (input_buffer, "Timed out"))
408 return STATE_WARNING;
409
410 return STATE_OK;
411 333
412} 334 const int nxdomain = strstr(input_buffer, "Non-existent") || strstr(input_buffer, "** server can't find") ||
335 strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN");
336 if (nxdomain)
337 *is_nxdomain = true;
338
339 /* the DNS lookup timed out */
340 if (strstr(input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) ||
341 strstr(input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) ||
342 strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing.")))
343 return STATE_OK;
344
345 /* DNS server is not running... */
346 else if (strstr(input_buffer, "No response from server"))
347 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
348 else if (strstr(input_buffer, "no servers could be reached"))
349 die(STATE_CRITICAL, _("No response from DNS %s\n"), dns_server);
350
351 /* Host name is valid, but server doesn't have records... */
352 else if (strstr(input_buffer, "No records"))
353 die(STATE_CRITICAL, _("DNS %s has no records\n"), dns_server);
354
355 /* Connection was refused */
356 else if (strstr(input_buffer, "Connection refused") || strstr(input_buffer, "Couldn't find server") ||
357 strstr(input_buffer, "Refused") || (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED")))
358 die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server);
359
360 /* Query refused (usually by an ACL in the namserver) */
361 else if (strstr(input_buffer, "Query refused"))
362 die(STATE_CRITICAL, _("Query was refused by DNS server at %s\n"), dns_server);
363
364 /* No information (e.g. nameserver IP has two PTR records) */
365 else if (strstr(input_buffer, "No information"))
366 die(STATE_CRITICAL, _("No information returned by DNS server at %s\n"), dns_server);
367
368 /* Network is unreachable */
369 else if (strstr(input_buffer, "Network is unreachable"))
370 die(STATE_CRITICAL, _("Network is unreachable\n"));
371
372 /* Internal server failure */
373 else if (strstr(input_buffer, "Server failure"))
374 die(STATE_CRITICAL, _("DNS failure for %s\n"), dns_server);
413 375
376 /* Request error or the DNS lookup timed out */
377 else if (strstr(input_buffer, "Format error") || strstr(input_buffer, "Timed out"))
378 return STATE_WARNING;
379
380 return STATE_OK;
381}
414 382
415/* process command-line arguments */ 383/* process command-line arguments */
416int 384int process_arguments(int argc, char **argv) {
417process_arguments (int argc, char **argv) 385 int c;
418{ 386 char *warning = NULL;
419 int c; 387 char *critical = NULL;
420 char *warning = NULL; 388
421 char *critical = NULL; 389 int opt_index = 0;
422 390 static struct option long_opts[] = {{"help", no_argument, 0, 'h'},
423 int opt_index = 0; 391 {"version", no_argument, 0, 'V'},
424 static struct option long_opts[] = { 392 {"verbose", no_argument, 0, 'v'},
425 {"help", no_argument, 0, 'h'}, 393 {"timeout", required_argument, 0, 't'},
426 {"version", no_argument, 0, 'V'}, 394 {"hostname", required_argument, 0, 'H'},
427 {"verbose", no_argument, 0, 'v'}, 395 {"server", required_argument, 0, 's'},
428 {"timeout", required_argument, 0, 't'}, 396 {"reverse-server", required_argument, 0, 'r'},
429 {"hostname", required_argument, 0, 'H'}, 397 {"expected-address", required_argument, 0, 'a'},
430 {"server", required_argument, 0, 's'}, 398 {"expect-nxdomain", no_argument, 0, 'n'},
431 {"reverse-server", required_argument, 0, 'r'}, 399 {"expect-authority", no_argument, 0, 'A'},
432 {"expected-address", required_argument, 0, 'a'}, 400 {"all", no_argument, 0, 'L'},
433 {"expect-nxdomain", no_argument, 0, 'n'}, 401 {"warning", required_argument, 0, 'w'},
434 {"expect-authority", no_argument, 0, 'A'}, 402 {"critical", required_argument, 0, 'c'},
435 {"all", no_argument, 0, 'L'}, 403 {0, 0, 0, 0}};
436 {"warning", required_argument, 0, 'w'}, 404
437 {"critical", required_argument, 0, 'c'}, 405 if (argc < 2)
438 {0, 0, 0, 0} 406 return ERROR;
439 }; 407
440 408 for (c = 1; c < argc; c++)
441 if (argc < 2) 409 if (strcmp("-to", argv[c]) == 0)
442 return ERROR; 410 strcpy(argv[c], "-t");
443 411
444 for (c = 1; c < argc; c++) 412 while (1) {
445 if (strcmp ("-to", argv[c]) == 0) 413 c = getopt_long(argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index);
446 strcpy (argv[c], "-t"); 414
447 415 if (c == -1 || c == EOF)
448 while (1) { 416 break;
449 c = getopt_long (argc, argv, "hVvALnt:H:s:r:a:w:c:", long_opts, &opt_index); 417
450 418 switch (c) {
451 if (c == -1 || c == EOF) 419 case 'h': /* help */
452 break; 420 print_help();
453 421 exit(STATE_UNKNOWN);
454 switch (c) { 422 case 'V': /* version */
455 case 'h': /* help */ 423 print_revision(progname, NP_VERSION);
456 print_help (); 424 exit(STATE_UNKNOWN);
457 exit (STATE_UNKNOWN); 425 case 'v': /* version */
458 case 'V': /* version */ 426 verbose = true;
459 print_revision (progname, NP_VERSION); 427 break;
460 exit (STATE_UNKNOWN); 428 case 't': /* timeout period */
461 case 'v': /* version */ 429 timeout_interval = atoi(optarg);
462 verbose = true; 430 break;
463 break; 431 case 'H': /* hostname */
464 case 't': /* timeout period */ 432 if (strlen(optarg) >= ADDRESS_LENGTH)
465 timeout_interval = atoi (optarg); 433 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
466 break; 434 strcpy(query_address, optarg);
467 case 'H': /* hostname */ 435 break;
468 if (strlen (optarg) >= ADDRESS_LENGTH) 436 case 's': /* server name */
469 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 437 /* TODO: this host_or_die check is probably unnecessary.
470 strcpy (query_address, optarg); 438 * Better to confirm nslookup response matches */
471 break; 439 host_or_die(optarg);
472 case 's': /* server name */ 440 if (strlen(optarg) >= ADDRESS_LENGTH)
473 /* TODO: this host_or_die check is probably unnecessary. 441 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
474 * Better to confirm nslookup response matches */ 442 strcpy(dns_server, optarg);
475 host_or_die(optarg); 443 break;
476 if (strlen (optarg) >= ADDRESS_LENGTH) 444 case 'r': /* reverse server name */
477 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 445 /* TODO: Is this host_or_die necessary? */
478 strcpy (dns_server, optarg); 446 host_or_die(optarg);
479 break; 447 if (strlen(optarg) >= ADDRESS_LENGTH)
480 case 'r': /* reverse server name */ 448 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
481 /* TODO: Is this host_or_die necessary? */ 449 strcpy(ptr_server, optarg);
482 host_or_die(optarg); 450 break;
483 if (strlen (optarg) >= ADDRESS_LENGTH) 451 case 'a': /* expected address */
484 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 452 if (strlen(optarg) >= ADDRESS_LENGTH)
485 strcpy (ptr_server, optarg); 453 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
486 break; 454 if (strchr(optarg, ',') != NULL) {
487 case 'a': /* expected address */ 455 char *comma = strchr(optarg, ',');
488 if (strlen (optarg) >= ADDRESS_LENGTH) 456 while (comma != NULL) {
489 die (STATE_UNKNOWN, _("Input buffer overflow\n")); 457 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **));
490 if (strchr(optarg, ',') != NULL) { 458 expected_address[expected_address_cnt] = strndup(optarg, comma - optarg);
491 char *comma = strchr(optarg, ','); 459 expected_address_cnt++;
492 while (comma != NULL) { 460 optarg = comma + 1;
493 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**)); 461 comma = strchr(optarg, ',');
494 expected_address[expected_address_cnt] = strndup(optarg, comma - optarg); 462 }
495 expected_address_cnt++; 463 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **));
496 optarg = comma + 1; 464 expected_address[expected_address_cnt] = strdup(optarg);
497 comma = strchr(optarg, ','); 465 expected_address_cnt++;
466 } else {
467 expected_address = (char **)realloc(expected_address, (expected_address_cnt + 1) * sizeof(char **));
468 expected_address[expected_address_cnt] = strdup(optarg);
469 expected_address_cnt++;
470 }
471 break;
472 case 'n': /* expect NXDOMAIN */
473 expect_nxdomain = true;
474 break;
475 case 'A': /* expect authority */
476 expect_authority = true;
477 break;
478 case 'L': /* all must match */
479 all_match = true;
480 break;
481 case 'w':
482 warning = optarg;
483 break;
484 case 'c':
485 critical = optarg;
486 break;
487 default: /* args not parsable */
488 usage5();
489 }
498 } 490 }
499 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
500 expected_address[expected_address_cnt] = strdup(optarg);
501 expected_address_cnt++;
502 } else {
503 expected_address = (char **)realloc(expected_address, (expected_address_cnt+1) * sizeof(char**));
504 expected_address[expected_address_cnt] = strdup(optarg);
505 expected_address_cnt++;
506 }
507 break;
508 case 'n': /* expect NXDOMAIN */
509 expect_nxdomain = true;
510 break;
511 case 'A': /* expect authority */
512 expect_authority = true;
513 break;
514 case 'L': /* all must match */
515 all_match = true;
516 break;
517 case 'w':
518 warning = optarg;
519 break;
520 case 'c':
521 critical = optarg;
522 break;
523 default: /* args not parsable */
524 usage5();
525 }
526 }
527
528 c = optind;
529 if (strlen(query_address)==0 && c<argc) {
530 if (strlen(argv[c])>=ADDRESS_LENGTH)
531 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
532 strcpy (query_address, argv[c++]);
533 }
534
535 if (strlen(dns_server)==0 && c<argc) {
536 /* TODO: See -s option */
537 host_or_die(argv[c]);
538 if (strlen(argv[c]) >= ADDRESS_LENGTH)
539 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
540 strcpy (dns_server, argv[c++]);
541 }
542
543 set_thresholds(&time_thresholds, warning, critical);
544
545 return validate_arguments ();
546}
547 491
492 c = optind;
493 if (strlen(query_address) == 0 && c < argc) {
494 if (strlen(argv[c]) >= ADDRESS_LENGTH)
495 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
496 strcpy(query_address, argv[c++]);
497 }
548 498
549int 499 if (strlen(dns_server) == 0 && c < argc) {
550validate_arguments () 500 /* TODO: See -s option */
551{ 501 host_or_die(argv[c]);
552 if (query_address[0] == 0) { 502 if (strlen(argv[c]) >= ADDRESS_LENGTH)
553 printf ("missing --host argument\n"); 503 die(STATE_UNKNOWN, _("Input buffer overflow\n"));
554 return ERROR; 504 strcpy(dns_server, argv[c++]);
555 } 505 }
556 506
557 if (expected_address_cnt > 0 && expect_nxdomain) { 507 set_thresholds(&time_thresholds, warning, critical);
558 printf ("--expected-address and --expect-nxdomain cannot be combined\n");
559 return ERROR;
560 }
561 508
562 return OK; 509 return validate_arguments();
563} 510}
564 511
512int validate_arguments(void) {
513 if (query_address[0] == 0) {
514 printf("missing --host argument\n");
515 return ERROR;
516 }
517
518 if (expected_address_cnt > 0 && expect_nxdomain) {
519 printf("--expected-address and --expect-nxdomain cannot be combined\n");
520 return ERROR;
521 }
565 522
566void 523 return OK;
567print_help (void)
568{
569 print_revision (progname, NP_VERSION);
570
571 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
572 printf (COPYRIGHT, copyright, email);
573
574 printf ("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query."));
575 printf ("%s\n", _("An optional DNS server to use may be specified."));
576 printf ("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used."));
577
578 printf ("\n\n");
579
580 print_usage ();
581
582 printf (UT_HELP_VRSN);
583 printf (UT_EXTRA_OPTS);
584
585 printf (" -H, --hostname=HOST\n");
586 printf (" %s\n", _("The name or address you want to query"));
587 printf (" -s, --server=HOST\n");
588 printf (" %s\n", _("Optional DNS server you want to use for the lookup"));
589 printf (" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
590 printf (" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
591 printf (" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
592 printf (" %s\n", _("value matches)."));
593 printf (" -n, --expect-nxdomain\n");
594 printf (" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
595 printf (" %s\n", _("Cannot be used together with -a"));
596 printf (" -A, --expect-authority\n");
597 printf (" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
598 printf (" -w, --warning=seconds\n");
599 printf (" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
600 printf (" -c, --critical=seconds\n");
601 printf (" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
602 printf (" -L, --all\n");
603 printf (" %s\n", _("Return critical if the list of expected addresses does not match all addresses"));
604 printf (" %s\n", _("returned. Default off"));
605
606 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
607
608 printf (UT_SUPPORT);
609} 524}
610 525
526void print_help(void) {
527 print_revision(progname, NP_VERSION);
528
529 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
530 printf(COPYRIGHT, copyright, email);
531
532 printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query."));
533 printf("%s\n", _("An optional DNS server to use may be specified."));
534 printf("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used."));
535
536 printf("\n\n");
537
538 print_usage();
539
540 printf(UT_HELP_VRSN);
541 printf(UT_EXTRA_OPTS);
542
543 printf(" -H, --hostname=HOST\n");
544 printf(" %s\n", _("The name or address you want to query"));
545 printf(" -s, --server=HOST\n");
546 printf(" %s\n", _("Optional DNS server you want to use for the lookup"));
547 printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n");
548 printf(" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end"));
549 printf(" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any"));
550 printf(" %s\n", _("value matches)."));
551 printf(" -n, --expect-nxdomain\n");
552 printf(" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)"));
553 printf(" %s\n", _("Cannot be used together with -a"));
554 printf(" -A, --expect-authority\n");
555 printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup"));
556 printf(" -w, --warning=seconds\n");
557 printf(" %s\n", _("Return warning if elapsed time exceeds value. Default off"));
558 printf(" -c, --critical=seconds\n");
559 printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off"));
560 printf(" -L, --all\n");
561 printf(" %s\n", _("Return critical if the list of expected addresses does not match all addresses"));
562 printf(" %s\n", _("returned. Default off"));
563
564 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
565
566 printf(UT_SUPPORT);
567}
611 568
612void 569void print_usage(void) {
613print_usage (void) 570 printf("%s\n", _("Usage:"));
614{ 571 printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname);
615 printf ("%s\n", _("Usage:"));
616 printf ("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname);
617} 572}