Code

27127d6af63e8bafd7097a9b146a39251555aa1b
[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 server_type_check(server_type) \
83 (strcmp (server_type, "https") ? FALSE : TRUE)
85 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
87 #define HTTP_URL "/"
88 #define CRLF "\r\n"
90 char timestamp[17] = "";
91 int specify_port = FALSE;
92 int server_port = HTTP_PORT;
93 char server_port_text[6] = "";
94 char server_type[6] = "http";
95 char *server_address;
96 char *host_name;
97 char *server_url;
98 int server_url_length;
99 int server_expect_yn = 0;
100 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
101 char string_expect[MAX_INPUT_BUFFER] = "";
102 double warning_time = 0;
103 int check_warning_time = FALSE;
104 double critical_time = 0;
105 int check_critical_time = FALSE;
106 char user_auth[MAX_INPUT_BUFFER] = "";
107 int display_html = FALSE;
108 int onredirect = STATE_OK;
109 int use_ssl = FALSE;
110 int verbose = FALSE;
111 int sd;
112 int min_page_len = 0;
113 char *http_method;
114 char *http_post_data;
115 char buffer[MAX_INPUT_BUFFER];
117 int process_arguments (int, char **);
118 static char *base64 (char *bin, size_t len);
119 int check_http (void);
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 = optarg;
308                         break;
309                 case 'I': /* Server IP-address */
310                         server_address = optarg;
311                         break;
312                 case 'u': /* URL path */
313                         asprintf (&server_url, "%s", 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 = 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                 asprintf (&host_name, "%s", 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 (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
453 /* per RFC 2396 */
454 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
455 #define URI_HTTP "%[HTPShtps]://"
456 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
457 #define URI_PORT ":%[0123456789]"
458 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
459 #define HD1 HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH
460 #define HD2 HDR_LOCATION URI_HTTP URI_HOST URI_PATH
461 #define HD3 HDR_LOCATION URI_HTTP URI_HOST URI_PORT
462 #define HD4 HDR_LOCATION URI_HTTP URI_HOST
463 #define HD5 HDR_LOCATION URI_PATH
465 int
466 check_http (void)
468         char *msg;
469         char *status_line;
470         char *header;
471         char *page;
472         char *auth;
473         int i = 0;
474         size_t pagesize = 0;
475         char *full_page;
476         char *buf;
477         char *pos;
478         char *x;
479         char *orig_url;
480         long microsec;
481         double elapsed_time;
482         int page_len = 0;
483 #ifdef HAVE_SSL
484         int sslerr;
485 #endif
487         /* try to connect to the host at the given port number */
488 #ifdef HAVE_SSL
489         if (use_ssl == TRUE) {
491                 if (connect_SSL () != OK) {
492                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
493                 }
495                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
496                         X509_free (server_cert);
497                 }
498                 else {
499                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
500                         return STATE_CRITICAL;
501                 }
503         }
504         else {
505 #endif
506                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
507                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
508 #ifdef HAVE_SSL
509         }
510 #endif
512         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
514         /* optionally send the host header info (not clear if it's usable) */
515         if (host_name)
516                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
518         /* send user agent */
519         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
520                   buf, clean_revstring (revision), VERSION);
522         /* optionally send the authentication info */
523         if (strlen(user_auth)) {
524                 auth = base64 (user_auth, strlen (user_auth));
525                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
526         }
528         /* either send http POST data */
529         if (http_post_data) {
530                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
531                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
532                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
533         }
534         else {
535                 /* or just a newline so the server knows we're done with the request */
536                 asprintf (&buf, "%s%s", buf, CRLF);
537         }
539 #ifdef HAVE_SSL
540         if (use_ssl == TRUE) {
541                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
542                         ERR_print_errors_fp (stderr);
543                         return STATE_CRITICAL;
544                 }
545         }
546         else {
547 #endif
548                 send (sd, buf, strlen (buf), 0);
549 #ifdef HAVE_SSL
550         }
551 #endif
553         /* fetch the page */
554         full_page = strdup("");
555         while ((i = my_recv ()) > 0) {
556                 buffer[i] = '\0';
557                 asprintf (&full_page, "%s%s", full_page, buffer);
558                 pagesize += i;
559         }
561         if (i < 0 && errno != ECONNRESET) {
562 #ifdef HAVE_SSL
563                 if (use_ssl) {
564                         sslerr=SSL_get_error(ssl, i);
565                         if ( sslerr == SSL_ERROR_SSL ) {
566                                 die (STATE_WARNING, _("Client Certificate Required\n"));
567                         } else {
568                                 die (STATE_CRITICAL, _("Error in recv()"));
569                         }
570                 }
571                 else {
572 #endif
573                         die (STATE_CRITICAL, _("Error in recv()"));
574 #ifdef HAVE_SSL
575                 }
576 #endif
577         }
579         /* return a CRITICAL status if we couldn't read any data */
580         if (pagesize == (size_t) 0)
581                 die (STATE_CRITICAL, _("No data received %s"), timestamp);
583         /* close the connection */
584         my_close ();
586         /* reset the alarm */
587         alarm (0);
589         /* leave full_page untouched so we can free it later */
590         page = full_page;
592         if (verbose)
593                 printf ("Page is %d characters\n", pagesize);
595         /* find status line and null-terminate it */
596         status_line = page;
597         page += (size_t) strcspn (page, "\r\n");
598         pos = page;
599         page += (size_t) strspn (page, "\r\n");
600         status_line[strcspn(status_line, "\r\n")] = 0;
601         strip (status_line);
602         if (verbose)
603                 printf ("STATUS: %s\n", status_line);
605         /* find header info and null-terminate it */
606         header = page;
607         while (strcspn (page, "\r\n") > 0) {
608                 page += (size_t) strcspn (page, "\r\n");
609                 pos = page;
610                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
611                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
612                         page += (size_t) 2;
613                 else
614                         page += (size_t) 1;
615         }
616         page += (size_t) strspn (page, "\r\n");
617         header[pos - header] = 0;
618         if (verbose)
619                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
621         /* make sure the status line matches the response we are looking for */
622         if (!strstr (status_line, server_expect)) {
623                 if (server_port == HTTP_PORT)
624                         asprintf (&msg, _("Invalid HTTP response received from host\n"));
625                 else
626                         asprintf (&msg,
627                                         _("Invalid HTTP response received from host on port %d\n"),
628                                         server_port);
629                 die (STATE_CRITICAL, "%s", msg);
630         }
633         /* Exit here if server_expect was set by user and not default */
634         if ( server_expect_yn  )  {
635                 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
636                           server_expect);
637                 if (verbose)
638                         printf ("%s\n",msg);
640         }
641         else {
642         
644                 /* check the return code */
645                 /* server errors result in a critical state */
646                 if (strstr (status_line, "500") ||
647                   strstr (status_line, "501") ||
648                 strstr (status_line, "502") ||
649                     strstr (status_line, "503")) {
650                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
651                 }
653                 /* client errors result in a warning state */
654                 if (strstr (status_line, "400") ||
655                   strstr (status_line, "401") ||
656                 strstr (status_line, "402") ||
657                     strstr (status_line, "403") ||
658                     strstr (status_line, "404")) {
659                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
660                 }
662                 /* check redirected page if specified */
663                 if (strstr (status_line, "300") ||
664                   strstr (status_line, "301") ||
665                 strstr (status_line, "302") ||
666                     strstr (status_line, "303") ||
667                     strstr (status_line, "304")) {
668                         if (onredirect == STATE_DEPENDENT) {
670                                 asprintf (&orig_url, "%s", server_url);
671                                 pos = header;
672                                 while (pos) {
673                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
674                                         if (server_address == NULL)
675                                                 die (STATE_UNKNOWN,_("ERROR: could not allocate server_address"));
676                                         if (strcspn (pos, "\r\n") > (size_t)server_url_length) {
677                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
678                                                 if (server_url == NULL)
679                                                         die (STATE_UNKNOWN, _("ERROR: could not allocate server_url"));
680                                                 server_url_length = strcspn (pos, "\r\n");
681                                         }
682                                         /* HDR_LOCATION, URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
683                                         if (sscanf (pos, HD1, server_type, server_address, server_port_text, server_url) == 4) {
684                                                 if (host_name != NULL) free(host_name);
685                                                 host_name = strdup(server_address);
686                                                 use_ssl = server_type_check (server_type);
687                                                 server_port = atoi (server_port_text);
688                                                 check_http ();
689                                         }
690                                         /* HDR_LOCATION URI_HTTP URI_HOST URI_PATH */
691                                         else if (sscanf (pos, HD2, server_type, server_address, server_url) == 3 ) { 
692                                                 if (host_name != NULL) free(host_name);
693                                                 host_name = strdup(server_address);
694                                                 use_ssl = server_type_check (server_type);
695                                                 server_port = server_port_check (use_ssl);
696                                                 check_http ();
697                                         }
698                                         /* HDR_LOCATION URI_HTTP URI_HOST URI_PORT */
699                                         else if(sscanf (pos, HD3, server_type, server_address, server_port_text) == 3) {
700                                                 if (host_name != NULL) free(host_name);
701                                                 host_name = strdup(server_address);
702                                                 strcpy (server_url, "/");
703                                                 use_ssl = server_type_check (server_type);
704                                                 server_port = atoi (server_port_text);
705                                                 check_http ();
706                                         }
707                                         /* HDR_LOCATION URI_HTTP URI_HOST */
708                                         else if(sscanf (pos, HD4, server_type, server_address) == 2) {
709                                                 if (host_name != NULL) free(host_name);
710                                                 host_name = strdup(server_address);
711                                                 strcpy (server_url, "/");
712                                                 use_ssl = server_type_check (server_type);
713                                                 server_port = server_port_check (use_ssl);
714                                                 check_http ();
715                                         }
716                                         /* HDR_LOCATION URI_PATH */
717                                         else if (sscanf (pos, HD5, server_url) == 1) {
718                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
719                                                         *x = '\0';
720                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
721                                                 }
722                                                 check_http ();
723                                         }                                       
724                                         pos += (size_t) strcspn (pos, "\r\n");
725                                         pos += (size_t) strspn (pos, "\r\n");
726                                 } /* end while (pos) */
727                                 printf (_("UNKNOWN - Could not find redirect location - %s%s"),
728                                         status_line, (display_html ? "</A>" : ""));
729                                 exit (STATE_UNKNOWN);
730                         } /* end if (onredirect == STATE_DEPENDENT) */
731                         
732                         else if (onredirect == STATE_UNKNOWN)
733                                 printf (_("UNKNOWN"));
734                         else if (onredirect == STATE_OK)
735                                 printf (_("OK"));
736                         else if (onredirect == STATE_WARNING)
737                                 printf (_("WARNING"));
738                         else if (onredirect == STATE_CRITICAL)
739                                 printf (_("CRITICAL"));
740                         microsec = deltime (tv);
741                         elapsed_time = (double)microsec / 1.0e6;
742                         asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
743                                  status_line, elapsed_time, timestamp,
744                            (display_html ? "</A>" : ""), microsec, pagesize);
745                         die (onredirect, "%s", msg);
746                 } /* end if (strstr (status_line, "30[0-4]") */
749         } /* end else (server_expect_yn)  */
751                 
752         /* check elapsed time */
753         microsec = deltime (tv);
754         elapsed_time = (double)microsec / 1.0e6;
755         asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
756                        status_line, elapsed_time, timestamp,
757                        (display_html ? "</A>" : ""), microsec, pagesize);
758         if (check_critical_time == TRUE && elapsed_time > critical_time)
759                 die (STATE_CRITICAL, "%s", msg);
760         if (check_warning_time == TRUE && elapsed_time > warning_time)
761                 die (STATE_WARNING, "%s", msg);
763         /* Page and Header content checks go here */
764         /* these checks should be last */
766         if (strlen (string_expect)) {
767                 if (strstr (page, string_expect)) {
768                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
769                                 status_line, elapsed_time,
770                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
771                         exit (STATE_OK);
772                 }
773                 else {
774                         printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
775                                 (display_html ? "</A>" : ""), microsec, pagesize);
776                         exit (STATE_CRITICAL);
777                 }
778         }
779 #ifdef HAVE_REGEX_H
780         if (strlen (regexp)) {
781                 errcode = regexec (&preg, page, REGS, pmatch, 0);
782                 if (errcode == 0) {
783                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
784                                 status_line, elapsed_time,
785                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
786                         exit (STATE_OK);
787                 }
788                 else {
789                         if (errcode == REG_NOMATCH) {
790                                 printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
791                                         (display_html ? "</A>" : ""), microsec, pagesize);
792                                 exit (STATE_CRITICAL);
793                         }
794                         else {
795                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
796                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
797                                 exit (STATE_CRITICAL);
798                         }
799                 }
800         }
801 #endif
803         /* make sure the page is of an appropriate size */
804         page_len = strlen (page);
805         if ((min_page_len > 0) && (page_len < min_page_len)) {
806                 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
807                         (display_html ? "</A>" : ""), page_len );
808                 exit (STATE_WARNING);
809         }
810         /* We only get here if all tests have been passed */
811         asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
812                         status_line, elapsed_time,
813                         timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
814         die (STATE_OK, "%s", msg);
815         return STATE_UNKNOWN;
820 #ifdef HAVE_SSL
821 int connect_SSL (void)
823         SSL_METHOD *meth;
825         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
826         RAND_seed (randbuff, (int)strlen(randbuff));
827         if (verbose)
828                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
830         /* Initialize SSL context */
831         SSLeay_add_ssl_algorithms ();
832         meth = SSLv23_client_method ();
833         SSL_load_error_strings ();
834         if ((ctx = SSL_CTX_new (meth)) == NULL) {
835                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
836                 return STATE_CRITICAL;
837         }
839         /* Initialize alarm signal handling */
840         signal (SIGALRM, socket_timeout_alarm_handler);
842         /* Set socket timeout */
843         alarm (socket_timeout);
845         /* Save start time */
846         gettimeofday (&tv, NULL);
848         /* Make TCP connection */
849         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
850                 /* Do the SSL handshake */
851                 if ((ssl = SSL_new (ctx)) != NULL) {
852                         SSL_set_cipher_list(ssl, "ALL");
853                         SSL_set_fd (ssl, sd);
854                         if (SSL_connect (ssl) != -1)
855                                 return OK;
856                         ERR_print_errors_fp (stderr);
857                 }
858                 else {
859                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
860                 }
861                 SSL_free (ssl);
862         }
864         SSL_CTX_free (ctx);
865         close (sd);
867         return STATE_CRITICAL;
869 #endif
871 #ifdef HAVE_SSL
872 int
873 check_certificate (X509 ** certificate)
875         ASN1_STRING *tm;
876         int offset;
877         struct tm stamp;
878         int days_left;
881         /* Retrieve timestamp of certificate */
882         tm = X509_get_notAfter (*certificate);
884         /* Generate tm structure to process timestamp */
885         if (tm->type == V_ASN1_UTCTIME) {
886                 if (tm->length < 10) {
887                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
888                         return STATE_CRITICAL;
889                 }
890                 else {
891                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
892                         if (stamp.tm_year < 50)
893                                 stamp.tm_year += 100;
894                         offset = 0;
895                 }
896         }
897         else {
898                 if (tm->length < 12) {
899                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
900                         return STATE_CRITICAL;
901                 }
902                 else {
903                         stamp.tm_year =
904                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
905                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
906                         stamp.tm_year -= 1900;
907                         offset = 2;
908                 }
909         }
910         stamp.tm_mon =
911                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
912         stamp.tm_mday =
913                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
914         stamp.tm_hour =
915                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
916         stamp.tm_min =
917                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
918         stamp.tm_sec = 0;
919         stamp.tm_isdst = -1;
921         days_left = (mktime (&stamp) - time (NULL)) / 86400;
922         snprintf
923                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
924                  stamp.tm_mon + 1,
925                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
927         if (days_left > 0 && days_left <= days_till_exp) {
928                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
929                 return STATE_WARNING;
930         }
931         if (days_left < 0) {
932                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
933                 return STATE_CRITICAL;
934         }
936         if (days_left == 0) {
937                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
938                 return STATE_WARNING;
939         }
941         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
943         return STATE_OK;
945 #endif
946 \f
949 int
950 my_recv (void)
952         int i;
953 #ifdef HAVE_SSL
954         if (use_ssl) {
955                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
956         }
957         else {
958                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
959         }
960 #else
961         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
962 #endif
963         return i;
967 int
968 my_close (void)
970 #ifdef HAVE_SSL
971         if (use_ssl == TRUE) {
972                 SSL_shutdown (ssl);
973                 SSL_free (ssl);
974                 SSL_CTX_free (ctx);
975                 return 0;
976         }
977         else {
978 #endif
979                 return close (sd);
980 #ifdef HAVE_SSL
981         }
982 #endif
989 \f
990 void
991 print_help (void)
993         print_revision (progname, revision);
995         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
996         printf (_(COPYRIGHT), copyright, email);
998         printf (_("\
999 This plugin tests the HTTP service on the specified host. It can test\n\
1000 normal (http) and secure (https) servers, follow redirects, search for\n\
1001 strings and regular expressions, check connection times, and report on\n\
1002 certificate expiration times.\n"));
1004         print_usage ();
1006         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1008         printf (_(UT_HELP_VRSN));
1010         printf (_("\
1011  -H, --hostname=ADDRESS\n\
1012     Host name argument for servers using host headers (virtual host)\n\
1013  -I, --IP-address=ADDRESS\n\
1014    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1015  -p, --port=INTEGER\n\
1016    Port number (default: %d)\n"), HTTP_PORT);
1018         printf (_(UT_IPv46));
1020 #ifdef HAVE_SSL
1021         printf (_("\
1022  -S, --ssl\n\
1023     Connect via SSL\n\
1024  -C, --certificate=INTEGER\n\
1025     Minimum number of days a certificate has to be valid.\n\
1026     (when this option is used the url is not checked.)\n"));
1027 #endif
1029         printf (_("\
1030  -e, --expect=STRING\n\
1031    String to expect in first (status) line of server response (default: %s)\n\
1032    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1033  -s, --string=STRING\n\
1034    String to expect in the content\n\
1035  -u, --url=PATH\n\
1036    URL to GET or POST (default: /)\n\
1037  -P, --post=STRING\n\
1038    URL encoded http POST data\n"), HTTP_EXPECT);
1040 #ifdef HAVE_REGEX_H
1041         printf (_("\
1042  -l, --linespan\n\
1043     Allow regex to span newlines (must precede -r or -R)\n\
1044  -r, --regex, --ereg=STRING\n\
1045     Search page for regex STRING\n\
1046  -R, --eregi=STRING\n\
1047     Search page for case-insensitive regex STRING\n"));
1048 #endif
1050         printf (_("\
1051  -a, --authorization=AUTH_PAIR\n\
1052    Username:password on sites with basic authentication\n\
1053  -L, --link=URL\n\
1054    Wrap output in HTML link (obsoleted by urlize)\n\
1055  -f, --onredirect=<ok|warning|critical|follow>\n\
1056    How to handle redirected pages\n\
1057  -m, --min=INTEGER\n\
1058    Minimum page size required (bytes)\n"));
1060         printf (_(UT_WARN_CRIT));
1062         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1064         printf (_(UT_VERBOSE));
1066                                         printf (_("\
1067 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1068 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1069 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1070 messages from the host result in STATE_WARNING return values.  If you are\n\
1071 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1072 (fully qualified domain name) as the [host_name] argument.\n"));
1074 #ifdef HAVE_SSL
1075         printf (_("\n\
1076 This plugin can also check whether an SSL enabled web server is able to\n\
1077 serve content (optionally within a specified time) or whether the X509 \n\
1078 certificate is still valid for the specified number of days.\n"));
1079         printf (_("\n\
1080 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1081 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1082 STATE_OK will be returned. When the server returns its content but exceeds\n\
1083 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1084 a STATE_CRITICAL will be returned.\n\n"));
1086         printf (_("\
1087 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1088 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1089 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1090 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1091 the certificate is expired.\n"));
1092 #endif
1094         printf (_(UT_SUPPORT));
1101 void
1102 print_usage (void)
1104         printf (_("\
1105 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1106   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1107   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1108   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1109   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1110         printf (_(UT_HLP_VRS), progname, progname);