1 /******************************************************************************
2 *
3 * CHECK_DNS.C
4 *
5 * Program: DNS plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 *
9 * Last Modified: $Date$
10 *
11 * Notes:
12 * - Safe popen added by Karl DeBisschop 9-11-99
13 * - expected-address parameter added by Alex Chaffee - 7 Oct 2002
14 *
15 * Command line: (see print_usage)
16 *
17 * Description:
18 *
19 * This program will use the nslookup program to obtain the IP address
20 * for a given host name. A optional DNS server may be specified. If
21 * no DNS server is specified, the default server(s) for the system
22 * are used.
23 *
24 * Return Values:
25 * OK The DNS query was successful (host IP address was returned).
26 * WARNING The DNS server responded, but could not fulfill the request.
27 * CRITICAL The DNS server is not responding or encountered an error.
28 *
29 * License Information:
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
44 *
45 *****************************************************************************/
47 #include "common.h"
48 #include "popen.h"
49 #include "utils.h"
51 int process_arguments (int, char **);
52 int call_getopt (int, char **);
53 int validate_arguments (void);
54 void print_usage (char *);
55 void print_help (char *);
56 int error_scan (char *);
58 #define ADDRESS_LENGTH 256
59 char query_address[ADDRESS_LENGTH] = "";
60 char dns_server[ADDRESS_LENGTH] = "";
61 char ptr_server[ADDRESS_LENGTH] = "";
62 int verbose = FALSE;
63 char expected_address[ADDRESS_LENGTH] = "";
64 int match_expected_address = FALSE;
66 int
67 main (int argc, char **argv)
68 {
69 char *command_line = NULL;
70 char input_buffer[MAX_INPUT_BUFFER];
71 char *output = NULL;
72 char *address = NULL;
73 char *temp_buffer = NULL;
74 int result = STATE_UNKNOWN;
76 /* Set signal handling and alarm */
77 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
78 printf ("Cannot catch SIGALRM");
79 return STATE_UNKNOWN;
80 }
82 if (process_arguments (argc, argv) != OK) {
83 print_usage (my_basename (argv[0]));
84 return STATE_UNKNOWN;
85 }
87 /* get the command to run */
88 command_line = ssprintf (command_line, "%s %s %s", NSLOOKUP_COMMAND,
89 query_address, dns_server);
91 alarm (timeout_interval);
92 time (&start_time);
94 if (verbose)
95 printf ("%s\n", command_line);
96 /* run the command */
97 child_process = spopen (command_line);
98 if (child_process == NULL) {
99 printf ("Could not open pipe: %s\n", command_line);
100 return STATE_UNKNOWN;
101 }
103 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
104 if (child_stderr == NULL)
105 printf ("Could not open stderr for %s\n", command_line);
107 /* scan stdout */
108 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
110 if (verbose)
111 printf ("%s\n", input_buffer);
113 if (strstr (input_buffer, ".in-addr.arpa")) {
114 if ((temp_buffer = strstr (input_buffer, "name = ")))
115 address = strscpy (address, temp_buffer + 7);
116 else {
117 output = strscpy (output, "Unknown error (plugin)");
118 result = STATE_WARNING;
119 }
120 }
122 /* the server is responding, we just got the host name... */
123 if (strstr (input_buffer, "Name:")) {
125 /* get the host address */
126 if (!fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
127 break;
129 if (verbose)
130 printf ("%s\n", input_buffer);
132 if ((temp_buffer = index (input_buffer, ':'))) {
133 address = strscpy (address, temp_buffer + 2);
134 strip (address);
135 result = STATE_OK;
136 }
137 else {
138 output = strscpy (output, "Unknown error (plugin)");
139 result = STATE_WARNING;
140 }
142 break;
143 }
145 result = error_scan (input_buffer);
146 if (result != STATE_OK) {
147 output = strscpy (output, 1 + index (input_buffer, ':'));
148 break;
149 }
151 }
153 /* scan stderr */
154 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
155 if (error_scan (input_buffer) != STATE_OK) {
156 result = max_state (result, error_scan (input_buffer));
157 output = strscpy (output, 1 + index (input_buffer, ':'));
158 }
159 }
161 /* close stderr */
162 (void) fclose (child_stderr);
164 /* close stdout */
165 if (spclose (child_process)) {
166 result = max_state (result, STATE_WARNING);
167 if (!strcmp (output, ""))
168 output = strscpy (output, "nslookup returned error status");
169 }
171 /* compare to expected address */
172 if (result == STATE_OK && match_expected_address && strcmp(address, expected_address)) {
173 result = STATE_CRITICAL;
174 output = ssprintf(output, "expected %s but got %s", expected_address, address);
175 }
177 (void) time (&end_time);
179 if (result == STATE_OK)
180 printf ("DNS ok - %d seconds response time, Address(es) is/are %s\n",
181 (int) (end_time - start_time), address);
182 else if (result == STATE_WARNING)
183 printf ("DNS WARNING - %s\n",
184 !strcmp (output, "") ? " Probably a non-existent host/domain" : output);
185 else if (result == STATE_CRITICAL)
186 printf ("DNS CRITICAL - %s\n",
187 !strcmp (output, "") ? " Probably a non-existent host/domain" : output);
188 else
189 printf ("DNS problem - %s\n",
190 !strcmp (output, "") ? " Probably a non-existent host/domain" : output);
192 return result;
193 }
195 int
196 error_scan (char *input_buffer)
197 {
199 /* the DNS lookup timed out */
200 if (strstr (input_buffer,
201 "Note: nslookup is deprecated and may be removed from future releases.")
202 || strstr (input_buffer,
203 "Consider using the `dig' or `host' programs instead. Run nslookup with")
204 || strstr (input_buffer,
205 "the `-sil[ent]' option to prevent this message from appearing."))
206 return STATE_OK;
208 /* the DNS lookup timed out */
209 else if (strstr (input_buffer, "Timed out"))
210 return STATE_WARNING;
212 /* DNS server is not running... */
213 else if (strstr (input_buffer, "No response from server"))
214 return STATE_CRITICAL;
216 /* Host name is valid, but server doesn't have records... */
217 else if (strstr (input_buffer, "No records"))
218 return STATE_WARNING;
220 /* Host or domain name does not exist */
221 else if (strstr (input_buffer, "Non-existent"))
222 return STATE_CRITICAL;
223 else if (strstr (input_buffer, "** server can't find"))
224 return STATE_CRITICAL;
225 else if(strstr(input_buffer,"NXDOMAIN")) /* 9.x */
226 return STATE_CRITICAL;
228 /* Connection was refused */
229 else if (strstr (input_buffer, "Connection refused"))
230 return STATE_CRITICAL;
232 /* Network is unreachable */
233 else if (strstr (input_buffer, "Network is unreachable"))
234 return STATE_CRITICAL;
236 /* Internal server failure */
237 else if (strstr (input_buffer, "Server failure"))
238 return STATE_CRITICAL;
240 /* DNS server refused to service request */
241 else if (strstr (input_buffer, "Refused"))
242 return STATE_CRITICAL;
244 /* Request error */
245 else if (strstr (input_buffer, "Format error"))
246 return STATE_WARNING;
248 else
249 return STATE_OK;
251 }
253 /* process command-line arguments */
254 int
255 process_arguments (int argc, char **argv)
256 {
257 int c;
259 if (argc < 2)
260 return ERROR;
262 for (c = 1; c < argc; c++)
263 if (strcmp ("-to", argv[c]) == 0)
264 strcpy (argv[c], "-t");
266 c = 0;
267 while (c += (call_getopt (argc - c, &argv[c]))) {
268 if (argc <= c)
269 break;
270 if (query_address[0] == 0) {
271 if (is_host (argv[c]) == FALSE) {
272 printf ("Invalid name/address: %s\n\n", argv[c]);
273 return ERROR;
274 }
275 if (strlen (argv[c]) >= ADDRESS_LENGTH)
276 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
277 strcpy (query_address, argv[c]);
278 }
279 else if (dns_server[0] == 0) {
280 if (is_host (argv[c]) == FALSE) {
281 printf ("Invalid name/address: %s\n\n", argv[c]);
282 return ERROR;
283 }
284 if (strlen (argv[c]) >= ADDRESS_LENGTH)
285 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
286 strcpy (dns_server, argv[c]);
287 }
288 }
290 return validate_arguments ();
292 }
294 int
295 call_getopt (int argc, char **argv)
296 {
297 int c, i = 1;
299 #ifdef HAVE_GETOPT_H
300 int opt_index = 0;
301 static struct option long_opts[] = {
302 {"help", no_argument, 0, 'h'},
303 {"version", no_argument, 0, 'V'},
304 {"verbose", no_argument, 0, 'v'},
305 {"timeout", required_argument, 0, 't'},
306 {"hostname", required_argument, 0, 'H'},
307 {"server", required_argument, 0, 's'},
308 {"reverse-server", required_argument, 0, 'r'},
309 {"expected-address", required_argument, 0, 'a'},
310 {0, 0, 0, 0}
311 };
312 #endif
315 while (1) {
316 #ifdef HAVE_GETOPT_H
317 c = getopt_long (argc, argv, "+?hVvt:H:s:r:a:", long_opts, &opt_index);
318 #else
319 c = getopt (argc, argv, "+?hVvt:H:s:r:a:");
320 #endif
322 if (c == -1 || c == EOF)
323 break;
325 i++;
326 switch (c) {
327 case 't':
328 case 'H':
329 case 's':
330 case 'r':
331 case 'a':
332 i++;
333 }
335 switch (c) {
336 case '?': /* args not parsable */
337 printf ("%s: Unknown argument: %s\n\n", my_basename (argv[0]), optarg);
338 print_usage (my_basename (argv[0]));
339 exit (STATE_UNKNOWN);
340 case 'h': /* help */
341 print_help (my_basename (argv[0]));
342 exit (STATE_OK);
343 case 'V': /* version */
344 print_revision (my_basename (argv[0]), "$Revision$");
345 exit (STATE_OK);
346 case 'v': /* version */
347 verbose = TRUE;
348 break;
349 case 't': /* timeout period */
350 timeout_interval = atoi (optarg);
351 break;
352 case 'H': /* hostname */
353 if (is_host (optarg) == FALSE) {
354 printf ("Invalid host name/address\n\n");
355 print_usage (my_basename (argv[0]));
356 exit (STATE_UNKNOWN);
357 }
358 if (strlen (optarg) >= ADDRESS_LENGTH)
359 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
360 strcpy (query_address, optarg);
361 break;
362 case 's': /* server name */
363 if (is_host (optarg) == FALSE) {
364 printf ("Invalid server name/address\n\n");
365 print_usage (my_basename (argv[0]));
366 exit (STATE_UNKNOWN);
367 }
368 if (strlen (optarg) >= ADDRESS_LENGTH)
369 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
370 strcpy (dns_server, optarg);
371 break;
372 case 'r': /* reverse server name */
373 if (is_host (optarg) == FALSE) {
374 printf ("Invalid host name/address\n\n");
375 print_usage (my_basename (argv[0]));
376 exit (STATE_UNKNOWN);
377 }
378 if (strlen (optarg) >= ADDRESS_LENGTH)
379 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
380 strcpy (ptr_server, optarg);
381 break;
382 case 'a': /* expected address */
383 if (is_dotted_quad (optarg) == FALSE) {
384 printf ("Invalid expected address\n\n");
385 print_usage (my_basename (argv[0]));
386 exit (STATE_UNKNOWN);
387 }
388 if (strlen (optarg) >= ADDRESS_LENGTH)
389 terminate (STATE_UNKNOWN, "Input buffer overflow\n");
390 strcpy (expected_address, optarg);
391 match_expected_address = TRUE;
392 break;
393 }
394 }
395 return i;
396 }
398 int
399 validate_arguments ()
400 {
401 if (query_address[0] == 0)
402 return ERROR;
403 else
404 return OK;
405 }
407 void
408 print_usage (char *cmd)
409 {
410 printf ("Usage: %s -H host [-s server] [-a expected-address] [-t timeout]\n" " %s --help\n"
411 " %s --version\n", cmd, cmd, cmd);
412 }
414 void
415 print_help (char *cmd)
416 {
417 print_revision (cmd, "$Revision$");
418 printf ("Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)\n\n");
419 print_usage (cmd);
420 printf ("\n");
421 printf
422 ("-H, --hostname=HOST\n"
423 " The name or address you want to query\n"
424 "-s, --server=HOST\n"
425 " Optional DNS server you want to use for the lookup\n"
426 "-a, --expected-address=IP-ADDRESS\n"
427 " Optional IP address you expect the DNS server to return\n"
428 "-t, --timeout=INTEGER\n"
429 " Seconds before connection times out (default: %d)\n"
430 "-h, --help\n"
431 " Print detailed help\n"
432 "-V, --version\n"
433 " Print version numbers and license information\n"
434 "\n"
435 "This plugin uses the nslookup program to obtain the IP address\n"
436 "for the given host/domain query. A optional DNS server to use may\n"
437 "be specified. If no DNS server is specified, the default server(s)\n"
438 "specified in /etc/resolv.conf will be used.\n", DEFAULT_SOCKET_TIMEOUT);
439 }