Code

b4e60041f0fc1d27cb3b1f06007720590c1f05a3
[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 double warning_time = 0;
104 int check_warning_time = FALSE;
105 double critical_time = 0;
106 int check_critical_time = FALSE;
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 verbose = FALSE;
116 int sd;
117 int min_page_len = 0;
118 int max_page_len = 0;
119 int redir_depth = 0;
120 int max_depth = 15;
121 char *http_method;
122 char *http_post_data;
123 char *http_content_type;
124 char buffer[MAX_INPUT_BUFFER];
126 int process_arguments (int, char **);
127 int check_http (void);
128 void redir (char *pos, char *status_line);
129 int server_type_check(const char *type);
130 int server_port_check(int ssl_flag);
131 char *perfd_time (double microsec);
132 char *perfd_size (int page_len);
133 void print_help (void);
134 void print_usage (void);
136 int
137 main (int argc, char **argv)
139   int result = STATE_UNKNOWN;
141   setlocale (LC_ALL, "");
142   bindtextdomain (PACKAGE, LOCALEDIR);
143   textdomain (PACKAGE);
145   /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
146   server_url = strdup(HTTP_URL);
147   server_url_length = strlen(server_url);
148   asprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
149             NP_VERSION, VERSION);
151   /* Parse extra opts if any */
152   argv=np_extra_opts (&argc, argv, progname);
154   if (process_arguments (argc, argv) == ERROR)
155     usage4 (_("Could not parse arguments"));
157   if (display_html == TRUE)
158     printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
159       use_ssl ? "https" : "http", host_name ? host_name : server_address,
160       server_port, server_url);
162   /* initialize alarm signal handling, set socket timeout, start timer */
163   (void) signal (SIGALRM, socket_timeout_alarm_handler);
164   (void) alarm (socket_timeout);
165   gettimeofday (&tv, NULL);
167   result = check_http ();
168   return result;
173 /* process command-line arguments */
174 int
175 process_arguments (int argc, char **argv)
177   int c = 1;
178   char *p;
180   enum {
181     INVERT_REGEX = CHAR_MAX + 1
182   };
184   int option = 0;
185   static struct option longopts[] = {
186     STD_LONG_OPTS,
187     {"link", no_argument, 0, 'L'},
188     {"nohtml", no_argument, 0, 'n'},
189     {"ssl", no_argument, 0, 'S'},
190     {"post", required_argument, 0, 'P'},
191     {"method", required_argument, 0, 'j'},
192     {"IP-address", required_argument, 0, 'I'},
193     {"url", required_argument, 0, 'u'},
194     {"port", required_argument, 0, 'p'},
195     {"authorization", required_argument, 0, 'a'},
196     {"proxy_authorization", required_argument, 0, 'b'},
197     {"string", required_argument, 0, 's'},
198     {"expect", required_argument, 0, 'e'},
199     {"regex", required_argument, 0, 'r'},
200     {"ereg", required_argument, 0, 'r'},
201     {"eregi", required_argument, 0, 'R'},
202     {"linespan", no_argument, 0, 'l'},
203     {"onredirect", required_argument, 0, 'f'},
204     {"certificate", required_argument, 0, 'C'},
205     {"useragent", required_argument, 0, 'A'},
206     {"header", required_argument, 0, 'k'},
207     {"no-body", no_argument, 0, 'N'},
208     {"max-age", required_argument, 0, 'M'},
209     {"content-type", required_argument, 0, 'T'},
210     {"pagesize", required_argument, 0, 'm'},
211     {"invert-regex", no_argument, NULL, INVERT_REGEX},
212     {"use-ipv4", no_argument, 0, '4'},
213     {"use-ipv6", no_argument, 0, '6'},
214     {0, 0, 0, 0}
215   };
217   if (argc < 2)
218     return ERROR;
220   for (c = 1; c < argc; c++) {
221     if (strcmp ("-to", argv[c]) == 0)
222       strcpy (argv[c], "-t");
223     if (strcmp ("-hn", argv[c]) == 0)
224       strcpy (argv[c], "-H");
225     if (strcmp ("-wt", argv[c]) == 0)
226       strcpy (argv[c], "-w");
227     if (strcmp ("-ct", argv[c]) == 0)
228       strcpy (argv[c], "-c");
229     if (strcmp ("-nohtml", argv[c]) == 0)
230       strcpy (argv[c], "-n");
231   }
233   while (1) {
234     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);
235     if (c == -1 || c == EOF)
236       break;
238     switch (c) {
239     case '?': /* usage */
240       usage5 ();
241       break;
242     case 'h': /* help */
243       print_help ();
244       exit (STATE_OK);
245       break;
246     case 'V': /* version */
247       print_revision (progname, NP_VERSION);
248       exit (STATE_OK);
249       break;
250     case 't': /* timeout period */
251       if (!is_intnonneg (optarg))
252         usage2 (_("Timeout interval must be a positive integer"), optarg);
253       else
254         socket_timeout = atoi (optarg);
255       break;
256     case 'c': /* critical time threshold */
257       if (!is_nonnegative (optarg))
258         usage2 (_("Critical threshold must be integer"), optarg);
259       else {
260         critical_time = strtod (optarg, NULL);
261         check_critical_time = TRUE;
262       }
263       break;
264     case 'w': /* warning time threshold */
265       if (!is_nonnegative (optarg))
266         usage2 (_("Warning threshold must be integer"), optarg);
267       else {
268         warning_time = strtod (optarg, NULL);
269         check_warning_time = TRUE;
270       }
271       break;
272     case 'A': /* User Agent String */
273       asprintf (&user_agent, "User-Agent: %s", optarg);
274       break;
275     case 'k': /* Additional headers */
276       if (http_opt_headers_count == 0)
277         http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
278       else
279         http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
280       http_opt_headers[http_opt_headers_count - 1] = optarg;
281       /* asprintf (&http_opt_headers, "%s", optarg); */
282       break;
283     case 'L': /* show html link */
284       display_html = TRUE;
285       break;
286     case 'n': /* do not show html link */
287       display_html = FALSE;
288       break;
289     case 'C': /* Check SSL cert validity */
290 #ifdef HAVE_SSL
291       if (!is_intnonneg (optarg))
292         usage2 (_("Invalid certificate expiration period"), optarg);
293       else {
294         days_till_exp = atoi (optarg);
295         check_cert = TRUE;
296       }
297      /* Fall through to -S option */
298 #endif
299     case 'S': /* use SSL */
300 #ifndef HAVE_SSL
301       usage4 (_("Invalid option - SSL is not available"));
302 #endif
303       use_ssl = TRUE;
304       if (specify_port == FALSE)
305         server_port = HTTPS_PORT;
306       break;
307     case 'f': /* onredirect */
308       if (!strcmp (optarg, "stickyport"))
309         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
310       else if (!strcmp (optarg, "sticky"))
311         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
312       else if (!strcmp (optarg, "follow"))
313         onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
314       else if (!strcmp (optarg, "unknown"))
315         onredirect = STATE_UNKNOWN;
316       else if (!strcmp (optarg, "ok"))
317         onredirect = STATE_OK;
318       else if (!strcmp (optarg, "warning"))
319         onredirect = STATE_WARNING;
320       else if (!strcmp (optarg, "critical"))
321         onredirect = STATE_CRITICAL;
322       else usage2 (_("Invalid onredirect option"), optarg);
323       if (verbose)
324         printf(_("option f:%d \n"), onredirect);
325       break;
326     /* Note: H, I, and u must be malloc'd or will fail on redirects */
327     case 'H': /* Host Name (virtual host) */
328       host_name = strdup (optarg);
329       if (host_name[0] == '[') {
330         if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
331           server_port = atoi (p + 2);
332       } else if ((p = strchr (host_name, ':')) != NULL
333                  && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
334           server_port = atoi (p);
335       break;
336     case 'I': /* Server IP-address */
337       server_address = strdup (optarg);
338       break;
339     case 'u': /* URL path */
340       server_url = strdup (optarg);
341       server_url_length = strlen (server_url);
342       break;
343     case 'p': /* Server port */
344       if (!is_intnonneg (optarg))
345         usage2 (_("Invalid port number"), optarg);
346       else {
347         server_port = atoi (optarg);
348         specify_port = TRUE;
349       }
350       break;
351     case 'a': /* authorization info */
352       strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
353       user_auth[MAX_INPUT_BUFFER - 1] = 0;
354       break;
355     case 'b': /* proxy-authorization info */
356       strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
357       proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
358       break;
359     case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
360       if (! http_post_data)
361         http_post_data = strdup (optarg);
362       if (! http_method)
363         http_method = strdup("POST");
364       break;
365     case 'j': /* Set HTTP method */
366       if (http_method)
367         free(http_method);
368       http_method = strdup (optarg);
369       break;
370     case 's': /* string or substring */
371       strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
372       string_expect[MAX_INPUT_BUFFER - 1] = 0;
373       break;
374     case 'e': /* string or substring */
375       strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
376       server_expect[MAX_INPUT_BUFFER - 1] = 0;
377       server_expect_yn = 1;
378       break;
379     case 'T': /* Content-type */
380       asprintf (&http_content_type, "%s", optarg);
381       break;
382     case 'l': /* linespan */
383       cflags &= ~REG_NEWLINE;
384       break;
385     case 'R': /* regex */
386       cflags |= REG_ICASE;
387     case 'r': /* regex */
388       strncpy (regexp, optarg, MAX_RE_SIZE - 1);
389       regexp[MAX_RE_SIZE - 1] = 0;
390       errcode = regcomp (&preg, regexp, cflags);
391       if (errcode != 0) {
392         (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
393         printf (_("Could Not Compile Regular Expression: %s"), errbuf);
394         return ERROR;
395       }
396       break;
397     case INVERT_REGEX:
398       invert_regex = 1;
399       break;
400     case '4':
401       address_family = AF_INET;
402       break;
403     case '6':
404 #ifdef USE_IPV6
405       address_family = AF_INET6;
406 #else
407       usage4 (_("IPv6 support not available"));
408 #endif
409       break;
410     case 'v': /* verbose */
411       verbose = TRUE;
412       break;
413     case 'm': /* min_page_length */
414       {
415       char *tmp;
416       if (strchr(optarg, ':') != (char *)NULL) {
417         /* range, so get two values, min:max */
418         tmp = strtok(optarg, ":");
419         if (tmp == NULL) {
420           printf("Bad format: try \"-m min:max\"\n");
421           exit (STATE_WARNING);
422         } else
423           min_page_len = atoi(tmp);
425         tmp = strtok(NULL, ":");
426         if (tmp == NULL) {
427           printf("Bad format: try \"-m min:max\"\n");
428           exit (STATE_WARNING);
429         } else
430           max_page_len = atoi(tmp);
431       } else
432         min_page_len = atoi (optarg);
433       break;
434       }
435     case 'N': /* no-body */
436       no_body = TRUE;
437       break;
438     case 'M': /* max-age */
439                   {
440                     int L = strlen(optarg);
441                     if (L && optarg[L-1] == 'm')
442                       maximum_age = atoi (optarg) * 60;
443                     else if (L && optarg[L-1] == 'h')
444                       maximum_age = atoi (optarg) * 60 * 60;
445                     else if (L && optarg[L-1] == 'd')
446                       maximum_age = atoi (optarg) * 60 * 60 * 24;
447                     else if (L && (optarg[L-1] == 's' ||
448                                    isdigit (optarg[L-1])))
449                       maximum_age = atoi (optarg);
450                     else {
451                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
452                       exit (STATE_WARNING);
453                     }
454                   }
455                   break;
456     }
457   }
459   c = optind;
461   if (server_address == NULL && c < argc)
462     server_address = strdup (argv[c++]);
464   if (host_name == NULL && c < argc)
465     host_name = strdup (argv[c++]);
467   if (server_address == NULL) {
468     if (host_name == NULL)
469       usage4 (_("You must specify a server address or host name"));
470     else
471       server_address = strdup (host_name);
472   }
474   if (check_critical_time && critical_time>(double)socket_timeout)
475     socket_timeout = (int)critical_time + 1;
477   if (http_method == NULL)
478     http_method = strdup ("GET");
480   return TRUE;
485 /* Returns 1 if we're done processing the document body; 0 to keep going */
486 static int
487 document_headers_done (char *full_page)
489   const char *body;
491   for (body = full_page; *body; body++) {
492     if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
493       break;
494   }
496   if (!*body)
497     return 0;  /* haven't read end of headers yet */
499   full_page[body - full_page] = 0;
500   return 1;
503 static time_t
504 parse_time_string (const char *string)
506   struct tm tm;
507   time_t t;
508   memset (&tm, 0, sizeof(tm));
510   /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
512   if (isupper (string[0])  &&  /* Tue */
513     islower (string[1])  &&
514     islower (string[2])  &&
515     ',' ==   string[3]   &&
516     ' ' ==   string[4]   &&
517     (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
518     isdigit (string[6])  &&
519     ' ' ==   string[7]   &&
520     isupper (string[8])  &&  /* Dec */
521     islower (string[9])  &&
522     islower (string[10]) &&
523     ' ' ==   string[11]  &&
524     isdigit (string[12]) &&  /* 2001 */
525     isdigit (string[13]) &&
526     isdigit (string[14]) &&
527     isdigit (string[15]) &&
528     ' ' ==   string[16]  &&
529     isdigit (string[17]) &&  /* 02: */
530     isdigit (string[18]) &&
531     ':' ==   string[19]  &&
532     isdigit (string[20]) &&  /* 59: */
533     isdigit (string[21]) &&
534     ':' ==   string[22]  &&
535     isdigit (string[23]) &&  /* 03 */
536     isdigit (string[24]) &&
537     ' ' ==   string[25]  &&
538     'G' ==   string[26]  &&  /* GMT */
539     'M' ==   string[27]  &&  /* GMT */
540     'T' ==   string[28]) {
542     tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
543     tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
544     tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
545     tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
546     tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
547       !strncmp (string+8, "Feb", 3) ? 1 :
548       !strncmp (string+8, "Mar", 3) ? 2 :
549       !strncmp (string+8, "Apr", 3) ? 3 :
550       !strncmp (string+8, "May", 3) ? 4 :
551       !strncmp (string+8, "Jun", 3) ? 5 :
552       !strncmp (string+8, "Jul", 3) ? 6 :
553       !strncmp (string+8, "Aug", 3) ? 7 :
554       !strncmp (string+8, "Sep", 3) ? 8 :
555       !strncmp (string+8, "Oct", 3) ? 9 :
556       !strncmp (string+8, "Nov", 3) ? 10 :
557       !strncmp (string+8, "Dec", 3) ? 11 :
558       -1);
559     tm.tm_year = ((1000 * (string[12]-'0') +
560       100 * (string[13]-'0') +
561       10 * (string[14]-'0') +
562       (string[15]-'0'))
563       - 1900);
565     tm.tm_isdst = 0;  /* GMT is never in DST, right? */
567     if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
568       return 0;
570     /*
571     This is actually wrong: we need to subtract the local timezone
572     offset from GMT from this value.  But, that's ok in this usage,
573     because we only comparing these two GMT dates against each other,
574     so it doesn't matter what time zone we parse them in.
575     */
577     t = mktime (&tm);
578     if (t == (time_t) -1) t = 0;
580     if (verbose) {
581       const char *s = string;
582       while (*s && *s != '\r' && *s != '\n')
583       fputc (*s++, stdout);
584       printf (" ==> %lu\n", (unsigned long) t);
585     }
587     return t;
589   } else {
590     return 0;
591   }
594 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
595 static int
596 expected_statuscode (const char *reply, const char *statuscodes)
598   char *expected, *code;
599   int result = 0;
601   if ((expected = strdup (statuscodes)) == NULL)
602     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
604   for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
605     if (strstr (reply, code) != NULL) {
606       result = 1;
607       break;
608     }
610   free (expected);
611   return result;
614 static int
615 check_document_dates (const char *headers, char **msg)
617   const char *s;
618   char *server_date = 0;
619   char *document_date = 0;
620   int date_result = STATE_OK;
622   s = headers;
623   while (*s) {
624     const char *field = s;
625     const char *value = 0;
627     /* Find the end of the header field */
628     while (*s && !isspace(*s) && *s != ':')
629       s++;
631     /* Remember the header value, if any. */
632     if (*s == ':')
633       value = ++s;
635     /* Skip to the end of the header, including continuation lines. */
636     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
637       s++;
639     /* Avoid stepping over end-of-string marker */
640     if (*s)
641       s++;
643     /* Process this header. */
644     if (value && value > field+2) {
645       char *ff = (char *) malloc (value-field);
646       char *ss = ff;
647       while (field < value-1)
648         *ss++ = tolower(*field++);
649       *ss++ = 0;
651       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
652         const char *e;
653         while (*value && isspace (*value))
654           value++;
655         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
656           ;
657         ss = (char *) malloc (e - value + 1);
658         strncpy (ss, value, e - value);
659         ss[e - value] = 0;
660         if (!strcmp (ff, "date")) {
661           if (server_date) free (server_date);
662           server_date = ss;
663         } else {
664           if (document_date) free (document_date);
665           document_date = ss;
666         }
667       }
668       free (ff);
669     }
670   }
672   /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
673   if (!server_date || !*server_date) {
674     asprintf (msg, _("%sServer date unknown, "), *msg);
675     date_result = max_state_alt(STATE_UNKNOWN, date_result);
676   } else if (!document_date || !*document_date) {
677     asprintf (msg, _("%sDocument modification date unknown, "), *msg);
678     date_result = max_state_alt(STATE_CRITICAL, date_result);
679   } else {
680     time_t srv_data = parse_time_string (server_date);
681     time_t doc_data = parse_time_string (document_date);
683     if (srv_data <= 0) {
684       asprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
685       date_result = max_state_alt(STATE_CRITICAL, date_result);
686     } else if (doc_data <= 0) {
687       asprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
688       date_result = max_state_alt(STATE_CRITICAL, date_result);
689     } else if (doc_data > srv_data + 30) {
690       asprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
691       date_result = max_state_alt(STATE_CRITICAL, date_result);
692     } else if (doc_data < srv_data - maximum_age) {
693       int n = (srv_data - doc_data);
694       if (n > (60 * 60 * 24 * 2)) {
695         asprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
696         date_result = max_state_alt(STATE_CRITICAL, date_result);
697       } else {
698         asprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
699         date_result = max_state_alt(STATE_CRITICAL, date_result);
700       }
701     }
702     free (server_date);
703     free (document_date);
704   }
705   return date_result;
708 int
709 get_content_length (const char *headers)
711   const char *s;
712   int content_length = 0;
714   s = headers;
715   while (*s) {
716     const char *field = s;
717     const char *value = 0;
719     /* Find the end of the header field */
720     while (*s && !isspace(*s) && *s != ':')
721       s++;
723     /* Remember the header value, if any. */
724     if (*s == ':')
725       value = ++s;
727     /* Skip to the end of the header, including continuation lines. */
728     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
729       s++;
730     s++;
732     /* Process this header. */
733     if (value && value > field+2) {
734       char *ff = (char *) malloc (value-field);
735       char *ss = ff;
736       while (field < value-1)
737         *ss++ = tolower(*field++);
738       *ss++ = 0;
740       if (!strcmp (ff, "content-length")) {
741         const char *e;
742         while (*value && isspace (*value))
743           value++;
744         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
745           ;
746         ss = (char *) malloc (e - value + 1);
747         strncpy (ss, value, e - value);
748         ss[e - value] = 0;
749         content_length = atoi(ss);
750         free (ss);
751       }
752       free (ff);
753     }
754   }
755   return (content_length);
758 char *
759 prepend_slash (char *path)
761   char *newpath;
763   if (path[0] == '/')
764     return path;
766   if ((newpath = malloc (strlen(path) + 2)) == NULL)
767     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
768   newpath[0] = '/';
769   strcpy (newpath + 1, path);
770   free (path);
771   return newpath;
774 int
775 check_http (void)
777   char *msg;
778   char *status_line;
779   char *status_code;
780   char *header;
781   char *page;
782   char *auth;
783   int http_status;
784   int i = 0;
785   size_t pagesize = 0;
786   char *full_page;
787   char *buf;
788   char *pos;
789   long microsec;
790   double elapsed_time;
791   int page_len = 0;
792   int result = STATE_OK;
794   /* try to connect to the host at the given port number */
795   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
796     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
797 #ifdef HAVE_SSL
798   if (use_ssl == TRUE) {
799     np_net_ssl_init_with_hostname(sd, host_name);
800     if (check_cert == TRUE) {
801       result = np_net_ssl_check_cert(days_till_exp);
802       np_net_ssl_cleanup();
803       if (sd) close(sd);
804       return result;
805     }
806   }
807 #endif /* HAVE_SSL */
809   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
811   /* tell HTTP/1.1 servers not to keep the connection alive */
812   asprintf (&buf, "%sConnection: close\r\n", buf);
814   /* optionally send the host header info */
815   if (host_name) {
816     /*
817      * Specify the port only if we're using a non-default port (see RFC 2616,
818      * 14.23).  Some server applications/configurations cause trouble if the
819      * (default) port is explicitly specified in the "Host:" header line.
820      */
821     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
822         (use_ssl == TRUE && server_port == HTTPS_PORT))
823       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
824     else
825       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
826   }
828   /* optionally send any other header tag */
829   if (http_opt_headers_count) {
830     for (i = 0; i < http_opt_headers_count ; i++) {
831       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
832         asprintf (&buf, "%s%s\r\n", buf, pos);
833     }
834     /* This cannot be free'd here because a redirection will then try to access this and segfault */
835     /* Covered in a testcase in tests/check_http.t */
836     /* free(http_opt_headers); */
837   }
839   /* optionally send the authentication info */
840   if (strlen(user_auth)) {
841     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
842     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
843   }
845   /* optionally send the proxy authentication info */
846   if (strlen(proxy_auth)) {
847     base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
848     asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
849   }
851   /* either send http POST data (any data, not only POST)*/
852   if (http_post_data) {
853     if (http_content_type) {
854       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
855     } else {
856       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
857     }
859     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
860     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
861   }
862   else {
863     /* or just a newline so the server knows we're done with the request */
864     asprintf (&buf, "%s%s", buf, CRLF);
865   }
867   if (verbose) printf ("%s\n", buf);
868   my_send (buf, strlen (buf));
870   /* fetch the page */
871   full_page = strdup("");
872   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
873     buffer[i] = '\0';
874     asprintf (&full_page, "%s%s", full_page, buffer);
875     pagesize += i;
877                 if (no_body && document_headers_done (full_page)) {
878                   i = 0;
879                   break;
880                 }
881   }
883   if (i < 0 && errno != ECONNRESET) {
884 #ifdef HAVE_SSL
885     /*
886     if (use_ssl) {
887       sslerr=SSL_get_error(ssl, i);
888       if ( sslerr == SSL_ERROR_SSL ) {
889         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
890       } else {
891         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
892       }
893     }
894     else {
895     */
896 #endif
897       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
898 #ifdef HAVE_SSL
899       /* XXX
900     }
901     */
902 #endif
903   }
905   /* return a CRITICAL status if we couldn't read any data */
906   if (pagesize == (size_t) 0)
907     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
909   /* close the connection */
910 #ifdef HAVE_SSL
911   np_net_ssl_cleanup();
912 #endif
913   if (sd) close(sd);
915   /* Save check time */
916   microsec = deltime (tv);
917   elapsed_time = (double)microsec / 1.0e6;
919   /* leave full_page untouched so we can free it later */
920   page = full_page;
922   if (verbose)
923     printf ("%s://%s:%d%s is %d characters\n",
924       use_ssl ? "https" : "http", server_address,
925       server_port, server_url, (int)pagesize);
927   /* find status line and null-terminate it */
928   status_line = page;
929   page += (size_t) strcspn (page, "\r\n");
930   pos = page;
931   page += (size_t) strspn (page, "\r\n");
932   status_line[strcspn(status_line, "\r\n")] = 0;
933   strip (status_line);
934   if (verbose)
935     printf ("STATUS: %s\n", status_line);
937   /* find header info and null-terminate it */
938   header = page;
939   while (strcspn (page, "\r\n") > 0) {
940     page += (size_t) strcspn (page, "\r\n");
941     pos = page;
942     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
943         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
944       page += (size_t) 2;
945     else
946       page += (size_t) 1;
947   }
948   page += (size_t) strspn (page, "\r\n");
949   header[pos - header] = 0;
950   if (verbose)
951     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
952                 (no_body ? "  [[ skipped ]]" : page));
954   /* make sure the status line matches the response we are looking for */
955   if (!expected_statuscode (status_line, server_expect)) {
956     if (server_port == HTTP_PORT)
957       asprintf (&msg,
958                 _("Invalid HTTP response received from host: %s\n"),
959                 status_line);
960     else
961       asprintf (&msg,
962                 _("Invalid HTTP response received from host on port %d: %s\n"),
963                 server_port, status_line);
964     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
965   }
967   /* Bypass normal status line check if server_expect was set by user and not default */
968   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
969   if ( server_expect_yn  )  {
970     asprintf (&msg,
971               _("Status line output matched \"%s\" - "), server_expect);
972     if (verbose)
973       printf ("%s\n",msg);
974   }
975   else {
976     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
977     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
978     /* Status-Code = 3 DIGITS */
980     status_code = strchr (status_line, ' ') + sizeof (char);
981     if (strspn (status_code, "1234567890") != 3)
982       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
984     http_status = atoi (status_code);
986     /* check the return code */
988     if (http_status >= 600 || http_status < 100) {
989       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
990     }
991     /* server errors result in a critical state */
992     else if (http_status >= 500) {
993       asprintf (&msg, _("%s - "), status_line);
994       result = STATE_CRITICAL;
995     }
996     /* client errors result in a warning state */
997     else if (http_status >= 400) {
998       asprintf (&msg, _("%s - "), status_line);
999       result = max_state_alt(STATE_WARNING, result);
1000     }
1001     /* check redirected page if specified */
1002     else if (http_status >= 300) {
1004       if (onredirect == STATE_DEPENDENT)
1005         redir (header, status_line);
1006       else
1007         result = max_state_alt(onredirect, result);
1008       asprintf (&msg, _("%s - "), status_line);
1009     } /* end if (http_status >= 300) */
1010     else {
1011       /* Print OK status anyway */
1012       asprintf (&msg, _("%s - "), status_line);
1013     }
1015   } /* end else (server_expect_yn)  */
1017   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1018   alarm (0);
1020   if (maximum_age >= 0) {
1021     result = max_state_alt(check_document_dates(header, &msg), result);
1022   }
1024   /* Page and Header content checks go here */
1026   if (strlen (string_expect)) {
1027     if (!strstr (page, string_expect)) {
1028       asprintf (&msg, _("%sstring not found, "), msg);
1029       result = STATE_CRITICAL;
1030     }
1031   }
1033   if (strlen (regexp)) {
1034     errcode = regexec (&preg, page, REGS, pmatch, 0);
1035     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1036       /* OK - No-op to avoid changing the logic around it */
1037       result = max_state_alt(STATE_OK, result);
1038     }
1039     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1040       if (invert_regex == 0)
1041         asprintf (&msg, _("%spattern not found, "), msg);
1042       else
1043         asprintf (&msg, _("%spattern found, "), msg);
1044       result = STATE_CRITICAL;
1045     }
1046     else {
1047       /* FIXME: Shouldn't that be UNKNOWN? */
1048       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1049       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1050       result = STATE_CRITICAL;
1051     }
1052   }
1054   /* make sure the page is of an appropriate size */
1055   /* page_len = get_content_length(header); */
1056   /* FIXME: Will this work with -N ? IMHO we should use
1057    * get_content_length(header) and always check if it's different than the
1058    * returned pagesize
1059    */
1060   /* FIXME: IIRC pagesize returns headers - shouldn't we make
1061    * it == get_content_length(header) ??
1062    */
1063   page_len = pagesize;
1064   if ((max_page_len > 0) && (page_len > max_page_len)) {
1065     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1066     result = max_state_alt(STATE_WARNING, result);
1067   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1068     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1069     result = max_state_alt(STATE_WARNING, result);
1070   }
1072   /* Cut-off trailing characters */
1073   if(msg[strlen(msg)-2] == ',')
1074     msg[strlen(msg)-2] = '\0';
1075   else
1076     msg[strlen(msg)-3] = '\0';
1078   /* check elapsed time */
1079   asprintf (&msg,
1080             _("%s - %d bytes in %.3f second response time %s|%s %s"),
1081             msg, page_len, elapsed_time,
1082             (display_html ? "</A>" : ""),
1083             perfd_time (elapsed_time), perfd_size (page_len));
1085   if (check_critical_time == TRUE && elapsed_time > critical_time)
1086     result = STATE_CRITICAL;
1087   if (check_warning_time == TRUE && elapsed_time > warning_time)
1088     result =  max_state_alt(STATE_WARNING, result);
1090   die (result, "HTTP %s: %s\n", state_text(result), msg);
1091   /* die failed? */
1092   return STATE_UNKNOWN;
1097 /* per RFC 2396 */
1098 #define URI_HTTP "%5[HTPShtps]"
1099 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1100 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1101 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1102 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1103 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1104 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1105 #define HD4 URI_HTTP "://" URI_HOST
1106 #define HD5 URI_PATH
1108 void
1109 redir (char *pos, char *status_line)
1111   int i = 0;
1112   char *x;
1113   char xx[2];
1114   char type[6];
1115   char *addr;
1116   char *url;
1118   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1119   if (addr == NULL)
1120     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1122   url = malloc (strcspn (pos, "\r\n"));
1123   if (url == NULL)
1124     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1126   while (pos) {
1127     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1128     if (i == 0) {
1129       pos += (size_t) strcspn (pos, "\r\n");
1130       pos += (size_t) strspn (pos, "\r\n");
1131       if (strlen(pos) == 0)
1132         die (STATE_UNKNOWN,
1133              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1134              status_line, (display_html ? "</A>" : ""));
1135       continue;
1136     }
1138     pos += i;
1139     pos += strspn (pos, " \t");
1141     /*
1142      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1143      * preceding each extra line with at least one SP or HT.''
1144      */
1145     for (; (i = strspn (pos, "\r\n")); pos += i) {
1146       pos += i;
1147       if (!(i = strspn (pos, " \t"))) {
1148         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1149              display_html ? "</A>" : "");
1150       }
1151     }
1153     url = realloc (url, strcspn (pos, "\r\n") + 1);
1154     if (url == NULL)
1155       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1157     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1158     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1159       url = prepend_slash (url);
1160       use_ssl = server_type_check (type);
1161     }
1163     /* URI_HTTP URI_HOST URI_PATH */
1164     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1165       url = prepend_slash (url);
1166       use_ssl = server_type_check (type);
1167       i = server_port_check (use_ssl);
1168     }
1170     /* URI_HTTP URI_HOST URI_PORT */
1171     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1172       strcpy (url, HTTP_URL);
1173       use_ssl = server_type_check (type);
1174     }
1176     /* URI_HTTP URI_HOST */
1177     else if (sscanf (pos, HD4, type, addr) == 2) {
1178       strcpy (url, HTTP_URL);
1179       use_ssl = server_type_check (type);
1180       i = server_port_check (use_ssl);
1181     }
1183     /* URI_PATH */
1184     else if (sscanf (pos, HD5, url) == 1) {
1185       /* relative url */
1186       if ((url[0] != '/')) {
1187         if ((x = strrchr(server_url, '/')))
1188           *x = '\0';
1189         asprintf (&url, "%s/%s", server_url, url);
1190       }
1191       i = server_port;
1192       strcpy (type, server_type);
1193       strcpy (addr, host_name ? host_name : server_address);
1194     }
1196     else {
1197       die (STATE_UNKNOWN,
1198            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1199            pos, (display_html ? "</A>" : ""));
1200     }
1202     break;
1204   } /* end while (pos) */
1206   if (++redir_depth > max_depth)
1207     die (STATE_WARNING,
1208          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1209          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1211   if (server_port==i &&
1212       !strcmp(server_address, addr) &&
1213       (host_name && !strcmp(host_name, addr)) &&
1214       !strcmp(server_url, url))
1215     die (STATE_WARNING,
1216          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1217          type, addr, i, url, (display_html ? "</A>" : ""));
1219   strcpy (server_type, type);
1221   free (host_name);
1222   host_name = strdup (addr);
1224   if (!(followsticky & STICKY_HOST)) {
1225     free (server_address);
1226     server_address = strdup (addr);
1227   }
1228   if (!(followsticky & STICKY_PORT)) {
1229     server_port = i;
1230   }
1232   free (server_url);
1233   server_url = url;
1235   if (server_port > MAX_PORT)
1236     die (STATE_UNKNOWN,
1237          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1238          MAX_PORT, server_type, server_address, server_port, server_url,
1239          display_html ? "</A>" : "");
1241   if (verbose)
1242     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1243             host_name ? host_name : server_address, server_port, server_url);
1245   check_http ();
1249 int
1250 server_type_check (const char *type)
1252   if (strcmp (type, "https"))
1253     return FALSE;
1254   else
1255     return TRUE;
1258 int
1259 server_port_check (int ssl_flag)
1261   if (ssl_flag)
1262     return HTTPS_PORT;
1263   else
1264     return HTTP_PORT;
1267 char *perfd_time (double elapsed_time)
1269   return fperfdata ("time", elapsed_time, "s",
1270             check_warning_time, warning_time,
1271             check_critical_time, critical_time,
1272                    TRUE, 0, FALSE, 0);
1277 char *perfd_size (int page_len)
1279   return perfdata ("size", page_len, "B",
1280             (min_page_len>0?TRUE:FALSE), min_page_len,
1281             (min_page_len>0?TRUE:FALSE), 0,
1282             TRUE, 0, FALSE, 0);
1285 void
1286 print_help (void)
1288   print_revision (progname, NP_VERSION);
1290   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1291   printf (COPYRIGHT, copyright, email);
1293   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1294   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1295   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1296   printf ("%s\n", _("certificate expiration times."));
1298   printf ("\n\n");
1300   print_usage ();
1302   printf (_("NOTE: One or both of -H and -I must be specified"));
1304   printf ("\n");
1306   printf (_(UT_HELP_VRSN));
1307   printf (_(UT_EXTRA_OPTS));
1309   printf (" %s\n", "-H, --hostname=ADDRESS");
1310   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1311   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1312   printf (" %s\n", "-I, --IP-address=ADDRESS");
1313   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1314   printf (" %s\n", "-p, --port=INTEGER");
1315   printf ("    %s", _("Port number (default: "));
1316   printf ("%d)\n", HTTP_PORT);
1318   printf (_(UT_IPv46));
1320 #ifdef HAVE_SSL
1321   printf (" %s\n", "-S, --ssl");
1322   printf ("   %s\n", _("Connect via SSL. Port defaults to 443"));
1323   printf (" %s\n", "-C, --certificate=INTEGER");
1324   printf ("   %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1325   printf ("   %s\n", _("(when this option is used the URL is not checked.)\n"));
1326 #endif
1328   printf (" %s\n", "-e, --expect=STRING");
1329   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1330   printf ("    %s", _("the first (status) line of the server response (default: "));
1331   printf ("%s)\n", HTTP_EXPECT);
1332   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1333   printf (" %s\n", "-s, --string=STRING");
1334   printf ("    %s\n", _("String to expect in the content"));
1335   printf (" %s\n", "-u, --url=PATH");
1336   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1337   printf (" %s\n", "-P, --post=STRING");
1338   printf ("    %s\n", _("URL encoded http POST data"));
1339   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1340   printf ("    %s\n", _("Set HTTP method."));
1341   printf (" %s\n", "-N, --no-body");
1342   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1343   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1344   printf (" %s\n", "-M, --max-age=SECONDS");
1345   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1346   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1347   printf (" %s\n", "-T, --content-type=STRING");
1348   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1350   printf (" %s\n", "-l, --linespan");
1351   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1352   printf (" %s\n", "-r, --regex, --ereg=STRING");
1353   printf ("    %s\n", _("Search page for regex STRING"));
1354   printf (" %s\n", "-R, --eregi=STRING");
1355   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1356   printf (" %s\n", "--invert-regex");
1357   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1359   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1360   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1361   printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1362   printf ("     %s\n", _("Username:password on proxy-servers with basic authentication"));
1363   printf (" %s\n", "-A, --useragent=STRING");
1364   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1365   printf (" %s\n", "-k, --header=STRING");
1366   printf ("    %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1367   printf (" %s\n", "-L, --link");
1368   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1369   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1370   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1371   printf ("    %s\n", _("specified IP address. stickyport also ensure post stays the same."));
1372   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1373   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1375   printf (_(UT_WARN_CRIT));
1377   printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1379   printf (_(UT_VERBOSE));
1381   printf ("\n");
1382   printf ("%s\n", _("Notes:"));
1383   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1384   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1385   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1386   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1387   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1388   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1389   printf ("\n");
1390   printf (_(UT_EXTRA_OPTS_NOTES));
1392 #ifdef HAVE_SSL
1393   printf ("\n");
1394   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1395   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1396   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1397   printf ("\n");
1398   printf ("%s\n", _("Examples:"));
1399   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1400   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1401   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1402   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1403   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1405   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1406   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1407   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1408   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1409   printf (" %s\n", _("the certificate is expired."));
1410 #endif
1412   printf (_(UT_SUPPORT));
1418 void
1419 print_usage (void)
1421   printf (_("Usage:"));
1422   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1423   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1424   printf ("       [-a auth] [-b proxy_auth] [-f <ok | warn | critcal | follow | sticky | stickyport>]\n");
1425   printf ("       [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1426   printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1427   printf ("       [-A string] [-k string] [-S] [-C <age>] [-T <content-type>] [-j method]\n");