Code

Fix memory leak in check_http for large pages (Jimmy Bergman - #2957455)
[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 *full_page_new;
788   char *buf;
789   char *pos;
790   long microsec;
791   double elapsed_time;
792   int page_len = 0;
793   int result = STATE_OK;
795   /* try to connect to the host at the given port number */
796   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
797     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
798 #ifdef HAVE_SSL
799   if (use_ssl == TRUE) {
800     np_net_ssl_init_with_hostname(sd, host_name);
801     if (check_cert == TRUE) {
802       result = np_net_ssl_check_cert(days_till_exp);
803       np_net_ssl_cleanup();
804       if (sd) close(sd);
805       return result;
806     }
807   }
808 #endif /* HAVE_SSL */
810   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
812   /* tell HTTP/1.1 servers not to keep the connection alive */
813   asprintf (&buf, "%sConnection: close\r\n", buf);
815   /* optionally send the host header info */
816   if (host_name) {
817     /*
818      * Specify the port only if we're using a non-default port (see RFC 2616,
819      * 14.23).  Some server applications/configurations cause trouble if the
820      * (default) port is explicitly specified in the "Host:" header line.
821      */
822     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
823         (use_ssl == TRUE && server_port == HTTPS_PORT))
824       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
825     else
826       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
827   }
829   /* optionally send any other header tag */
830   if (http_opt_headers_count) {
831     for (i = 0; i < http_opt_headers_count ; i++) {
832       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
833         asprintf (&buf, "%s%s\r\n", buf, pos);
834     }
835     /* This cannot be free'd here because a redirection will then try to access this and segfault */
836     /* Covered in a testcase in tests/check_http.t */
837     /* free(http_opt_headers); */
838   }
840   /* optionally send the authentication info */
841   if (strlen(user_auth)) {
842     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
843     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
844   }
846   /* optionally send the proxy authentication info */
847   if (strlen(proxy_auth)) {
848     base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
849     asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
850   }
852   /* either send http POST data (any data, not only POST)*/
853   if (http_post_data) {
854     if (http_content_type) {
855       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
856     } else {
857       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
858     }
860     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
861     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
862   }
863   else {
864     /* or just a newline so the server knows we're done with the request */
865     asprintf (&buf, "%s%s", buf, CRLF);
866   }
868   if (verbose) printf ("%s\n", buf);
869   my_send (buf, strlen (buf));
871   /* fetch the page */
872   full_page = strdup("");
873   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
874     buffer[i] = '\0';
875     asprintf (&full_page_new, "%s%s", full_page, buffer);
876     free (full_page);
877     full_page = full_page_new;
878     pagesize += i;
880                 if (no_body && document_headers_done (full_page)) {
881                   i = 0;
882                   break;
883                 }
884   }
886   if (i < 0 && errno != ECONNRESET) {
887 #ifdef HAVE_SSL
888     /*
889     if (use_ssl) {
890       sslerr=SSL_get_error(ssl, i);
891       if ( sslerr == SSL_ERROR_SSL ) {
892         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
893       } else {
894         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
895       }
896     }
897     else {
898     */
899 #endif
900       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
901 #ifdef HAVE_SSL
902       /* XXX
903     }
904     */
905 #endif
906   }
908   /* return a CRITICAL status if we couldn't read any data */
909   if (pagesize == (size_t) 0)
910     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
912   /* close the connection */
913 #ifdef HAVE_SSL
914   np_net_ssl_cleanup();
915 #endif
916   if (sd) close(sd);
918   /* Save check time */
919   microsec = deltime (tv);
920   elapsed_time = (double)microsec / 1.0e6;
922   /* leave full_page untouched so we can free it later */
923   page = full_page;
925   if (verbose)
926     printf ("%s://%s:%d%s is %d characters\n",
927       use_ssl ? "https" : "http", server_address,
928       server_port, server_url, (int)pagesize);
930   /* find status line and null-terminate it */
931   status_line = page;
932   page += (size_t) strcspn (page, "\r\n");
933   pos = page;
934   page += (size_t) strspn (page, "\r\n");
935   status_line[strcspn(status_line, "\r\n")] = 0;
936   strip (status_line);
937   if (verbose)
938     printf ("STATUS: %s\n", status_line);
940   /* find header info and null-terminate it */
941   header = page;
942   while (strcspn (page, "\r\n") > 0) {
943     page += (size_t) strcspn (page, "\r\n");
944     pos = page;
945     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
946         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
947       page += (size_t) 2;
948     else
949       page += (size_t) 1;
950   }
951   page += (size_t) strspn (page, "\r\n");
952   header[pos - header] = 0;
953   if (verbose)
954     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
955                 (no_body ? "  [[ skipped ]]" : page));
957   /* make sure the status line matches the response we are looking for */
958   if (!expected_statuscode (status_line, server_expect)) {
959     if (server_port == HTTP_PORT)
960       asprintf (&msg,
961                 _("Invalid HTTP response received from host: %s\n"),
962                 status_line);
963     else
964       asprintf (&msg,
965                 _("Invalid HTTP response received from host on port %d: %s\n"),
966                 server_port, status_line);
967     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
968   }
970   /* Bypass normal status line check if server_expect was set by user and not default */
971   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
972   if ( server_expect_yn  )  {
973     asprintf (&msg,
974               _("Status line output matched \"%s\" - "), server_expect);
975     if (verbose)
976       printf ("%s\n",msg);
977   }
978   else {
979     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
980     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
981     /* Status-Code = 3 DIGITS */
983     status_code = strchr (status_line, ' ') + sizeof (char);
984     if (strspn (status_code, "1234567890") != 3)
985       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
987     http_status = atoi (status_code);
989     /* check the return code */
991     if (http_status >= 600 || http_status < 100) {
992       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
993     }
994     /* server errors result in a critical state */
995     else if (http_status >= 500) {
996       asprintf (&msg, _("%s - "), status_line);
997       result = STATE_CRITICAL;
998     }
999     /* client errors result in a warning state */
1000     else if (http_status >= 400) {
1001       asprintf (&msg, _("%s - "), status_line);
1002       result = max_state_alt(STATE_WARNING, result);
1003     }
1004     /* check redirected page if specified */
1005     else if (http_status >= 300) {
1007       if (onredirect == STATE_DEPENDENT)
1008         redir (header, status_line);
1009       else
1010         result = max_state_alt(onredirect, result);
1011       asprintf (&msg, _("%s - "), status_line);
1012     } /* end if (http_status >= 300) */
1013     else {
1014       /* Print OK status anyway */
1015       asprintf (&msg, _("%s - "), status_line);
1016     }
1018   } /* end else (server_expect_yn)  */
1020   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1021   alarm (0);
1023   if (maximum_age >= 0) {
1024     result = max_state_alt(check_document_dates(header, &msg), result);
1025   }
1027   /* Page and Header content checks go here */
1029   if (strlen (string_expect)) {
1030     if (!strstr (page, string_expect)) {
1031       asprintf (&msg, _("%sstring not found, "), msg);
1032       result = STATE_CRITICAL;
1033     }
1034   }
1036   if (strlen (regexp)) {
1037     errcode = regexec (&preg, page, REGS, pmatch, 0);
1038     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1039       /* OK - No-op to avoid changing the logic around it */
1040       result = max_state_alt(STATE_OK, result);
1041     }
1042     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1043       if (invert_regex == 0)
1044         asprintf (&msg, _("%spattern not found, "), msg);
1045       else
1046         asprintf (&msg, _("%spattern found, "), msg);
1047       result = STATE_CRITICAL;
1048     }
1049     else {
1050       /* FIXME: Shouldn't that be UNKNOWN? */
1051       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1052       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1053       result = STATE_CRITICAL;
1054     }
1055   }
1057   /* make sure the page is of an appropriate size */
1058   /* page_len = get_content_length(header); */
1059   /* FIXME: Will this work with -N ? IMHO we should use
1060    * get_content_length(header) and always check if it's different than the
1061    * returned pagesize
1062    */
1063   /* FIXME: IIRC pagesize returns headers - shouldn't we make
1064    * it == get_content_length(header) ??
1065    */
1066   page_len = pagesize;
1067   if ((max_page_len > 0) && (page_len > max_page_len)) {
1068     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1069     result = max_state_alt(STATE_WARNING, result);
1070   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1071     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1072     result = max_state_alt(STATE_WARNING, result);
1073   }
1075   /* Cut-off trailing characters */
1076   if(msg[strlen(msg)-2] == ',')
1077     msg[strlen(msg)-2] = '\0';
1078   else
1079     msg[strlen(msg)-3] = '\0';
1081   /* check elapsed time */
1082   asprintf (&msg,
1083             _("%s - %d bytes in %.3f second response time %s|%s %s"),
1084             msg, page_len, elapsed_time,
1085             (display_html ? "</A>" : ""),
1086             perfd_time (elapsed_time), perfd_size (page_len));
1088   if (check_critical_time == TRUE && elapsed_time > critical_time)
1089     result = STATE_CRITICAL;
1090   if (check_warning_time == TRUE && elapsed_time > warning_time)
1091     result =  max_state_alt(STATE_WARNING, result);
1093   die (result, "HTTP %s: %s\n", state_text(result), msg);
1094   /* die failed? */
1095   return STATE_UNKNOWN;
1100 /* per RFC 2396 */
1101 #define URI_HTTP "%5[HTPShtps]"
1102 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1103 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1104 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1105 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1106 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1107 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1108 #define HD4 URI_HTTP "://" URI_HOST
1109 #define HD5 URI_PATH
1111 void
1112 redir (char *pos, char *status_line)
1114   int i = 0;
1115   char *x;
1116   char xx[2];
1117   char type[6];
1118   char *addr;
1119   char *url;
1121   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1122   if (addr == NULL)
1123     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1125   url = malloc (strcspn (pos, "\r\n"));
1126   if (url == NULL)
1127     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1129   while (pos) {
1130     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1131     if (i == 0) {
1132       pos += (size_t) strcspn (pos, "\r\n");
1133       pos += (size_t) strspn (pos, "\r\n");
1134       if (strlen(pos) == 0)
1135         die (STATE_UNKNOWN,
1136              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1137              status_line, (display_html ? "</A>" : ""));
1138       continue;
1139     }
1141     pos += i;
1142     pos += strspn (pos, " \t");
1144     /*
1145      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1146      * preceding each extra line with at least one SP or HT.''
1147      */
1148     for (; (i = strspn (pos, "\r\n")); pos += i) {
1149       pos += i;
1150       if (!(i = strspn (pos, " \t"))) {
1151         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1152              display_html ? "</A>" : "");
1153       }
1154     }
1156     url = realloc (url, strcspn (pos, "\r\n") + 1);
1157     if (url == NULL)
1158       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1160     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1161     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1162       url = prepend_slash (url);
1163       use_ssl = server_type_check (type);
1164     }
1166     /* URI_HTTP URI_HOST URI_PATH */
1167     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1168       url = prepend_slash (url);
1169       use_ssl = server_type_check (type);
1170       i = server_port_check (use_ssl);
1171     }
1173     /* URI_HTTP URI_HOST URI_PORT */
1174     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1175       strcpy (url, HTTP_URL);
1176       use_ssl = server_type_check (type);
1177     }
1179     /* URI_HTTP URI_HOST */
1180     else if (sscanf (pos, HD4, type, addr) == 2) {
1181       strcpy (url, HTTP_URL);
1182       use_ssl = server_type_check (type);
1183       i = server_port_check (use_ssl);
1184     }
1186     /* URI_PATH */
1187     else if (sscanf (pos, HD5, url) == 1) {
1188       /* relative url */
1189       if ((url[0] != '/')) {
1190         if ((x = strrchr(server_url, '/')))
1191           *x = '\0';
1192         asprintf (&url, "%s/%s", server_url, url);
1193       }
1194       i = server_port;
1195       strcpy (type, server_type);
1196       strcpy (addr, host_name ? host_name : server_address);
1197     }
1199     else {
1200       die (STATE_UNKNOWN,
1201            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1202            pos, (display_html ? "</A>" : ""));
1203     }
1205     break;
1207   } /* end while (pos) */
1209   if (++redir_depth > max_depth)
1210     die (STATE_WARNING,
1211          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1212          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1214   if (server_port==i &&
1215       !strcmp(server_address, addr) &&
1216       (host_name && !strcmp(host_name, addr)) &&
1217       !strcmp(server_url, url))
1218     die (STATE_WARNING,
1219          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1220          type, addr, i, url, (display_html ? "</A>" : ""));
1222   strcpy (server_type, type);
1224   free (host_name);
1225   host_name = strdup (addr);
1227   if (!(followsticky & STICKY_HOST)) {
1228     free (server_address);
1229     server_address = strdup (addr);
1230   }
1231   if (!(followsticky & STICKY_PORT)) {
1232     server_port = i;
1233   }
1235   free (server_url);
1236   server_url = url;
1238   if (server_port > MAX_PORT)
1239     die (STATE_UNKNOWN,
1240          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1241          MAX_PORT, server_type, server_address, server_port, server_url,
1242          display_html ? "</A>" : "");
1244   if (verbose)
1245     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1246             host_name ? host_name : server_address, server_port, server_url);
1248   check_http ();
1252 int
1253 server_type_check (const char *type)
1255   if (strcmp (type, "https"))
1256     return FALSE;
1257   else
1258     return TRUE;
1261 int
1262 server_port_check (int ssl_flag)
1264   if (ssl_flag)
1265     return HTTPS_PORT;
1266   else
1267     return HTTP_PORT;
1270 char *perfd_time (double elapsed_time)
1272   return fperfdata ("time", elapsed_time, "s",
1273             check_warning_time, warning_time,
1274             check_critical_time, critical_time,
1275                    TRUE, 0, FALSE, 0);
1280 char *perfd_size (int page_len)
1282   return perfdata ("size", page_len, "B",
1283             (min_page_len>0?TRUE:FALSE), min_page_len,
1284             (min_page_len>0?TRUE:FALSE), 0,
1285             TRUE, 0, FALSE, 0);
1288 void
1289 print_help (void)
1291   print_revision (progname, NP_VERSION);
1293   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1294   printf (COPYRIGHT, copyright, email);
1296   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1297   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1298   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1299   printf ("%s\n", _("certificate expiration times."));
1301   printf ("\n\n");
1303   print_usage ();
1305   printf (_("NOTE: One or both of -H and -I must be specified"));
1307   printf ("\n");
1309   printf (_(UT_HELP_VRSN));
1310   printf (_(UT_EXTRA_OPTS));
1312   printf (" %s\n", "-H, --hostname=ADDRESS");
1313   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1314   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1315   printf (" %s\n", "-I, --IP-address=ADDRESS");
1316   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1317   printf (" %s\n", "-p, --port=INTEGER");
1318   printf ("    %s", _("Port number (default: "));
1319   printf ("%d)\n", HTTP_PORT);
1321   printf (_(UT_IPv46));
1323 #ifdef HAVE_SSL
1324   printf (" %s\n", "-S, --ssl");
1325   printf ("   %s\n", _("Connect via SSL. Port defaults to 443"));
1326   printf (" %s\n", "-C, --certificate=INTEGER");
1327   printf ("   %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1328   printf ("   %s\n", _("(when this option is used the URL is not checked.)\n"));
1329 #endif
1331   printf (" %s\n", "-e, --expect=STRING");
1332   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1333   printf ("    %s", _("the first (status) line of the server response (default: "));
1334   printf ("%s)\n", HTTP_EXPECT);
1335   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1336   printf (" %s\n", "-s, --string=STRING");
1337   printf ("    %s\n", _("String to expect in the content"));
1338   printf (" %s\n", "-u, --url=PATH");
1339   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1340   printf (" %s\n", "-P, --post=STRING");
1341   printf ("    %s\n", _("URL encoded http POST data"));
1342   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1343   printf ("    %s\n", _("Set HTTP method."));
1344   printf (" %s\n", "-N, --no-body");
1345   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1346   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1347   printf (" %s\n", "-M, --max-age=SECONDS");
1348   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1349   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1350   printf (" %s\n", "-T, --content-type=STRING");
1351   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1353   printf (" %s\n", "-l, --linespan");
1354   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1355   printf (" %s\n", "-r, --regex, --ereg=STRING");
1356   printf ("    %s\n", _("Search page for regex STRING"));
1357   printf (" %s\n", "-R, --eregi=STRING");
1358   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1359   printf (" %s\n", "--invert-regex");
1360   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1362   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1363   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1364   printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1365   printf ("     %s\n", _("Username:password on proxy-servers with basic authentication"));
1366   printf (" %s\n", "-A, --useragent=STRING");
1367   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1368   printf (" %s\n", "-k, --header=STRING");
1369   printf ("    %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1370   printf (" %s\n", "-L, --link");
1371   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1372   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1373   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1374   printf ("    %s\n", _("specified IP address. stickyport also ensure post stays the same."));
1375   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1376   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1378   printf (_(UT_WARN_CRIT));
1380   printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1382   printf (_(UT_VERBOSE));
1384   printf ("\n");
1385   printf ("%s\n", _("Notes:"));
1386   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1387   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1388   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1389   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1390   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1391   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1392   printf ("\n");
1393   printf (_(UT_EXTRA_OPTS_NOTES));
1395 #ifdef HAVE_SSL
1396   printf ("\n");
1397   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1398   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1399   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1400   printf ("\n");
1401   printf ("%s\n", _("Examples:"));
1402   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1403   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1404   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1405   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1406   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1408   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1409   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1410   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1411   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1412   printf (" %s\n", _("the certificate is expired."));
1413 #endif
1415   printf (_(UT_SUPPORT));
1421 void
1422 print_usage (void)
1424   printf (_("Usage:"));
1425   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1426   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
1427   printf ("       [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1428   printf ("       [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1429   printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1430   printf ("       [-A string] [-k string] [-S] [-C <age>] [-T <content-type>] [-j method]\n");