Code

* bugfix: snprintf of timestamp truncated '\0'
[nagiosplug.git] / plugins / check_dns.c
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"
50 #include "netutils.h"
52 const char *progname = "check_dns";
53 const char *revision = "$Revision$";
54 const char *copyright = "2000-2003";
55 const char *email = "nagiosplug-devel@lists.sourceforge.net";
57 int process_arguments (int, char **);
58 int validate_arguments (void);
59 int error_scan (char *);
60 void print_help (void);
61 void print_usage (void);
63 #define ADDRESS_LENGTH 256
64 char query_address[ADDRESS_LENGTH] = "";
65 char dns_server[ADDRESS_LENGTH] = "";
66 char ptr_server[ADDRESS_LENGTH] = "";
67 int verbose = FALSE;
68 char expected_address[ADDRESS_LENGTH] = "";
69 int match_expected_address = FALSE;
71 int
72 main (int argc, char **argv)
73 {
74         char *command_line = NULL;
75         char input_buffer[MAX_INPUT_BUFFER];
76         char *output = NULL;
77         char *address = NULL;
78         char *temp_buffer = NULL;
79         int result = STATE_UNKNOWN;
80         double elapsed_time;
81         long microsec;
82         struct timeval tv;
83         int multi_address;
85         /* Set signal handling and alarm */
86         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
87                 printf (_("Cannot catch SIGALRM"));
88                 return STATE_UNKNOWN;
89         }
91         if (process_arguments (argc, argv) != OK) {
92                 print_usage ();
93                 return STATE_UNKNOWN;
94         }
96         /* get the command to run */
97         asprintf (&command_line, "%s %s %s", NSLOOKUP_COMMAND,  query_address, dns_server);
99         alarm (timeout_interval);
100         gettimeofday (&tv, NULL);
102         if (verbose)
103                 printf ("%s\n", command_line);
104         /* run the command */
105         child_process = spopen (command_line);
106         if (child_process == NULL) {
107                 printf (_("Could not open pipe: %s\n"), command_line);
108                 return STATE_UNKNOWN;
109         }
111         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
112         if (child_stderr == NULL)
113                 printf (_("Could not open stderr for %s\n"), command_line);
115         /* scan stdout */
116         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
118                 if (verbose)
119                         printf ("%s\n", input_buffer);
121                 if (strstr (input_buffer, ".in-addr.arpa")) {
122                         if ((temp_buffer = strstr (input_buffer, "name = ")))
123                                 address = strdup (temp_buffer + 7);
124                         else {
125                                 output = strdup (_("Unknown error (plugin)"));
126                                 result = STATE_WARNING;
127                         }
128                 }
130                 /* the server is responding, we just got the host name... */
131                 if (strstr (input_buffer, "Name:")) {
133                         /* get the host address */
134                         if (!fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
135                                 break;
137                         if (verbose)
138                                 printf ("%s\n", input_buffer);
140                         if ((temp_buffer = index (input_buffer, ':'))) {
141                                 temp_buffer++;
142                                 /* Strip leading spaces */
143                                 for (; *temp_buffer != '\0' && *temp_buffer == ' '; temp_buffer++)
144                                         /* NOOP */;
145                                 address = strdup (temp_buffer);
146                                 strip (address);
147                                 if (address==NULL || strlen(address)==0)
148                                         die (STATE_CRITICAL,
149                                                                                  _("DNS CRITICAL - '%s' returned empty host name string\n"),
150                                                                                  NSLOOKUP_COMMAND);
151                                 result = STATE_OK;
152                         }
153                         else {
154                                 output = strdup (_("Unknown error (plugin)"));
155                                 result = STATE_WARNING;
156                         }
158                         break;
159                 }
161                 result = error_scan (input_buffer);
162                 if (result != STATE_OK) {
163                         output = strdup (1 + index (input_buffer, ':'));
164                         strip (output);
165                         break;
166                 }
168         }
170         /* scan stderr */
171         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
172                 if (error_scan (input_buffer) != STATE_OK) {
173                         result = max_state (result, error_scan (input_buffer));
174                         output = strdup (1 + index (input_buffer, ':'));
175                         strip (output);
176                 }
177         }
179         /* close stderr */
180         (void) fclose (child_stderr);
182         /* close stdout */
183         if (spclose (child_process)) {
184                 result = max_state (result, STATE_WARNING);
185                 if (!strcmp (output, ""))
186                         output = strdup (_("nslookup returned error status"));
187         }
189         /* If we got here, we should have an address string, 
190                  and we can segfault if we do not */
191         if (address==NULL || strlen(address)==0)
192                 die (STATE_CRITICAL,
193                                                          _("DNS CRITICAL - '%s' output parsing exited with no address\n"),
194                                                          NSLOOKUP_COMMAND);
196         /* compare to expected address */
197         if (result == STATE_OK && match_expected_address && strcmp(address, expected_address)) {
198                 result = STATE_CRITICAL;
199                 asprintf(&output, _("expected %s but got %s"), expected_address, address);
200         }
202         microsec = deltime (tv);
203         elapsed_time = (double)microsec / 1.0e6;
205         if (result == STATE_OK) {
206                 if (strchr (address, ',') == NULL)
207                         multi_address = FALSE;
208                 else
209                         multi_address = TRUE;
211                 printf (_("DNS ok - %.3f seconds response time, address%s %s|time=%ldus\n"),
212                                                 elapsed_time, (multi_address==TRUE ? "es are" : " is"), address, microsec);
213         }
214         else if (result == STATE_WARNING)
215                 printf (_("DNS WARNING - %s\n"),
216                                                 !strcmp (output, "") ? _(" Probably a non-existent host/domain") : output);
217         else if (result == STATE_CRITICAL)
218                 printf (_("DNS CRITICAL - %s\n"),
219                                                 !strcmp (output, "") ? _(" Probably a non-existent host/domain") : output);
220         else
221                 printf (_("DNS problem - %s\n"),
222                                                 !strcmp (output, "") ? _(" Probably a non-existent host/domain") : output);
224         return result;
227 int
228 error_scan (char *input_buffer)
231         /* the DNS lookup timed out */
232         if (strstr (input_buffer,       "Note:  nslookup is deprecated and may be removed from future releases.") ||
233             strstr (input_buffer, "Consider using the `dig' or `host' programs instead.  Run nslookup with") ||
234             strstr (input_buffer, "the `-sil[ent]' option to prevent this message from appearing."))
235                 return STATE_OK;
237         /* DNS server is not running... */
238         else if (strstr (input_buffer, "No response from server"))
239                 die (STATE_CRITICAL, _("No response from name server %s\n"), dns_server);
241         /* Host name is valid, but server doesn't have records... */
242         else if (strstr (input_buffer, "No records"))
243                 die (STATE_CRITICAL, _("Name server %s has no records\n"), dns_server);
245         /* Connection was refused */
246         else if (strstr (input_buffer, "Connection refused") ||
247                  (strstr (input_buffer, "** server can't find") &&
248                   strstr (input_buffer, ": REFUSED")) ||
249                  (strstr (input_buffer, "Refused")))
250                 die (STATE_CRITICAL, _("Connection to name server %s was refused\n"), dns_server);
252         /* Host or domain name does not exist */
253         else if (strstr (input_buffer, "Non-existent") ||
254                  strstr (input_buffer, "** server can't find") ||
255                  strstr (input_buffer,"NXDOMAIN"))
256                 die (STATE_CRITICAL, _("Domain %s was not found by the server\n"), query_address);
258         /* Network is unreachable */
259         else if (strstr (input_buffer, "Network is unreachable"))
260                 die (STATE_CRITICAL, _("Network is unreachable\n"));
262         /* Internal server failure */
263         else if (strstr (input_buffer, "Server failure"))
264                 die (STATE_CRITICAL, _("Server failure for %s\n"), dns_server);
266         /* Request error or the DNS lookup timed out */
267         else if (strstr (input_buffer, "Format error") ||
268                  strstr (input_buffer, "Timed out"))
269                 return STATE_WARNING;
271         return STATE_OK;
275 /* process command-line arguments */
276 int
277 process_arguments (int argc, char **argv)
279         int c;
281         int opt_index = 0;
282         static struct option long_opts[] = {
283                 {"help", no_argument, 0, 'h'},
284                 {"version", no_argument, 0, 'V'},
285                 {"verbose", no_argument, 0, 'v'},
286                 {"timeout", required_argument, 0, 't'},
287                 {"hostname", required_argument, 0, 'H'},
288                 {"server", required_argument, 0, 's'},
289                 {"reverse-server", required_argument, 0, 'r'},
290                 {"expected-address", required_argument, 0, 'a'},
291                 {0, 0, 0, 0}
292         };
294         if (argc < 2)
295                 return ERROR;
297         for (c = 1; c < argc; c++)
298                 if (strcmp ("-to", argv[c]) == 0)
299                         strcpy (argv[c], "-t");
301         while (1) {
302                 c = getopt_long (argc, argv, "hVvt:H:s:r:a:", long_opts, &opt_index);
304                 if (c == -1 || c == EOF)
305                         break;
307                 switch (c) {
308                 case '?': /* args not parsable */
309                         printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
310                         print_usage ();
311                         exit (STATE_UNKNOWN);
312                 case 'h': /* help */
313                         print_help ();
314                         exit (STATE_OK);
315                 case 'V': /* version */
316                         print_revision (progname, revision);
317                         exit (STATE_OK);
318                 case 'v': /* version */
319                         verbose = TRUE;
320                         break;
321                 case 't': /* timeout period */
322                         timeout_interval = atoi (optarg);
323                         break;
324                 case 'H': /* hostname */
325                         if (strlen (optarg) >= ADDRESS_LENGTH)
326                                 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
327                         strcpy (query_address, optarg);
328                         break;
329                 case 's': /* server name */
330                         /* TODO: this is_host check is probably unnecessary. Better to confirm nslookup
331                            response matches */
332                         if (is_host (optarg) == FALSE) {
333                                 printf (_("Invalid server name/address\n\n"));
334                                 print_usage ();
335                                 exit (STATE_UNKNOWN);
336                         }
337                         if (strlen (optarg) >= ADDRESS_LENGTH)
338                                 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
339                         strcpy (dns_server, optarg);
340                         break;
341                 case 'r': /* reverse server name */
342                         /* TODO: Is this is_host necessary? */
343                         if (is_host (optarg) == FALSE) {
344                                 printf (_("Invalid host name/address\n\n"));
345                                 print_usage ();
346                                 exit (STATE_UNKNOWN);
347                         }
348                         if (strlen (optarg) >= ADDRESS_LENGTH)
349                                 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
350                         strcpy (ptr_server, optarg);
351                         break;
352                 case 'a': /* expected address */
353                         if (strlen (optarg) >= ADDRESS_LENGTH)
354                                 die (STATE_UNKNOWN, _("Input buffer overflow\n"));
355                         strcpy (expected_address, optarg);
356                         match_expected_address = TRUE;
357                         break;
358                 }
359         }
361         c = optind;
362         if (strlen(query_address)==0 && c<argc) {
363                 if (strlen(argv[c])>=ADDRESS_LENGTH)
364                         die (STATE_UNKNOWN, _("Input buffer overflow\n"));
365                 strcpy (query_address, argv[c++]);
366         }
368         if (strlen(dns_server)==0 && c<argc) {
369                 /* TODO: See -s option */
370                 if (is_host(argv[c]) == FALSE) {
371                         printf (_("Invalid name/address: %s\n\n"), argv[c]);
372                         return ERROR;
373                 }
374                 if (strlen(argv[c]) >= ADDRESS_LENGTH)
375                         die (STATE_UNKNOWN, _("Input buffer overflow\n"));
376                 strcpy (dns_server, argv[c++]);
377         }
379         return validate_arguments ();
382 int
383 validate_arguments ()
385         if (query_address[0] == 0)
386                 return ERROR;
387         else
388                 return OK;
395 \f
396 void
397 print_help (void)
399         print_revision (progname, revision);
401         printf (_(COPYRIGHT), copyright, email);
403         print_usage ();
405         printf (_(UT_HELP_VRSN));
407         printf (_("\
408 -H, --hostname=HOST\n\
409    The name or address you want to query\n\
410 -s, --server=HOST\n\
411    Optional DNS server you want to use for the lookup\n\
412 -a, --expected-address=IP-ADDRESS\n\
413    Optional IP address you expect the DNS server to return\n"));
415         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
417         printf (_("\n\
418 This plugin uses the nslookup program to obtain the IP address\n\
419 for the given host/domain query.  A optional DNS server to use may\n\
420 be specified.  If no DNS server is specified, the default server(s)\n\
421 specified in /etc/resolv.conf will be used.\n"));
423         printf (_(UT_SUPPORT));
429 void
430 print_usage (void)
432         printf (_("\
433 Usage: %s -H host [-s server] [-a expected-address] [-t timeout]\n\
434        %s --help\n\
435        %s --version\n"),
436                                         progname, progname, progname);