Code

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