Code

add function for elapsed tim ein microseconds
[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 #endif
45 #ifdef HAVE_OPENSSL_SSL_H
46 #include <openssl/rsa.h>
47 #include <openssl/crypto.h>
48 #include <openssl/x509.h>
49 #include <openssl/pem.h>
50 #include <openssl/ssl.h>
51 #include <openssl/err.h>
52 #include <openssl/rand.h>
53 #endif
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 /* per RFC 2396 */
88  
89 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
90 #define URI_HTTP "%[HTPShtps]://"
91 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
92 #define URI_PORT ":%[0123456789]"
93 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
95 #define HTTP_URL "/"
96 #define CRLF "\r\n"
98 char timestamp[17] = "";
99 int specify_port = FALSE;
100 int server_port = HTTP_PORT;
101 char server_port_text[6] = "";
102 char server_type[6] = "http";
103 char *server_address = ""; 
104 char *host_name = "";
105 char *server_url = "";
106 int server_url_length;
107 int server_expect_yn = 0;
108 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
109 char string_expect[MAX_INPUT_BUFFER] = "";
110 double warning_time = 0;
111 int check_warning_time = FALSE;
112 double critical_time = 0;
113 int check_critical_time = FALSE;
114 char user_auth[MAX_INPUT_BUFFER] = "";
115 int display_html = FALSE;
116 int onredirect = STATE_OK;
117 int use_ssl = FALSE;
118 int verbose = FALSE;
119 int sd;
120 int min_page_len = 0;
121 char *http_method = "GET";
122 char *http_post_data = "";
123 char buffer[MAX_INPUT_BUFFER];
125 int process_arguments (int, char **);
126 static char *base64 (char *bin, size_t len);
127 int check_http (void);
128 int my_recv (void);
129 int my_close (void);
130 void print_help (void);
131 void print_usage (void);
133 int
134 main (int argc, char **argv)
136         int result = STATE_UNKNOWN;
138         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
139         asprintf (&server_url, "%s", HTTP_URL);
140         server_url_length = strlen(server_url);
142         if (process_arguments (argc, argv) == ERROR)
143                 usage (_("check_http: could not parse arguments\n"));
145         if (strstr (timestamp, ":")) {
146                 if (strstr (server_url, "?"))
147                         asprintf (&server_url, "%s&%s", server_url, timestamp);
148                 else
149                         asprintf (&server_url, "%s?%s", server_url, timestamp);
150         }
152         if (display_html == TRUE)
153                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
154                         host_name, server_port, server_url);
156         /* initialize alarm signal handling, set socket timeout, start timer */
157         (void) signal (SIGALRM, socket_timeout_alarm_handler);
158         (void) alarm (socket_timeout);
159         gettimeofday (&tv, NULL);
161 #ifdef HAVE_SSL
162         if (use_ssl && check_cert == TRUE) {
163                 if (connect_SSL () != OK)
164                         die (STATE_CRITICAL,
165                                    _("HTTP CRITICAL - Could not make SSL connection\n"));
166                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
167                         result = check_certificate (&server_cert);
168                         X509_free (server_cert);
169                 }
170                 else {
171                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
172                         result = STATE_CRITICAL;
173                 }
174                 SSL_shutdown (ssl);
175                 SSL_free (ssl);
176                 SSL_CTX_free (ctx);
177                 close (sd);
178         }
179         else {
180                 result = check_http ();
181         }
182 #else
183         result = check_http ();
184 #endif
185         return result;
187 \f
190 /* process command-line arguments */
191 int
192 process_arguments (int argc, char **argv)
194         int c = 1;
196         int option_index = 0;
197         static struct option long_options[] = {
198                 STD_LONG_OPTS,
199                 {"file",required_argument,0,'F'},
200                 {"link", no_argument, 0, 'L'},
201                 {"nohtml", no_argument, 0, 'n'},
202                 {"ssl", no_argument, 0, 'S'},
203                 {"verbose", no_argument, 0, 'v'},
204                 {"post", required_argument, 0, 'P'},
205                 {"IP-address", required_argument, 0, 'I'},
206                 {"string", required_argument, 0, 's'},
207                 {"regex", required_argument, 0, 'r'},
208                 {"ereg", required_argument, 0, 'r'},
209                 {"eregi", required_argument, 0, 'R'},
210                 {"linespan", no_argument, 0, 'l'},
211                 {"onredirect", required_argument, 0, 'f'},
212                 {"certificate", required_argument, 0, 'C'},
213                 {"min", required_argument, 0, 'm'},
214                 {"use-ipv4", no_argument, 0, '4'},
215                 {"use-ipv6", no_argument, 0, '6'},
216                 {0, 0, 0, 0}
217         };
219         if (argc < 2)
220                 return ERROR;
222         for (c = 1; c < argc; c++) {
223                 if (strcmp ("-to", argv[c]) == 0)
224                         strcpy (argv[c], "-t");
225                 if (strcmp ("-hn", argv[c]) == 0)
226                         strcpy (argv[c], "-H");
227                 if (strcmp ("-wt", argv[c]) == 0)
228                         strcpy (argv[c], "-w");
229                 if (strcmp ("-ct", argv[c]) == 0)
230                         strcpy (argv[c], "-c");
231                 if (strcmp ("-nohtml", argv[c]) == 0)
232                         strcpy (argv[c], "-n");
233         }
235         while (1) {
236                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", long_options, &option_index);
237                 if (c == -1 || c == EOF)
238                         break;
240                 switch (c) {
241                 case '?': /* usage */
242                         usage3 (_("unknown argument"), optopt);
243                         break;
244                 case 'h': /* help */
245                         print_help ();
246                         exit (STATE_OK);
247                         break;
248                 case 'V': /* version */
249                         print_revision (progname, revision);
250                         exit (STATE_OK);
251                         break;
252                 case 't': /* timeout period */
253                         if (!is_intnonneg (optarg))
254                                 usage2 (_("timeout interval must be a non-negative integer"), optarg);
255                         else
256                                 socket_timeout = atoi (optarg);
257                         break;
258                 case 'c': /* critical time threshold */
259                         if (!is_intnonneg (optarg))
260                                 usage2 (_("invalid critical threshold"), optarg);
261                         else {
262                                 critical_time = strtod (optarg, NULL);
263                                 check_critical_time = TRUE;
264                         }
265                         break;
266                 case 'w': /* warning time threshold */
267                         if (!is_intnonneg (optarg))
268                                 usage2 (_("invalid warning threshold"), optarg);
269                         else {
270                                 warning_time = strtod (optarg, NULL);
271                                 check_warning_time = TRUE;
272                         }
273                         break;
274                 case 'L': /* show html link */
275                         display_html = TRUE;
276                         break;
277                 case 'n': /* do not show html link */
278                         display_html = FALSE;
279                         break;
280                 case 'S': /* use SSL */
281 #ifndef HAVE_SSL
282                         usage (_("check_http: invalid option - SSL is not available\n"));
283 #endif
284                         use_ssl = TRUE;
285                         if (specify_port == FALSE)
286                                 server_port = HTTPS_PORT;
287                         break;
288                 case 'C': /* Check SSL cert validity */
289 #ifdef HAVE_SSL
290                         if (!is_intnonneg (optarg))
291                                 usage2 (_("invalid certificate expiration period"), optarg);
292                         else {
293                                 days_till_exp = atoi (optarg);
294                                 check_cert = TRUE;
295                         }
296 #else
297                         usage (_("check_http: invalid option - SSL is not available\n"));
298 #endif
299                         break;
300                 case 'f': /* onredirect */
301                         if (!strcmp (optarg, "follow"))
302                                 onredirect = STATE_DEPENDENT;
303                         if (!strcmp (optarg, "unknown"))
304                                 onredirect = STATE_UNKNOWN;
305                         if (!strcmp (optarg, "ok"))
306                                 onredirect = STATE_OK;
307                         if (!strcmp (optarg, "warning"))
308                                 onredirect = STATE_WARNING;
309                         if (!strcmp (optarg, "critical"))
310                                 onredirect = STATE_CRITICAL;
311                         if (verbose)
312                                 printf(_("option f:%d \n"), onredirect);  
313                         break;
314                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
315                 case 'H': /* Host Name (virtual host) */
316                         asprintf (&host_name, "%s", optarg);
317                         break;
318                 case 'I': /* Server IP-address */
319                         asprintf (&server_address, "%s", optarg);
320                         break;
321                 case 'u': /* Host or server */
322                         asprintf (&server_url, "%s", optarg);
323                         server_url_length = strlen (server_url);
324                         break;
325                 case 'p': /* Host or server */
326                         if (!is_intnonneg (optarg))
327                                 usage2 (_("invalid port number"), optarg);
328                         else {
329                                 server_port = atoi (optarg);
330                                 specify_port = TRUE;
331                         }
332                         break;
333                 case 'a': /* authorization info */
334                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
335                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
336                         break;
337                 case 'P': /* HTTP POST data in URL encoded format */
338                         asprintf (&http_method, "%s", "POST");
339                         asprintf (&http_post_data, "%s", optarg);
340                         break;
341                 case 's': /* string or substring */
342                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
343                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
344                         break;
345                 case 'e': /* string or substring */
346                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
347                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
348                         server_expect_yn = 1;
349                         break;
350 #ifndef HAVE_REGEX_H
351                 case 'l': /* linespan */
352                 case 'r': /* linespan */
353                 case 'R': /* linespan */
354                         usage (_("check_http: call for regex which was not a compiled option\n"));
355                         break;
356 #else
357                 case 'l': /* linespan */
358                         cflags &= ~REG_NEWLINE;
359                         break;
360                 case 'R': /* regex */
361                         cflags |= REG_ICASE;
362                 case 'r': /* regex */
363                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
364                         regexp[MAX_RE_SIZE - 1] = 0;
365                         errcode = regcomp (&preg, regexp, cflags);
366                         if (errcode != 0) {
367                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
368                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
369                                 return ERROR;
370                         }
371                         break;
372 #endif
373                 case '4':
374                         address_family = AF_INET;
375                         break;
376                 case '6':
377 #ifdef USE_IPV6
378                         address_family = AF_INET6;
379 #else
380                         usage (_("IPv6 support not available\n"));
381 #endif
382                         break;
383                 case 'v': /* verbose */
384                         verbose = TRUE;
385                         break;
386                 case 'm': /* min_page_length */
387                         min_page_len = atoi (optarg);
388                         break;
389                 }
390         }
392         c = optind;
394         if (strcmp (server_address, "") == 0 && c < argc)
395                         asprintf (&server_address, "%s", argv[c++]);
397         if (strcmp (host_name, "") == 0 && c < argc)
398                 asprintf (&host_name, "%s", argv[c++]);
400         if (strcmp (server_address ,"") == 0) {
401                 if (strcmp (host_name, "") == 0)
402                         usage (_("check_http: you must specify a server address or host name\n"));
403                 else
404                         asprintf (&server_address, "%s", host_name);
405         }
407         if (check_critical_time && critical_time>(double)socket_timeout)
408                 socket_timeout = (int)critical_time + 1;
410         return TRUE;
412 \f
415 /* written by lauri alanko */
416 static char *
417 base64 (char *bin, size_t len)
420         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
421         size_t i = 0, j = 0;
423         char BASE64_END = '=';
424         char base64_table[64];
425         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
427         while (j < len - 2) {
428                 buf[i++] = base64_table[bin[j] >> 2];
429                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
430                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
431                 buf[i++] = base64_table[bin[j + 2] & 63];
432                 j += 3;
433         }
435         switch (len - j) {
436         case 1:
437                 buf[i++] = base64_table[bin[j] >> 2];
438                 buf[i++] = base64_table[(bin[j] & 3) << 4];
439                 buf[i++] = BASE64_END;
440                 buf[i++] = BASE64_END;
441                 break;
442         case 2:
443                 buf[i++] = base64_table[bin[j] >> 2];
444                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
445                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
446                 buf[i++] = BASE64_END;
447                 break;
448         case 0:
449                 break;
450         }
452         buf[i] = '\0';
453         return buf;
455 \f
458 int
459 check_http (void)
461         char *msg = NULL;
462         char *status_line = "";
463         char *header = NULL;
464         char *page = "";
465         char *auth = NULL;
466         int i = 0;
467         size_t pagesize = 0;
468         char *full_page = "";
469         char *buf = NULL;
470         char *pos = "";
471         char *x = NULL;
472         char *orig_url = NULL;
473         double elapsed_time;
474         int page_len = 0;
475 #ifdef HAVE_SSL
476         int sslerr;
477 #endif
479         /* try to connect to the host at the given port number */
480 #ifdef HAVE_SSL
481         if (use_ssl == TRUE) {
483                 if (connect_SSL () != OK) {
484                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
485                 }
487                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
488                         X509_free (server_cert);
489                 }
490                 else {
491                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
492                         return STATE_CRITICAL;
493                 }
495         }
496         else {
497 #endif
498                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
499                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
500 #ifdef HAVE_SSL
501         }
502 #endif
504         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
506         /* optionally send the host header info (not clear if it's usable) */
507         if (strcmp (host_name, ""))
508                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
510         /* send user agent */
511         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
512                   buf, clean_revstring (revision), VERSION);
514         /* optionally send the authentication info */
515         if (strcmp (user_auth, "")) {
516                 auth = base64 (user_auth, strlen (user_auth));
517                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
518         }
520         /* either send http POST data */
521         if (strlen (http_post_data)) {
522                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
523                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
524                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
525         }
526         else {
527                 /* or just a newline so the server knows we're done with the request */
528                 asprintf (&buf, "%s%s", buf, CRLF);
529         }
531 #ifdef HAVE_SSL
532         if (use_ssl == TRUE) {
533                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
534                         ERR_print_errors_fp (stderr);
535                         return STATE_CRITICAL;
536                 }
537         }
538         else {
539 #endif
540                 send (sd, buf, strlen (buf), 0);
541 #ifdef HAVE_SSL
542         }
543 #endif
545         /* fetch the page */
546         while ((i = my_recv ()) > 0) {
547                 buffer[i] = '\0';
548                 asprintf (&full_page, "%s%s", full_page, buffer);
549                 pagesize += i;
550         }
552         if (i < 0 && errno != ECONNRESET) {
553 #ifdef HAVE_SSL
554                 if (use_ssl) {
555                         sslerr=SSL_get_error(ssl, i);
556                         if ( sslerr == SSL_ERROR_SSL ) {
557                                 die (STATE_WARNING, _("Client Certificate Required\n"));
558                         } else {
559                                 die (STATE_CRITICAL, _("Error in recv()"));
560                         }
561                 }
562                 else {
563 #endif
564                         die (STATE_CRITICAL, _("Error in recv()"));
565 #ifdef HAVE_SSL
566                 }
567 #endif
568         }
570         /* return a CRITICAL status if we couldn't read any data */
571         if (pagesize == (size_t) 0)
572                 die (STATE_CRITICAL, _("No data received %s"), timestamp);
574         /* close the connection */
575         my_close ();
577         /* reset the alarm */
578         alarm (0);
580         /* leave full_page untouched so we can free it later */
581         page = full_page;
583         if (verbose)
584                 printf ("Page is %d characters\n", pagesize);
586         /* find status line and null-terminate it */
587         status_line = page;
588         page += (size_t) strcspn (page, "\r\n");
589         pos = page;
590         page += (size_t) strspn (page, "\r\n");
591         status_line[strcspn(status_line, "\r\n")] = 0;
592         strip (status_line);
593         if (verbose)
594                 printf ("STATUS: %s\n", status_line);
596         /* find header info and null-terminate it */
597         header = page;
598         while (strcspn (page, "\r\n") > 0) {
599                 page += (size_t) strcspn (page, "\r\n");
600                 pos = page;
601                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
602                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
603                         page += (size_t) 2;
604                 else
605                         page += (size_t) 1;
606         }
607         page += (size_t) strspn (page, "\r\n");
608         header[pos - header] = 0;
609         if (verbose)
610                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
612         /* make sure the status line matches the response we are looking for */
613         if (!strstr (status_line, server_expect)) {
614                 if (server_port == HTTP_PORT)
615                         asprintf (&msg, _("Invalid HTTP response received from host\n"));
616                 else
617                         asprintf (&msg,
618                                         _("Invalid HTTP response received from host on port %d\n"),
619                                         server_port);
620                 die (STATE_CRITICAL, "%s", msg);
621         }
624         /* Exit here if server_expect was set by user and not default */
625         if ( server_expect_yn  )  {
626                 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
627                           server_expect);
628                 if (verbose)
629                         printf ("%s\n",msg);
631         }
632         else {
633         
635                 /* check the return code */
636                 /* server errors result in a critical state */
637                 if (strstr (status_line, "500") ||
638                   strstr (status_line, "501") ||
639                 strstr (status_line, "502") ||
640                     strstr (status_line, "503")) {
641                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
642                 }
644                 /* client errors result in a warning state */
645                 if (strstr (status_line, "400") ||
646                   strstr (status_line, "401") ||
647                 strstr (status_line, "402") ||
648                     strstr (status_line, "403") ||
649                     strstr (status_line, "404")) {
650                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
651                 }
653                 /* check redirected page if specified */
654                 if (strstr (status_line, "300") ||
655                   strstr (status_line, "301") ||
656                 strstr (status_line, "302") ||
657                     strstr (status_line, "303") ||
658                     strstr (status_line, "304")) {
659                         if (onredirect == STATE_DEPENDENT) {
661                                 asprintf (&orig_url, "%s", server_url);
662                                 pos = header;
663                                 while (pos) {
664                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
665                                         if (server_address == NULL)
666                                                 die (STATE_UNKNOWN,
667                                                                                  _("HTTP UNKNOWN: could not allocate server_address"));
668                                         if (strcspn (pos, "\r\n") > (size_t)server_url_length) {
669                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
670                                                 if (server_url == NULL)
671                                                         die (STATE_UNKNOWN,
672                                                                    _("HTTP UNKNOWN: could not allocate server_url"));
673                                                 server_url_length = strcspn (pos, "\r\n");
674                                         }
675                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
676                                                 asprintf (&host_name, "%s", server_address);
677                                                 use_ssl = server_type_check (server_type);
678                                                 server_port = atoi (server_port_text);
679                                                 check_http ();
680                                         }
681                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
682                                                 asprintf (&host_name, "%s", server_address);
683                                                 use_ssl = server_type_check (server_type);
684                                                 server_port = server_port_check (use_ssl);
685                                                 check_http ();
686                                         }
687                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
688                                                 asprintf (&host_name, "%s", server_address);
689                                                 strcpy (server_url, "/");
690                                                 use_ssl = server_type_check (server_type);
691                                                 server_port = atoi (server_port_text);
692                                                 check_http ();
693                                         }
694                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
695                                                 asprintf (&host_name, "%s", server_address);
696                                                 strcpy (server_url, "/");
697                                                 use_ssl = server_type_check (server_type);
698                                                 server_port = server_port_check (use_ssl);
699                                                 check_http ();
700                                         }
701                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
702                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
703                                                         *x = '\0';
704                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
705                                                 }
706                                                 check_http ();
707                                         }                                       
708                                         pos += (size_t) strcspn (pos, "\r\n");
709                                         pos += (size_t) strspn (pos, "\r\n");
710                                 } /* end while (pos) */
711                                 printf (_("UNKNOWN - Could not find redirect location - %s%s"),
712                                         status_line, (display_html ? "</A>" : ""));
713                                 exit (STATE_UNKNOWN);
714                         } /* end if (onredirect == STATE_DEPENDENT) */
715                         
716                         else if (onredirect == STATE_UNKNOWN)
717                                 printf (_("UNKNOWN"));
718                         else if (onredirect == STATE_OK)
719                                 printf (_("OK"));
720                         else if (onredirect == STATE_WARNING)
721                                 printf (_("WARNING"));
722                         else if (onredirect == STATE_CRITICAL)
723                                 printf (_("CRITICAL"));
724                         elapsed_time = delta_time (tv);
725                         asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%.3f\n"),
726                                  status_line, elapsed_time, timestamp,
727                            (display_html ? "</A>" : ""), elapsed_time);
728                         die (onredirect, "%s", msg);
729                 } /* end if (strstr (status_line, "30[0-4]") */
732         } /* end else (server_expect_yn)  */
734                 
735         /* check elapsed time */
736         elapsed_time = delta_time (tv);
737         asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%.3f\n"),
738                        status_line, elapsed_time, timestamp,
739                        (display_html ? "</A>" : ""), elapsed_time);
740         if (check_critical_time == TRUE && elapsed_time > critical_time)
741                 die (STATE_CRITICAL, "%s", msg);
742         if (check_warning_time == TRUE && elapsed_time > warning_time)
743                 die (STATE_WARNING, "%s", msg);
745         /* Page and Header content checks go here */
746         /* these checks should be last */
748         if (strlen (string_expect)) {
749                 if (strstr (page, string_expect)) {
750                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n"),
751                                 status_line, elapsed_time,
752                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
753                         exit (STATE_OK);
754                 }
755                 else {
756                         printf (_("CRITICAL - string not found%s|time=%.3f\n"),
757                                 (display_html ? "</A>" : ""), elapsed_time);
758                         exit (STATE_CRITICAL);
759                 }
760         }
761 #ifdef HAVE_REGEX_H
762         if (strlen (regexp)) {
763                 errcode = regexec (&preg, page, REGS, pmatch, 0);
764                 if (errcode == 0) {
765                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n"),
766                                 status_line, elapsed_time,
767                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
768                         exit (STATE_OK);
769                 }
770                 else {
771                         if (errcode == REG_NOMATCH) {
772                                 printf (_("CRITICAL - pattern not found%s|time=%.3f\n"),
773                                         (display_html ? "</A>" : ""), elapsed_time);
774                                 exit (STATE_CRITICAL);
775                         }
776                         else {
777                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
778                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
779                                 exit (STATE_CRITICAL);
780                         }
781                 }
782         }
783 #endif
785         /* make sure the page is of an appropriate size */
786         page_len = strlen (page);
787         if ((min_page_len > 0) && (page_len < min_page_len)) {
788                 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
789                         (display_html ? "</A>" : ""), page_len );
790                 exit (STATE_WARNING);
791         }
792         /* We only get here if all tests have been passed */
793         asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n"),
794                         status_line, (float)elapsed_time,
795                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
796         die (STATE_OK, "%s", msg);
797         return STATE_UNKNOWN;
802 #ifdef HAVE_SSL
803 int connect_SSL (void)
805         SSL_METHOD *meth;
807         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
808         RAND_seed (randbuff, (int)strlen(randbuff));
809         if (verbose)
810                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
812         /* Initialize SSL context */
813         SSLeay_add_ssl_algorithms ();
814         meth = SSLv23_client_method ();
815         SSL_load_error_strings ();
816         if ((ctx = SSL_CTX_new (meth)) == NULL) {
817                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
818                 return STATE_CRITICAL;
819         }
821         /* Initialize alarm signal handling */
822         signal (SIGALRM, socket_timeout_alarm_handler);
824         /* Set socket timeout */
825         alarm (socket_timeout);
827         /* Save start time */
828         gettimeofday (&tv, NULL);
830         /* Make TCP connection */
831         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
832                 /* Do the SSL handshake */
833                 if ((ssl = SSL_new (ctx)) != NULL) {
834                         SSL_set_cipher_list(ssl, "ALL");
835                         SSL_set_fd (ssl, sd);
836                         if (SSL_connect (ssl) != -1)
837                                 return OK;
838                         ERR_print_errors_fp (stderr);
839                 }
840                 else {
841                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
842                 }
843                 SSL_free (ssl);
844         }
846         SSL_CTX_free (ctx);
847         close (sd);
849         return STATE_CRITICAL;
851 #endif
853 #ifdef HAVE_SSL
854 int
855 check_certificate (X509 ** certificate)
857         ASN1_STRING *tm;
858         int offset;
859         struct tm stamp;
860         int days_left;
863         /* Retrieve timestamp of certificate */
864         tm = X509_get_notAfter (*certificate);
866         /* Generate tm structure to process timestamp */
867         if (tm->type == V_ASN1_UTCTIME) {
868                 if (tm->length < 10) {
869                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
870                         return STATE_CRITICAL;
871                 }
872                 else {
873                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
874                         if (stamp.tm_year < 50)
875                                 stamp.tm_year += 100;
876                         offset = 0;
877                 }
878         }
879         else {
880                 if (tm->length < 12) {
881                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
882                         return STATE_CRITICAL;
883                 }
884                 else {
885                         stamp.tm_year =
886                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
887                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
888                         stamp.tm_year -= 1900;
889                         offset = 2;
890                 }
891         }
892         stamp.tm_mon =
893                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
894         stamp.tm_mday =
895                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
896         stamp.tm_hour =
897                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
898         stamp.tm_min =
899                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
900         stamp.tm_sec = 0;
901         stamp.tm_isdst = -1;
903         days_left = (mktime (&stamp) - time (NULL)) / 86400;
904         snprintf
905                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
906                  stamp.tm_mon + 1,
907                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
909         if (days_left > 0 && days_left <= days_till_exp) {
910                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
911                 return STATE_WARNING;
912         }
913         if (days_left < 0) {
914                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
915                 return STATE_CRITICAL;
916         }
918         if (days_left == 0) {
919                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
920                 return STATE_WARNING;
921         }
923         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
925         return STATE_OK;
927 #endif
928 \f
931 int
932 my_recv (void)
934         int i;
935 #ifdef HAVE_SSL
936         if (use_ssl) {
937                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
938         }
939         else {
940                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
941         }
942 #else
943         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
944 #endif
945         return i;
949 int
950 my_close (void)
952 #ifdef HAVE_SSL
953         if (use_ssl == TRUE) {
954                 SSL_shutdown (ssl);
955                 SSL_free (ssl);
956                 SSL_CTX_free (ctx);
957                 return 0;
958         }
959         else {
960 #endif
961                 return close (sd);
962 #ifdef HAVE_SSL
963         }
964 #endif
971 \f
972 void
973 print_help (void)
975         print_revision (progname, revision);
977         printf (_(COPYRIGHT), copyright, email);
979         printf (_("\
980 This plugin tests the HTTP service on the specified host. It can test\n\
981 normal (http) and secure (https) servers, follow redirects, search for\n\
982 strings and regular expressions, check connection times, and report on\n\
983 certificate expiration times.\n"));
985         print_usage ();
987         printf (_("NOTE: One or both of -H and -I must be specified\n"));
989         printf (_(UT_HELP_VRSN));
991         printf (_("\
992  -H, --hostname=ADDRESS\n\
993     Host name argument for servers using host headers (virtual host)\n\
994  -I, --IP-address=ADDRESS\n\
995    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
996  -p, --port=INTEGER\n\
997    Port number (default: %d)\n"), HTTP_PORT);
999         printf (_(UT_IPv46));
1001 #ifdef HAVE_SSL
1002         printf (_("\
1003  -S, --ssl\n\
1004     Connect via SSL\n\
1005  -C, --certificate=INTEGER\n\
1006     Minimum number of days a certificate has to be valid.\n\
1007     (when this option is used the url is not checked.)\n"));
1008 #endif
1010         printf (_("\
1011  -e, --expect=STRING\n\
1012    String to expect in first (status) line of server response (default: %s)\n\
1013    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1014  -s, --string=STRING\n\
1015    String to expect in the content\n\
1016  -u, --url=PATH\n\
1017    URL to GET or POST (default: /)\n\
1018  -P, --post=STRING\n\
1019    URL encoded http POST data\n"), HTTP_EXPECT);
1021 #ifdef HAVE_REGEX_H
1022         printf (_("\
1023  -l, --linespan\n\
1024     Allow regex to span newlines (must precede -r or -R)\n\
1025  -r, --regex, --ereg=STRING\n\
1026     Search page for regex STRING\n\
1027  -R, --eregi=STRING\n\
1028     Search page for case-insensitive regex STRING\n"));
1029 #endif
1031         printf (_("\
1032  -a, --authorization=AUTH_PAIR\n\
1033    Username:password on sites with basic authentication\n\
1034  -L, --link=URL\n\
1035    Wrap output in HTML link (obsoleted by urlize)\n\
1036  -f, --onredirect=<ok|warning|critical|follow>\n\
1037    How to handle redirected pages\n\
1038  -m, --min=INTEGER\n\
1039    Minimum page size required (bytes)\n"));
1041         printf (_(UT_WARN_CRIT));
1043         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1045         printf (_(UT_VERBOSE));
1047                                         printf (_("\
1048 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1049 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1050 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1051 messages from the host result in STATE_WARNING return values.  If you are\n\
1052 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1053 (fully qualified domain name) as the [host_name] argument.\n"));
1055 #ifdef HAVE_SSL
1056         printf (_("\n\
1057 This plugin can also check whether an SSL enabled web server is able to\n\
1058 serve content (optionally within a specified time) or whether the X509 \n\
1059 certificate is still valid for the specified number of days.\n"));
1060         printf (_("\n\
1061 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1062 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1063 STATE_OK will be returned. When the server returns its content but exceeds\n\
1064 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1065 a STATE_CRITICAL will be returned.\n\n"));
1067         printf (_("\
1068 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1069 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1070 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1071 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1072 the certificate is expired.\n"));
1073 #endif
1075         printf (_(UT_SUPPORT));
1082 void
1083 print_usage (void)
1085         printf (_("\
1086 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1087   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1088   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1089   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1090   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1091         printf (_(UT_HLP_VRS), progname, progname);