Code

- hacked interim fix to segfault on redirect - for tesing only
[nagiosplug.git] / plugins / check_http.c
1 /******************************************************************************
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; either version 2 of the License, or
6  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ******************************************************************************/
19 const char *progname = "check_http";
20 const char *revision = "$Revision$";
21 const char *copyright = "1999-2001";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "netutils.h"
26 #include "utils.h"
28 #define HTTP_EXPECT "HTTP/1."
29 enum {
30         MAX_IPV4_HOSTLENGTH = 255,
31         HTTP_PORT = 80,
32         HTTPS_PORT = 443
33 };
35 #ifdef HAVE_SSL_H
36 #include <rsa.h>
37 #include <crypto.h>
38 #include <x509.h>
39 #include <pem.h>
40 #include <ssl.h>
41 #include <err.h>
42 #include <rand.h>
43 #else
44 # ifdef HAVE_OPENSSL_SSL_H
45 # include <openssl/rsa.h>
46 # include <openssl/crypto.h>
47 # include <openssl/x509.h>
48 # include <openssl/pem.h>
49 # include <openssl/ssl.h>
50 # include <openssl/err.h>
51 # include <openssl/rand.h>
52 # endif
53 #endif
55 #ifdef HAVE_SSL
56 int check_cert = FALSE;
57 int days_till_exp;
58 char *randbuff;
59 SSL_CTX *ctx;
60 SSL *ssl;
61 X509 *server_cert;
62 int connect_SSL (void);
63 int check_certificate (X509 **);
64 #endif
66 #ifdef HAVE_REGEX_H
67 enum {
68         REGS = 2,
69         MAX_RE_SIZE = 256
70 };
71 #include <regex.h>
72 regex_t preg;
73 regmatch_t pmatch[REGS];
74 char regexp[MAX_RE_SIZE];
75 char errbuf[MAX_INPUT_BUFFER];
76 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
77 int errcode;
78 #endif
80 struct timeval tv;
82 #define HTTP_URL "/"
83 #define CRLF "\r\n"
85 char timestamp[17] = "";
86 int specify_port = FALSE;
87 int server_port = HTTP_PORT;
88 char server_port_text[6] = "";
89 char server_type[6] = "http";
90 char *server_address;
91 char *host_name;
92 char *server_url;
93 char *user_agent;
94 int server_url_length;
95 int server_expect_yn = 0;
96 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
97 char string_expect[MAX_INPUT_BUFFER] = "";
98 double warning_time = 0;
99 int check_warning_time = FALSE;
100 double critical_time = 0;
101 int check_critical_time = FALSE;
102 char user_auth[MAX_INPUT_BUFFER] = "";
103 int display_html = FALSE;
104 int onredirect = STATE_OK;
105 int use_ssl = FALSE;
106 int verbose = FALSE;
107 int sd;
108 int min_page_len = 0;
109 int redir_depth = 0;
110 int max_depth = 15;
111 char *http_method;
112 char *http_post_data;
113 char buffer[MAX_INPUT_BUFFER];
115 int process_arguments (int, char **);
116 static char *base64 (const char *bin, size_t len);
117 int check_http (void);
118 int redir (char *pos, char *status_line);
119 int server_type_check(const char *type);
120 int server_port_check(int ssl_flag);
121 int my_recv (void);
122 int my_close (void);
123 void print_help (void);
124 void print_usage (void);
126 int
127 main (int argc, char **argv)
129         int result = STATE_UNKNOWN;
131         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
132         server_url = strdup(HTTP_URL);
133         server_url_length = strlen(server_url);
134         asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
135                   clean_revstring (revision), VERSION);
137         if (process_arguments (argc, argv) == ERROR)
138                 usage (_("check_http: could not parse arguments\n"));
140         if (strstr (timestamp, ":")) {
141                 if (strstr (server_url, "?"))
142                         asprintf (&server_url, "%s&%s", server_url, timestamp);
143                 else
144                         asprintf (&server_url, "%s?%s", server_url, timestamp);
145         }
147         if (display_html == TRUE)
148                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
149                         host_name, server_port, server_url);
151         /* initialize alarm signal handling, set socket timeout, start timer */
152         (void) signal (SIGALRM, socket_timeout_alarm_handler);
153         (void) alarm (socket_timeout);
154         gettimeofday (&tv, NULL);
156 #ifdef HAVE_SSL
157         if (use_ssl && check_cert == TRUE) {
158                 if (connect_SSL () != OK)
159                         die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
160                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
161                         result = check_certificate (&server_cert);
162                         X509_free (server_cert);
163                 }
164                 else {
165                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
166                         result = STATE_CRITICAL;
167                 }
168                 SSL_shutdown (ssl);
169                 SSL_free (ssl);
170                 SSL_CTX_free (ctx);
171                 close (sd);
172         }
173         else {
174                 result = check_http ();
175         }
176 #else
177         result = check_http ();
178 #endif
179         return result;
181 \f
184 /* process command-line arguments */
185 int
186 process_arguments (int argc, char **argv)
188         int c = 1;
190         int option = 0;
191         static struct option longopts[] = {
192                 STD_LONG_OPTS,
193                 {"file",required_argument,0,'F'},
194                 {"link", no_argument, 0, 'L'},
195                 {"nohtml", no_argument, 0, 'n'},
196                 {"ssl", no_argument, 0, 'S'},
197                 {"verbose", no_argument, 0, 'v'},
198                 {"post", required_argument, 0, 'P'},
199                 {"IP-address", required_argument, 0, 'I'},
200                 {"string", required_argument, 0, 's'},
201                 {"regex", required_argument, 0, 'r'},
202                 {"ereg", required_argument, 0, 'r'},
203                 {"eregi", required_argument, 0, 'R'},
204                 {"linespan", no_argument, 0, 'l'},
205                 {"onredirect", required_argument, 0, 'f'},
206                 {"certificate", required_argument, 0, 'C'},
207                 {"min", required_argument, 0, 'm'},
208                 {"use-ipv4", no_argument, 0, '4'},
209                 {"use-ipv6", no_argument, 0, '6'},
210                 {0, 0, 0, 0}
211         };
213         if (argc < 2)
214                 return ERROR;
216         for (c = 1; c < argc; c++) {
217                 if (strcmp ("-to", argv[c]) == 0)
218                         strcpy (argv[c], "-t");
219                 if (strcmp ("-hn", argv[c]) == 0)
220                         strcpy (argv[c], "-H");
221                 if (strcmp ("-wt", argv[c]) == 0)
222                         strcpy (argv[c], "-w");
223                 if (strcmp ("-ct", argv[c]) == 0)
224                         strcpy (argv[c], "-c");
225                 if (strcmp ("-nohtml", argv[c]) == 0)
226                         strcpy (argv[c], "-n");
227         }
229         while (1) {
230                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
231                 if (c == -1 || c == EOF)
232                         break;
234                 switch (c) {
235                 case '?': /* usage */
236                         usage3 (_("unknown argument"), optopt);
237                         break;
238                 case 'h': /* help */
239                         print_help ();
240                         exit (STATE_OK);
241                         break;
242                 case 'V': /* version */
243                         print_revision (progname, revision);
244                         exit (STATE_OK);
245                         break;
246                 case 't': /* timeout period */
247                         if (!is_intnonneg (optarg))
248                                 usage2 (_("timeout interval must be a non-negative integer"), optarg);
249                         else
250                                 socket_timeout = atoi (optarg);
251                         break;
252                 case 'c': /* critical time threshold */
253                         if (!is_intnonneg (optarg))
254                                 usage2 (_("invalid critical threshold"), optarg);
255                         else {
256                                 critical_time = strtod (optarg, NULL);
257                                 check_critical_time = TRUE;
258                         }
259                         break;
260                 case 'w': /* warning time threshold */
261                         if (!is_intnonneg (optarg))
262                                 usage2 (_("invalid warning threshold"), optarg);
263                         else {
264                                 warning_time = strtod (optarg, NULL);
265                                 check_warning_time = TRUE;
266                         }
267                         break;
268                 case 'L': /* show html link */
269                         display_html = TRUE;
270                         break;
271                 case 'n': /* do not show html link */
272                         display_html = FALSE;
273                         break;
274                 case 'S': /* use SSL */
275 #ifndef HAVE_SSL
276                         usage (_("check_http: invalid option - SSL is not available\n"));
277 #endif
278                         use_ssl = TRUE;
279                         if (specify_port == FALSE)
280                                 server_port = HTTPS_PORT;
281                         break;
282                 case 'C': /* Check SSL cert validity */
283 #ifdef HAVE_SSL
284                         if (!is_intnonneg (optarg))
285                                 usage2 (_("invalid certificate expiration period"), optarg);
286                         else {
287                                 days_till_exp = atoi (optarg);
288                                 check_cert = TRUE;
289                         }
290 #else
291                         usage (_("check_http: invalid option - SSL is not available\n"));
292 #endif
293                         break;
294                 case 'f': /* onredirect */
295                         if (!strcmp (optarg, "follow"))
296                                 onredirect = STATE_DEPENDENT;
297                         if (!strcmp (optarg, "unknown"))
298                                 onredirect = STATE_UNKNOWN;
299                         if (!strcmp (optarg, "ok"))
300                                 onredirect = STATE_OK;
301                         if (!strcmp (optarg, "warning"))
302                                 onredirect = STATE_WARNING;
303                         if (!strcmp (optarg, "critical"))
304                                 onredirect = STATE_CRITICAL;
305                         if (verbose)
306                                 printf(_("option f:%d \n"), onredirect);  
307                         break;
308                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
309                 case 'H': /* Host Name (virtual host) */
310                         host_name = strdup (optarg);
311                         break;
312                 case 'I': /* Server IP-address */
313                         server_address = strdup (optarg);
314                         break;
315                 case 'u': /* URL path */
316                         server_url = strdup (optarg);
317                         server_url_length = strlen (server_url);
318                         break;
319                 case 'p': /* Server port */
320                         if (!is_intnonneg (optarg))
321                                 usage2 (_("invalid port number"), optarg);
322                         else {
323                                 server_port = atoi (optarg);
324                                 specify_port = TRUE;
325                         }
326                         break;
327                 case 'a': /* authorization info */
328                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
329                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
330                         break;
331                 case 'P': /* HTTP POST data in URL encoded format */
332                         if (http_method || http_post_data) break;
333                         http_method = strdup("POST");
334                         http_post_data = strdup (optarg);
335                         break;
336                 case 's': /* string or substring */
337                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
338                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
339                         break;
340                 case 'e': /* string or substring */
341                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
342                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
343                         server_expect_yn = 1;
344                         break;
345 #ifndef HAVE_REGEX_H
346                 case 'l': /* linespan */
347                 case 'r': /* linespan */
348                 case 'R': /* linespan */
349                         usage (_("check_http: call for regex which was not a compiled option\n"));
350                         break;
351 #else
352                 case 'l': /* linespan */
353                         cflags &= ~REG_NEWLINE;
354                         break;
355                 case 'R': /* regex */
356                         cflags |= REG_ICASE;
357                 case 'r': /* regex */
358                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
359                         regexp[MAX_RE_SIZE - 1] = 0;
360                         errcode = regcomp (&preg, regexp, cflags);
361                         if (errcode != 0) {
362                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
363                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
364                                 return ERROR;
365                         }
366                         break;
367 #endif
368                 case '4':
369                         address_family = AF_INET;
370                         break;
371                 case '6':
372 #ifdef USE_IPV6
373                         address_family = AF_INET6;
374 #else
375                         usage (_("IPv6 support not available\n"));
376 #endif
377                         break;
378                 case 'v': /* verbose */
379                         verbose = TRUE;
380                         break;
381                 case 'm': /* min_page_length */
382                         min_page_len = atoi (optarg);
383                         break;
384                 }
385         }
387         c = optind;
389         if (server_address == NULL && c < argc)
390                 server_address = strdup (argv[c++]);
392         if (host_name == NULL && c < argc)
393                 host_name = strdup (argv[c++]);
395         if (server_address == NULL) {
396                 if (host_name == NULL)
397                         usage (_("check_http: you must specify a server address or host name\n"));
398                 else
399                         server_address = strdup (host_name);
400         }
402         if (check_critical_time && critical_time>(double)socket_timeout)
403                 socket_timeout = (int)critical_time + 1;
405         if (http_method == NULL)
406                 http_method = strdup ("GET");
408         return TRUE;
410 \f
413 /* written by lauri alanko */
414 static char *
415 base64 (const char *bin, size_t len)
418         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
419         size_t i = 0, j = 0;
421         char BASE64_END = '=';
422         char base64_table[64];
423         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
425         while (j < len - 2) {
426                 buf[i++] = base64_table[bin[j] >> 2];
427                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
428                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
429                 buf[i++] = base64_table[bin[j + 2] & 63];
430                 j += 3;
431         }
433         switch (len - j) {
434         case 1:
435                 buf[i++] = base64_table[bin[j] >> 2];
436                 buf[i++] = base64_table[(bin[j] & 3) << 4];
437                 buf[i++] = BASE64_END;
438                 buf[i++] = BASE64_END;
439                 break;
440         case 2:
441                 buf[i++] = base64_table[bin[j] >> 2];
442                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
443                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
444                 buf[i++] = BASE64_END;
445                 break;
446         case 0:
447                 break;
448         }
450         buf[i] = '\0';
451         return buf;
453 \f
458 int
459 check_http (void)
461         char *msg;
462         char *status_line;
463         char *header;
464         char *page;
465         char *auth;
466         int i = 0;
467         size_t pagesize = 0;
468         char *full_page;
469         char *buf;
470         char *pos;
471         long microsec;
472         double elapsed_time;
473         int page_len = 0;
474 #ifdef HAVE_SSL
475         int sslerr;
476 #endif
478         if (verbose)
479                 printf ("%s://%s:%d%s [%s]\n", server_type, server_address, server_port, server_url, host_name);
481         /* try to connect to the host at the given port number */
482 #ifdef HAVE_SSL
483         if (use_ssl == TRUE) {
485                 if (connect_SSL () != OK) {
486                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
487                 }
489                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
490                         X509_free (server_cert);
491                 }
492                 else {
493                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
494                         return STATE_CRITICAL;
495                 }
497         }
498         else {
499 #endif
500                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
501                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
502 #ifdef HAVE_SSL
503         }
504 #endif
506         asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
508         /* optionally send the host header info */
509         if (host_name)
510                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
512         /* optionally send the authentication info */
513         if (strlen(user_auth)) {
514                 auth = base64 (user_auth, strlen (user_auth));
515                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
516         }
518         if (verbose)
519                 printf ("%s://%s:%d%s\n", server_type, server_address, server_port, server_url);
521         /* either send http POST data */
522         if (http_post_data) {
523                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
524                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
525                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
526         }
527         else {
528                 /* or just a newline so the server knows we're done with the request */
529                 asprintf (&buf, "%s%s", buf, CRLF);
530         }
532         if (verbose)
533                 printf ("%s\n", buf);
535 #ifdef HAVE_SSL
536         if (use_ssl == TRUE) {
537                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
538                         ERR_print_errors_fp (stderr);
539                         return STATE_CRITICAL;
540                 }
541         }
542         else {
543 #endif
544                 send (sd, buf, strlen (buf), 0);
545 #ifdef HAVE_SSL
546         }
547 #endif
549         /* fetch the page */
550         full_page = strdup("");
551         while ((i = my_recv ()) > 0) {
552                 buffer[i] = '\0';
553                 asprintf (&full_page, "%s%s", full_page, buffer);
554                 pagesize += i;
555         }
557         if (i < 0 && errno != ECONNRESET) {
558 #ifdef HAVE_SSL
559                 if (use_ssl) {
560                         sslerr=SSL_get_error(ssl, i);
561                         if ( sslerr == SSL_ERROR_SSL ) {
562                                 die (STATE_WARNING, _("Client Certificate Required\n"));
563                         } else {
564                                 die (STATE_CRITICAL, _("Error in recv()\n"));
565                         }
566                 }
567                 else {
568 #endif
569                         die (STATE_CRITICAL, _("Error in recv()\n"));
570 #ifdef HAVE_SSL
571                 }
572 #endif
573         }
575         /* return a CRITICAL status if we couldn't read any data */
576         if (pagesize == (size_t) 0)
577                 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
579         /* close the connection */
580         my_close ();
582         /* reset the alarm */
583         alarm (0);
585         /* leave full_page untouched so we can free it later */
586         page = full_page;
588         if (verbose)
589                 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
591         /* find status line and null-terminate it */
592         status_line = page;
593         page += (size_t) strcspn (page, "\r\n");
594         pos = page;
595         page += (size_t) strspn (page, "\r\n");
596         status_line[strcspn(status_line, "\r\n")] = 0;
597         strip (status_line);
598         if (verbose)
599                 printf ("STATUS: %s\n", status_line);
601         /* find header info and null-terminate it */
602         header = page;
603         while (strcspn (page, "\r\n") > 0) {
604                 page += (size_t) strcspn (page, "\r\n");
605                 pos = page;
606                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
607                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
608                         page += (size_t) 2;
609                 else
610                         page += (size_t) 1;
611         }
612         page += (size_t) strspn (page, "\r\n");
613         header[pos - header] = 0;
614         if (verbose)
615                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
617         /* make sure the status line matches the response we are looking for */
618         if (!strstr (status_line, server_expect)) {
619                 if (server_port == HTTP_PORT)
620                         asprintf (&msg, _("Invalid HTTP response received from host\n"));
621                 else
622                         asprintf (&msg,
623                                         _("Invalid HTTP response received from host on port %d\n"),
624                                         server_port);
625                 die (STATE_CRITICAL, "%s", msg);
626         }
628         /* Exit here if server_expect was set by user and not default */
629         if ( server_expect_yn  )  {
630                 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
631                           server_expect);
632                 if (verbose)
633                         printf ("%s\n",msg);
635         }
636         else {
637         
639                 /* check the return code */
640                 /* server errors result in a critical state */
641                 if (strstr (status_line, "500") || strstr (status_line, "501") ||
642                     strstr (status_line, "502") || strstr (status_line, "503") ||
643                     strstr (status_line, "504") || strstr (status_line, "505")) {
644                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
645                 }
647                 /* client errors result in a warning state */
648                 if (strstr (status_line, "400") || strstr (status_line, "401") ||
649                     strstr (status_line, "402") || strstr (status_line, "403") ||
650                     strstr (status_line, "404") || strstr (status_line, "405") ||
651                     strstr (status_line, "406") || strstr (status_line, "407") ||
652                     strstr (status_line, "408") || strstr (status_line, "409") ||
653                     strstr (status_line, "410") || strstr (status_line, "411") ||
654                     strstr (status_line, "412") || strstr (status_line, "413") ||
655                     strstr (status_line, "414") || strstr (status_line, "415") ||
656                     strstr (status_line, "416") || strstr (status_line, "417")) {
657                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
658                 }
660                 /* check redirected page if specified */
661                 if (strstr (status_line, "300") || strstr (status_line, "301") ||
662                     strstr (status_line, "302") || strstr (status_line, "303") ||
663                     strstr (status_line, "304") || strstr (status_line, "305") ||
664                     strstr (status_line, "306")) {
666                         if (onredirect == STATE_DEPENDENT)
667                                 redir (header, status_line);
668                         else if (onredirect == STATE_UNKNOWN)
669                                 printf (_("UNKNOWN"));
670                         else if (onredirect == STATE_OK)
671                                 printf (_("OK"));
672                         else if (onredirect == STATE_WARNING)
673                                 printf (_("WARNING"));
674                         else if (onredirect == STATE_CRITICAL)
675                                 printf (_("CRITICAL"));
676                         microsec = deltime (tv);
677                         elapsed_time = (double)microsec / 1.0e6;
678                         asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
679                                  status_line, elapsed_time, timestamp,
680                            (display_html ? "</A>" : ""), microsec, pagesize);
681                         die (onredirect, "%s", msg);
682                 } /* end if (strstr (status_line, "30[0-4]") */
685         } /* end else (server_expect_yn)  */
687                 
688         /* check elapsed time */
689         microsec = deltime (tv);
690         elapsed_time = (double)microsec / 1.0e6;
691         asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
692                        status_line, elapsed_time, timestamp,
693                        (display_html ? "</A>" : ""), microsec, pagesize);
694         if (check_critical_time == TRUE && elapsed_time > critical_time)
695                 die (STATE_CRITICAL, "%s", msg);
696         if (check_warning_time == TRUE && elapsed_time > warning_time)
697                 die (STATE_WARNING, "%s", msg);
699         /* Page and Header content checks go here */
700         /* these checks should be last */
702         if (strlen (string_expect)) {
703                 if (strstr (page, string_expect)) {
704                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
705                                 status_line, elapsed_time,
706                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
707                         exit (STATE_OK);
708                 }
709                 else {
710                         printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
711                                 (display_html ? "</A>" : ""), microsec, pagesize);
712                         exit (STATE_CRITICAL);
713                 }
714         }
715 #ifdef HAVE_REGEX_H
716         if (strlen (regexp)) {
717                 errcode = regexec (&preg, page, REGS, pmatch, 0);
718                 if (errcode == 0) {
719                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
720                                 status_line, elapsed_time,
721                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
722                         exit (STATE_OK);
723                 }
724                 else {
725                         if (errcode == REG_NOMATCH) {
726                                 printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
727                                         (display_html ? "</A>" : ""), microsec, pagesize);
728                                 exit (STATE_CRITICAL);
729                         }
730                         else {
731                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
732                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
733                                 exit (STATE_CRITICAL);
734                         }
735                 }
736         }
737 #endif
739         /* make sure the page is of an appropriate size */
740         page_len = strlen (page);
741         if ((min_page_len > 0) && (page_len < min_page_len)) {
742                 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
743                         (display_html ? "</A>" : ""), page_len );
744                 exit (STATE_WARNING);
745         }
746         /* We only get here if all tests have been passed */
747         asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
748                         status_line, elapsed_time,
749                         timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
750         die (STATE_OK, "%s", msg);
751         return STATE_UNKNOWN;
757 /* per RFC 2396 */
758 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
759 #define URI_HTTP "%[HTPShtps]://"
760 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
761 #define URI_PORT ":%[0123456789]"
762 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
763 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
764 #define HD2 URI_HTTP URI_HOST URI_PATH
765 #define HD3 URI_HTTP URI_HOST URI_PORT
766 #define HD4 URI_HTTP URI_HOST
767 #define HD5 URI_PATH
769 int
770 redir (char *pos, char *status_line)
772         int i = 0;
773         char *x;
774         char xx[2];
775         char type[6];
776         char *addr;
777         char port[6];
778         char *url;
780         addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
781         if (addr == NULL)
782                 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
783         
784         url = malloc (strcspn (pos, "\r\n"));
785         if (url == NULL)
786                 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
788         while (pos) {
790                 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) > 0) {
792                         pos += i;
793                         pos += strspn (pos, " \t\r\n");
795                         /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
796                         if (sscanf (pos, HD1, type, addr, port, url) == 4) {
797                                 use_ssl = server_type_check (type);
798                                 i = atoi (port);
799                         }
801                         /* URI_HTTP URI_HOST URI_PATH */
802                         else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
803                                 use_ssl = server_type_check (type);
804                                 i = server_port_check (use_ssl);
805                         }
807                         /* URI_HTTP URI_HOST URI_PORT */
808                         else if(sscanf (pos, HD3, type, addr, port) == 3) {
809                                 strcpy (url, HTTP_URL);
810                                 use_ssl = server_type_check (type);
811                                 i = atoi (port);
812                         }
814                         /* URI_HTTP URI_HOST */
815                         else if(sscanf (pos, HD4, type, addr) == 2) {
816                                 strcpy (url, HTTP_URL);
817                                 use_ssl = server_type_check (type);
818                                 i = server_port_check (use_ssl);
819                         }
821                         /* URI_PATH */
822                         else if (sscanf (pos, HD5, url) == 1) {
823                                 /* relative url */
824                                 if ((url[0] != '/')) {
825                                         if ((x = strrchr(url, '/')))
826                                                 *x = '\0';
827                                         asprintf (&server_url, "%s/%s", server_url, url);
828                                 }
829                                 i = server_port;
830                                 strcpy (type, server_type);
831                                 strcpy (addr, host_name);
832                         }                                       
834                         else {
835                                 die (STATE_UNKNOWN,
836                                                  _("UNKNOWN - Could not parse redirect location - %s%s\n"),
837                                                  pos, (display_html ? "</A>" : ""));
838                         }
840                         break;
842                 } else {
844                         pos += (size_t) strcspn (pos, "\r\n");
845                         pos += (size_t) strspn (pos, "\r\n");
846                         if (strlen(pos) == 0) 
847                                 die (STATE_UNKNOWN,
848                                                  _("UNKNOWN - Could not find redirect location - %s%s\n"),
849                                                  status_line, (display_html ? "</A>" : ""));
851                 }
853         } /* end while (pos) */
855         if (++redir_depth > max_depth)
856                 die (STATE_WARNING,
857                      _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
858                      max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
860         if (server_port==i &&
861             !strcmp(server_address, addr) &&
862             (host_name && !strcmp(host_name, addr)) &&
863             !strcmp(server_url, url))
864                 die (STATE_WARNING,
865                      _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
866                      type, addr, i, url, (display_html ? "</A>" : ""));
868         server_port = i;
869         strcpy (server_type, type);
871         free (host_name);
872         host_name = strdup (addr);
874         free (server_address);
875         server_address = strdup (addr);
877         free (server_url);
878         server_url = strdup (url);
880         return check_http ();
885 int
886 server_type_check (const char *type)
888         if (strcmp (type, "https"))
889                 return FALSE;
890         else
891                 return TRUE;
894 int
895 server_port_check (int ssl_flag)
897         if (ssl_flag)
898                 return HTTPS_PORT;
899         else
900                 return HTTP_PORT;
905 #ifdef HAVE_SSL
906 int connect_SSL (void)
908         SSL_METHOD *meth;
910         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
911         RAND_seed (randbuff, (int)strlen(randbuff));
912         if (verbose)
913                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
915         /* Initialize SSL context */
916         SSLeay_add_ssl_algorithms ();
917         meth = SSLv23_client_method ();
918         SSL_load_error_strings ();
919         if ((ctx = SSL_CTX_new (meth)) == NULL) {
920                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
921                 return STATE_CRITICAL;
922         }
924         /* Initialize alarm signal handling */
925         signal (SIGALRM, socket_timeout_alarm_handler);
927         /* Set socket timeout */
928         alarm (socket_timeout);
930         /* Save start time */
931         gettimeofday (&tv, NULL);
933         /* Make TCP connection */
934         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
935                 /* Do the SSL handshake */
936                 if ((ssl = SSL_new (ctx)) != NULL) {
937                         SSL_set_cipher_list(ssl, "ALL");
938                         SSL_set_fd (ssl, sd);
939                         if (SSL_connect (ssl) != -1)
940                                 return OK;
941                         ERR_print_errors_fp (stderr);
942                 }
943                 else {
944                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
945                 }
946                 SSL_free (ssl);
947         }
949         SSL_CTX_free (ctx);
950         close (sd);
952         return STATE_CRITICAL;
954 #endif
956 #ifdef HAVE_SSL
957 int
958 check_certificate (X509 ** certificate)
960         ASN1_STRING *tm;
961         int offset;
962         struct tm stamp;
963         int days_left;
966         /* Retrieve timestamp of certificate */
967         tm = X509_get_notAfter (*certificate);
969         /* Generate tm structure to process timestamp */
970         if (tm->type == V_ASN1_UTCTIME) {
971                 if (tm->length < 10) {
972                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
973                         return STATE_CRITICAL;
974                 }
975                 else {
976                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
977                         if (stamp.tm_year < 50)
978                                 stamp.tm_year += 100;
979                         offset = 0;
980                 }
981         }
982         else {
983                 if (tm->length < 12) {
984                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
985                         return STATE_CRITICAL;
986                 }
987                 else {
988                         stamp.tm_year =
989                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
990                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
991                         stamp.tm_year -= 1900;
992                         offset = 2;
993                 }
994         }
995         stamp.tm_mon =
996                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
997         stamp.tm_mday =
998                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
999         stamp.tm_hour =
1000                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1001         stamp.tm_min =
1002                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1003         stamp.tm_sec = 0;
1004         stamp.tm_isdst = -1;
1006         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1007         snprintf
1008                 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1009                  stamp.tm_mon + 1,
1010                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1012         if (days_left > 0 && days_left <= days_till_exp) {
1013                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1014                 return STATE_WARNING;
1015         }
1016         if (days_left < 0) {
1017                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1018                 return STATE_CRITICAL;
1019         }
1021         if (days_left == 0) {
1022                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1023                 return STATE_WARNING;
1024         }
1026         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1028         return STATE_OK;
1030 #endif
1031 \f
1034 int
1035 my_recv (void)
1037         int i;
1038 #ifdef HAVE_SSL
1039         if (use_ssl) {
1040                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1041         }
1042         else {
1043                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1044         }
1045 #else
1046         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1047 #endif
1048         return i;
1052 int
1053 my_close (void)
1055 #ifdef HAVE_SSL
1056         if (use_ssl == TRUE) {
1057                 SSL_shutdown (ssl);
1058                 SSL_free (ssl);
1059                 SSL_CTX_free (ctx);
1060                 return 0;
1061         }
1062         else {
1063 #endif
1064                 return close (sd);
1065 #ifdef HAVE_SSL
1066         }
1067 #endif
1074 \f
1075 void
1076 print_help (void)
1078         print_revision (progname, revision);
1080         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1081         printf (_(COPYRIGHT), copyright, email);
1083         printf (_("\
1084 This plugin tests the HTTP service on the specified host. It can test\n\
1085 normal (http) and secure (https) servers, follow redirects, search for\n\
1086 strings and regular expressions, check connection times, and report on\n\
1087 certificate expiration times.\n"));
1089         print_usage ();
1091         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1093         printf (_(UT_HELP_VRSN));
1095         printf (_("\
1096  -H, --hostname=ADDRESS\n\
1097     Host name argument for servers using host headers (virtual host)\n\
1098  -I, --IP-address=ADDRESS\n\
1099    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1100  -p, --port=INTEGER\n\
1101    Port number (default: %d)\n"), HTTP_PORT);
1103         printf (_(UT_IPv46));
1105 #ifdef HAVE_SSL
1106         printf (_("\
1107  -S, --ssl\n\
1108     Connect via SSL\n\
1109  -C, --certificate=INTEGER\n\
1110     Minimum number of days a certificate has to be valid.\n\
1111     (when this option is used the url is not checked.)\n"));
1112 #endif
1114         printf (_("\
1115  -e, --expect=STRING\n\
1116    String to expect in first (status) line of server response (default: %s)\n\
1117    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1118  -s, --string=STRING\n\
1119    String to expect in the content\n\
1120  -u, --url=PATH\n\
1121    URL to GET or POST (default: /)\n\
1122  -P, --post=STRING\n\
1123    URL encoded http POST data\n"), HTTP_EXPECT);
1125 #ifdef HAVE_REGEX_H
1126         printf (_("\
1127  -l, --linespan\n\
1128     Allow regex to span newlines (must precede -r or -R)\n\
1129  -r, --regex, --ereg=STRING\n\
1130     Search page for regex STRING\n\
1131  -R, --eregi=STRING\n\
1132     Search page for case-insensitive regex STRING\n"));
1133 #endif
1135         printf (_("\
1136  -a, --authorization=AUTH_PAIR\n\
1137    Username:password on sites with basic authentication\n\
1138  -L, --link=URL\n\
1139    Wrap output in HTML link (obsoleted by urlize)\n\
1140  -f, --onredirect=<ok|warning|critical|follow>\n\
1141    How to handle redirected pages\n\
1142  -m, --min=INTEGER\n\
1143    Minimum page size required (bytes)\n"));
1145         printf (_(UT_WARN_CRIT));
1147         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1149         printf (_(UT_VERBOSE));
1151                                         printf (_("\
1152 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1153 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1154 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1155 messages from the host result in STATE_WARNING return values.  If you are\n\
1156 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1157 (fully qualified domain name) as the [host_name] argument.\n"));
1159 #ifdef HAVE_SSL
1160         printf (_("\n\
1161 This plugin can also check whether an SSL enabled web server is able to\n\
1162 serve content (optionally within a specified time) or whether the X509 \n\
1163 certificate is still valid for the specified number of days.\n"));
1164         printf (_("\n\
1165 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1166 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1167 STATE_OK will be returned. When the server returns its content but exceeds\n\
1168 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1169 a STATE_CRITICAL will be returned.\n\n"));
1171         printf (_("\
1172 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1173 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1174 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1175 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1176 the certificate is expired.\n"));
1177 #endif
1179         printf (_(UT_SUPPORT));
1186 void
1187 print_usage (void)
1189         printf (_("\
1190 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1191   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1192   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1193   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1194   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1195         printf (_(UT_HLP_VRS), progname, progname);