Code

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