Code

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