Back to index

nagios-plugins  1.4.16
check_http.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002 * 
00003 * Nagios check_http plugin
00004 * 
00005 * License: GPL
00006 * Copyright (c) 1999-2008 Nagios Plugins Development Team
00007 * 
00008 * Description:
00009 * 
00010 * This file contains the check_http plugin
00011 * 
00012 * This plugin tests the HTTP service on the specified host. It can test
00013 * normal (http) and secure (https) servers, follow redirects, search for
00014 * strings and regular expressions, check connection times, and report on
00015 * certificate expiration times.
00016 * 
00017 * 
00018 * This program is free software: you can redistribute it and/or modify
00019 * it under the terms of the GNU General Public License as published by
00020 * the Free Software Foundation, either version 3 of the License, or
00021 * (at your option) any later version.
00022 * 
00023 * This program is distributed in the hope that it will be useful,
00024 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00025 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00026 * GNU General Public License for more details.
00027 * 
00028 * You should have received a copy of the GNU General Public License
00029 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00030 * 
00031 * 
00032 *****************************************************************************/
00033 
00034 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
00035 
00036 const char *progname = "check_http";
00037 const char *copyright = "1999-2011";
00038 const char *email = "nagiosplug-devel@lists.sourceforge.net";
00039 
00040 #include "common.h"
00041 #include "netutils.h"
00042 #include "utils.h"
00043 #include "base64.h"
00044 #include <ctype.h>
00045 
00046 #define INPUT_DELIMITER ";"
00047 #define STICKY_NONE 0
00048 #define STICKY_HOST 1
00049 #define STICKY_PORT 2
00050 
00051 #define HTTP_EXPECT "HTTP/1."
00052 enum {
00053   MAX_IPV4_HOSTLENGTH = 255,
00054   HTTP_PORT = 80,
00055   HTTPS_PORT = 443,
00056   MAX_PORT = 65535
00057 };
00058 
00059 #ifdef HAVE_SSL
00060 int check_cert = FALSE;
00061 int ssl_version;
00062 int days_till_exp_warn, days_till_exp_crit;
00063 char *randbuff;
00064 X509 *server_cert;
00065 #  define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
00066 #  define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
00067 #else /* ifndef HAVE_SSL */
00068 #  define my_recv(buf, len) read(sd, buf, len)
00069 #  define my_send(buf, len) send(sd, buf, len, 0)
00070 #endif /* HAVE_SSL */
00071 int no_body = FALSE;
00072 int maximum_age = -1;
00073 
00074 enum {
00075   REGS = 2,
00076   MAX_RE_SIZE = 256
00077 };
00078 #include "regex.h"
00079 regex_t preg;
00080 regmatch_t pmatch[REGS];
00081 char regexp[MAX_RE_SIZE];
00082 char errbuf[MAX_INPUT_BUFFER];
00083 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
00084 int errcode;
00085 int invert_regex = 0;
00086 
00087 struct timeval tv;
00088 
00089 #define HTTP_URL "/"
00090 #define CRLF "\r\n"
00091 
00092 int specify_port = FALSE;
00093 int server_port = HTTP_PORT;
00094 char server_port_text[6] = "";
00095 char server_type[6] = "http";
00096 char *server_address;
00097 char *host_name;
00098 char *server_url;
00099 char *user_agent;
00100 int server_url_length;
00101 int server_expect_yn = 0;
00102 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
00103 char string_expect[MAX_INPUT_BUFFER] = "";
00104 char output_string_search[30] = "";
00105 char *warning_thresholds = NULL;
00106 char *critical_thresholds = NULL;
00107 thresholds *thlds;
00108 char user_auth[MAX_INPUT_BUFFER] = "";
00109 char proxy_auth[MAX_INPUT_BUFFER] = "";
00110 int display_html = FALSE;
00111 char **http_opt_headers;
00112 int http_opt_headers_count = 0;
00113 int onredirect = STATE_OK;
00114 int followsticky = STICKY_NONE;
00115 int use_ssl = FALSE;
00116 int use_sni = FALSE;
00117 int verbose = FALSE;
00118 int sd;
00119 int min_page_len = 0;
00120 int max_page_len = 0;
00121 int redir_depth = 0;
00122 int max_depth = 15;
00123 char *http_method;
00124 char *http_post_data;
00125 char *http_content_type;
00126 char buffer[MAX_INPUT_BUFFER];
00127 
00128 int process_arguments (int, char **);
00129 int check_http (void);
00130 void redir (char *pos, char *status_line);
00131 int server_type_check(const char *type);
00132 int server_port_check(int ssl_flag);
00133 char *perfd_time (double microsec);
00134 char *perfd_size (int page_len);
00135 void print_help (void);
00136 void print_usage (void);
00137 
00138 int
00139 main (int argc, char **argv)
00140 {
00141   int result = STATE_UNKNOWN;
00142 
00143   setlocale (LC_ALL, "");
00144   bindtextdomain (PACKAGE, LOCALEDIR);
00145   textdomain (PACKAGE);
00146 
00147   /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
00148   server_url = strdup(HTTP_URL);
00149   server_url_length = strlen(server_url);
00150   asprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
00151             NP_VERSION, VERSION);
00152 
00153   /* Parse extra opts if any */
00154   argv=np_extra_opts (&argc, argv, progname);
00155 
00156   if (process_arguments (argc, argv) == ERROR)
00157     usage4 (_("Could not parse arguments"));
00158 
00159   if (display_html == TRUE)
00160     printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
00161       use_ssl ? "https" : "http", host_name ? host_name : server_address,
00162       server_port, server_url);
00163 
00164   /* initialize alarm signal handling, set socket timeout, start timer */
00165   (void) signal (SIGALRM, socket_timeout_alarm_handler);
00166   (void) alarm (socket_timeout);
00167   gettimeofday (&tv, NULL);
00168 
00169   result = check_http ();
00170   return result;
00171 }
00172 
00173 
00174 
00175 /* process command-line arguments */
00176 int
00177 process_arguments (int argc, char **argv)
00178 {
00179   int c = 1;
00180   char *p;
00181   char *temp;
00182 
00183   enum {
00184     INVERT_REGEX = CHAR_MAX + 1,
00185     SNI_OPTION
00186   };
00187 
00188   int option = 0;
00189   static struct option longopts[] = {
00190     STD_LONG_OPTS,
00191     {"link", no_argument, 0, 'L'},
00192     {"nohtml", no_argument, 0, 'n'},
00193     {"ssl", optional_argument, 0, 'S'},
00194     {"sni", no_argument, 0, SNI_OPTION},
00195     {"post", required_argument, 0, 'P'},
00196     {"method", required_argument, 0, 'j'},
00197     {"IP-address", required_argument, 0, 'I'},
00198     {"url", required_argument, 0, 'u'},
00199     {"port", required_argument, 0, 'p'},
00200     {"authorization", required_argument, 0, 'a'},
00201     {"proxy_authorization", required_argument, 0, 'b'},
00202     {"string", required_argument, 0, 's'},
00203     {"expect", required_argument, 0, 'e'},
00204     {"regex", required_argument, 0, 'r'},
00205     {"ereg", required_argument, 0, 'r'},
00206     {"eregi", required_argument, 0, 'R'},
00207     {"linespan", no_argument, 0, 'l'},
00208     {"onredirect", required_argument, 0, 'f'},
00209     {"certificate", required_argument, 0, 'C'},
00210     {"useragent", required_argument, 0, 'A'},
00211     {"header", required_argument, 0, 'k'},
00212     {"no-body", no_argument, 0, 'N'},
00213     {"max-age", required_argument, 0, 'M'},
00214     {"content-type", required_argument, 0, 'T'},
00215     {"pagesize", required_argument, 0, 'm'},
00216     {"invert-regex", no_argument, NULL, INVERT_REGEX},
00217     {"use-ipv4", no_argument, 0, '4'},
00218     {"use-ipv6", no_argument, 0, '6'},
00219     {0, 0, 0, 0}
00220   };
00221 
00222   if (argc < 2)
00223     return ERROR;
00224 
00225   for (c = 1; c < argc; c++) {
00226     if (strcmp ("-to", argv[c]) == 0)
00227       strcpy (argv[c], "-t");
00228     if (strcmp ("-hn", argv[c]) == 0)
00229       strcpy (argv[c], "-H");
00230     if (strcmp ("-wt", argv[c]) == 0)
00231       strcpy (argv[c], "-w");
00232     if (strcmp ("-ct", argv[c]) == 0)
00233       strcpy (argv[c], "-c");
00234     if (strcmp ("-nohtml", argv[c]) == 0)
00235       strcpy (argv[c], "-n");
00236   }
00237 
00238   while (1) {
00239     c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:e:p:s:R:r:u:f:C:nlLS::m:M:N", longopts, &option);
00240     if (c == -1 || c == EOF)
00241       break;
00242 
00243     switch (c) {
00244     case '?': /* usage */
00245       usage5 ();
00246       break;
00247     case 'h': /* help */
00248       print_help ();
00249       exit (STATE_OK);
00250       break;
00251     case 'V': /* version */
00252       print_revision (progname, NP_VERSION);
00253       exit (STATE_OK);
00254       break;
00255     case 't': /* timeout period */
00256       if (!is_intnonneg (optarg))
00257         usage2 (_("Timeout interval must be a positive integer"), optarg);
00258       else
00259         socket_timeout = atoi (optarg);
00260       break;
00261     case 'c': /* critical time threshold */
00262       critical_thresholds = optarg;
00263       break;
00264     case 'w': /* warning time threshold */
00265       warning_thresholds = optarg;
00266       break;
00267     case 'A': /* User Agent String */
00268       asprintf (&user_agent, "User-Agent: %s", optarg);
00269       break;
00270     case 'k': /* Additional headers */
00271       if (http_opt_headers_count == 0)
00272         http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
00273       else
00274         http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
00275       http_opt_headers[http_opt_headers_count - 1] = optarg;
00276       /* asprintf (&http_opt_headers, "%s", optarg); */
00277       break;
00278     case 'L': /* show html link */
00279       display_html = TRUE;
00280       break;
00281     case 'n': /* do not show html link */
00282       display_html = FALSE;
00283       break;
00284     case 'C': /* Check SSL cert validity */
00285 #ifdef HAVE_SSL
00286       if ((temp=strchr(optarg,','))!=NULL) {
00287        *temp='\0';
00288        if (!is_intnonneg (temp))
00289          usage2 (_("Invalid certificate expiration period"), optarg);
00290        days_till_exp_warn = atoi(optarg);
00291        *temp=',';
00292        temp++;
00293        if (!is_intnonneg (temp))
00294          usage2 (_("Invalid certificate expiration period"), temp);
00295        days_till_exp_crit = atoi (temp);
00296       }
00297       else {
00298        days_till_exp_crit=0;
00299         if (!is_intnonneg (optarg))
00300           usage2 (_("Invalid certificate expiration period"), optarg);
00301         days_till_exp_warn = atoi (optarg);
00302       }
00303       check_cert = TRUE;
00304       /* Fall through to -S option */
00305 #endif
00306     case 'S': /* use SSL */
00307 #ifndef HAVE_SSL
00308       usage4 (_("Invalid option - SSL is not available"));
00309 #endif
00310       use_ssl = TRUE;
00311       if (optarg == NULL || c != 'S')
00312         ssl_version = 0;
00313       else {
00314         ssl_version = atoi(optarg);
00315         if (ssl_version < 1 || ssl_version > 3)
00316             usage4 (_("Invalid option - Valid values for SSL Version are 1 (TLSv1), 2 (SSLv2) or 3 (SSLv3)"));
00317       }
00318       if (specify_port == FALSE)
00319         server_port = HTTPS_PORT;
00320       break;
00321     case SNI_OPTION:
00322       use_sni = TRUE;
00323       break;
00324     case 'f': /* onredirect */
00325       if (!strcmp (optarg, "stickyport"))
00326         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
00327       else if (!strcmp (optarg, "sticky"))
00328         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
00329       else if (!strcmp (optarg, "follow"))
00330         onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
00331       else if (!strcmp (optarg, "unknown"))
00332         onredirect = STATE_UNKNOWN;
00333       else if (!strcmp (optarg, "ok"))
00334         onredirect = STATE_OK;
00335       else if (!strcmp (optarg, "warning"))
00336         onredirect = STATE_WARNING;
00337       else if (!strcmp (optarg, "critical"))
00338         onredirect = STATE_CRITICAL;
00339       else usage2 (_("Invalid onredirect option"), optarg);
00340       if (verbose)
00341         printf(_("option f:%d \n"), onredirect);
00342       break;
00343     /* Note: H, I, and u must be malloc'd or will fail on redirects */
00344     case 'H': /* Host Name (virtual host) */
00345       host_name = strdup (optarg);
00346       if (host_name[0] == '[') {
00347         if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
00348           server_port = atoi (p + 2);
00349       } else if ((p = strchr (host_name, ':')) != NULL
00350                  && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
00351           server_port = atoi (p);
00352       break;
00353     case 'I': /* Server IP-address */
00354       server_address = strdup (optarg);
00355       break;
00356     case 'u': /* URL path */
00357       server_url = strdup (optarg);
00358       server_url_length = strlen (server_url);
00359       break;
00360     case 'p': /* Server port */
00361       if (!is_intnonneg (optarg))
00362         usage2 (_("Invalid port number"), optarg);
00363       else {
00364         server_port = atoi (optarg);
00365         specify_port = TRUE;
00366       }
00367       break;
00368     case 'a': /* authorization info */
00369       strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
00370       user_auth[MAX_INPUT_BUFFER - 1] = 0;
00371       break;
00372     case 'b': /* proxy-authorization info */
00373       strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
00374       proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
00375       break;
00376     case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
00377       if (! http_post_data)
00378         http_post_data = strdup (optarg);
00379       if (! http_method)
00380         http_method = strdup("POST");
00381       break;
00382     case 'j': /* Set HTTP method */
00383       if (http_method)
00384         free(http_method);
00385       http_method = strdup (optarg);
00386       break;
00387     case 's': /* string or substring */
00388       strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
00389       string_expect[MAX_INPUT_BUFFER - 1] = 0;
00390       break;
00391     case 'e': /* string or substring */
00392       strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
00393       server_expect[MAX_INPUT_BUFFER - 1] = 0;
00394       server_expect_yn = 1;
00395       break;
00396     case 'T': /* Content-type */
00397       asprintf (&http_content_type, "%s", optarg);
00398       break;
00399     case 'l': /* linespan */
00400       cflags &= ~REG_NEWLINE;
00401       break;
00402     case 'R': /* regex */
00403       cflags |= REG_ICASE;
00404     case 'r': /* regex */
00405       strncpy (regexp, optarg, MAX_RE_SIZE - 1);
00406       regexp[MAX_RE_SIZE - 1] = 0;
00407       errcode = regcomp (&preg, regexp, cflags);
00408       if (errcode != 0) {
00409         (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
00410         printf (_("Could Not Compile Regular Expression: %s"), errbuf);
00411         return ERROR;
00412       }
00413       break;
00414     case INVERT_REGEX:
00415       invert_regex = 1;
00416       break;
00417     case '4':
00418       address_family = AF_INET;
00419       break;
00420     case '6':
00421 #ifdef USE_IPV6
00422       address_family = AF_INET6;
00423 #else
00424       usage4 (_("IPv6 support not available"));
00425 #endif
00426       break;
00427     case 'v': /* verbose */
00428       verbose = TRUE;
00429       break;
00430     case 'm': /* min_page_length */
00431       {
00432       char *tmp;
00433       if (strchr(optarg, ':') != (char *)NULL) {
00434         /* range, so get two values, min:max */
00435         tmp = strtok(optarg, ":");
00436         if (tmp == NULL) {
00437           printf("Bad format: try \"-m min:max\"\n");
00438           exit (STATE_WARNING);
00439         } else
00440           min_page_len = atoi(tmp);
00441 
00442         tmp = strtok(NULL, ":");
00443         if (tmp == NULL) {
00444           printf("Bad format: try \"-m min:max\"\n");
00445           exit (STATE_WARNING);
00446         } else
00447           max_page_len = atoi(tmp);
00448       } else
00449         min_page_len = atoi (optarg);
00450       break;
00451       }
00452     case 'N': /* no-body */
00453       no_body = TRUE;
00454       break;
00455     case 'M': /* max-age */
00456                   {
00457                     int L = strlen(optarg);
00458                     if (L && optarg[L-1] == 'm')
00459                       maximum_age = atoi (optarg) * 60;
00460                     else if (L && optarg[L-1] == 'h')
00461                       maximum_age = atoi (optarg) * 60 * 60;
00462                     else if (L && optarg[L-1] == 'd')
00463                       maximum_age = atoi (optarg) * 60 * 60 * 24;
00464                     else if (L && (optarg[L-1] == 's' ||
00465                                    isdigit (optarg[L-1])))
00466                       maximum_age = atoi (optarg);
00467                     else {
00468                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
00469                       exit (STATE_WARNING);
00470                     }
00471                   }
00472                   break;
00473     }
00474   }
00475 
00476   c = optind;
00477 
00478   if (server_address == NULL && c < argc)
00479     server_address = strdup (argv[c++]);
00480 
00481   if (host_name == NULL && c < argc)
00482     host_name = strdup (argv[c++]);
00483 
00484   if (server_address == NULL) {
00485     if (host_name == NULL)
00486       usage4 (_("You must specify a server address or host name"));
00487     else
00488       server_address = strdup (host_name);
00489   }
00490 
00491   set_thresholds(&thlds, warning_thresholds, critical_thresholds);
00492 
00493   if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
00494     socket_timeout = (int)thlds->critical->end + 1;
00495 
00496   if (http_method == NULL)
00497     http_method = strdup ("GET");
00498 
00499   return TRUE;
00500 }
00501 
00502 
00503 
00504 /* Returns 1 if we're done processing the document body; 0 to keep going */
00505 static int
00506 document_headers_done (char *full_page)
00507 {
00508   const char *body;
00509 
00510   for (body = full_page; *body; body++) {
00511     if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
00512       break;
00513   }
00514 
00515   if (!*body)
00516     return 0;  /* haven't read end of headers yet */
00517 
00518   full_page[body - full_page] = 0;
00519   return 1;
00520 }
00521 
00522 static time_t
00523 parse_time_string (const char *string)
00524 {
00525   struct tm tm;
00526   time_t t;
00527   memset (&tm, 0, sizeof(tm));
00528 
00529   /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
00530 
00531   if (isupper (string[0])  &&  /* Tue */
00532     islower (string[1])  &&
00533     islower (string[2])  &&
00534     ',' ==   string[3]   &&
00535     ' ' ==   string[4]   &&
00536     (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
00537     isdigit (string[6])  &&
00538     ' ' ==   string[7]   &&
00539     isupper (string[8])  &&  /* Dec */
00540     islower (string[9])  &&
00541     islower (string[10]) &&
00542     ' ' ==   string[11]  &&
00543     isdigit (string[12]) &&  /* 2001 */
00544     isdigit (string[13]) &&
00545     isdigit (string[14]) &&
00546     isdigit (string[15]) &&
00547     ' ' ==   string[16]  &&
00548     isdigit (string[17]) &&  /* 02: */
00549     isdigit (string[18]) &&
00550     ':' ==   string[19]  &&
00551     isdigit (string[20]) &&  /* 59: */
00552     isdigit (string[21]) &&
00553     ':' ==   string[22]  &&
00554     isdigit (string[23]) &&  /* 03 */
00555     isdigit (string[24]) &&
00556     ' ' ==   string[25]  &&
00557     'G' ==   string[26]  &&  /* GMT */
00558     'M' ==   string[27]  &&  /* GMT */
00559     'T' ==   string[28]) {
00560 
00561     tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
00562     tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
00563     tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
00564     tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
00565     tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
00566       !strncmp (string+8, "Feb", 3) ? 1 :
00567       !strncmp (string+8, "Mar", 3) ? 2 :
00568       !strncmp (string+8, "Apr", 3) ? 3 :
00569       !strncmp (string+8, "May", 3) ? 4 :
00570       !strncmp (string+8, "Jun", 3) ? 5 :
00571       !strncmp (string+8, "Jul", 3) ? 6 :
00572       !strncmp (string+8, "Aug", 3) ? 7 :
00573       !strncmp (string+8, "Sep", 3) ? 8 :
00574       !strncmp (string+8, "Oct", 3) ? 9 :
00575       !strncmp (string+8, "Nov", 3) ? 10 :
00576       !strncmp (string+8, "Dec", 3) ? 11 :
00577       -1);
00578     tm.tm_year = ((1000 * (string[12]-'0') +
00579       100 * (string[13]-'0') +
00580       10 * (string[14]-'0') +
00581       (string[15]-'0'))
00582       - 1900);
00583 
00584     tm.tm_isdst = 0;  /* GMT is never in DST, right? */
00585 
00586     if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
00587       return 0;
00588 
00589     /*
00590     This is actually wrong: we need to subtract the local timezone
00591     offset from GMT from this value.  But, that's ok in this usage,
00592     because we only comparing these two GMT dates against each other,
00593     so it doesn't matter what time zone we parse them in.
00594     */
00595 
00596     t = mktime (&tm);
00597     if (t == (time_t) -1) t = 0;
00598 
00599     if (verbose) {
00600       const char *s = string;
00601       while (*s && *s != '\r' && *s != '\n')
00602       fputc (*s++, stdout);
00603       printf (" ==> %lu\n", (unsigned long) t);
00604     }
00605 
00606     return t;
00607 
00608   } else {
00609     return 0;
00610   }
00611 }
00612 
00613 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
00614 static int
00615 expected_statuscode (const char *reply, const char *statuscodes)
00616 {
00617   char *expected, *code;
00618   int result = 0;
00619 
00620   if ((expected = strdup (statuscodes)) == NULL)
00621     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
00622 
00623   for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
00624     if (strstr (reply, code) != NULL) {
00625       result = 1;
00626       break;
00627     }
00628 
00629   free (expected);
00630   return result;
00631 }
00632 
00633 static int
00634 check_document_dates (const char *headers, char **msg)
00635 {
00636   const char *s;
00637   char *server_date = 0;
00638   char *document_date = 0;
00639   int date_result = STATE_OK;
00640 
00641   s = headers;
00642   while (*s) {
00643     const char *field = s;
00644     const char *value = 0;
00645 
00646     /* Find the end of the header field */
00647     while (*s && !isspace(*s) && *s != ':')
00648       s++;
00649 
00650     /* Remember the header value, if any. */
00651     if (*s == ':')
00652       value = ++s;
00653 
00654     /* Skip to the end of the header, including continuation lines. */
00655     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
00656       s++;
00657 
00658     /* Avoid stepping over end-of-string marker */
00659     if (*s)
00660       s++;
00661 
00662     /* Process this header. */
00663     if (value && value > field+2) {
00664       char *ff = (char *) malloc (value-field);
00665       char *ss = ff;
00666       while (field < value-1)
00667         *ss++ = tolower(*field++);
00668       *ss++ = 0;
00669 
00670       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
00671         const char *e;
00672         while (*value && isspace (*value))
00673           value++;
00674         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
00675           ;
00676         ss = (char *) malloc (e - value + 1);
00677         strncpy (ss, value, e - value);
00678         ss[e - value] = 0;
00679         if (!strcmp (ff, "date")) {
00680           if (server_date) free (server_date);
00681           server_date = ss;
00682         } else {
00683           if (document_date) free (document_date);
00684           document_date = ss;
00685         }
00686       }
00687       free (ff);
00688     }
00689   }
00690 
00691   /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
00692   if (!server_date || !*server_date) {
00693     asprintf (msg, _("%sServer date unknown, "), *msg);
00694     date_result = max_state_alt(STATE_UNKNOWN, date_result);
00695   } else if (!document_date || !*document_date) {
00696     asprintf (msg, _("%sDocument modification date unknown, "), *msg);
00697     date_result = max_state_alt(STATE_CRITICAL, date_result);
00698   } else {
00699     time_t srv_data = parse_time_string (server_date);
00700     time_t doc_data = parse_time_string (document_date);
00701 
00702     if (srv_data <= 0) {
00703       asprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
00704       date_result = max_state_alt(STATE_CRITICAL, date_result);
00705     } else if (doc_data <= 0) {
00706       asprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
00707       date_result = max_state_alt(STATE_CRITICAL, date_result);
00708     } else if (doc_data > srv_data + 30) {
00709       asprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
00710       date_result = max_state_alt(STATE_CRITICAL, date_result);
00711     } else if (doc_data < srv_data - maximum_age) {
00712       int n = (srv_data - doc_data);
00713       if (n > (60 * 60 * 24 * 2)) {
00714         asprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
00715         date_result = max_state_alt(STATE_CRITICAL, date_result);
00716       } else {
00717         asprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
00718         date_result = max_state_alt(STATE_CRITICAL, date_result);
00719       }
00720     }
00721     free (server_date);
00722     free (document_date);
00723   }
00724   return date_result;
00725 }
00726 
00727 int
00728 get_content_length (const char *headers)
00729 {
00730   const char *s;
00731   int content_length = 0;
00732 
00733   s = headers;
00734   while (*s) {
00735     const char *field = s;
00736     const char *value = 0;
00737 
00738     /* Find the end of the header field */
00739     while (*s && !isspace(*s) && *s != ':')
00740       s++;
00741 
00742     /* Remember the header value, if any. */
00743     if (*s == ':')
00744       value = ++s;
00745 
00746     /* Skip to the end of the header, including continuation lines. */
00747     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
00748       s++;
00749 
00750     /* Avoid stepping over end-of-string marker */
00751     if (*s)
00752       s++;
00753 
00754     /* Process this header. */
00755     if (value && value > field+2) {
00756       char *ff = (char *) malloc (value-field);
00757       char *ss = ff;
00758       while (field < value-1)
00759         *ss++ = tolower(*field++);
00760       *ss++ = 0;
00761 
00762       if (!strcmp (ff, "content-length")) {
00763         const char *e;
00764         while (*value && isspace (*value))
00765           value++;
00766         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
00767           ;
00768         ss = (char *) malloc (e - value + 1);
00769         strncpy (ss, value, e - value);
00770         ss[e - value] = 0;
00771         content_length = atoi(ss);
00772         free (ss);
00773       }
00774       free (ff);
00775     }
00776   }
00777   return (content_length);
00778 }
00779 
00780 char *
00781 prepend_slash (char *path)
00782 {
00783   char *newpath;
00784 
00785   if (path[0] == '/')
00786     return path;
00787 
00788   if ((newpath = malloc (strlen(path) + 2)) == NULL)
00789     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
00790   newpath[0] = '/';
00791   strcpy (newpath + 1, path);
00792   free (path);
00793   return newpath;
00794 }
00795 
00796 int
00797 check_http (void)
00798 {
00799   char *msg;
00800   char *status_line;
00801   char *status_code;
00802   char *header;
00803   char *page;
00804   char *auth;
00805   int http_status;
00806   int i = 0;
00807   size_t pagesize = 0;
00808   char *full_page;
00809   char *full_page_new;
00810   char *buf;
00811   char *pos;
00812   long microsec;
00813   double elapsed_time;
00814   int page_len = 0;
00815   int result = STATE_OK;
00816 
00817   /* try to connect to the host at the given port number */
00818   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
00819     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
00820 #ifdef HAVE_SSL
00821   if (use_ssl == TRUE) {
00822     result = np_net_ssl_init_with_hostname_and_version(sd, (use_sni ? host_name : NULL), ssl_version);
00823     if (result != STATE_OK)
00824       return result;
00825     if (check_cert == TRUE) {
00826       result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
00827       np_net_ssl_cleanup();
00828       if (sd) close(sd);
00829       return result;
00830     }
00831   }
00832 #endif /* HAVE_SSL */
00833 
00834   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
00835 
00836   /* tell HTTP/1.1 servers not to keep the connection alive */
00837   asprintf (&buf, "%sConnection: close\r\n", buf);
00838 
00839   /* optionally send the host header info */
00840   if (host_name) {
00841     /*
00842      * Specify the port only if we're using a non-default port (see RFC 2616,
00843      * 14.23).  Some server applications/configurations cause trouble if the
00844      * (default) port is explicitly specified in the "Host:" header line.
00845      */
00846     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
00847         (use_ssl == TRUE && server_port == HTTPS_PORT))
00848       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
00849     else
00850       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
00851   }
00852 
00853   /* optionally send any other header tag */
00854   if (http_opt_headers_count) {
00855     for (i = 0; i < http_opt_headers_count ; i++) {
00856       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
00857         asprintf (&buf, "%s%s\r\n", buf, pos);
00858     }
00859     /* This cannot be free'd here because a redirection will then try to access this and segfault */
00860     /* Covered in a testcase in tests/check_http.t */
00861     /* free(http_opt_headers); */
00862   }
00863 
00864   /* optionally send the authentication info */
00865   if (strlen(user_auth)) {
00866     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
00867     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
00868   }
00869 
00870   /* optionally send the proxy authentication info */
00871   if (strlen(proxy_auth)) {
00872     base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
00873     asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
00874   }
00875 
00876   /* either send http POST data (any data, not only POST)*/
00877   if (http_post_data) {
00878     if (http_content_type) {
00879       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
00880     } else {
00881       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
00882     }
00883 
00884     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
00885     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
00886   }
00887   else {
00888     /* or just a newline so the server knows we're done with the request */
00889     asprintf (&buf, "%s%s", buf, CRLF);
00890   }
00891 
00892   if (verbose) printf ("%s\n", buf);
00893   my_send (buf, strlen (buf));
00894 
00895   /* fetch the page */
00896   full_page = strdup("");
00897   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
00898     buffer[i] = '\0';
00899     asprintf (&full_page_new, "%s%s", full_page, buffer);
00900     free (full_page);
00901     full_page = full_page_new;
00902     pagesize += i;
00903 
00904                 if (no_body && document_headers_done (full_page)) {
00905                   i = 0;
00906                   break;
00907                 }
00908   }
00909 
00910   if (i < 0 && errno != ECONNRESET) {
00911 #ifdef HAVE_SSL
00912     /*
00913     if (use_ssl) {
00914       sslerr=SSL_get_error(ssl, i);
00915       if ( sslerr == SSL_ERROR_SSL ) {
00916         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
00917       } else {
00918         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
00919       }
00920     }
00921     else {
00922     */
00923 #endif
00924       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
00925 #ifdef HAVE_SSL
00926       /* XXX
00927     }
00928     */
00929 #endif
00930   }
00931 
00932   /* return a CRITICAL status if we couldn't read any data */
00933   if (pagesize == (size_t) 0)
00934     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
00935 
00936   /* close the connection */
00937 #ifdef HAVE_SSL
00938   np_net_ssl_cleanup();
00939 #endif
00940   if (sd) close(sd);
00941 
00942   /* Save check time */
00943   microsec = deltime (tv);
00944   elapsed_time = (double)microsec / 1.0e6;
00945 
00946   /* leave full_page untouched so we can free it later */
00947   page = full_page;
00948 
00949   if (verbose)
00950     printf ("%s://%s:%d%s is %d characters\n",
00951       use_ssl ? "https" : "http", server_address,
00952       server_port, server_url, (int)pagesize);
00953 
00954   /* find status line and null-terminate it */
00955   status_line = page;
00956   page += (size_t) strcspn (page, "\r\n");
00957   pos = page;
00958   page += (size_t) strspn (page, "\r\n");
00959   status_line[strcspn(status_line, "\r\n")] = 0;
00960   strip (status_line);
00961   if (verbose)
00962     printf ("STATUS: %s\n", status_line);
00963 
00964   /* find header info and null-terminate it */
00965   header = page;
00966   while (strcspn (page, "\r\n") > 0) {
00967     page += (size_t) strcspn (page, "\r\n");
00968     pos = page;
00969     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
00970         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
00971       page += (size_t) 2;
00972     else
00973       page += (size_t) 1;
00974   }
00975   page += (size_t) strspn (page, "\r\n");
00976   header[pos - header] = 0;
00977   if (verbose)
00978     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
00979                 (no_body ? "  [[ skipped ]]" : page));
00980 
00981   /* make sure the status line matches the response we are looking for */
00982   if (!expected_statuscode (status_line, server_expect)) {
00983     if (server_port == HTTP_PORT)
00984       asprintf (&msg,
00985                 _("Invalid HTTP response received from host: %s\n"),
00986                 status_line);
00987     else
00988       asprintf (&msg,
00989                 _("Invalid HTTP response received from host on port %d: %s\n"),
00990                 server_port, status_line);
00991     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
00992   }
00993 
00994   /* Bypass normal status line check if server_expect was set by user and not default */
00995   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
00996   if ( server_expect_yn  )  {
00997     asprintf (&msg,
00998               _("Status line output matched \"%s\" - "), server_expect);
00999     if (verbose)
01000       printf ("%s\n",msg);
01001   }
01002   else {
01003     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
01004     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
01005     /* Status-Code = 3 DIGITS */
01006 
01007     status_code = strchr (status_line, ' ') + sizeof (char);
01008     if (strspn (status_code, "1234567890") != 3)
01009       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
01010 
01011     http_status = atoi (status_code);
01012 
01013     /* check the return code */
01014 
01015     if (http_status >= 600 || http_status < 100) {
01016       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
01017     }
01018     /* server errors result in a critical state */
01019     else if (http_status >= 500) {
01020       asprintf (&msg, _("%s - "), status_line);
01021       result = STATE_CRITICAL;
01022     }
01023     /* client errors result in a warning state */
01024     else if (http_status >= 400) {
01025       asprintf (&msg, _("%s - "), status_line);
01026       result = max_state_alt(STATE_WARNING, result);
01027     }
01028     /* check redirected page if specified */
01029     else if (http_status >= 300) {
01030 
01031       if (onredirect == STATE_DEPENDENT)
01032         redir (header, status_line);
01033       else
01034         result = max_state_alt(onredirect, result);
01035       asprintf (&msg, _("%s - "), status_line);
01036     } /* end if (http_status >= 300) */
01037     else {
01038       /* Print OK status anyway */
01039       asprintf (&msg, _("%s - "), status_line);
01040     }
01041 
01042   } /* end else (server_expect_yn)  */
01043 
01044   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
01045   alarm (0);
01046 
01047   if (maximum_age >= 0) {
01048     result = max_state_alt(check_document_dates(header, &msg), result);
01049   }
01050 
01051   /* Page and Header content checks go here */
01052 
01053   if (strlen (string_expect)) {
01054     if (!strstr (page, string_expect)) {
01055       strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
01056       if(output_string_search[sizeof(output_string_search)-1]!='\0') {
01057         bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
01058       }
01059       asprintf (&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
01060       result = STATE_CRITICAL;
01061     }
01062   }
01063 
01064   if (strlen (regexp)) {
01065     errcode = regexec (&preg, page, REGS, pmatch, 0);
01066     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
01067       /* OK - No-op to avoid changing the logic around it */
01068       result = max_state_alt(STATE_OK, result);
01069     }
01070     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
01071       if (invert_regex == 0)
01072         asprintf (&msg, _("%spattern not found, "), msg);
01073       else
01074         asprintf (&msg, _("%spattern found, "), msg);
01075       result = STATE_CRITICAL;
01076     }
01077     else {
01078       /* FIXME: Shouldn't that be UNKNOWN? */
01079       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
01080       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
01081       result = STATE_CRITICAL;
01082     }
01083   }
01084 
01085   /* make sure the page is of an appropriate size */
01086   /* page_len = get_content_length(header); */
01087   /* FIXME: Will this work with -N ? IMHO we should use
01088    * get_content_length(header) and always check if it's different than the
01089    * returned pagesize
01090    */
01091   /* FIXME: IIRC pagesize returns headers - shouldn't we make
01092    * it == get_content_length(header) ??
01093    */
01094   page_len = pagesize;
01095   if ((max_page_len > 0) && (page_len > max_page_len)) {
01096     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
01097     result = max_state_alt(STATE_WARNING, result);
01098   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
01099     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
01100     result = max_state_alt(STATE_WARNING, result);
01101   }
01102 
01103   /* Cut-off trailing characters */
01104   if(msg[strlen(msg)-2] == ',')
01105     msg[strlen(msg)-2] = '\0';
01106   else
01107     msg[strlen(msg)-3] = '\0';
01108 
01109   /* check elapsed time */
01110   asprintf (&msg,
01111             _("%s - %d bytes in %.3f second response time %s|%s %s"),
01112             msg, page_len, elapsed_time,
01113             (display_html ? "</A>" : ""),
01114             perfd_time (elapsed_time), perfd_size (page_len));
01115 
01116   result = max_state_alt(get_status(elapsed_time, thlds), result);
01117 
01118   die (result, "HTTP %s: %s\n", state_text(result), msg);
01119   /* die failed? */
01120   return STATE_UNKNOWN;
01121 }
01122 
01123 
01124 
01125 /* per RFC 2396 */
01126 #define URI_HTTP "%5[HTPShtps]"
01127 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
01128 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
01129 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
01130 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
01131 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
01132 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
01133 #define HD4 URI_HTTP "://" URI_HOST
01134 #define HD5 URI_PATH
01135 
01136 void
01137 redir (char *pos, char *status_line)
01138 {
01139   int i = 0;
01140   char *x;
01141   char xx[2];
01142   char type[6];
01143   char *addr;
01144   char *url;
01145 
01146   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
01147   if (addr == NULL)
01148     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
01149 
01150   url = malloc (strcspn (pos, "\r\n"));
01151   if (url == NULL)
01152     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
01153 
01154   while (pos) {
01155     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
01156     if (i == 0) {
01157       pos += (size_t) strcspn (pos, "\r\n");
01158       pos += (size_t) strspn (pos, "\r\n");
01159       if (strlen(pos) == 0)
01160         die (STATE_UNKNOWN,
01161              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
01162              status_line, (display_html ? "</A>" : ""));
01163       continue;
01164     }
01165 
01166     pos += i;
01167     pos += strspn (pos, " \t");
01168 
01169     /*
01170      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
01171      * preceding each extra line with at least one SP or HT.''
01172      */
01173     for (; (i = strspn (pos, "\r\n")); pos += i) {
01174       pos += i;
01175       if (!(i = strspn (pos, " \t"))) {
01176         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
01177              display_html ? "</A>" : "");
01178       }
01179     }
01180 
01181     url = realloc (url, strcspn (pos, "\r\n") + 1);
01182     if (url == NULL)
01183       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
01184 
01185     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
01186     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
01187       url = prepend_slash (url);
01188       use_ssl = server_type_check (type);
01189     }
01190 
01191     /* URI_HTTP URI_HOST URI_PATH */
01192     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
01193       url = prepend_slash (url);
01194       use_ssl = server_type_check (type);
01195       i = server_port_check (use_ssl);
01196     }
01197 
01198     /* URI_HTTP URI_HOST URI_PORT */
01199     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
01200       strcpy (url, HTTP_URL);
01201       use_ssl = server_type_check (type);
01202     }
01203 
01204     /* URI_HTTP URI_HOST */
01205     else if (sscanf (pos, HD4, type, addr) == 2) {
01206       strcpy (url, HTTP_URL);
01207       use_ssl = server_type_check (type);
01208       i = server_port_check (use_ssl);
01209     }
01210 
01211     /* URI_PATH */
01212     else if (sscanf (pos, HD5, url) == 1) {
01213       /* relative url */
01214       if ((url[0] != '/')) {
01215         if ((x = strrchr(server_url, '/')))
01216           *x = '\0';
01217         asprintf (&url, "%s/%s", server_url, url);
01218       }
01219       i = server_port;
01220       strcpy (type, server_type);
01221       strcpy (addr, host_name ? host_name : server_address);
01222     }
01223 
01224     else {
01225       die (STATE_UNKNOWN,
01226            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
01227            pos, (display_html ? "</A>" : ""));
01228     }
01229 
01230     break;
01231 
01232   } /* end while (pos) */
01233 
01234   if (++redir_depth > max_depth)
01235     die (STATE_WARNING,
01236          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
01237          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
01238 
01239   if (server_port==i &&
01240       !strcmp(server_address, addr) &&
01241       (host_name && !strcmp(host_name, addr)) &&
01242       !strcmp(server_url, url))
01243     die (STATE_WARNING,
01244          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
01245          type, addr, i, url, (display_html ? "</A>" : ""));
01246 
01247   strcpy (server_type, type);
01248 
01249   free (host_name);
01250   host_name = strdup (addr);
01251 
01252   if (!(followsticky & STICKY_HOST)) {
01253     free (server_address);
01254     server_address = strdup (addr);
01255   }
01256   if (!(followsticky & STICKY_PORT)) {
01257     server_port = i;
01258   }
01259 
01260   free (server_url);
01261   server_url = url;
01262 
01263   if (server_port > MAX_PORT)
01264     die (STATE_UNKNOWN,
01265          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
01266          MAX_PORT, server_type, server_address, server_port, server_url,
01267          display_html ? "</A>" : "");
01268 
01269   if (verbose)
01270     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
01271             host_name ? host_name : server_address, server_port, server_url);
01272 
01273   check_http ();
01274 }
01275 
01276 
01277 int
01278 server_type_check (const char *type)
01279 {
01280   if (strcmp (type, "https"))
01281     return FALSE;
01282   else
01283     return TRUE;
01284 }
01285 
01286 int
01287 server_port_check (int ssl_flag)
01288 {
01289   if (ssl_flag)
01290     return HTTPS_PORT;
01291   else
01292     return HTTP_PORT;
01293 }
01294 
01295 char *perfd_time (double elapsed_time)
01296 {
01297   return fperfdata ("time", elapsed_time, "s",
01298             thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
01299             thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
01300                    TRUE, 0, FALSE, 0);
01301 }
01302 
01303 
01304 
01305 char *perfd_size (int page_len)
01306 {
01307   return perfdata ("size", page_len, "B",
01308             (min_page_len>0?TRUE:FALSE), min_page_len,
01309             (min_page_len>0?TRUE:FALSE), 0,
01310             TRUE, 0, FALSE, 0);
01311 }
01312 
01313 void
01314 print_help (void)
01315 {
01316   print_revision (progname, NP_VERSION);
01317 
01318   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
01319   printf (COPYRIGHT, copyright, email);
01320 
01321   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
01322   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
01323   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
01324   printf ("%s\n", _("certificate expiration times."));
01325 
01326   printf ("\n\n");
01327 
01328   print_usage ();
01329 
01330   printf (_("NOTE: One or both of -H and -I must be specified"));
01331 
01332   printf ("\n");
01333 
01334   printf (UT_HELP_VRSN);
01335   printf (UT_EXTRA_OPTS);
01336 
01337   printf (" %s\n", "-H, --hostname=ADDRESS");
01338   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
01339   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
01340   printf (" %s\n", "-I, --IP-address=ADDRESS");
01341   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
01342   printf (" %s\n", "-p, --port=INTEGER");
01343   printf ("    %s", _("Port number (default: "));
01344   printf ("%d)\n", HTTP_PORT);
01345 
01346   printf (UT_IPv46);
01347 
01348 #ifdef HAVE_SSL
01349   printf (" %s\n", "-S, --ssl=VERSION");
01350   printf ("    %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
01351   printf ("    %s\n", _("auto-negotiation (1 = TLSv1, 2 = SSLv2, 3 = SSLv3)."));
01352   printf (" %s\n", "--sni");
01353   printf ("    %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
01354   printf (" %s\n", "-C, --certificate=INTEGER");
01355   printf ("    %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
01356   printf ("    %s\n", _("(when this option is used the URL is not checked.)\n"));
01357 #endif
01358 
01359   printf (" %s\n", "-e, --expect=STRING");
01360   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
01361   printf ("    %s", _("the first (status) line of the server response (default: "));
01362   printf ("%s)\n", HTTP_EXPECT);
01363   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
01364   printf (" %s\n", "-s, --string=STRING");
01365   printf ("    %s\n", _("String to expect in the content"));
01366   printf (" %s\n", "-u, --url=PATH");
01367   printf ("    %s\n", _("URL to GET or POST (default: /)"));
01368   printf (" %s\n", "-P, --post=STRING");
01369   printf ("    %s\n", _("URL encoded http POST data"));
01370   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
01371   printf ("    %s\n", _("Set HTTP method."));
01372   printf (" %s\n", "-N, --no-body");
01373   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
01374   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
01375   printf (" %s\n", "-M, --max-age=SECONDS");
01376   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
01377   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
01378   printf (" %s\n", "-T, --content-type=STRING");
01379   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
01380 
01381   printf (" %s\n", "-l, --linespan");
01382   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
01383   printf (" %s\n", "-r, --regex, --ereg=STRING");
01384   printf ("    %s\n", _("Search page for regex STRING"));
01385   printf (" %s\n", "-R, --eregi=STRING");
01386   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
01387   printf (" %s\n", "--invert-regex");
01388   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
01389 
01390   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
01391   printf ("    %s\n", _("Username:password on sites with basic authentication"));
01392   printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
01393   printf ("    %s\n", _("Username:password on proxy-servers with basic authentication"));
01394   printf (" %s\n", "-A, --useragent=STRING");
01395   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
01396   printf (" %s\n", "-k, --header=STRING");
01397   printf ("    %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
01398   printf (" %s\n", "-L, --link");
01399   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
01400   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
01401   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
01402   printf ("    %s\n", _("specified IP address. stickyport also ensures port stays the same."));
01403   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
01404   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
01405 
01406   printf (UT_WARN_CRIT);
01407 
01408   printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
01409 
01410   printf (UT_VERBOSE);
01411 
01412   printf ("\n");
01413   printf ("%s\n", _("Notes:"));
01414   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
01415   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
01416   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
01417   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
01418   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
01419   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
01420 
01421 #ifdef HAVE_SSL
01422   printf ("\n");
01423   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
01424   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
01425   printf (" %s\n", _("certificate is still valid for the specified number of days."));
01426   printf ("\n");
01427   printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
01428   printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
01429   printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
01430   printf ("\n");
01431   printf ("%s\n", _("Examples:"));
01432   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
01433   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
01434   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
01435   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
01436   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
01437 
01438   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
01439   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
01440   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
01441   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
01442   printf (" %s\n", _("the certificate is expired."));
01443 
01444   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
01445   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
01446   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
01447   printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
01448   printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
01449 
01450 #endif
01451 
01452   printf (UT_SUPPORT);
01453 
01454 }
01455 
01456 
01457 
01458 void
01459 print_usage (void)
01460 {
01461   printf ("%s\n", _("Usage:"));
01462   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
01463   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
01464   printf ("       [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
01465   printf ("       [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
01466   printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
01467   printf ("       [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
01468   printf ("       [-T <content-type>] [-j method]\n");
01469 }