Code

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