Code

c68b2087da55d81a20b19af2e807d7d8df72f865
[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 int my_recv (void);
124 int my_close (void);
125 void print_help (void);
126 void print_usage (void);
128 int
129 main (int argc, char **argv)
131         int result = STATE_UNKNOWN;
133         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
134         server_url = strdup(HTTP_URL);
135         server_url_length = strlen(server_url);
136         asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
137                   clean_revstring (revision), VERSION);
139         if (process_arguments (argc, argv) == ERROR)
140                 usage (_("check_http: could not parse arguments\n"));
142         if (strstr (timestamp, ":")) {
143                 if (strstr (server_url, "?"))
144                         asprintf (&server_url, "%s&%s", server_url, timestamp);
145                 else
146                         asprintf (&server_url, "%s?%s", server_url, timestamp);
147         }
149         if (display_html == TRUE)
150                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
151                         host_name, server_port, server_url);
153         /* initialize alarm signal handling, set socket timeout, start timer */
154         (void) signal (SIGALRM, socket_timeout_alarm_handler);
155         (void) alarm (socket_timeout);
156         gettimeofday (&tv, NULL);
158 #ifdef HAVE_SSL
159         if (use_ssl && check_cert == TRUE) {
160                 if (connect_SSL () != OK)
161                         die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
162                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
163                         result = check_certificate (&server_cert);
164                         X509_free (server_cert);
165                 }
166                 else {
167                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
168                         result = STATE_CRITICAL;
169                 }
170                 SSL_shutdown (ssl);
171                 SSL_free (ssl);
172                 SSL_CTX_free (ctx);
173                 close (sd);
174         }
175         else {
176                 result = check_http ();
177         }
178 #else
179         result = check_http ();
180 #endif
181         return result;
183 \f
186 /* process command-line arguments */
187 int
188 process_arguments (int argc, char **argv)
190         int c = 1;
192         int option = 0;
193         static struct option longopts[] = {
194                 STD_LONG_OPTS,
195                 {"file",required_argument,0,'F'},
196                 {"link", no_argument, 0, 'L'},
197                 {"nohtml", no_argument, 0, 'n'},
198                 {"ssl", no_argument, 0, 'S'},
199                 {"verbose", no_argument, 0, 'v'},
200                 {"post", required_argument, 0, 'P'},
201                 {"IP-address", required_argument, 0, 'I'},
202                 {"string", required_argument, 0, 's'},
203                 {"regex", required_argument, 0, 'r'},
204                 {"ereg", required_argument, 0, 'r'},
205                 {"eregi", required_argument, 0, 'R'},
206                 {"linespan", no_argument, 0, 'l'},
207                 {"onredirect", required_argument, 0, 'f'},
208                 {"certificate", required_argument, 0, 'C'},
209                 {"min", required_argument, 0, 'm'},
210                 {"use-ipv4", no_argument, 0, '4'},
211                 {"use-ipv6", no_argument, 0, '6'},
212                 {0, 0, 0, 0}
213         };
215         if (argc < 2)
216                 return ERROR;
218         for (c = 1; c < argc; c++) {
219                 if (strcmp ("-to", argv[c]) == 0)
220                         strcpy (argv[c], "-t");
221                 if (strcmp ("-hn", argv[c]) == 0)
222                         strcpy (argv[c], "-H");
223                 if (strcmp ("-wt", argv[c]) == 0)
224                         strcpy (argv[c], "-w");
225                 if (strcmp ("-ct", argv[c]) == 0)
226                         strcpy (argv[c], "-c");
227                 if (strcmp ("-nohtml", argv[c]) == 0)
228                         strcpy (argv[c], "-n");
229         }
231         while (1) {
232                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
233                 if (c == -1 || c == EOF)
234                         break;
236                 switch (c) {
237                 case '?': /* usage */
238                         usage3 (_("unknown argument"), optopt);
239                         break;
240                 case 'h': /* help */
241                         print_help ();
242                         exit (STATE_OK);
243                         break;
244                 case 'V': /* version */
245                         print_revision (progname, revision);
246                         exit (STATE_OK);
247                         break;
248                 case 't': /* timeout period */
249                         if (!is_intnonneg (optarg))
250                                 usage2 (_("timeout interval must be a non-negative integer"), optarg);
251                         else
252                                 socket_timeout = atoi (optarg);
253                         break;
254                 case 'c': /* critical time threshold */
255                         if (!is_intnonneg (optarg))
256                                 usage2 (_("invalid critical threshold"), optarg);
257                         else {
258                                 critical_time = strtod (optarg, NULL);
259                                 check_critical_time = TRUE;
260                         }
261                         break;
262                 case 'w': /* warning time threshold */
263                         if (!is_intnonneg (optarg))
264                                 usage2 (_("invalid warning threshold"), optarg);
265                         else {
266                                 warning_time = strtod (optarg, NULL);
267                                 check_warning_time = TRUE;
268                         }
269                         break;
270                 case 'L': /* show html link */
271                         display_html = TRUE;
272                         break;
273                 case 'n': /* do not show html link */
274                         display_html = FALSE;
275                         break;
276                 case 'S': /* use SSL */
277 #ifndef HAVE_SSL
278                         usage (_("check_http: invalid option - SSL is not available\n"));
279 #endif
280                         use_ssl = TRUE;
281                         if (specify_port == FALSE)
282                                 server_port = HTTPS_PORT;
283                         break;
284                 case 'C': /* Check SSL cert validity */
285 #ifdef HAVE_SSL
286                         if (!is_intnonneg (optarg))
287                                 usage2 (_("invalid certificate expiration period"), optarg);
288                         else {
289                                 days_till_exp = atoi (optarg);
290                                 check_cert = TRUE;
291                         }
292 #else
293                         usage (_("check_http: invalid option - SSL is not available\n"));
294 #endif
295                         break;
296                 case 'f': /* onredirect */
297                         if (!strcmp (optarg, "follow"))
298                                 onredirect = STATE_DEPENDENT;
299                         if (!strcmp (optarg, "unknown"))
300                                 onredirect = STATE_UNKNOWN;
301                         if (!strcmp (optarg, "ok"))
302                                 onredirect = STATE_OK;
303                         if (!strcmp (optarg, "warning"))
304                                 onredirect = STATE_WARNING;
305                         if (!strcmp (optarg, "critical"))
306                                 onredirect = STATE_CRITICAL;
307                         if (verbose)
308                                 printf(_("option f:%d \n"), onredirect);  
309                         break;
310                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
311                 case 'H': /* Host Name (virtual host) */
312                         host_name = strdup (optarg);
313                         break;
314                 case 'I': /* Server IP-address */
315                         server_address = strdup (optarg);
316                         break;
317                 case 'u': /* URL path */
318                         server_url = strdup (optarg);
319                         server_url_length = strlen (server_url);
320                         break;
321                 case 'p': /* Server port */
322                         if (!is_intnonneg (optarg))
323                                 usage2 (_("invalid port number"), optarg);
324                         else {
325                                 server_port = atoi (optarg);
326                                 specify_port = TRUE;
327                         }
328                         break;
329                 case 'a': /* authorization info */
330                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
331                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
332                         break;
333                 case 'P': /* HTTP POST data in URL encoded format */
334                         if (http_method || http_post_data) break;
335                         http_method = strdup("POST");
336                         http_post_data = strdup (optarg);
337                         break;
338                 case 's': /* string or substring */
339                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
340                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
341                         break;
342                 case 'e': /* string or substring */
343                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
344                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
345                         server_expect_yn = 1;
346                         break;
347 #ifndef HAVE_REGEX_H
348                 case 'l': /* linespan */
349                 case 'r': /* linespan */
350                 case 'R': /* linespan */
351                         usage (_("check_http: call for regex which was not a compiled option\n"));
352                         break;
353 #else
354                 case 'l': /* linespan */
355                         cflags &= ~REG_NEWLINE;
356                         break;
357                 case 'R': /* regex */
358                         cflags |= REG_ICASE;
359                 case 'r': /* regex */
360                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
361                         regexp[MAX_RE_SIZE - 1] = 0;
362                         errcode = regcomp (&preg, regexp, cflags);
363                         if (errcode != 0) {
364                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
365                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
366                                 return ERROR;
367                         }
368                         break;
369 #endif
370                 case '4':
371                         address_family = AF_INET;
372                         break;
373                 case '6':
374 #ifdef USE_IPV6
375                         address_family = AF_INET6;
376 #else
377                         usage (_("IPv6 support not available\n"));
378 #endif
379                         break;
380                 case 'v': /* verbose */
381                         verbose = TRUE;
382                         break;
383                 case 'm': /* min_page_length */
384                         min_page_len = atoi (optarg);
385                         break;
386                 }
387         }
389         c = optind;
391         if (server_address == NULL && c < argc)
392                 server_address = strdup (argv[c++]);
394         if (host_name == NULL && c < argc)
395                 host_name = strdup (argv[c++]);
397         if (server_address == NULL) {
398                 if (host_name == NULL)
399                         usage (_("check_http: you must specify a server address or host name\n"));
400                 else
401                         server_address = strdup (host_name);
402         }
404         if (check_critical_time && critical_time>(double)socket_timeout)
405                 socket_timeout = (int)critical_time + 1;
407         if (http_method == NULL)
408                 http_method = strdup ("GET");
410         return TRUE;
412 \f
415 /* written by lauri alanko */
416 static char *
417 base64 (const char *bin, size_t len)
420         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
421         size_t i = 0, j = 0;
423         char BASE64_END = '=';
424         char base64_table[64];
425         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
427         while (j < len - 2) {
428                 buf[i++] = base64_table[bin[j] >> 2];
429                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
430                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
431                 buf[i++] = base64_table[bin[j + 2] & 63];
432                 j += 3;
433         }
435         switch (len - j) {
436         case 1:
437                 buf[i++] = base64_table[bin[j] >> 2];
438                 buf[i++] = base64_table[(bin[j] & 3) << 4];
439                 buf[i++] = BASE64_END;
440                 buf[i++] = BASE64_END;
441                 break;
442         case 2:
443                 buf[i++] = base64_table[bin[j] >> 2];
444                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
445                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
446                 buf[i++] = BASE64_END;
447                 break;
448         case 0:
449                 break;
450         }
452         buf[i] = '\0';
453         return buf;
455 \f
460 int
461 check_http (void)
463         char *msg;
464         char *status_line;
465         char *header;
466         char *page;
467         char *auth;
468         int i = 0;
469         size_t pagesize = 0;
470         char *full_page;
471         char *buf;
472         char *pos;
473         long microsec;
474         double elapsed_time;
475         int page_len = 0;
476 #ifdef HAVE_SSL
477         int sslerr;
478 #endif
480         /* try to connect to the host at the given port number */
481 #ifdef HAVE_SSL
482         if (use_ssl == TRUE) {
484                 if (connect_SSL () != OK) {
485                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
486                 }
488                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
489                         X509_free (server_cert);
490                 }
491                 else {
492                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
493                         return STATE_CRITICAL;
494                 }
496         }
497         else {
498 #endif
499                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
500                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
501 #ifdef HAVE_SSL
502         }
503 #endif
505         asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
507         /* optionally send the host header info */
508         if (host_name)
509                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
511         /* optionally send the authentication info */
512         if (strlen(user_auth)) {
513                 auth = base64 (user_auth, strlen (user_auth));
514                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
515         }
517         /* either send http POST data */
518         if (http_post_data) {
519                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
520                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
521                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
522         }
523         else {
524                 /* or just a newline so the server knows we're done with the request */
525                 asprintf (&buf, "%s%s", buf, CRLF);
526         }
528         if (verbose)
529                 printf ("%s\n", buf);
531 #ifdef HAVE_SSL
532         if (use_ssl == TRUE) {
533                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
534                         ERR_print_errors_fp (stderr);
535                         return STATE_CRITICAL;
536                 }
537         }
538         else {
539 #endif
540                 send (sd, buf, strlen (buf), 0);
541 #ifdef HAVE_SSL
542         }
543 #endif
545         /* fetch the page */
546         full_page = strdup("");
547         while ((i = my_recv ()) > 0) {
548                 buffer[i] = '\0';
549                 asprintf (&full_page, "%s%s", full_page, buffer);
550                 pagesize += i;
551         }
553         if (i < 0 && errno != ECONNRESET) {
554 #ifdef HAVE_SSL
555                 if (use_ssl) {
556                         sslerr=SSL_get_error(ssl, i);
557                         if ( sslerr == SSL_ERROR_SSL ) {
558                                 die (STATE_WARNING, _("Client Certificate Required\n"));
559                         } else {
560                                 die (STATE_CRITICAL, _("Error in recv()\n"));
561                         }
562                 }
563                 else {
564 #endif
565                         die (STATE_CRITICAL, _("Error in recv()\n"));
566 #ifdef HAVE_SSL
567                 }
568 #endif
569         }
571         /* return a CRITICAL status if we couldn't read any data */
572         if (pagesize == (size_t) 0)
573                 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
575         /* close the connection */
576         my_close ();
578         /* reset the alarm */
579         alarm (0);
581         /* leave full_page untouched so we can free it later */
582         page = full_page;
584         if (verbose)
585                 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
587         /* find status line and null-terminate it */
588         status_line = page;
589         page += (size_t) strcspn (page, "\r\n");
590         pos = page;
591         page += (size_t) strspn (page, "\r\n");
592         status_line[strcspn(status_line, "\r\n")] = 0;
593         strip (status_line);
594         if (verbose)
595                 printf ("STATUS: %s\n", status_line);
597         /* find header info and null-terminate it */
598         header = page;
599         while (strcspn (page, "\r\n") > 0) {
600                 page += (size_t) strcspn (page, "\r\n");
601                 pos = page;
602                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
603                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
604                         page += (size_t) 2;
605                 else
606                         page += (size_t) 1;
607         }
608         page += (size_t) strspn (page, "\r\n");
609         header[pos - header] = 0;
610         if (verbose)
611                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
613         /* make sure the status line matches the response we are looking for */
614         if (!strstr (status_line, server_expect)) {
615                 if (server_port == HTTP_PORT)
616                         asprintf (&msg, _("Invalid HTTP response received from host\n"));
617                 else
618                         asprintf (&msg,
619                                         _("Invalid HTTP response received from host on port %d\n"),
620                                         server_port);
621                 die (STATE_CRITICAL, "%s", msg);
622         }
624         /* Exit here if server_expect was set by user and not default */
625         if ( server_expect_yn  )  {
626                 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
627                           server_expect);
628                 if (verbose)
629                         printf ("%s\n",msg);
631         }
632         else {
633         
635                 /* check the return code */
636                 /* server errors result in a critical state */
637                 if (strstr (status_line, "500") || strstr (status_line, "501") ||
638                     strstr (status_line, "502") || strstr (status_line, "503") ||
639                     strstr (status_line, "504") || strstr (status_line, "505")) {
640                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
641                 }
643                 /* client errors result in a warning state */
644                 if (strstr (status_line, "400") || strstr (status_line, "401") ||
645                     strstr (status_line, "402") || strstr (status_line, "403") ||
646                     strstr (status_line, "404") || strstr (status_line, "405") ||
647                     strstr (status_line, "406") || strstr (status_line, "407") ||
648                     strstr (status_line, "408") || strstr (status_line, "409") ||
649                     strstr (status_line, "410") || strstr (status_line, "411") ||
650                     strstr (status_line, "412") || strstr (status_line, "413") ||
651                     strstr (status_line, "414") || strstr (status_line, "415") ||
652                     strstr (status_line, "416") || strstr (status_line, "417")) {
653                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
654                 }
656                 /* check redirected page if specified */
657                 if (strstr (status_line, "300") || strstr (status_line, "301") ||
658                     strstr (status_line, "302") || strstr (status_line, "303") ||
659                     strstr (status_line, "304") || strstr (status_line, "305") ||
660                     strstr (status_line, "306")) {
662                         if (onredirect == STATE_DEPENDENT)
663                                 redir (header, status_line);
664                         else if (onredirect == STATE_UNKNOWN)
665                                 printf (_("UNKNOWN"));
666                         else if (onredirect == STATE_OK)
667                                 printf (_("OK"));
668                         else if (onredirect == STATE_WARNING)
669                                 printf (_("WARNING"));
670                         else if (onredirect == STATE_CRITICAL)
671                                 printf (_("CRITICAL"));
672                         microsec = deltime (tv);
673                         elapsed_time = (double)microsec / 1.0e6;
674                         die (onredirect,
675                              _(" - %s - %.3f second response time %s%s|%s size=%dB\n"),
676                              status_line, elapsed_time, timestamp,
677                              (display_html ? "</A>" : ""),
678                                          perfd_time (microsec), pagesize);
679                 } /* end if (strstr (status_line, "30[0-4]") */
682         } /* end else (server_expect_yn)  */
684                 
685         /* check elapsed time */
686         microsec = deltime (tv);
687         elapsed_time = (double)microsec / 1.0e6;
688         asprintf (&msg,
689                   _("HTTP problem: %s - %.3f second response time %s%s|%s size=%dB\n"),
690                   status_line, elapsed_time, timestamp,
691                   (display_html ? "</A>" : ""),
692                                                 perfd_time (microsec), pagesize);
693         if (check_critical_time == TRUE && elapsed_time > critical_time)
694                 die (STATE_CRITICAL, "%s", msg);
695         if (check_warning_time == TRUE && elapsed_time > warning_time)
696                 die (STATE_WARNING, "%s", msg);
698         /* Page and Header content checks go here */
699         /* these checks should be last */
701         if (strlen (string_expect)) {
702                 if (strstr (page, string_expect)) {
703                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s size=%dB\n"),
704                                 status_line, elapsed_time,
705                                 timestamp, (display_html ? "</A>" : ""),
706                                 perfd_time (microsec), pagesize);
707                         exit (STATE_OK);
708                 }
709                 else {
710                         printf (_("CRITICAL - string not found%s|%s size=%dB\n"),
711                                 (display_html ? "</A>" : ""),
712                                 perfd_time (microsec), pagesize);
713                         exit (STATE_CRITICAL);
714                 }
715         }
716 #ifdef HAVE_REGEX_H
717         if (strlen (regexp)) {
718                 errcode = regexec (&preg, page, REGS, pmatch, 0);
719                 if (errcode == 0) {
720                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s size=%dB\n"),
721                                 status_line, elapsed_time,
722                                 timestamp, (display_html ? "</A>" : ""),
723                                 perfd_time (microsec), pagesize);
724                         exit (STATE_OK);
725                 }
726                 else {
727                         if (errcode == REG_NOMATCH) {
728                                 printf (_("CRITICAL - pattern not found%s|%s size=%dB\n"),
729                                         (display_html ? "</A>" : ""),
730                                         perfd_time (microsec), pagesize);
731                                 exit (STATE_CRITICAL);
732                         }
733                         else {
734                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
735                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
736                                 exit (STATE_CRITICAL);
737                         }
738                 }
739         }
740 #endif
742         /* make sure the page is of an appropriate size */
743         page_len = strlen (page);
744         if ((min_page_len > 0) && (page_len < min_page_len)) {
745                 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
746                         (display_html ? "</A>" : ""), page_len );
747                 exit (STATE_WARNING);
748         }
749         /* We only get here if all tests have been passed */
750         asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|%s size=%dB\n"),
751                   status_line, elapsed_time,
752                   timestamp, (display_html ? "</A>" : ""),
753                                                 perfd_time (microsec), pagesize);
754         die (STATE_OK, "%s", msg);
755         return STATE_UNKNOWN;
761 /* per RFC 2396 */
762 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
763 #define URI_HTTP "%[HTPShtps]://"
764 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
765 #define URI_PORT ":%[0123456789]"
766 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
767 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
768 #define HD2 URI_HTTP URI_HOST URI_PATH
769 #define HD3 URI_HTTP URI_HOST URI_PORT
770 #define HD4 URI_HTTP URI_HOST
771 #define HD5 URI_PATH
773 void
774 redir (char *pos, char *status_line)
776         int i = 0;
777         char *x;
778         char xx[2];
779         char type[6];
780         char *addr;
781         char port[6];
782         char *url;
784         addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
785         if (addr == NULL)
786                 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
787         
788         url = malloc (strcspn (pos, "\r\n"));
789         if (url == NULL)
790                 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
792         while (pos) {
794                 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
796                         pos += (size_t) strcspn (pos, "\r\n");
797                         pos += (size_t) strspn (pos, "\r\n");
798                         if (strlen(pos) == 0) 
799                                 die (STATE_UNKNOWN,
800                                                  _("UNKNOWN - Could not find redirect location - %s%s\n"),
801                                                  status_line, (display_html ? "</A>" : ""));
802                         continue;
803                 }
805                 pos += i;
806                 pos += strspn (pos, " \t\r\n");
808                 url = realloc (url, strcspn (pos, "\r\n"));
809                 if (url == NULL)
810                         die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
812                 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
813                 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
814                         use_ssl = server_type_check (type);
815                         i = atoi (port);
816                 }
818                 /* URI_HTTP URI_HOST URI_PATH */
819                 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
820                         use_ssl = server_type_check (type);
821                         i = server_port_check (use_ssl);
822                 }
824                 /* URI_HTTP URI_HOST URI_PORT */
825                 else if(sscanf (pos, HD3, type, addr, port) == 3) {
826                         strcpy (url, HTTP_URL);
827                         use_ssl = server_type_check (type);
828                         i = atoi (port);
829                 }
831                 /* URI_HTTP URI_HOST */
832                 else if(sscanf (pos, HD4, type, addr) == 2) {
833                         strcpy (url, HTTP_URL);
834                         use_ssl = server_type_check (type);
835                         i = server_port_check (use_ssl);
836                 }
838                 /* URI_PATH */
839                 else if (sscanf (pos, HD5, url) == 1) {
840                         /* relative url */
841                         if ((url[0] != '/')) {
842                                 if ((x = strrchr(url, '/')))
843                                         *x = '\0';
844                                 asprintf (&server_url, "%s/%s", server_url, url);
845                         }
846                         i = server_port;
847                         strcpy (type, server_type);
848                         strcpy (addr, host_name);
849                 }                                       
851                 else {
852                         die (STATE_UNKNOWN,
853                                          _("UNKNOWN - Could not parse redirect location - %s%s\n"),
854                                          pos, (display_html ? "</A>" : ""));
855                 }
857                 break;
859         } /* end while (pos) */
861         if (++redir_depth > max_depth)
862                 die (STATE_WARNING,
863                      _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
864                      max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
866         if (server_port==i &&
867             !strcmp(server_address, addr) &&
868             (host_name && !strcmp(host_name, addr)) &&
869             !strcmp(server_url, url))
870                 die (STATE_WARNING,
871                      _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
872                      type, addr, i, url, (display_html ? "</A>" : ""));
874         server_port = i;
875         strcpy (server_type, type);
877         free (host_name);
878         host_name = strdup (addr);
880         free (server_address);
881         server_address = strdup (addr);
883         free (server_url);
884         server_url = strdup (url);
886         check_http ();
891 int
892 server_type_check (const char *type)
894         if (strcmp (type, "https"))
895                 return FALSE;
896         else
897                 return TRUE;
900 int
901 server_port_check (int ssl_flag)
903         if (ssl_flag)
904                 return HTTPS_PORT;
905         else
906                 return HTTP_PORT;
911 #ifdef HAVE_SSL
912 int connect_SSL (void)
914         SSL_METHOD *meth;
916         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
917         RAND_seed (randbuff, (int)strlen(randbuff));
918         if (verbose)
919                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
921         /* Initialize SSL context */
922         SSLeay_add_ssl_algorithms ();
923         meth = SSLv23_client_method ();
924         SSL_load_error_strings ();
925         if ((ctx = SSL_CTX_new (meth)) == NULL) {
926                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
927                 return STATE_CRITICAL;
928         }
930         /* Initialize alarm signal handling */
931         signal (SIGALRM, socket_timeout_alarm_handler);
933         /* Set socket timeout */
934         alarm (socket_timeout);
936         /* Save start time */
937         gettimeofday (&tv, NULL);
939         /* Make TCP connection */
940         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
941                 /* Do the SSL handshake */
942                 if ((ssl = SSL_new (ctx)) != NULL) {
943                         SSL_set_cipher_list(ssl, "ALL");
944                         SSL_set_fd (ssl, sd);
945                         if (SSL_connect (ssl) != -1)
946                                 return OK;
947                         ERR_print_errors_fp (stderr);
948                 }
949                 else {
950                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
951                 }
952                 SSL_free (ssl);
953         }
955         SSL_CTX_free (ctx);
956         close (sd);
958         return STATE_CRITICAL;
960 #endif
962 #ifdef HAVE_SSL
963 int
964 check_certificate (X509 ** certificate)
966         ASN1_STRING *tm;
967         int offset;
968         struct tm stamp;
969         int days_left;
972         /* Retrieve timestamp of certificate */
973         tm = X509_get_notAfter (*certificate);
975         /* Generate tm structure to process timestamp */
976         if (tm->type == V_ASN1_UTCTIME) {
977                 if (tm->length < 10) {
978                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
979                         return STATE_CRITICAL;
980                 }
981                 else {
982                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
983                         if (stamp.tm_year < 50)
984                                 stamp.tm_year += 100;
985                         offset = 0;
986                 }
987         }
988         else {
989                 if (tm->length < 12) {
990                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
991                         return STATE_CRITICAL;
992                 }
993                 else {
994                         stamp.tm_year =
995                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
996                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
997                         stamp.tm_year -= 1900;
998                         offset = 2;
999                 }
1000         }
1001         stamp.tm_mon =
1002                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1003         stamp.tm_mday =
1004                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1005         stamp.tm_hour =
1006                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1007         stamp.tm_min =
1008                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1009         stamp.tm_sec = 0;
1010         stamp.tm_isdst = -1;
1012         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1013         snprintf
1014                 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1015                  stamp.tm_mon + 1,
1016                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1018         if (days_left > 0 && days_left <= days_till_exp) {
1019                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1020                 return STATE_WARNING;
1021         }
1022         if (days_left < 0) {
1023                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1024                 return STATE_CRITICAL;
1025         }
1027         if (days_left == 0) {
1028                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1029                 return STATE_WARNING;
1030         }
1032         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1034         return STATE_OK;
1036 #endif
1037 \f
1040 char *perfd_time (long microsec)
1042         perfdata ("time", microsec, "us",
1043                   check_warning_time, (int)(1e6*warning_time),
1044                   check_critical_time, (int)(1e6*critical_time),
1045                   TRUE, 0, FALSE, 0);
1048 int
1049 my_recv (void)
1051         int i;
1052 #ifdef HAVE_SSL
1053         if (use_ssl) {
1054                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1055         }
1056         else {
1057                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1058         }
1059 #else
1060         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1061 #endif
1062         return i;
1066 int
1067 my_close (void)
1069 #ifdef HAVE_SSL
1070         if (use_ssl == TRUE) {
1071                 SSL_shutdown (ssl);
1072                 SSL_free (ssl);
1073                 SSL_CTX_free (ctx);
1074                 return 0;
1075         }
1076         else {
1077 #endif
1078                 return close (sd);
1079 #ifdef HAVE_SSL
1080         }
1081 #endif
1088 \f
1089 void
1090 print_help (void)
1092         print_revision (progname, revision);
1094         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1095         printf (_(COPYRIGHT), copyright, email);
1097         printf (_("\
1098 This plugin tests the HTTP service on the specified host. It can test\n\
1099 normal (http) and secure (https) servers, follow redirects, search for\n\
1100 strings and regular expressions, check connection times, and report on\n\
1101 certificate expiration times.\n"));
1103         print_usage ();
1105         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1107         printf (_(UT_HELP_VRSN));
1109         printf (_("\
1110  -H, --hostname=ADDRESS\n\
1111     Host name argument for servers using host headers (virtual host)\n\
1112  -I, --IP-address=ADDRESS\n\
1113    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1114  -p, --port=INTEGER\n\
1115    Port number (default: %d)\n"), HTTP_PORT);
1117         printf (_(UT_IPv46));
1119 #ifdef HAVE_SSL
1120         printf (_("\
1121  -S, --ssl\n\
1122     Connect via SSL\n\
1123  -C, --certificate=INTEGER\n\
1124     Minimum number of days a certificate has to be valid.\n\
1125     (when this option is used the url is not checked.)\n"));
1126 #endif
1128         printf (_("\
1129  -e, --expect=STRING\n\
1130    String to expect in first (status) line of server response (default: %s)\n\
1131    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1132  -s, --string=STRING\n\
1133    String to expect in the content\n\
1134  -u, --url=PATH\n\
1135    URL to GET or POST (default: /)\n\
1136  -P, --post=STRING\n\
1137    URL encoded http POST data\n"), HTTP_EXPECT);
1139 #ifdef HAVE_REGEX_H
1140         printf (_("\
1141  -l, --linespan\n\
1142     Allow regex to span newlines (must precede -r or -R)\n\
1143  -r, --regex, --ereg=STRING\n\
1144     Search page for regex STRING\n\
1145  -R, --eregi=STRING\n\
1146     Search page for case-insensitive regex STRING\n"));
1147 #endif
1149         printf (_("\
1150  -a, --authorization=AUTH_PAIR\n\
1151    Username:password on sites with basic authentication\n\
1152  -L, --link=URL\n\
1153    Wrap output in HTML link (obsoleted by urlize)\n\
1154  -f, --onredirect=<ok|warning|critical|follow>\n\
1155    How to handle redirected pages\n\
1156  -m, --min=INTEGER\n\
1157    Minimum page size required (bytes)\n"));
1159         printf (_(UT_WARN_CRIT));
1161         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1163         printf (_(UT_VERBOSE));
1165                                         printf (_("\
1166 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1167 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1168 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1169 messages from the host result in STATE_WARNING return values.  If you are\n\
1170 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1171 (fully qualified domain name) as the [host_name] argument.\n"));
1173 #ifdef HAVE_SSL
1174         printf (_("\n\
1175 This plugin can also check whether an SSL enabled web server is able to\n\
1176 serve content (optionally within a specified time) or whether the X509 \n\
1177 certificate is still valid for the specified number of days.\n"));
1178         printf (_("\n\
1179 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1180 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1181 STATE_OK will be returned. When the server returns its content but exceeds\n\
1182 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1183 a STATE_CRITICAL will be returned.\n\n"));
1185         printf (_("\
1186 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1187 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1188 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1189 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1190 the certificate is expired.\n"));
1191 #endif
1193         printf (_(UT_SUPPORT));
1200 void
1201 print_usage (void)
1203         printf (_("\
1204 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1205   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1206   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1207   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1208   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1209         printf (_(UT_HLP_VRS), progname, progname);