Code

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