Code

8e45e88d5bec3593cbe15a3ed5fcb483382bc6a9
[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 *status_code;
467         char *header;
468         char *page;
469         char *auth;
470         int http_status;
471         int i = 0;
472         size_t pagesize = 0;
473         char *full_page;
474         char *buf;
475         char *pos;
476         long microsec;
477         double elapsed_time;
478         int page_len = 0;
479 #ifdef HAVE_SSL
480         int sslerr;
481 #endif
483         /* try to connect to the host at the given port number */
484 #ifdef HAVE_SSL
485         if (use_ssl == TRUE) {
487                 if (connect_SSL () != OK) {
488                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
489                 }
491                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
492                         X509_free (server_cert);
493                 }
494                 else {
495                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
496                         return STATE_CRITICAL;
497                 }
499         }
500         else {
501 #endif
502                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
503                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
504 #ifdef HAVE_SSL
505         }
506 #endif
508         asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
510         /* optionally send the host header info */
511         if (host_name)
512                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
514         /* optionally send the authentication info */
515         if (strlen(user_auth)) {
516                 auth = base64 (user_auth, strlen (user_auth));
517                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
518         }
520         /* either send http POST data */
521         if (http_post_data) {
522                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
523                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
524                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
525         }
526         else {
527                 /* or just a newline so the server knows we're done with the request */
528                 asprintf (&buf, "%s%s", buf, CRLF);
529         }
531         if (verbose)
532                 printf ("%s\n", buf);
534 #ifdef HAVE_SSL
535         if (use_ssl == TRUE) {
536                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
537                         ERR_print_errors_fp (stderr);
538                         return STATE_CRITICAL;
539                 }
540         }
541         else {
542 #endif
543                 send (sd, buf, strlen (buf), 0);
544 #ifdef HAVE_SSL
545         }
546 #endif
548         /* fetch the page */
549         full_page = strdup("");
550         while ((i = my_recv ()) > 0) {
551                 buffer[i] = '\0';
552                 asprintf (&full_page, "%s%s", full_page, buffer);
553                 pagesize += i;
554         }
556         if (i < 0 && errno != ECONNRESET) {
557 #ifdef HAVE_SSL
558                 if (use_ssl) {
559                         sslerr=SSL_get_error(ssl, i);
560                         if ( sslerr == SSL_ERROR_SSL ) {
561                                 die (STATE_WARNING, _("Client Certificate Required\n"));
562                         } else {
563                                 die (STATE_CRITICAL, _("Error in recv()\n"));
564                         }
565                 }
566                 else {
567 #endif
568                         die (STATE_CRITICAL, _("Error in recv()\n"));
569 #ifdef HAVE_SSL
570                 }
571 #endif
572         }
574         /* return a CRITICAL status if we couldn't read any data */
575         if (pagesize == (size_t) 0)
576                 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
578         /* close the connection */
579         my_close ();
581         /* reset the alarm */
582         alarm (0);
584         /* leave full_page untouched so we can free it later */
585         page = full_page;
587         if (verbose)
588                 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
590         /* find status line and null-terminate it */
591         status_line = page;
592         page += (size_t) strcspn (page, "\r\n");
593         pos = page;
594         page += (size_t) strspn (page, "\r\n");
595         status_line[strcspn(status_line, "\r\n")] = 0;
596         strip (status_line);
597         if (verbose)
598                 printf ("STATUS: %s\n", status_line);
600         /* find header info and null-terminate it */
601         header = page;
602         while (strcspn (page, "\r\n") > 0) {
603                 page += (size_t) strcspn (page, "\r\n");
604                 pos = page;
605                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
606                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
607                         page += (size_t) 2;
608                 else
609                         page += (size_t) 1;
610         }
611         page += (size_t) strspn (page, "\r\n");
612         header[pos - header] = 0;
613         if (verbose)
614                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
616         /* make sure the status line matches the response we are looking for */
617         if (!strstr (status_line, server_expect)) {
618                 if (server_port == HTTP_PORT)
619                         asprintf (&msg,
620                             _("Invalid HTTP response received from host\n"));
621                 else
622                         asprintf (&msg,
623                                   _("Invalid HTTP response received from host on port %d\n"),
624                                   server_port);
625                 die (STATE_CRITICAL, "%s", msg);
626         }
628         /* Exit here if server_expect was set by user and not default */
629         if ( server_expect_yn  )  {
630                 asprintf (&msg,
631                           _("HTTP OK: Status line output matched \"%s\"\n"),
632                           server_expect);
633                 if (verbose)
634                         printf ("%s\n",msg);
635         }
636         else {
637                 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
638                 /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
639     /* Status-Code = 3 DIGITS */
641                 status_code = strchr (status_line, ' ') + sizeof (char);
642                 if (strspn (status_code, "1234567890") != 3)
643                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
645                 http_status = atoi (status_code);
647                 /* check the return code */
649                 if (http_status >= 600 || http_status < 100)
650                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
652                 /* server errors result in a critical state */
653                 else if (http_status >= 500)
654                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
656                 /* client errors result in a warning state */
657                 else if (http_status >= 400)
658                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
660                 /* check redirected page if specified */
661                 else if (http_status >= 300) {
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 (http_status >= 300) */
682         } /* end else (server_expect_yn)  */
683                 
684         /* check elapsed time */
685         microsec = deltime (tv);
686         elapsed_time = (double)microsec / 1.0e6;
687         asprintf (&msg,
688                   _("HTTP problem: %s - %.3f second response time %s%s|%s %s\n"),
689                   status_line, elapsed_time, timestamp,
690                   (display_html ? "</A>" : ""),
691                                                 perfd_time (microsec), perfd_size (pagesize));
692         if (check_critical_time == TRUE && elapsed_time > critical_time)
693                 die (STATE_CRITICAL, "%s", msg);
694         if (check_warning_time == TRUE && elapsed_time > warning_time)
695                 die (STATE_WARNING, "%s", msg);
697         /* Page and Header content checks go here */
698         /* these checks should be last */
700         if (strlen (string_expect)) {
701                 if (strstr (page, string_expect)) {
702                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
703                                 status_line, elapsed_time,
704                                 timestamp, (display_html ? "</A>" : ""),
705                                 perfd_time (microsec), perfd_size (pagesize));
706                         exit (STATE_OK);
707                 }
708                 else {
709                         printf (_("CRITICAL - string not found%s|%s %s\n"),
710                                 (display_html ? "</A>" : ""),
711                                 perfd_time (microsec), perfd_size (pagesize));
712                         exit (STATE_CRITICAL);
713                 }
714         }
715 #ifdef HAVE_REGEX_H
716         if (strlen (regexp)) {
717                 errcode = regexec (&preg, page, REGS, pmatch, 0);
718                 if (errcode == 0) {
719                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
720                                 status_line, elapsed_time,
721                                 timestamp, (display_html ? "</A>" : ""),
722                                 perfd_time (microsec), perfd_size (pagesize));
723                         exit (STATE_OK);
724                 }
725                 else {
726                         if (errcode == REG_NOMATCH) {
727                                 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
728                                         (display_html ? "</A>" : ""),
729                                         perfd_time (microsec), perfd_size (pagesize));
730                                 exit (STATE_CRITICAL);
731                         }
732                         else {
733                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
734                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
735                                 exit (STATE_CRITICAL);
736                         }
737                 }
738         }
739 #endif
741         /* make sure the page is of an appropriate size */
742         page_len = strlen (page);
743         if ((min_page_len > 0) && (page_len < min_page_len)) {
744                 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
745                         page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
746                 exit (STATE_WARNING);
747         }
748         /* We only get here if all tests have been passed */
749         asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
750                   status_line, page_len, elapsed_time,
751                   timestamp, (display_html ? "</A>" : ""),
752                                                 perfd_time (microsec), perfd_size (page_len));
753         die (STATE_OK, "%s", msg);
754         return STATE_UNKNOWN;
760 /* per RFC 2396 */
761 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
762 #define URI_HTTP "%[HTPShtps]://"
763 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
764 #define URI_PORT ":%[0123456789]"
765 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
766 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
767 #define HD2 URI_HTTP URI_HOST URI_PATH
768 #define HD3 URI_HTTP URI_HOST URI_PORT
769 #define HD4 URI_HTTP URI_HOST
770 #define HD5 URI_PATH
772 void
773 redir (char *pos, char *status_line)
775         int i = 0;
776         char *x;
777         char xx[2];
778         char type[6];
779         char *addr;
780         char port[6];
781         char *url;
783         addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
784         if (addr == NULL)
785                 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
786         
787         url = malloc (strcspn (pos, "\r\n"));
788         if (url == NULL)
789                 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
791         while (pos) {
793                 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
795                         pos += (size_t) strcspn (pos, "\r\n");
796                         pos += (size_t) strspn (pos, "\r\n");
797                         if (strlen(pos) == 0) 
798                                 die (STATE_UNKNOWN,
799                                                  _("UNKNOWN - Could not find redirect location - %s%s\n"),
800                                                  status_line, (display_html ? "</A>" : ""));
801                         continue;
802                 }
804                 pos += i;
805                 pos += strspn (pos, " \t\r\n");
807                 url = realloc (url, strcspn (pos, "\r\n"));
808                 if (url == NULL)
809                         die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
811                 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
812                 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
813                         use_ssl = server_type_check (type);
814                         i = atoi (port);
815                 }
817                 /* URI_HTTP URI_HOST URI_PATH */
818                 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
819                         use_ssl = server_type_check (type);
820                         i = server_port_check (use_ssl);
821                 }
823                 /* URI_HTTP URI_HOST URI_PORT */
824                 else if(sscanf (pos, HD3, type, addr, port) == 3) {
825                         strcpy (url, HTTP_URL);
826                         use_ssl = server_type_check (type);
827                         i = atoi (port);
828                 }
830                 /* URI_HTTP URI_HOST */
831                 else if(sscanf (pos, HD4, type, addr) == 2) {
832                         strcpy (url, HTTP_URL);
833                         use_ssl = server_type_check (type);
834                         i = server_port_check (use_ssl);
835                 }
837                 /* URI_PATH */
838                 else if (sscanf (pos, HD5, url) == 1) {
839                         /* relative url */
840                         if ((url[0] != '/')) {
841                                 if ((x = strrchr(server_url, '/')))
842                                         *x = '\0';
843                                 asprintf (&url, "%s/%s", server_url, url);
844                         }
845                         i = server_port;
846                         strcpy (type, server_type);
847                         strcpy (addr, host_name);
848                 }                                       
850                 else {
851                         die (STATE_UNKNOWN,
852                                          _("UNKNOWN - Could not parse redirect location - %s%s\n"),
853                                          pos, (display_html ? "</A>" : ""));
854                 }
856                 break;
858         } /* end while (pos) */
860         if (++redir_depth > max_depth)
861                 die (STATE_WARNING,
862                      _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
863                      max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
865         if (server_port==i &&
866             !strcmp(server_address, addr) &&
867             (host_name && !strcmp(host_name, addr)) &&
868             !strcmp(server_url, url))
869                 die (STATE_WARNING,
870                      _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
871                      type, addr, i, url, (display_html ? "</A>" : ""));
873         server_port = i;
874         strcpy (server_type, type);
876         free (host_name);
877         host_name = strdup (addr);
879         free (server_address);
880         server_address = strdup (addr);
882         free (server_url);
883         server_url = strdup (url);
885         check_http ();
890 int
891 server_type_check (const char *type)
893         if (strcmp (type, "https"))
894                 return FALSE;
895         else
896                 return TRUE;
899 int
900 server_port_check (int ssl_flag)
902         if (ssl_flag)
903                 return HTTPS_PORT;
904         else
905                 return HTTP_PORT;
910 #ifdef HAVE_SSL
911 int connect_SSL (void)
913         SSL_METHOD *meth;
915         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
916         RAND_seed (randbuff, (int)strlen(randbuff));
917         if (verbose)
918                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
920         /* Initialize SSL context */
921         SSLeay_add_ssl_algorithms ();
922         meth = SSLv23_client_method ();
923         SSL_load_error_strings ();
924         if ((ctx = SSL_CTX_new (meth)) == NULL) {
925                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
926                 return STATE_CRITICAL;
927         }
929         /* Initialize alarm signal handling */
930         signal (SIGALRM, socket_timeout_alarm_handler);
932         /* Set socket timeout */
933         alarm (socket_timeout);
935         /* Save start time */
936         gettimeofday (&tv, NULL);
938         /* Make TCP connection */
939         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
940                 /* Do the SSL handshake */
941                 if ((ssl = SSL_new (ctx)) != NULL) {
942                         SSL_set_cipher_list(ssl, "ALL");
943                         SSL_set_fd (ssl, sd);
944                         if (SSL_connect (ssl) != -1)
945                                 return OK;
946                         ERR_print_errors_fp (stderr);
947                 }
948                 else {
949                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
950                 }
951                 SSL_free (ssl);
952         }
954         SSL_CTX_free (ctx);
955         close (sd);
957         return STATE_CRITICAL;
959 #endif
961 #ifdef HAVE_SSL
962 int
963 check_certificate (X509 ** certificate)
965         ASN1_STRING *tm;
966         int offset;
967         struct tm stamp;
968         int days_left;
971         /* Retrieve timestamp of certificate */
972         tm = X509_get_notAfter (*certificate);
974         /* Generate tm structure to process timestamp */
975         if (tm->type == V_ASN1_UTCTIME) {
976                 if (tm->length < 10) {
977                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
978                         return STATE_CRITICAL;
979                 }
980                 else {
981                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
982                         if (stamp.tm_year < 50)
983                                 stamp.tm_year += 100;
984                         offset = 0;
985                 }
986         }
987         else {
988                 if (tm->length < 12) {
989                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
990                         return STATE_CRITICAL;
991                 }
992                 else {
993                         stamp.tm_year =
994                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
995                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
996                         stamp.tm_year -= 1900;
997                         offset = 2;
998                 }
999         }
1000         stamp.tm_mon =
1001                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1002         stamp.tm_mday =
1003                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1004         stamp.tm_hour =
1005                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1006         stamp.tm_min =
1007                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1008         stamp.tm_sec = 0;
1009         stamp.tm_isdst = -1;
1011         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1012         snprintf
1013                 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1014                  stamp.tm_mon + 1,
1015                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1017         if (days_left > 0 && days_left <= days_till_exp) {
1018                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1019                 return STATE_WARNING;
1020         }
1021         if (days_left < 0) {
1022                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1023                 return STATE_CRITICAL;
1024         }
1026         if (days_left == 0) {
1027                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1028                 return STATE_WARNING;
1029         }
1031         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1033         return STATE_OK;
1035 #endif
1036 \f
1039 char *perfd_time (long microsec)
1041         return perfdata ("time", microsec, "us",
1042                   check_warning_time, (int)(1e6*warning_time),
1043                   check_critical_time, (int)(1e6*critical_time),
1044                   TRUE, 0, FALSE, 0);
1048 char *perfd_size (int page_len)
1050         return perfdata ("size", page_len, "B",
1051                   (min_page_len>0?TRUE:FALSE), min_page_len,
1052                   (min_page_len>0?TRUE:FALSE), 0,
1053                   TRUE, 0, FALSE, 0);
1057 int
1058 my_recv (void)
1060         int i;
1061 #ifdef HAVE_SSL
1062         if (use_ssl) {
1063                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1064         }
1065         else {
1066                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1067         }
1068 #else
1069         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1070 #endif
1071         return i;
1075 int
1076 my_close (void)
1078 #ifdef HAVE_SSL
1079         if (use_ssl == TRUE) {
1080                 SSL_shutdown (ssl);
1081                 SSL_free (ssl);
1082                 SSL_CTX_free (ctx);
1083                 return 0;
1084         }
1085         else {
1086 #endif
1087                 return close (sd);
1088 #ifdef HAVE_SSL
1089         }
1090 #endif
1097 \f
1098 void
1099 print_help (void)
1101         print_revision (progname, revision);
1103         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1104         printf (_(COPYRIGHT), copyright, email);
1106         printf (_("\
1107 This plugin tests the HTTP service on the specified host. It can test\n\
1108 normal (http) and secure (https) servers, follow redirects, search for\n\
1109 strings and regular expressions, check connection times, and report on\n\
1110 certificate expiration times.\n"));
1112         print_usage ();
1114         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1116         printf (_(UT_HELP_VRSN));
1118         printf (_("\
1119  -H, --hostname=ADDRESS\n\
1120     Host name argument for servers using host headers (virtual host)\n\
1121  -I, --IP-address=ADDRESS\n\
1122    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1123  -p, --port=INTEGER\n\
1124    Port number (default: %d)\n"), HTTP_PORT);
1126         printf (_(UT_IPv46));
1128 #ifdef HAVE_SSL
1129         printf (_("\
1130  -S, --ssl\n\
1131     Connect via SSL\n\
1132  -C, --certificate=INTEGER\n\
1133     Minimum number of days a certificate has to be valid.\n\
1134     (when this option is used the url is not checked.)\n"));
1135 #endif
1137         printf (_("\
1138  -e, --expect=STRING\n\
1139    String to expect in first (status) line of server response (default: %s)\n\
1140    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1141  -s, --string=STRING\n\
1142    String to expect in the content\n\
1143  -u, --url=PATH\n\
1144    URL to GET or POST (default: /)\n\
1145  -P, --post=STRING\n\
1146    URL encoded http POST data\n"), HTTP_EXPECT);
1148 #ifdef HAVE_REGEX_H
1149         printf (_("\
1150  -l, --linespan\n\
1151     Allow regex to span newlines (must precede -r or -R)\n\
1152  -r, --regex, --ereg=STRING\n\
1153     Search page for regex STRING\n\
1154  -R, --eregi=STRING\n\
1155     Search page for case-insensitive regex STRING\n"));
1156 #endif
1158         printf (_("\
1159  -a, --authorization=AUTH_PAIR\n\
1160    Username:password on sites with basic authentication\n\
1161  -L, --link=URL\n\
1162    Wrap output in HTML link (obsoleted by urlize)\n\
1163  -f, --onredirect=<ok|warning|critical|follow>\n\
1164    How to handle redirected pages\n\
1165  -m, --min=INTEGER\n\
1166    Minimum page size required (bytes)\n"));
1168         printf (_(UT_WARN_CRIT));
1170         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1172         printf (_(UT_VERBOSE));
1174                                         printf (_("\
1175 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1176 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1177 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1178 messages from the host result in STATE_WARNING return values.  If you are\n\
1179 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1180 (fully qualified domain name) as the [host_name] argument.\n"));
1182 #ifdef HAVE_SSL
1183         printf (_("\n\
1184 This plugin can also check whether an SSL enabled web server is able to\n\
1185 serve content (optionally within a specified time) or whether the X509 \n\
1186 certificate is still valid for the specified number of days.\n"));
1187         printf (_("\n\
1188 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1189 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1190 STATE_OK will be returned. When the server returns its content but exceeds\n\
1191 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1192 a STATE_CRITICAL will be returned.\n\n"));
1194         printf (_("\
1195 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1196 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1197 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1198 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1199 the certificate is expired.\n"));
1200 #endif
1202         printf (_(UT_SUPPORT));
1209 void
1210 print_usage (void)
1212         printf (_("\
1213 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1214   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1215   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1216   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1217   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1218         printf (_(UT_HLP_VRS), progname, progname);