Code

Make check_http use standard threshold functions
[nagiosplug.git] / plugins / check_http.c
1 /*****************************************************************************
2
3 * Nagios check_http plugin
4
5 * License: GPL
6 * Copyright (c) 1999-2008 Nagios Plugins Development Team
7
8 * Description:
9
10 * This file contains the check_http plugin
11
12 * This plugin tests the HTTP service on the specified host. It can test
13 * normal (http) and secure (https) servers, follow redirects, search for
14 * strings and regular expressions, check connection times, and report on
15 * certificate expiration times.
16
17
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 * GNU General Public License for more details.
27
28 * You should have received a copy of the GNU General Public License
29 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30
31
32 *****************************************************************************/
34 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
36 const char *progname = "check_http";
37 const char *copyright = "1999-2008";
38 const char *email = "nagiosplug-devel@lists.sourceforge.net";
40 #include "common.h"
41 #include "netutils.h"
42 #include "utils.h"
43 #include "base64.h"
44 #include <ctype.h>
46 #define INPUT_DELIMITER ";"
47 #define STICKY_NONE 0
48 #define STICKY_HOST 1
49 #define STICKY_PORT 2
51 #define HTTP_EXPECT "HTTP/1."
52 enum {
53   MAX_IPV4_HOSTLENGTH = 255,
54   HTTP_PORT = 80,
55   HTTPS_PORT = 443,
56   MAX_PORT = 65535
57 };
59 #ifdef HAVE_SSL
60 int check_cert = FALSE;
61 int days_till_exp;
62 char *randbuff;
63 X509 *server_cert;
64 #  define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
65 #  define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
66 #else /* ifndef HAVE_SSL */
67 #  define my_recv(buf, len) read(sd, buf, len)
68 #  define my_send(buf, len) send(sd, buf, len, 0)
69 #endif /* HAVE_SSL */
70 int no_body = FALSE;
71 int maximum_age = -1;
73 enum {
74   REGS = 2,
75   MAX_RE_SIZE = 256
76 };
77 #include "regex.h"
78 regex_t preg;
79 regmatch_t pmatch[REGS];
80 char regexp[MAX_RE_SIZE];
81 char errbuf[MAX_INPUT_BUFFER];
82 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
83 int errcode;
84 int invert_regex = 0;
86 struct timeval tv;
88 #define HTTP_URL "/"
89 #define CRLF "\r\n"
91 int specify_port = FALSE;
92 int server_port = HTTP_PORT;
93 char server_port_text[6] = "";
94 char server_type[6] = "http";
95 char *server_address;
96 char *host_name;
97 char *server_url;
98 char *user_agent;
99 int server_url_length;
100 int server_expect_yn = 0;
101 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
102 char string_expect[MAX_INPUT_BUFFER] = "";
103 char output_string_search[30] = "";
104 char *warning_thresholds = NULL;
105 char *critical_thresholds = NULL;
106 thresholds *thlds;
107 char user_auth[MAX_INPUT_BUFFER] = "";
108 char proxy_auth[MAX_INPUT_BUFFER] = "";
109 int display_html = FALSE;
110 char **http_opt_headers;
111 int http_opt_headers_count = 0;
112 int onredirect = STATE_OK;
113 int followsticky = STICKY_NONE;
114 int use_ssl = FALSE;
115 int use_sni = 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 int check_http (void);
129 void redir (char *pos, char *status_line);
130 int server_type_check(const char *type);
131 int server_port_check(int ssl_flag);
132 char *perfd_time (double microsec);
133 char *perfd_size (int page_len);
134 void print_help (void);
135 void print_usage (void);
137 int
138 main (int argc, char **argv)
140   int result = STATE_UNKNOWN;
142   setlocale (LC_ALL, "");
143   bindtextdomain (PACKAGE, LOCALEDIR);
144   textdomain (PACKAGE);
146   /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
147   server_url = strdup(HTTP_URL);
148   server_url_length = strlen(server_url);
149   asprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
150             NP_VERSION, VERSION);
152   /* Parse extra opts if any */
153   argv=np_extra_opts (&argc, argv, progname);
155   if (process_arguments (argc, argv) == ERROR)
156     usage4 (_("Could not parse arguments"));
158   if (display_html == TRUE)
159     printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
160       use_ssl ? "https" : "http", host_name ? host_name : server_address,
161       server_port, server_url);
163   /* initialize alarm signal handling, set socket timeout, start timer */
164   (void) signal (SIGALRM, socket_timeout_alarm_handler);
165   (void) alarm (socket_timeout);
166   gettimeofday (&tv, NULL);
168   result = check_http ();
169   return result;
174 /* process command-line arguments */
175 int
176 process_arguments (int argc, char **argv)
178   int c = 1;
179   char *p;
181   enum {
182     INVERT_REGEX = CHAR_MAX + 1,
183     SNI_OPTION
184   };
186   int option = 0;
187   static struct option longopts[] = {
188     STD_LONG_OPTS,
189     {"link", no_argument, 0, 'L'},
190     {"nohtml", no_argument, 0, 'n'},
191     {"ssl", no_argument, 0, 'S'},
192     {"sni", no_argument, 0, SNI_OPTION},
193     {"post", required_argument, 0, 'P'},
194     {"method", required_argument, 0, 'j'},
195     {"IP-address", required_argument, 0, 'I'},
196     {"url", required_argument, 0, 'u'},
197     {"port", required_argument, 0, 'p'},
198     {"authorization", required_argument, 0, 'a'},
199     {"proxy_authorization", required_argument, 0, 'b'},
200     {"string", required_argument, 0, 's'},
201     {"expect", required_argument, 0, 'e'},
202     {"regex", required_argument, 0, 'r'},
203     {"ereg", required_argument, 0, 'r'},
204     {"eregi", required_argument, 0, 'R'},
205     {"linespan", no_argument, 0, 'l'},
206     {"onredirect", required_argument, 0, 'f'},
207     {"certificate", required_argument, 0, 'C'},
208     {"useragent", required_argument, 0, 'A'},
209     {"header", required_argument, 0, 'k'},
210     {"no-body", no_argument, 0, 'N'},
211     {"max-age", required_argument, 0, 'M'},
212     {"content-type", required_argument, 0, 'T'},
213     {"pagesize", required_argument, 0, 'm'},
214     {"invert-regex", no_argument, NULL, INVERT_REGEX},
215     {"use-ipv4", no_argument, 0, '4'},
216     {"use-ipv6", no_argument, 0, '6'},
217     {0, 0, 0, 0}
218   };
220   if (argc < 2)
221     return ERROR;
223   for (c = 1; c < argc; c++) {
224     if (strcmp ("-to", argv[c]) == 0)
225       strcpy (argv[c], "-t");
226     if (strcmp ("-hn", argv[c]) == 0)
227       strcpy (argv[c], "-H");
228     if (strcmp ("-wt", argv[c]) == 0)
229       strcpy (argv[c], "-w");
230     if (strcmp ("-ct", argv[c]) == 0)
231       strcpy (argv[c], "-c");
232     if (strcmp ("-nohtml", argv[c]) == 0)
233       strcpy (argv[c], "-n");
234   }
236   while (1) {
237     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:nlLSm:M:N", longopts, &option);
238     if (c == -1 || c == EOF)
239       break;
241     switch (c) {
242     case '?': /* usage */
243       usage5 ();
244       break;
245     case 'h': /* help */
246       print_help ();
247       exit (STATE_OK);
248       break;
249     case 'V': /* version */
250       print_revision (progname, NP_VERSION);
251       exit (STATE_OK);
252       break;
253     case 't': /* timeout period */
254       if (!is_intnonneg (optarg))
255         usage2 (_("Timeout interval must be a positive integer"), optarg);
256       else
257         socket_timeout = atoi (optarg);
258       break;
259     case 'c': /* critical time threshold */
260       critical_thresholds = optarg;
261       break;
262     case 'w': /* warning time threshold */
263       warning_thresholds = optarg;
264       break;
265     case 'A': /* User Agent String */
266       asprintf (&user_agent, "User-Agent: %s", optarg);
267       break;
268     case 'k': /* Additional headers */
269       if (http_opt_headers_count == 0)
270         http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
271       else
272         http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
273       http_opt_headers[http_opt_headers_count - 1] = optarg;
274       /* asprintf (&http_opt_headers, "%s", optarg); */
275       break;
276     case 'L': /* show html link */
277       display_html = TRUE;
278       break;
279     case 'n': /* do not show html link */
280       display_html = FALSE;
281       break;
282     case 'C': /* Check SSL cert validity */
283 #ifdef HAVE_SSL
284       if (!is_intnonneg (optarg))
285         usage2 (_("Invalid certificate expiration period"), optarg);
286       else {
287         days_till_exp = atoi (optarg);
288         check_cert = TRUE;
289       }
290      /* Fall through to -S option */
291 #endif
292     case 'S': /* use SSL */
293 #ifndef HAVE_SSL
294       usage4 (_("Invalid option - SSL is not available"));
295 #endif
296       use_ssl = TRUE;
297       if (specify_port == FALSE)
298         server_port = HTTPS_PORT;
299       break;
300     case SNI_OPTION:
301       use_sni = TRUE;
302       break;
303     case 'f': /* onredirect */
304       if (!strcmp (optarg, "stickyport"))
305         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
306       else if (!strcmp (optarg, "sticky"))
307         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
308       else if (!strcmp (optarg, "follow"))
309         onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
310       else if (!strcmp (optarg, "unknown"))
311         onredirect = STATE_UNKNOWN;
312       else if (!strcmp (optarg, "ok"))
313         onredirect = STATE_OK;
314       else if (!strcmp (optarg, "warning"))
315         onredirect = STATE_WARNING;
316       else if (!strcmp (optarg, "critical"))
317         onredirect = STATE_CRITICAL;
318       else usage2 (_("Invalid onredirect option"), optarg);
319       if (verbose)
320         printf(_("option f:%d \n"), onredirect);
321       break;
322     /* Note: H, I, and u must be malloc'd or will fail on redirects */
323     case 'H': /* Host Name (virtual host) */
324       host_name = strdup (optarg);
325       if (host_name[0] == '[') {
326         if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
327           server_port = atoi (p + 2);
328       } else if ((p = strchr (host_name, ':')) != NULL
329                  && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
330           server_port = atoi (p);
331       break;
332     case 'I': /* Server IP-address */
333       server_address = strdup (optarg);
334       break;
335     case 'u': /* URL path */
336       server_url = strdup (optarg);
337       server_url_length = strlen (server_url);
338       break;
339     case 'p': /* Server port */
340       if (!is_intnonneg (optarg))
341         usage2 (_("Invalid port number"), optarg);
342       else {
343         server_port = atoi (optarg);
344         specify_port = TRUE;
345       }
346       break;
347     case 'a': /* authorization info */
348       strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
349       user_auth[MAX_INPUT_BUFFER - 1] = 0;
350       break;
351     case 'b': /* proxy-authorization info */
352       strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
353       proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
354       break;
355     case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
356       if (! http_post_data)
357         http_post_data = strdup (optarg);
358       if (! http_method)
359         http_method = strdup("POST");
360       break;
361     case 'j': /* Set HTTP method */
362       if (http_method)
363         free(http_method);
364       http_method = strdup (optarg);
365       break;
366     case 's': /* string or substring */
367       strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
368       string_expect[MAX_INPUT_BUFFER - 1] = 0;
369       break;
370     case 'e': /* string or substring */
371       strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
372       server_expect[MAX_INPUT_BUFFER - 1] = 0;
373       server_expect_yn = 1;
374       break;
375     case 'T': /* Content-type */
376       asprintf (&http_content_type, "%s", optarg);
377       break;
378     case 'l': /* linespan */
379       cflags &= ~REG_NEWLINE;
380       break;
381     case 'R': /* regex */
382       cflags |= REG_ICASE;
383     case 'r': /* regex */
384       strncpy (regexp, optarg, MAX_RE_SIZE - 1);
385       regexp[MAX_RE_SIZE - 1] = 0;
386       errcode = regcomp (&preg, regexp, cflags);
387       if (errcode != 0) {
388         (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
389         printf (_("Could Not Compile Regular Expression: %s"), errbuf);
390         return ERROR;
391       }
392       break;
393     case INVERT_REGEX:
394       invert_regex = 1;
395       break;
396     case '4':
397       address_family = AF_INET;
398       break;
399     case '6':
400 #ifdef USE_IPV6
401       address_family = AF_INET6;
402 #else
403       usage4 (_("IPv6 support not available"));
404 #endif
405       break;
406     case 'v': /* verbose */
407       verbose = TRUE;
408       break;
409     case 'm': /* min_page_length */
410       {
411       char *tmp;
412       if (strchr(optarg, ':') != (char *)NULL) {
413         /* range, so get two values, min:max */
414         tmp = strtok(optarg, ":");
415         if (tmp == NULL) {
416           printf("Bad format: try \"-m min:max\"\n");
417           exit (STATE_WARNING);
418         } else
419           min_page_len = atoi(tmp);
421         tmp = strtok(NULL, ":");
422         if (tmp == NULL) {
423           printf("Bad format: try \"-m min:max\"\n");
424           exit (STATE_WARNING);
425         } else
426           max_page_len = atoi(tmp);
427       } else
428         min_page_len = atoi (optarg);
429       break;
430       }
431     case 'N': /* no-body */
432       no_body = TRUE;
433       break;
434     case 'M': /* max-age */
435                   {
436                     int L = strlen(optarg);
437                     if (L && optarg[L-1] == 'm')
438                       maximum_age = atoi (optarg) * 60;
439                     else if (L && optarg[L-1] == 'h')
440                       maximum_age = atoi (optarg) * 60 * 60;
441                     else if (L && optarg[L-1] == 'd')
442                       maximum_age = atoi (optarg) * 60 * 60 * 24;
443                     else if (L && (optarg[L-1] == 's' ||
444                                    isdigit (optarg[L-1])))
445                       maximum_age = atoi (optarg);
446                     else {
447                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
448                       exit (STATE_WARNING);
449                     }
450                   }
451                   break;
452     }
453   }
455   c = optind;
457   if (server_address == NULL && c < argc)
458     server_address = strdup (argv[c++]);
460   if (host_name == NULL && c < argc)
461     host_name = strdup (argv[c++]);
463   if (server_address == NULL) {
464     if (host_name == NULL)
465       usage4 (_("You must specify a server address or host name"));
466     else
467       server_address = strdup (host_name);
468   }
470   set_thresholds(&thlds, warning_thresholds, critical_thresholds);
472   if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
473     socket_timeout = (int)thlds->critical->end + 1;
475   if (http_method == NULL)
476     http_method = strdup ("GET");
478   return TRUE;
483 /* Returns 1 if we're done processing the document body; 0 to keep going */
484 static int
485 document_headers_done (char *full_page)
487   const char *body;
489   for (body = full_page; *body; body++) {
490     if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
491       break;
492   }
494   if (!*body)
495     return 0;  /* haven't read end of headers yet */
497   full_page[body - full_page] = 0;
498   return 1;
501 static time_t
502 parse_time_string (const char *string)
504   struct tm tm;
505   time_t t;
506   memset (&tm, 0, sizeof(tm));
508   /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
510   if (isupper (string[0])  &&  /* Tue */
511     islower (string[1])  &&
512     islower (string[2])  &&
513     ',' ==   string[3]   &&
514     ' ' ==   string[4]   &&
515     (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
516     isdigit (string[6])  &&
517     ' ' ==   string[7]   &&
518     isupper (string[8])  &&  /* Dec */
519     islower (string[9])  &&
520     islower (string[10]) &&
521     ' ' ==   string[11]  &&
522     isdigit (string[12]) &&  /* 2001 */
523     isdigit (string[13]) &&
524     isdigit (string[14]) &&
525     isdigit (string[15]) &&
526     ' ' ==   string[16]  &&
527     isdigit (string[17]) &&  /* 02: */
528     isdigit (string[18]) &&
529     ':' ==   string[19]  &&
530     isdigit (string[20]) &&  /* 59: */
531     isdigit (string[21]) &&
532     ':' ==   string[22]  &&
533     isdigit (string[23]) &&  /* 03 */
534     isdigit (string[24]) &&
535     ' ' ==   string[25]  &&
536     'G' ==   string[26]  &&  /* GMT */
537     'M' ==   string[27]  &&  /* GMT */
538     'T' ==   string[28]) {
540     tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
541     tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
542     tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
543     tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
544     tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
545       !strncmp (string+8, "Feb", 3) ? 1 :
546       !strncmp (string+8, "Mar", 3) ? 2 :
547       !strncmp (string+8, "Apr", 3) ? 3 :
548       !strncmp (string+8, "May", 3) ? 4 :
549       !strncmp (string+8, "Jun", 3) ? 5 :
550       !strncmp (string+8, "Jul", 3) ? 6 :
551       !strncmp (string+8, "Aug", 3) ? 7 :
552       !strncmp (string+8, "Sep", 3) ? 8 :
553       !strncmp (string+8, "Oct", 3) ? 9 :
554       !strncmp (string+8, "Nov", 3) ? 10 :
555       !strncmp (string+8, "Dec", 3) ? 11 :
556       -1);
557     tm.tm_year = ((1000 * (string[12]-'0') +
558       100 * (string[13]-'0') +
559       10 * (string[14]-'0') +
560       (string[15]-'0'))
561       - 1900);
563     tm.tm_isdst = 0;  /* GMT is never in DST, right? */
565     if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
566       return 0;
568     /*
569     This is actually wrong: we need to subtract the local timezone
570     offset from GMT from this value.  But, that's ok in this usage,
571     because we only comparing these two GMT dates against each other,
572     so it doesn't matter what time zone we parse them in.
573     */
575     t = mktime (&tm);
576     if (t == (time_t) -1) t = 0;
578     if (verbose) {
579       const char *s = string;
580       while (*s && *s != '\r' && *s != '\n')
581       fputc (*s++, stdout);
582       printf (" ==> %lu\n", (unsigned long) t);
583     }
585     return t;
587   } else {
588     return 0;
589   }
592 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
593 static int
594 expected_statuscode (const char *reply, const char *statuscodes)
596   char *expected, *code;
597   int result = 0;
599   if ((expected = strdup (statuscodes)) == NULL)
600     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
602   for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
603     if (strstr (reply, code) != NULL) {
604       result = 1;
605       break;
606     }
608   free (expected);
609   return result;
612 static int
613 check_document_dates (const char *headers, char **msg)
615   const char *s;
616   char *server_date = 0;
617   char *document_date = 0;
618   int date_result = STATE_OK;
620   s = headers;
621   while (*s) {
622     const char *field = s;
623     const char *value = 0;
625     /* Find the end of the header field */
626     while (*s && !isspace(*s) && *s != ':')
627       s++;
629     /* Remember the header value, if any. */
630     if (*s == ':')
631       value = ++s;
633     /* Skip to the end of the header, including continuation lines. */
634     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
635       s++;
637     /* Avoid stepping over end-of-string marker */
638     if (*s)
639       s++;
641     /* Process this header. */
642     if (value && value > field+2) {
643       char *ff = (char *) malloc (value-field);
644       char *ss = ff;
645       while (field < value-1)
646         *ss++ = tolower(*field++);
647       *ss++ = 0;
649       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
650         const char *e;
651         while (*value && isspace (*value))
652           value++;
653         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
654           ;
655         ss = (char *) malloc (e - value + 1);
656         strncpy (ss, value, e - value);
657         ss[e - value] = 0;
658         if (!strcmp (ff, "date")) {
659           if (server_date) free (server_date);
660           server_date = ss;
661         } else {
662           if (document_date) free (document_date);
663           document_date = ss;
664         }
665       }
666       free (ff);
667     }
668   }
670   /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
671   if (!server_date || !*server_date) {
672     asprintf (msg, _("%sServer date unknown, "), *msg);
673     date_result = max_state_alt(STATE_UNKNOWN, date_result);
674   } else if (!document_date || !*document_date) {
675     asprintf (msg, _("%sDocument modification date unknown, "), *msg);
676     date_result = max_state_alt(STATE_CRITICAL, date_result);
677   } else {
678     time_t srv_data = parse_time_string (server_date);
679     time_t doc_data = parse_time_string (document_date);
681     if (srv_data <= 0) {
682       asprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
683       date_result = max_state_alt(STATE_CRITICAL, date_result);
684     } else if (doc_data <= 0) {
685       asprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
686       date_result = max_state_alt(STATE_CRITICAL, date_result);
687     } else if (doc_data > srv_data + 30) {
688       asprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
689       date_result = max_state_alt(STATE_CRITICAL, date_result);
690     } else if (doc_data < srv_data - maximum_age) {
691       int n = (srv_data - doc_data);
692       if (n > (60 * 60 * 24 * 2)) {
693         asprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
694         date_result = max_state_alt(STATE_CRITICAL, date_result);
695       } else {
696         asprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
697         date_result = max_state_alt(STATE_CRITICAL, date_result);
698       }
699     }
700     free (server_date);
701     free (document_date);
702   }
703   return date_result;
706 int
707 get_content_length (const char *headers)
709   const char *s;
710   int content_length = 0;
712   s = headers;
713   while (*s) {
714     const char *field = s;
715     const char *value = 0;
717     /* Find the end of the header field */
718     while (*s && !isspace(*s) && *s != ':')
719       s++;
721     /* Remember the header value, if any. */
722     if (*s == ':')
723       value = ++s;
725     /* Skip to the end of the header, including continuation lines. */
726     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
727       s++;
729     /* Avoid stepping over end-of-string marker */
730     if (*s)
731       s++;
733     /* Process this header. */
734     if (value && value > field+2) {
735       char *ff = (char *) malloc (value-field);
736       char *ss = ff;
737       while (field < value-1)
738         *ss++ = tolower(*field++);
739       *ss++ = 0;
741       if (!strcmp (ff, "content-length")) {
742         const char *e;
743         while (*value && isspace (*value))
744           value++;
745         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
746           ;
747         ss = (char *) malloc (e - value + 1);
748         strncpy (ss, value, e - value);
749         ss[e - value] = 0;
750         content_length = atoi(ss);
751         free (ss);
752       }
753       free (ff);
754     }
755   }
756   return (content_length);
759 char *
760 prepend_slash (char *path)
762   char *newpath;
764   if (path[0] == '/')
765     return path;
767   if ((newpath = malloc (strlen(path) + 2)) == NULL)
768     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
769   newpath[0] = '/';
770   strcpy (newpath + 1, path);
771   free (path);
772   return newpath;
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 *full_page_new;
789   char *buf;
790   char *pos;
791   long microsec;
792   double elapsed_time;
793   int page_len = 0;
794   int result = STATE_OK;
796   /* try to connect to the host at the given port number */
797   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
798     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
799 #ifdef HAVE_SSL
800   if (use_ssl == TRUE) {
801     np_net_ssl_init_with_hostname(sd, (use_sni ? host_name : NULL));
802     if (check_cert == TRUE) {
803       result = np_net_ssl_check_cert(days_till_exp);
804       np_net_ssl_cleanup();
805       if (sd) close(sd);
806       return result;
807     }
808   }
809 #endif /* HAVE_SSL */
811   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
813   /* tell HTTP/1.1 servers not to keep the connection alive */
814   asprintf (&buf, "%sConnection: close\r\n", buf);
816   /* optionally send the host header info */
817   if (host_name) {
818     /*
819      * Specify the port only if we're using a non-default port (see RFC 2616,
820      * 14.23).  Some server applications/configurations cause trouble if the
821      * (default) port is explicitly specified in the "Host:" header line.
822      */
823     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
824         (use_ssl == TRUE && server_port == HTTPS_PORT))
825       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
826     else
827       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
828   }
830   /* optionally send any other header tag */
831   if (http_opt_headers_count) {
832     for (i = 0; i < http_opt_headers_count ; i++) {
833       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
834         asprintf (&buf, "%s%s\r\n", buf, pos);
835     }
836     /* This cannot be free'd here because a redirection will then try to access this and segfault */
837     /* Covered in a testcase in tests/check_http.t */
838     /* free(http_opt_headers); */
839   }
841   /* optionally send the authentication info */
842   if (strlen(user_auth)) {
843     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
844     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
845   }
847   /* optionally send the proxy authentication info */
848   if (strlen(proxy_auth)) {
849     base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
850     asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
851   }
853   /* either send http POST data (any data, not only POST)*/
854   if (http_post_data) {
855     if (http_content_type) {
856       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
857     } else {
858       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
859     }
861     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
862     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
863   }
864   else {
865     /* or just a newline so the server knows we're done with the request */
866     asprintf (&buf, "%s%s", buf, CRLF);
867   }
869   if (verbose) printf ("%s\n", buf);
870   my_send (buf, strlen (buf));
872   /* fetch the page */
873   full_page = strdup("");
874   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
875     buffer[i] = '\0';
876     asprintf (&full_page_new, "%s%s", full_page, buffer);
877     free (full_page);
878     full_page = full_page_new;
879     pagesize += i;
881                 if (no_body && document_headers_done (full_page)) {
882                   i = 0;
883                   break;
884                 }
885   }
887   if (i < 0 && errno != ECONNRESET) {
888 #ifdef HAVE_SSL
889     /*
890     if (use_ssl) {
891       sslerr=SSL_get_error(ssl, i);
892       if ( sslerr == SSL_ERROR_SSL ) {
893         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
894       } else {
895         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
896       }
897     }
898     else {
899     */
900 #endif
901       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
902 #ifdef HAVE_SSL
903       /* XXX
904     }
905     */
906 #endif
907   }
909   /* return a CRITICAL status if we couldn't read any data */
910   if (pagesize == (size_t) 0)
911     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
913   /* close the connection */
914 #ifdef HAVE_SSL
915   np_net_ssl_cleanup();
916 #endif
917   if (sd) close(sd);
919   /* Save check time */
920   microsec = deltime (tv);
921   elapsed_time = (double)microsec / 1.0e6;
923   /* leave full_page untouched so we can free it later */
924   page = full_page;
926   if (verbose)
927     printf ("%s://%s:%d%s is %d characters\n",
928       use_ssl ? "https" : "http", server_address,
929       server_port, server_url, (int)pagesize);
931   /* find status line and null-terminate it */
932   status_line = page;
933   page += (size_t) strcspn (page, "\r\n");
934   pos = page;
935   page += (size_t) strspn (page, "\r\n");
936   status_line[strcspn(status_line, "\r\n")] = 0;
937   strip (status_line);
938   if (verbose)
939     printf ("STATUS: %s\n", status_line);
941   /* find header info and null-terminate it */
942   header = page;
943   while (strcspn (page, "\r\n") > 0) {
944     page += (size_t) strcspn (page, "\r\n");
945     pos = page;
946     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
947         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
948       page += (size_t) 2;
949     else
950       page += (size_t) 1;
951   }
952   page += (size_t) strspn (page, "\r\n");
953   header[pos - header] = 0;
954   if (verbose)
955     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
956                 (no_body ? "  [[ skipped ]]" : page));
958   /* make sure the status line matches the response we are looking for */
959   if (!expected_statuscode (status_line, server_expect)) {
960     if (server_port == HTTP_PORT)
961       asprintf (&msg,
962                 _("Invalid HTTP response received from host: %s\n"),
963                 status_line);
964     else
965       asprintf (&msg,
966                 _("Invalid HTTP response received from host on port %d: %s\n"),
967                 server_port, status_line);
968     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
969   }
971   /* Bypass normal status line check if server_expect was set by user and not default */
972   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
973   if ( server_expect_yn  )  {
974     asprintf (&msg,
975               _("Status line output matched \"%s\" - "), server_expect);
976     if (verbose)
977       printf ("%s\n",msg);
978   }
979   else {
980     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
981     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
982     /* Status-Code = 3 DIGITS */
984     status_code = strchr (status_line, ' ') + sizeof (char);
985     if (strspn (status_code, "1234567890") != 3)
986       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
988     http_status = atoi (status_code);
990     /* check the return code */
992     if (http_status >= 600 || http_status < 100) {
993       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
994     }
995     /* server errors result in a critical state */
996     else if (http_status >= 500) {
997       asprintf (&msg, _("%s - "), status_line);
998       result = STATE_CRITICAL;
999     }
1000     /* client errors result in a warning state */
1001     else if (http_status >= 400) {
1002       asprintf (&msg, _("%s - "), status_line);
1003       result = max_state_alt(STATE_WARNING, result);
1004     }
1005     /* check redirected page if specified */
1006     else if (http_status >= 300) {
1008       if (onredirect == STATE_DEPENDENT)
1009         redir (header, status_line);
1010       else
1011         result = max_state_alt(onredirect, result);
1012       asprintf (&msg, _("%s - "), status_line);
1013     } /* end if (http_status >= 300) */
1014     else {
1015       /* Print OK status anyway */
1016       asprintf (&msg, _("%s - "), status_line);
1017     }
1019   } /* end else (server_expect_yn)  */
1021   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1022   alarm (0);
1024   if (maximum_age >= 0) {
1025     result = max_state_alt(check_document_dates(header, &msg), result);
1026   }
1028   /* Page and Header content checks go here */
1030   if (strlen (string_expect)) {
1031     if (!strstr (page, string_expect)) {
1032       strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1033       if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1034         bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1035       }
1036       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);
1037       result = STATE_CRITICAL;
1038     }
1039   }
1041   if (strlen (regexp)) {
1042     errcode = regexec (&preg, page, REGS, pmatch, 0);
1043     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1044       /* OK - No-op to avoid changing the logic around it */
1045       result = max_state_alt(STATE_OK, result);
1046     }
1047     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1048       if (invert_regex == 0)
1049         asprintf (&msg, _("%spattern not found, "), msg);
1050       else
1051         asprintf (&msg, _("%spattern found, "), msg);
1052       result = STATE_CRITICAL;
1053     }
1054     else {
1055       /* FIXME: Shouldn't that be UNKNOWN? */
1056       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1057       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1058       result = STATE_CRITICAL;
1059     }
1060   }
1062   /* make sure the page is of an appropriate size */
1063   /* page_len = get_content_length(header); */
1064   /* FIXME: Will this work with -N ? IMHO we should use
1065    * get_content_length(header) and always check if it's different than the
1066    * returned pagesize
1067    */
1068   /* FIXME: IIRC pagesize returns headers - shouldn't we make
1069    * it == get_content_length(header) ??
1070    */
1071   page_len = pagesize;
1072   if ((max_page_len > 0) && (page_len > max_page_len)) {
1073     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1074     result = max_state_alt(STATE_WARNING, result);
1075   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1076     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1077     result = max_state_alt(STATE_WARNING, result);
1078   }
1080   /* Cut-off trailing characters */
1081   if(msg[strlen(msg)-2] == ',')
1082     msg[strlen(msg)-2] = '\0';
1083   else
1084     msg[strlen(msg)-3] = '\0';
1086   /* check elapsed time */
1087   asprintf (&msg,
1088             _("%s - %d bytes in %.3f second response time %s|%s %s"),
1089             msg, page_len, elapsed_time,
1090             (display_html ? "</A>" : ""),
1091             perfd_time (elapsed_time), perfd_size (page_len));
1093   result = max_state_alt(get_status(elapsed_time, thlds), result);
1095   die (result, "HTTP %s: %s\n", state_text(result), msg);
1096   /* die failed? */
1097   return STATE_UNKNOWN;
1102 /* per RFC 2396 */
1103 #define URI_HTTP "%5[HTPShtps]"
1104 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1105 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1106 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1107 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1108 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1109 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1110 #define HD4 URI_HTTP "://" URI_HOST
1111 #define HD5 URI_PATH
1113 void
1114 redir (char *pos, char *status_line)
1116   int i = 0;
1117   char *x;
1118   char xx[2];
1119   char type[6];
1120   char *addr;
1121   char *url;
1123   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1124   if (addr == NULL)
1125     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1127   url = malloc (strcspn (pos, "\r\n"));
1128   if (url == NULL)
1129     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1131   while (pos) {
1132     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1133     if (i == 0) {
1134       pos += (size_t) strcspn (pos, "\r\n");
1135       pos += (size_t) strspn (pos, "\r\n");
1136       if (strlen(pos) == 0)
1137         die (STATE_UNKNOWN,
1138              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1139              status_line, (display_html ? "</A>" : ""));
1140       continue;
1141     }
1143     pos += i;
1144     pos += strspn (pos, " \t");
1146     /*
1147      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1148      * preceding each extra line with at least one SP or HT.''
1149      */
1150     for (; (i = strspn (pos, "\r\n")); pos += i) {
1151       pos += i;
1152       if (!(i = strspn (pos, " \t"))) {
1153         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1154              display_html ? "</A>" : "");
1155       }
1156     }
1158     url = realloc (url, strcspn (pos, "\r\n") + 1);
1159     if (url == NULL)
1160       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1162     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1163     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1164       url = prepend_slash (url);
1165       use_ssl = server_type_check (type);
1166     }
1168     /* URI_HTTP URI_HOST URI_PATH */
1169     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1170       url = prepend_slash (url);
1171       use_ssl = server_type_check (type);
1172       i = server_port_check (use_ssl);
1173     }
1175     /* URI_HTTP URI_HOST URI_PORT */
1176     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1177       strcpy (url, HTTP_URL);
1178       use_ssl = server_type_check (type);
1179     }
1181     /* URI_HTTP URI_HOST */
1182     else if (sscanf (pos, HD4, type, addr) == 2) {
1183       strcpy (url, HTTP_URL);
1184       use_ssl = server_type_check (type);
1185       i = server_port_check (use_ssl);
1186     }
1188     /* URI_PATH */
1189     else if (sscanf (pos, HD5, url) == 1) {
1190       /* relative url */
1191       if ((url[0] != '/')) {
1192         if ((x = strrchr(server_url, '/')))
1193           *x = '\0';
1194         asprintf (&url, "%s/%s", server_url, url);
1195       }
1196       i = server_port;
1197       strcpy (type, server_type);
1198       strcpy (addr, host_name ? host_name : server_address);
1199     }
1201     else {
1202       die (STATE_UNKNOWN,
1203            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1204            pos, (display_html ? "</A>" : ""));
1205     }
1207     break;
1209   } /* end while (pos) */
1211   if (++redir_depth > max_depth)
1212     die (STATE_WARNING,
1213          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1214          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1216   if (server_port==i &&
1217       !strcmp(server_address, addr) &&
1218       (host_name && !strcmp(host_name, addr)) &&
1219       !strcmp(server_url, url))
1220     die (STATE_WARNING,
1221          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1222          type, addr, i, url, (display_html ? "</A>" : ""));
1224   strcpy (server_type, type);
1226   free (host_name);
1227   host_name = strdup (addr);
1229   if (!(followsticky & STICKY_HOST)) {
1230     free (server_address);
1231     server_address = strdup (addr);
1232   }
1233   if (!(followsticky & STICKY_PORT)) {
1234     server_port = i;
1235   }
1237   free (server_url);
1238   server_url = url;
1240   if (server_port > MAX_PORT)
1241     die (STATE_UNKNOWN,
1242          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1243          MAX_PORT, server_type, server_address, server_port, server_url,
1244          display_html ? "</A>" : "");
1246   if (verbose)
1247     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1248             host_name ? host_name : server_address, server_port, server_url);
1250   check_http ();
1254 int
1255 server_type_check (const char *type)
1257   if (strcmp (type, "https"))
1258     return FALSE;
1259   else
1260     return TRUE;
1263 int
1264 server_port_check (int ssl_flag)
1266   if (ssl_flag)
1267     return HTTPS_PORT;
1268   else
1269     return HTTP_PORT;
1272 char *perfd_time (double elapsed_time)
1274   return fperfdata ("time", elapsed_time, "s",
1275             thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1276             thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1277                    TRUE, 0, FALSE, 0);
1282 char *perfd_size (int page_len)
1284   return perfdata ("size", page_len, "B",
1285             (min_page_len>0?TRUE:FALSE), min_page_len,
1286             (min_page_len>0?TRUE:FALSE), 0,
1287             TRUE, 0, FALSE, 0);
1290 void
1291 print_help (void)
1293   print_revision (progname, NP_VERSION);
1295   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1296   printf (COPYRIGHT, copyright, email);
1298   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1299   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1300   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1301   printf ("%s\n", _("certificate expiration times."));
1303   printf ("\n\n");
1305   print_usage ();
1307   printf (_("NOTE: One or both of -H and -I must be specified"));
1309   printf ("\n");
1311   printf (UT_HELP_VRSN);
1312   printf (UT_EXTRA_OPTS);
1314   printf (" %s\n", "-H, --hostname=ADDRESS");
1315   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1316   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1317   printf (" %s\n", "-I, --IP-address=ADDRESS");
1318   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1319   printf (" %s\n", "-p, --port=INTEGER");
1320   printf ("    %s", _("Port number (default: "));
1321   printf ("%d)\n", HTTP_PORT);
1323   printf (UT_IPv46);
1325 #ifdef HAVE_SSL
1326   printf (" %s\n", "-S, --ssl");
1327   printf ("    %s\n", _("Connect via SSL. Port defaults to 443"));
1328   printf (" %s\n", "--sni");
1329   printf ("    %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1330   printf (" %s\n", "-C, --certificate=INTEGER");
1331   printf ("    %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1332   printf ("    %s\n", _("(when this option is used the URL is not checked.)\n"));
1333 #endif
1335   printf (" %s\n", "-e, --expect=STRING");
1336   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1337   printf ("    %s", _("the first (status) line of the server response (default: "));
1338   printf ("%s)\n", HTTP_EXPECT);
1339   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1340   printf (" %s\n", "-s, --string=STRING");
1341   printf ("    %s\n", _("String to expect in the content"));
1342   printf (" %s\n", "-u, --url=PATH");
1343   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1344   printf (" %s\n", "-P, --post=STRING");
1345   printf ("    %s\n", _("URL encoded http POST data"));
1346   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1347   printf ("    %s\n", _("Set HTTP method."));
1348   printf (" %s\n", "-N, --no-body");
1349   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1350   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1351   printf (" %s\n", "-M, --max-age=SECONDS");
1352   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1353   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1354   printf (" %s\n", "-T, --content-type=STRING");
1355   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1357   printf (" %s\n", "-l, --linespan");
1358   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1359   printf (" %s\n", "-r, --regex, --ereg=STRING");
1360   printf ("    %s\n", _("Search page for regex STRING"));
1361   printf (" %s\n", "-R, --eregi=STRING");
1362   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1363   printf (" %s\n", "--invert-regex");
1364   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1366   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1367   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1368   printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1369   printf ("    %s\n", _("Username:password on proxy-servers with basic authentication"));
1370   printf (" %s\n", "-A, --useragent=STRING");
1371   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1372   printf (" %s\n", "-k, --header=STRING");
1373   printf ("    %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1374   printf (" %s\n", "-L, --link");
1375   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1376   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1377   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1378   printf ("    %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1379   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1380   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1382   printf (UT_WARN_CRIT);
1384   printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1386   printf (UT_VERBOSE);
1388   printf ("\n");
1389   printf ("%s\n", _("Notes:"));
1390   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1391   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1392   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1393   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1394   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1395   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1397 #ifdef HAVE_SSL
1398   printf ("\n");
1399   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1400   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1401   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1402   printf ("\n");
1403   printf ("%s\n", _("Examples:"));
1404   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1405   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1406   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1407   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1408   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1410   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1411   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1412   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1413   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1414   printf (" %s\n", _("the certificate is expired."));
1415 #endif
1417   printf (UT_SUPPORT);
1423 void
1424 print_usage (void)
1426   printf ("%s\n", _("Usage:"));
1427   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1428   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
1429   printf ("       [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1430   printf ("       [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1431   printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1432   printf ("       [-A string] [-k string] [-S] [--sni] [-C <age>] [-T <content-type>]\n");
1433   printf ("       [-j method]\n");