Code

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