Code

Changed long argument --minmax to --pagesize and updated help and usage
[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  $Id$
18  
19 ******************************************************************************/
20 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
22 const char *progname = "check_http";
23 const char *revision = "$Revision$";
24 const char *copyright = "1999-2004";
25 const char *email = "nagiosplug-devel@lists.sourceforge.net";
27 #include "common.h"
28 #include "netutils.h"
29 #include "utils.h"
31 #define INPUT_DELIMITER ";"
33 #define HTTP_EXPECT "HTTP/1."
34 enum {
35         MAX_IPV4_HOSTLENGTH = 255,
36         HTTP_PORT = 80,
37         HTTPS_PORT = 443
38 };
40 #ifdef HAVE_SSL_H
41 #include <rsa.h>
42 #include <crypto.h>
43 #include <x509.h>
44 #include <pem.h>
45 #include <ssl.h>
46 #include <err.h>
47 #include <rand.h>
48 #else
49 # ifdef HAVE_OPENSSL_SSL_H
50 # include <openssl/rsa.h>
51 # include <openssl/crypto.h>
52 # include <openssl/x509.h>
53 # include <openssl/pem.h>
54 # include <openssl/ssl.h>
55 # include <openssl/err.h>
56 # include <openssl/rand.h>
57 # endif
58 #endif
60 #ifdef HAVE_SSL
61 int check_cert = FALSE;
62 int days_till_exp;
63 char *randbuff;
64 SSL_CTX *ctx;
65 SSL *ssl;
66 X509 *server_cert;
67 int connect_SSL (void);
68 int check_certificate (X509 **);
69 #endif
70 int no_body = FALSE;
71 int maximum_age = -1;
73 #ifdef HAVE_REGEX_H
74 enum {
75         REGS = 2,
76         MAX_RE_SIZE = 256
77 };
78 #include <regex.h>
79 regex_t preg;
80 regmatch_t pmatch[REGS];
81 char regexp[MAX_RE_SIZE];
82 char errbuf[MAX_INPUT_BUFFER];
83 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
84 int errcode;
85 #endif
87 struct timeval tv;
89 #define HTTP_URL "/"
90 #define CRLF "\r\n"
92 char timestamp[17] = "";
93 int specify_port = FALSE;
94 int server_port = HTTP_PORT;
95 char server_port_text[6] = "";
96 char server_type[6] = "http";
97 char *server_address;
98 char *host_name;
99 char *server_url;
100 char *user_agent;
101 int server_url_length;
102 int server_expect_yn = 0;
103 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
104 char string_expect[MAX_INPUT_BUFFER] = "";
105 double warning_time = 0;
106 int check_warning_time = FALSE;
107 double critical_time = 0;
108 int check_critical_time = FALSE;
109 char user_auth[MAX_INPUT_BUFFER] = "";
110 int display_html = FALSE;
111 char *http_opt_headers;
112 int onredirect = STATE_OK;
113 int use_ssl = FALSE;
114 int verbose = FALSE;
115 int sd;
116 int min_page_len = 0;
117 int max_page_len = 0;
118 int redir_depth = 0;
119 int max_depth = 15;
120 char *http_method;
121 char *http_post_data;
122 char *http_content_type;
123 char buffer[MAX_INPUT_BUFFER];
125 int process_arguments (int, char **);
126 static char *base64 (const char *bin, size_t len);
127 int check_http (void);
128 void redir (char *pos, char *status_line);
129 int server_type_check(const char *type);
130 int server_port_check(int ssl_flag);
131 char *perfd_time (double microsec);
132 char *perfd_size (int page_len);
133 int my_recv (void);
134 int my_close (void);
135 void print_help (void);
136 void print_usage (void);
138 int
139 main (int argc, char **argv)
141         int result = STATE_UNKNOWN;
143         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
144         server_url = strdup(HTTP_URL);
145         server_url_length = strlen(server_url);
146         asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
147                   clean_revstring (revision), VERSION);
149         if (process_arguments (argc, argv) == ERROR)
150                 usage4 (_("Could not parse arguments"));
152         if (strstr (timestamp, ":")) {
153                 if (strstr (server_url, "?"))
154                         asprintf (&server_url, "%s&%s", server_url, timestamp);
155                 else
156                         asprintf (&server_url, "%s?%s", server_url, timestamp);
157         }
159         if (display_html == TRUE)
160                 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 
161                         use_ssl ? "https" : "http", host_name,
162                         server_port, server_url);
164         /* initialize alarm signal handling, set socket timeout, start timer */
165         (void) signal (SIGALRM, socket_timeout_alarm_handler);
166         (void) alarm (socket_timeout);
167         gettimeofday (&tv, NULL);
169 #ifdef HAVE_SSL
170         if (use_ssl && check_cert == TRUE) {
171                 if (connect_SSL () != OK)
172                         die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
173                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
174                         result = check_certificate (&server_cert);
175                         X509_free (server_cert);
176                 }
177                 else {
178                         printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
179                         result = STATE_CRITICAL;
180                 }
181                 SSL_shutdown (ssl);
182                 SSL_free (ssl);
183                 SSL_CTX_free (ctx);
184                 close (sd);
185         }
186         else {
187                 result = check_http ();
188         }
189 #else
190         result = check_http ();
191 #endif
192         return result;
197 /* process command-line arguments */
198 int
199 process_arguments (int argc, char **argv)
201         int c = 1;
203         int option = 0;
204         static struct option longopts[] = {
205                 STD_LONG_OPTS,
206                 {"file",required_argument,0,'F'},
207                 {"link", no_argument, 0, 'L'},
208                 {"nohtml", no_argument, 0, 'n'},
209                 {"ssl", no_argument, 0, 'S'},
210                 {"verbose", no_argument, 0, 'v'},
211                 {"post", required_argument, 0, 'P'},
212                 {"IP-address", required_argument, 0, 'I'},
213                 {"url", required_argument, 0, 'u'},
214                 {"string", required_argument, 0, 's'},
215                 {"regex", required_argument, 0, 'r'},
216                 {"ereg", required_argument, 0, 'r'},
217                 {"eregi", required_argument, 0, 'R'},
218                 {"linespan", no_argument, 0, 'l'},
219                 {"onredirect", required_argument, 0, 'f'},
220                 {"certificate", required_argument, 0, 'C'},
221                 {"useragent", required_argument, 0, 'A'},
222                 {"header", required_argument, 0, 'k'},
223                 {"no-body", no_argument, 0, 'N'},
224                 {"max-age", required_argument, 0, 'M'},
225                 {"content-type", required_argument, 0, 'T'},
226                 {"pagesize", required_argument, 0, 'm'},
227                 {"use-ipv4", no_argument, 0, '4'},
228                 {"use-ipv6", no_argument, 0, '6'},
229                 {0, 0, 0, 0}
230         };
232         if (argc < 2)
233                 return ERROR;
235         for (c = 1; c < argc; c++) {
236                 if (strcmp ("-to", argv[c]) == 0)
237                         strcpy (argv[c], "-t");
238                 if (strcmp ("-hn", argv[c]) == 0)
239                         strcpy (argv[c], "-H");
240                 if (strcmp ("-wt", argv[c]) == 0)
241                         strcpy (argv[c], "-w");
242                 if (strcmp ("-ct", argv[c]) == 0)
243                         strcpy (argv[c], "-c");
244                 if (strcmp ("-nohtml", argv[c]) == 0)
245                         strcpy (argv[c], "-n");
246         }
248         while (1) {
249                 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option);
250                 if (c == -1 || c == EOF)
251                         break;
253                 switch (c) {
254                 case '?': /* usage */
255                         usage2 (_("Unknown argument"), optarg);
256                         break;
257                 case 'h': /* help */
258                         print_help ();
259                         exit (STATE_OK);
260                         break;
261                 case 'V': /* version */
262                         print_revision (progname, revision);
263                         exit (STATE_OK);
264                         break;
265                 case 't': /* timeout period */
266                         if (!is_intnonneg (optarg))
267                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
268                         else
269                                 socket_timeout = atoi (optarg);
270                         break;
271                 case 'c': /* critical time threshold */
272                         if (!is_nonnegative (optarg))
273                                 usage2 (_("Critical threshold must be integer"), optarg);
274                         else {
275                                 critical_time = strtod (optarg, NULL);
276                                 check_critical_time = TRUE;
277                         }
278                         break;
279                 case 'w': /* warning time threshold */
280                         if (!is_nonnegative (optarg))
281                                 usage2 (_("Warning threshold must be integer"), optarg);
282                         else {
283                                 warning_time = strtod (optarg, NULL);
284                                 check_warning_time = TRUE;
285                         }
286                         break;
287                 case 'A': /* User Agent String */
288                         asprintf (&user_agent, "User-Agent: %s", optarg);
289                         break;
290                 case 'k': /* Additional headers */
291                         asprintf (&http_opt_headers, "%s", optarg);
292                         break;
293                 case 'L': /* show html link */
294                         display_html = TRUE;
295                         break;
296                 case 'n': /* do not show html link */
297                         display_html = FALSE;
298                         break;
299                 case 'S': /* use SSL */
300 #ifndef HAVE_SSL
301                         usage4 (_("Invalid option - SSL is not available"));
302 #endif
303                         use_ssl = TRUE;
304                         if (specify_port == FALSE)
305                                 server_port = HTTPS_PORT;
306                         break;
307                 case 'C': /* Check SSL cert validity */
308 #ifdef HAVE_SSL
309                         if (!is_intnonneg (optarg))
310                                 usage2 (_("Invalid certificate expiration period"), optarg);
311                         else {
312                                 days_till_exp = atoi (optarg);
313                                 check_cert = TRUE;
314                         }
315 #else
316                         usage4 (_("Invalid option - SSL is not available"));
317 #endif
318                         break;
319                 case 'f': /* onredirect */
320                         if (!strcmp (optarg, "follow"))
321                                 onredirect = STATE_DEPENDENT;
322                         if (!strcmp (optarg, "unknown"))
323                                 onredirect = STATE_UNKNOWN;
324                         if (!strcmp (optarg, "ok"))
325                                 onredirect = STATE_OK;
326                         if (!strcmp (optarg, "warning"))
327                                 onredirect = STATE_WARNING;
328                         if (!strcmp (optarg, "critical"))
329                                 onredirect = STATE_CRITICAL;
330                         if (verbose)
331                                 printf(_("option f:%d \n"), onredirect);  
332                         break;
333                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
334                 case 'H': /* Host Name (virtual host) */
335                         host_name = strdup (optarg);
336                         if (strstr (optarg, ":"))
337                                 sscanf (optarg, "%*[^:]:%d", &server_port);
338                         break;
339                 case 'I': /* Server IP-address */
340                         server_address = strdup (optarg);
341                         break;
342                 case 'u': /* URL path */
343                         server_url = strdup (optarg);
344                         server_url_length = strlen (server_url);
345                         break;
346                 case 'p': /* Server port */
347                         if (!is_intnonneg (optarg))
348                                 usage2 (_("Invalid port number"), optarg);
349                         else {
350                                 server_port = atoi (optarg);
351                                 specify_port = TRUE;
352                         }
353                         break;
354                 case 'a': /* authorization info */
355                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
356                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
357                         break;
358                 case 'P': /* HTTP POST data in URL encoded format */
359                         if (http_method || http_post_data) break;
360                         http_method = strdup("POST");
361                         http_post_data = strdup (optarg);
362                         break;
363                 case 's': /* string or substring */
364                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
365                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
366                         break;
367                 case 'e': /* string or substring */
368                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
369                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
370                         server_expect_yn = 1;
371                         break;
372                 case 'T': /* Content-type */
373                         asprintf (&http_content_type, "%s", optarg);
374                         break;
375 #ifndef HAVE_REGEX_H
376                 case 'l': /* linespan */
377                 case 'r': /* linespan */
378                 case 'R': /* linespan */
379                         usage4 (_("Call for regex which was not a compiled option"));
380                         break;
381 #else
382                 case 'l': /* linespan */
383                         cflags &= ~REG_NEWLINE;
384                         break;
385                 case 'R': /* regex */
386                         cflags |= REG_ICASE;
387                 case 'r': /* regex */
388                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
389                         regexp[MAX_RE_SIZE - 1] = 0;
390                         errcode = regcomp (&preg, regexp, cflags);
391                         if (errcode != 0) {
392                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
393                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
394                                 return ERROR;
395                         }
396                         break;
397 #endif
398                 case '4':
399                         address_family = AF_INET;
400                         break;
401                 case '6':
402 #ifdef USE_IPV6
403                         address_family = AF_INET6;
404 #else
405                         usage4 (_("IPv6 support not available"));
406 #endif
407                         break;
408                 case 'v': /* verbose */
409                         verbose = TRUE;
410                         break;
411                 case 'm': /* min_page_length */
412                         {
413                         char *tmp;
414                         if (strchr(optarg, ':') != (char *)NULL) {
415                                 /* range, so get two values, min:max */
416                                 tmp = strtok(optarg, ":");
417                                 if (tmp == NULL) {
418                                         printf("Bad format: try \"-m min:max\"\n");
419                                         exit (STATE_WARNING);
420                                 } else
421                                         min_page_len = atoi(tmp);
423                                 tmp = strtok(NULL, ":");
424                                 if (tmp == NULL) {
425                                         printf("Bad format: try \"-m min:max\"\n");
426                                         exit (STATE_WARNING);
427                                 } else
428                                         max_page_len = atoi(tmp);
429                         } else 
430                                 min_page_len = atoi (optarg);
431                         break;
432                         }
433                 case 'N': /* no-body */
434                         no_body = TRUE;
435                         break;
436                 case 'M': /* max-age */
437                   {
438                     int L = strlen(optarg);
439                     if (L && optarg[L-1] == 'm')
440                       maximum_age = atoi (optarg) * 60;
441                     else if (L && optarg[L-1] == 'h')
442                       maximum_age = atoi (optarg) * 60 * 60;
443                     else if (L && optarg[L-1] == 'd')
444                       maximum_age = atoi (optarg) * 60 * 60 * 24;
445                     else if (L && (optarg[L-1] == 's' ||
446                                    isdigit (optarg[L-1])))
447                       maximum_age = atoi (optarg);
448                     else {
449                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
450                       exit (STATE_WARNING);
451                     }
452                   }
453                   break;
454                 }
455         }
457         c = optind;
459         if (server_address == NULL && c < argc)
460                 server_address = strdup (argv[c++]);
462         if (host_name == NULL && c < argc)
463                 host_name = strdup (argv[c++]);
465         if (server_address == NULL) {
466                 if (host_name == NULL)
467                         usage4 (_("You must specify a server address or host name"));
468                 else
469                         server_address = strdup (host_name);
470         }
472         if (check_critical_time && critical_time>(double)socket_timeout)
473                 socket_timeout = (int)critical_time + 1;
475         if (http_method == NULL)
476                 http_method = strdup ("GET");
478         return TRUE;
483 /* written by lauri alanko */
484 static char *
485 base64 (const char *bin, size_t len)
488         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
489         size_t i = 0, j = 0;
491         char BASE64_END = '=';
492         char base64_table[64];
493         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
495         while (j < len - 2) {
496                 buf[i++] = base64_table[bin[j] >> 2];
497                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
498                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
499                 buf[i++] = base64_table[bin[j + 2] & 63];
500                 j += 3;
501         }
503         switch (len - j) {
504         case 1:
505                 buf[i++] = base64_table[bin[j] >> 2];
506                 buf[i++] = base64_table[(bin[j] & 3) << 4];
507                 buf[i++] = BASE64_END;
508                 buf[i++] = BASE64_END;
509                 break;
510         case 2:
511                 buf[i++] = base64_table[bin[j] >> 2];
512                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
513                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
514                 buf[i++] = BASE64_END;
515                 break;
516         case 0:
517                 break;
518         }
520         buf[i] = '\0';
521         return buf;
526 /* Returns 1 if we're done processing the document body; 0 to keep going */
527 static int
528 document_headers_done (char *full_page)
530         const char *body;
532         for (body = full_page; *body; body++) {
533                 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
534                         break;
535         }
537         if (!*body)
538                 return 0;  /* haven't read end of headers yet */
540         full_page[body - full_page] = 0;
541         return 1;
544 static time_t
545 parse_time_string (const char *string)
547         struct tm tm;
548         time_t t;
549         memset (&tm, 0, sizeof(tm));
551         /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
553         if (isupper (string[0])  &&  /* Tue */
554                 islower (string[1])  &&
555                 islower (string[2])  &&
556                 ',' ==   string[3]   &&
557                 ' ' ==   string[4]   &&
558                 (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
559                 isdigit (string[6])  &&
560                 ' ' ==   string[7]   &&
561                 isupper (string[8])  &&  /* Dec */
562                 islower (string[9])  &&
563                 islower (string[10]) &&
564                 ' ' ==   string[11]  &&
565                 isdigit (string[12]) &&  /* 2001 */
566                 isdigit (string[13]) &&
567                 isdigit (string[14]) &&
568                 isdigit (string[15]) &&
569                 ' ' ==   string[16]  &&
570                 isdigit (string[17]) &&  /* 02: */
571                 isdigit (string[18]) &&
572                 ':' ==   string[19]  &&
573                 isdigit (string[20]) &&  /* 59: */
574                 isdigit (string[21]) &&
575                 ':' ==   string[22]  &&
576                 isdigit (string[23]) &&  /* 03 */
577                 isdigit (string[24]) &&
578                 ' ' ==   string[25]  &&
579                 'G' ==   string[26]  &&  /* GMT */
580                 'M' ==   string[27]  &&  /* GMT */
581                 'T' ==   string[28]) {
583                 tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
584                 tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
585                 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
586                 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
587                 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
588                         !strncmp (string+8, "Feb", 3) ? 1 :
589                         !strncmp (string+8, "Mar", 3) ? 2 :
590                         !strncmp (string+8, "Apr", 3) ? 3 :
591                         !strncmp (string+8, "May", 3) ? 4 :
592                         !strncmp (string+8, "Jun", 3) ? 5 :
593                         !strncmp (string+8, "Jul", 3) ? 6 :
594                         !strncmp (string+8, "Aug", 3) ? 7 :
595                         !strncmp (string+8, "Sep", 3) ? 8 :
596                         !strncmp (string+8, "Oct", 3) ? 9 :
597                         !strncmp (string+8, "Nov", 3) ? 10 :
598                         !strncmp (string+8, "Dec", 3) ? 11 :
599                         -1);
600                 tm.tm_year = ((1000 * (string[12]-'0') +
601                         100 * (string[13]-'0') +
602                         10 * (string[14]-'0') +
603                         (string[15]-'0'))
604                         - 1900);
606                 tm.tm_isdst = 0;  /* GMT is never in DST, right? */
608                 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
609                         return 0;
611                 /* 
612                 This is actually wrong: we need to subtract the local timezone
613                 offset from GMT from this value.  But, that's ok in this usage,
614                 because we only comparing these two GMT dates against each other,
615                 so it doesn't matter what time zone we parse them in.
616                 */
618                 t = mktime (&tm);
619                 if (t == (time_t) -1) t = 0;
621                 if (verbose) {
622                         const char *s = string;
623                         while (*s && *s != '\r' && *s != '\n')
624                         fputc (*s++, stdout);
625                         printf (" ==> %lu\n", (unsigned long) t);
626                 }
628                 return t;
630         } else {
631                 return 0;
632         }
637 static void
638 check_document_dates (const char *headers)
640         const char *s;
641         char *server_date = 0;
642         char *document_date = 0;
644         s = headers;
645         while (*s) {
646                 const char *field = s;
647                 const char *value = 0;
649                 /* Find the end of the header field */
650                 while (*s && !isspace(*s) && *s != ':')
651                         s++;
653                 /* Remember the header value, if any. */
654                 if (*s == ':')
655                         value = ++s;
657                 /* Skip to the end of the header, including continuation lines. */
658                 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
659                         s++;
660                 s++;
662                 /* Process this header. */
663                 if (value && value > field+2) {
664                         char *ff = (char *) malloc (value-field);
665                         char *ss = ff;
666                         while (field < value-1)
667                                 *ss++ = tolower(*field++);
668                         *ss++ = 0;
670                         if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
671                                 const char *e;
672                                 while (*value && isspace (*value))
673                                         value++;
674                                 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
675                                         ;
676                                 ss = (char *) malloc (e - value + 1);
677                                 strncpy (ss, value, e - value);
678                                 ss[e - value] = 0;
679                                 if (!strcmp (ff, "date")) {
680                                         if (server_date) free (server_date);
681                                         server_date = ss;
682                                 } else {
683                                         if (document_date) free (document_date);
684                                         document_date = ss;
685                                 }
686                         }
687                         free (ff);
688                 }
689         }
691         /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
692         if (!server_date || !*server_date) {
693                 die (STATE_UNKNOWN, _("Server date unknown\n"));
694         } else if (!document_date || !*document_date) {
695                 die (STATE_CRITICAL, _("Document modification date unknown\n"));
696         } else {
697                 time_t srv_data = parse_time_string (server_date);
698                 time_t doc_data = parse_time_string (document_date);
700                 if (srv_data <= 0) {
701                         die (STATE_CRITICAL, _("CRITICAL - Server date \"%100s\" unparsable"), server_date);
702                 } else if (doc_data <= 0) {
703                         die (STATE_CRITICAL, _("CRITICAL - Document date \"%100s\" unparsable"), document_date);
704                 } else if (doc_data > srv_data + 30) {
705                         die (STATE_CRITICAL, _("CRITICAL - Document is %d seconds in the future\n"), (int)doc_data - (int)srv_data);
706                 } else if (doc_data < srv_data - maximum_age) {
707                 int n = (srv_data - doc_data);
708                 if (n > (60 * 60 * 24 * 2))
709                         die (STATE_CRITICAL,
710                           _("CRITICAL - Last modified %.1f days ago\n"),
711                           ((float) n) / (60 * 60 * 24));
712         else
713                 die (STATE_CRITICAL,
714                     _("CRITICAL - Last modified %d:%02d:%02d ago\n"),
715                     n / (60 * 60), (n / 60) % 60, n % 60);
716     }
718     free (server_date);
719     free (document_date);
720   }
723 int
724 get_content_length (const char *headers)
726         const char *s;
727         int content_length = 0;
729         s = headers;
730         while (*s) {
731                 const char *field = s;
732                 const char *value = 0;
734                 /* Find the end of the header field */
735                 while (*s && !isspace(*s) && *s != ':')
736                         s++;
738                 /* Remember the header value, if any. */
739                 if (*s == ':')
740                         value = ++s;
742                 /* Skip to the end of the header, including continuation lines. */
743                 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
744                         s++;
745                 s++;
747                 /* Process this header. */
748                 if (value && value > field+2) {
749                         char *ff = (char *) malloc (value-field);
750                         char *ss = ff;
751                         while (field < value-1)
752                                 *ss++ = tolower(*field++);
753                         *ss++ = 0;
755                         if (!strcmp (ff, "content-length")) {
756                                 const char *e;
757                                 while (*value && isspace (*value))
758                                         value++;
759                                 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
760                                         ;
761                                 ss = (char *) malloc (e - value + 1);
762                                 strncpy (ss, value, e - value);
763                                 ss[e - value] = 0;
764                                 content_length = atoi(ss);
765                                 free (ss);
766                         }
767                         free (ff);
768                 }
769         }
770         return (content_length);
773 int
774 check_http (void)
776         char *msg;
777         char *status_line;
778         char *status_code;
779         char *header;
780         char *page;
781         char *auth;
782         int http_status;
783         int i = 0;
784         size_t pagesize = 0;
785         char *full_page;
786         char *buf;
787         char *pos;
788         long microsec;
789         double elapsed_time;
790         int page_len = 0;
791 #ifdef HAVE_SSL
792         int sslerr;
793 #endif
795         /* try to connect to the host at the given port number */
796 #ifdef HAVE_SSL
797         if (use_ssl == TRUE) {
799                 if (connect_SSL () != OK) {
800                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
801                 }
803                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
804                         X509_free (server_cert);
805                 }
806                 else {
807                         printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
808                         return STATE_CRITICAL;
809                 }
811         }
812         else {
813 #endif
814                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
815                         die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
816 #ifdef HAVE_SSL
817         }
818 #endif
820         asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
822         /* optionally send the host header info */
823         if (host_name)
824                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
826         /* optionally send any other header tag */
827         if (http_opt_headers) {
828                 for ((pos = strtok(http_opt_headers, INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
829                         asprintf (&buf, "%s%s\r\n", buf, pos);
830         }
832         /* optionally send the authentication info */
833         if (strlen(user_auth)) {
834                 auth = base64 (user_auth, strlen (user_auth));
835                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
836         }
838         /* either send http POST data */
839         if (http_post_data) {
840                 if (http_content_type) {
841                         asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
842                 } else {
843                         asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
844                 }
845                 
846                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
847                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
848         }
849         else {
850                 /* or just a newline so the server knows we're done with the request */
851                 asprintf (&buf, "%s%s", buf, CRLF);
852         }
854         if (verbose)
855                 printf ("%s\n", buf);
857 #ifdef HAVE_SSL
858         if (use_ssl == TRUE) {
859                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
860                         ERR_print_errors_fp (stderr);
861                         return STATE_CRITICAL;
862                 }
863         }
864         else {
865 #endif
866                 send (sd, buf, strlen (buf), 0);
867 #ifdef HAVE_SSL
868         }
869 #endif
871         /* fetch the page */
872         full_page = strdup("");
873         while ((i = my_recv ()) > 0) {
874                 buffer[i] = '\0';
875                 asprintf (&full_page, "%s%s", full_page, buffer);
876                 pagesize += i;
878                 if (no_body && document_headers_done (full_page)) {
879                   i = 0;
880                   break;
881                 }
882         }
884         if (i < 0 && errno != ECONNRESET) {
885 #ifdef HAVE_SSL
886                 if (use_ssl) {
887                         sslerr=SSL_get_error(ssl, i);
888                         if ( sslerr == SSL_ERROR_SSL ) {
889                                 die (STATE_WARNING, _("Client Certificate Required\n"));
890                         } else {
891                                 die (STATE_CRITICAL, _("Error on receive\n"));
892                         }
893                 }
894                 else {
895 #endif
896                         die (STATE_CRITICAL, _("Error on receive\n"));
897 #ifdef HAVE_SSL
898                 }
899 #endif
900         }
902         /* return a CRITICAL status if we couldn't read any data */
903         if (pagesize == (size_t) 0)
904                 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
906         /* close the connection */
907         my_close ();
909         /* reset the alarm */
910         alarm (0);
912         /* leave full_page untouched so we can free it later */
913         page = full_page;
915         if (verbose)
916                 printf ("%s://%s:%d%s is %d characters\n",
917                         use_ssl ? "https" : "http", server_address,
918                         server_port, server_url, pagesize);
920         /* find status line and null-terminate it */
921         status_line = page;
922         page += (size_t) strcspn (page, "\r\n");
923         pos = page;
924         page += (size_t) strspn (page, "\r\n");
925         status_line[strcspn(status_line, "\r\n")] = 0;
926         strip (status_line);
927         if (verbose)
928                 printf ("STATUS: %s\n", status_line);
930         /* find header info and null-terminate it */
931         header = page;
932         while (strcspn (page, "\r\n") > 0) {
933                 page += (size_t) strcspn (page, "\r\n");
934                 pos = page;
935                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
936                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
937                         page += (size_t) 2;
938                 else
939                         page += (size_t) 1;
940         }
941         page += (size_t) strspn (page, "\r\n");
942         header[pos - header] = 0;
943         if (verbose)
944                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
945                 (no_body ? "  [[ skipped ]]" : page));
947         /* make sure the status line matches the response we are looking for */
948         if (!strstr (status_line, server_expect)) {
949                 if (server_port == HTTP_PORT)
950                         asprintf (&msg,
951                             _("Invalid HTTP response received from host\n"));
952                 else
953                         asprintf (&msg,
954                                   _("Invalid HTTP response received from host on port %d\n"),
955                                   server_port);
956                 die (STATE_CRITICAL, "%s", msg);
957         }
959         /* Exit here if server_expect was set by user and not default */
960         if ( server_expect_yn  )  {
961                 asprintf (&msg,
962                           _("HTTP OK: Status line output matched \"%s\"\n"),
963                           server_expect);
964                 if (verbose)
965                         printf ("%s\n",msg);
966         }
967         else {
968                 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
969                 /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
970     /* Status-Code = 3 DIGITS */
972                 status_code = strchr (status_line, ' ') + sizeof (char);
973                 if (strspn (status_code, "1234567890") != 3)
974                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
976                 http_status = atoi (status_code);
978                 /* check the return code */
980                 if (http_status >= 600 || http_status < 100)
981                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
983                 /* server errors result in a critical state */
984                 else if (http_status >= 500)
985                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
987                 /* client errors result in a warning state */
988                 else if (http_status >= 400)
989                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
991                 /* check redirected page if specified */
992                 else if (http_status >= 300) {
994                         if (onredirect == STATE_DEPENDENT)
995                                 redir (header, status_line);
996                         else if (onredirect == STATE_UNKNOWN)
997                                 printf (_("UNKNOWN"));
998                         else if (onredirect == STATE_OK)
999                                 printf (_("OK"));
1000                         else if (onredirect == STATE_WARNING)
1001                                 printf (_("WARNING"));
1002                         else if (onredirect == STATE_CRITICAL)
1003                                 printf (_("CRITICAL"));
1004                         microsec = deltime (tv);
1005                         elapsed_time = (double)microsec / 1.0e6;
1006                         die (onredirect,
1007                              _(" - %s - %.3f second response time %s%s|%s %s\n"),
1008                              status_line, elapsed_time, timestamp,
1009                              (display_html ? "</A>" : ""),
1010                                          perfd_time (elapsed_time), perfd_size (pagesize));
1011                 } /* end if (http_status >= 300) */
1013         } /* end else (server_expect_yn)  */
1014                 
1015         if (maximum_age >= 0) {
1016           check_document_dates (header);
1017         }
1019         /* check elapsed time */
1020         microsec = deltime (tv);
1021         elapsed_time = (double)microsec / 1.0e6;
1022         asprintf (&msg,
1023                   _("HTTP WARNING: %s - %.3f second response time %s%s|%s %s\n"),
1024                   status_line, elapsed_time, timestamp,
1025                   (display_html ? "</A>" : ""),
1026                                                 perfd_time (elapsed_time), perfd_size (pagesize));
1027         if (check_critical_time == TRUE && elapsed_time > critical_time)
1028                 die (STATE_CRITICAL, "%s", msg);
1029         if (check_warning_time == TRUE && elapsed_time > warning_time)
1030                 die (STATE_WARNING, "%s", msg);
1032         /* Page and Header content checks go here */
1033         /* these checks should be last */
1035         if (strlen (string_expect)) {
1036                 if (strstr (page, string_expect)) {
1037                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
1038                                 status_line, elapsed_time,
1039                                 timestamp, (display_html ? "</A>" : ""),
1040                                 perfd_time (elapsed_time), perfd_size (pagesize));
1041                         exit (STATE_OK);
1042                 }
1043                 else {
1044                         printf (_("CRITICAL - string not found%s|%s %s\n"),
1045                                 (display_html ? "</A>" : ""),
1046                                 perfd_time (elapsed_time), perfd_size (pagesize));
1047                         exit (STATE_CRITICAL);
1048                 }
1049         }
1050 #ifdef HAVE_REGEX_H
1051         if (strlen (regexp)) {
1052                 errcode = regexec (&preg, page, REGS, pmatch, 0);
1053                 if (errcode == 0) {
1054                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
1055                                 status_line, elapsed_time,
1056                                 timestamp, (display_html ? "</A>" : ""),
1057                                 perfd_time (elapsed_time), perfd_size (pagesize));
1058                         exit (STATE_OK);
1059                 }
1060                 else {
1061                         if (errcode == REG_NOMATCH) {
1062                                 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
1063                                         (display_html ? "</A>" : ""),
1064                                         perfd_time (elapsed_time), perfd_size (pagesize));
1065                                 exit (STATE_CRITICAL);
1066                         }
1067                         else {
1068                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1069                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
1070                                 exit (STATE_CRITICAL);
1071                         }
1072                 }
1073         }
1074 #endif
1076         /* make sure the page is of an appropriate size */
1077         /* page_len = get_content_length(header); */
1078         page_len = pagesize;
1079         if ((max_page_len > 0) && (page_len > max_page_len)) {
1080                 printf (_("HTTP WARNING: page size %d too large%s|%s\n"),
1081                         page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1082                 exit (STATE_WARNING);
1083         } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1084                 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
1085                         page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1086                 exit (STATE_WARNING);
1087         }
1088         /* We only get here if all tests have been passed */
1089         asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
1090                   status_line, page_len, elapsed_time,
1091                   timestamp, (display_html ? "</A>" : ""),
1092                                                 perfd_time (elapsed_time), perfd_size (page_len));
1093         die (STATE_OK, "%s", msg);
1094         return STATE_UNKNOWN;
1099 /* per RFC 2396 */
1100 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
1101 #define URI_HTTP "%[HTPShtps]://"
1102 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1103 #define URI_PORT ":%[0123456789]"
1104 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1105 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
1106 #define HD2 URI_HTTP URI_HOST URI_PATH
1107 #define HD3 URI_HTTP URI_HOST URI_PORT
1108 #define HD4 URI_HTTP URI_HOST
1109 #define HD5 URI_PATH
1111 void
1112 redir (char *pos, char *status_line)
1114         int i = 0;
1115         char *x;
1116         char xx[2];
1117         char type[6];
1118         char *addr;
1119         char port[6];
1120         char *url;
1122         addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1123         if (addr == NULL)
1124                 die (STATE_UNKNOWN, _("Could not allocate addr\n"));
1125         
1126         url = malloc (strcspn (pos, "\r\n"));
1127         if (url == NULL)
1128                 die (STATE_UNKNOWN, _("Could not allocate url\n"));
1130         while (pos) {
1132                 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
1134                         pos += (size_t) strcspn (pos, "\r\n");
1135                         pos += (size_t) strspn (pos, "\r\n");
1136                         if (strlen(pos) == 0) 
1137                                 die (STATE_UNKNOWN,
1138                                                  _("UNKNOWN - Could not find redirect location - %s%s\n"),
1139                                                  status_line, (display_html ? "</A>" : ""));
1140                         continue;
1141                 }
1143                 pos += i;
1144                 pos += strspn (pos, " \t\r\n");
1146                 url = realloc (url, strcspn (pos, "\r\n"));
1147                 if (url == NULL)
1148                         die (STATE_UNKNOWN, _("could not allocate url\n"));
1150                 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1151                 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
1152                         use_ssl = server_type_check (type);
1153                         i = atoi (port);
1154                 }
1156                 /* URI_HTTP URI_HOST URI_PATH */
1157                 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
1158                         use_ssl = server_type_check (type);
1159                         i = server_port_check (use_ssl);
1160                 }
1162                 /* URI_HTTP URI_HOST URI_PORT */
1163                 else if(sscanf (pos, HD3, type, addr, port) == 3) {
1164                         strcpy (url, HTTP_URL);
1165                         use_ssl = server_type_check (type);
1166                         i = atoi (port);
1167                 }
1169                 /* URI_HTTP URI_HOST */
1170                 else if(sscanf (pos, HD4, type, addr) == 2) {
1171                         strcpy (url, HTTP_URL);
1172                         use_ssl = server_type_check (type);
1173                         i = server_port_check (use_ssl);
1174                 }
1176                 /* URI_PATH */
1177                 else if (sscanf (pos, HD5, url) == 1) {
1178                         /* relative url */
1179                         if ((url[0] != '/')) {
1180                                 if ((x = strrchr(server_url, '/')))
1181                                         *x = '\0';
1182                                 asprintf (&url, "%s/%s", server_url, url);
1183                         }
1184                         i = server_port;
1185                         strcpy (type, server_type);
1186                         strcpy (addr, host_name);
1187                 }                                       
1189                 else {
1190                         die (STATE_UNKNOWN,
1191                                          _("UNKNOWN - Could not parse redirect location - %s%s\n"),
1192                                          pos, (display_html ? "</A>" : ""));
1193                 }
1195                 break;
1197         } /* end while (pos) */
1199         if (++redir_depth > max_depth)
1200                 die (STATE_WARNING,
1201                      _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1202                      max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1204         if (server_port==i &&
1205             !strcmp(server_address, addr) &&
1206             (host_name && !strcmp(host_name, addr)) &&
1207             !strcmp(server_url, url))
1208                 die (STATE_WARNING,
1209                      _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1210                      type, addr, i, url, (display_html ? "</A>" : ""));
1212         server_port = i;
1213         strcpy (server_type, type);
1215         free (host_name);
1216         host_name = strdup (addr);
1218         free (server_address);
1219         server_address = strdup (addr);
1221         free (server_url);
1222         server_url = strdup (url);
1224         check_http ();
1229 int
1230 server_type_check (const char *type)
1232         if (strcmp (type, "https"))
1233                 return FALSE;
1234         else
1235                 return TRUE;
1238 int
1239 server_port_check (int ssl_flag)
1241         if (ssl_flag)
1242                 return HTTPS_PORT;
1243         else
1244                 return HTTP_PORT;
1249 #ifdef HAVE_SSL
1250 int connect_SSL (void)
1252         SSL_METHOD *meth;
1254         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
1255         RAND_seed (randbuff, (int)strlen(randbuff));
1256         if (verbose)
1257                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
1259         /* Initialize SSL context */
1260         SSLeay_add_ssl_algorithms ();
1261         meth = SSLv23_client_method ();
1262         SSL_load_error_strings ();
1263         if ((ctx = SSL_CTX_new (meth)) == NULL) {
1264                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
1265                 return STATE_CRITICAL;
1266         }
1268         /* Initialize alarm signal handling */
1269         signal (SIGALRM, socket_timeout_alarm_handler);
1271         /* Set socket timeout */
1272         alarm (socket_timeout);
1274         /* Save start time */
1275         gettimeofday (&tv, NULL);
1277         /* Make TCP connection */
1278         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
1279                 /* Do the SSL handshake */
1280                 if ((ssl = SSL_new (ctx)) != NULL) {
1281                         SSL_set_cipher_list(ssl, "ALL");
1282                         SSL_set_fd (ssl, sd);
1283                         if (SSL_connect (ssl) != -1)
1284                                 return OK;
1285                         ERR_print_errors_fp (stderr);
1286                 }
1287                 else {
1288                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
1289                 }
1290                 SSL_free (ssl);
1291         }
1293         SSL_CTX_free (ctx);
1294         close (sd);
1296         return STATE_CRITICAL;
1298 #endif
1302 #ifdef HAVE_SSL
1303 int
1304 check_certificate (X509 ** certificate)
1306         ASN1_STRING *tm;
1307         int offset;
1308         struct tm stamp;
1309         int days_left;
1312         /* Retrieve timestamp of certificate */
1313         tm = X509_get_notAfter (*certificate);
1315         /* Generate tm structure to process timestamp */
1316         if (tm->type == V_ASN1_UTCTIME) {
1317                 if (tm->length < 10) {
1318                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
1319                         return STATE_CRITICAL;
1320                 }
1321                 else {
1322                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
1323                         if (stamp.tm_year < 50)
1324                                 stamp.tm_year += 100;
1325                         offset = 0;
1326                 }
1327         }
1328         else {
1329                 if (tm->length < 12) {
1330                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
1331                         return STATE_CRITICAL;
1332                 }
1333                 else {
1334                         stamp.tm_year =
1335                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1336                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1337                         stamp.tm_year -= 1900;
1338                         offset = 2;
1339                 }
1340         }
1341         stamp.tm_mon =
1342                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1343         stamp.tm_mday =
1344                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1345         stamp.tm_hour =
1346                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1347         stamp.tm_min =
1348                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1349         stamp.tm_sec = 0;
1350         stamp.tm_isdst = -1;
1352         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1353         snprintf
1354                 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1355                  stamp.tm_mon + 1,
1356                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1358         if (days_left > 0 && days_left <= days_till_exp) {
1359                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1360                 return STATE_WARNING;
1361         }
1362         if (days_left < 0) {
1363                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1364                 return STATE_CRITICAL;
1365         }
1367         if (days_left == 0) {
1368                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1369                 return STATE_WARNING;
1370         }
1372         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1374         return STATE_OK;
1376 #endif
1380 char *perfd_time (double elapsed_time)
1382         return fperfdata ("time", elapsed_time, "s",
1383                   check_warning_time, warning_time,
1384                   check_critical_time, critical_time,
1385                                                                          TRUE, 0, FALSE, 0);
1390 char *perfd_size (int page_len)
1392         return perfdata ("size", page_len, "B",
1393                   (min_page_len>0?TRUE:FALSE), min_page_len,
1394                   (min_page_len>0?TRUE:FALSE), 0,
1395                   TRUE, 0, FALSE, 0);
1400 int
1401 my_recv (void)
1403         int i;
1404 #ifdef HAVE_SSL
1405         if (use_ssl) {
1406                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1407         }
1408         else {
1409                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1410         }
1411 #else
1412         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1413 #endif
1414         return i;
1419 int
1420 my_close (void)
1422 #ifdef HAVE_SSL
1423         if (use_ssl == TRUE) {
1424                 SSL_shutdown (ssl);
1425                 SSL_free (ssl);
1426                 SSL_CTX_free (ctx);
1427                 return 0;
1428         }
1429         else {
1430 #endif
1431                 return close (sd);
1432 #ifdef HAVE_SSL
1433         }
1434 #endif
1439 void
1440 print_help (void)
1442         print_revision (progname, revision);
1444         printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1445         printf (COPYRIGHT, copyright, email);
1447         printf (_("\
1448 This plugin tests the HTTP service on the specified host. It can test\n\
1449 normal (http) and secure (https) servers, follow redirects, search for\n\
1450 strings and regular expressions, check connection times, and report on\n\
1451 certificate expiration times.\n\n"));
1453         print_usage ();
1455         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1457         printf (_(UT_HELP_VRSN));
1459         printf (_("\
1460  -H, --hostname=ADDRESS\n\
1461     Host name argument for servers using host headers (virtual host)\n\
1462     Append a port to include it in the header (eg: example.com:5000)\n\
1463  -I, --IP-address=ADDRESS\n\
1464    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1465  -p, --port=INTEGER\n\
1466    Port number (default: %d)\n"), HTTP_PORT);
1468         printf (_(UT_IPv46));
1470 #ifdef HAVE_SSL
1471         printf (_("\
1472  -S, --ssl\n\
1473     Connect via SSL\n\
1474  -C, --certificate=INTEGER\n\
1475     Minimum number of days a certificate has to be valid.\n\
1476     (when this option is used the url is not checked.)\n"));
1477 #endif
1479         printf (_("\
1480  -e, --expect=STRING\n\
1481    String to expect in first (status) line of server response (default: %s)\n\
1482    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1483  -s, --string=STRING\n\
1484    String to expect in the content\n\
1485  -u, --url=PATH\n\
1486    URL to GET or POST (default: /)\n\
1487  -P, --post=STRING\n\
1488    URL encoded http POST data\n\
1489  -N, --no-body\n\
1490    Don't wait for document body: stop reading after headers.\n\
1491    (Note that this still does an HTTP GET or POST, not a HEAD.)\n\
1492  -M, --max-age=SECONDS\n\
1493    Warn if document is more than SECONDS old. the number can also be of \n\
1494    the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.\n\
1495  -T, --content-type=STRING\n\
1496    specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
1498 #ifdef HAVE_REGEX_H
1499         printf (_("\
1500  -l, --linespan\n\
1501     Allow regex to span newlines (must precede -r or -R)\n\
1502  -r, --regex, --ereg=STRING\n\
1503     Search page for regex STRING\n\
1504  -R, --eregi=STRING\n\
1505     Search page for case-insensitive regex STRING\n"));
1506 #endif
1508         printf (_("\
1509  -a, --authorization=AUTH_PAIR\n\
1510    Username:password on sites with basic authentication\n\
1511  -A, --useragent=STRING\n\
1512    String to be sent in http header as \"User Agent\"\n\
1513  -k, --header=STRING\n\
1514    Any other tags to be sent in http header, separated by semicolon\n\
1515  -L, --link=URL\n\
1516    Wrap output in HTML link (obsoleted by urlize)\n\
1517  -f, --onredirect=<ok|warning|critical|follow>\n\
1518    How to handle redirected pages\n\
1519  -m, --pagesize=INTEGER<:INTEGER>\n\
1520    Minimum page size required (bytes) : Maximum page size required (bytes)\n"));
1522         printf (_(UT_WARN_CRIT));
1524         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1526         printf (_(UT_VERBOSE));
1528                                         printf (_("\
1529 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1530 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1531 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1532 messages from the host result in STATE_WARNING return values.  If you are\n\
1533 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1534 (fully qualified domain name) as the [host_name] argument.\n"));
1536 #ifdef HAVE_SSL
1537         printf (_("\n\
1538 This plugin can also check whether an SSL enabled web server is able to\n\
1539 serve content (optionally within a specified time) or whether the X509 \n\
1540 certificate is still valid for the specified number of days.\n"));
1541         printf (_("\n\
1542 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1543 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1544 STATE_OK will be returned. When the server returns its content but exceeds\n\
1545 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1546 a STATE_CRITICAL will be returned.\n\n"));
1548         printf (_("\
1549 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1550 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1551 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1552 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1553 the certificate is expired.\n"));
1554 #endif
1556         printf (_(UT_SUPPORT));
1562 void
1563 print_usage (void)
1565         printf ("\
1566 Usage: %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n\
1567                   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1568                   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1569                   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1570                   [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] \n\
1571                   [-M <age>] [-A string] [-k string]\n", progname);