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