Code

check_http: add --onredirect=stickyport - also follow the same port
[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       if (!strcmp (optarg, "sticky"))
309         onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
310       if (!strcmp (optarg, "follow"))
311         onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
312       if (!strcmp (optarg, "unknown"))
313         onredirect = STATE_UNKNOWN;
314       if (!strcmp (optarg, "ok"))
315         onredirect = STATE_OK;
316       if (!strcmp (optarg, "warning"))
317         onredirect = STATE_WARNING;
318       if (!strcmp (optarg, "critical"))
319         onredirect = STATE_CRITICAL;
320       if (verbose)
321         printf(_("option f:%d \n"), onredirect);
322       break;
323     /* Note: H, I, and u must be malloc'd or will fail on redirects */
324     case 'H': /* Host Name (virtual host) */
325       host_name = strdup (optarg);
326       if (host_name[0] == '[') {
327         if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
328           server_port = atoi (p + 2);
329       } else if ((p = strchr (host_name, ':')) != NULL
330                  && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
331           server_port = atoi (p);
332       break;
333     case 'I': /* Server IP-address */
334       server_address = strdup (optarg);
335       break;
336     case 'u': /* URL path */
337       server_url = strdup (optarg);
338       server_url_length = strlen (server_url);
339       break;
340     case 'p': /* Server port */
341       if (!is_intnonneg (optarg))
342         usage2 (_("Invalid port number"), optarg);
343       else {
344         server_port = atoi (optarg);
345         specify_port = TRUE;
346       }
347       break;
348     case 'a': /* authorization info */
349       strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
350       user_auth[MAX_INPUT_BUFFER - 1] = 0;
351       break;
352     case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
353       if (! http_post_data)
354         http_post_data = strdup (optarg);
355       if (! http_method)
356         http_method = strdup("POST");
357       break;
358     case 'j': /* Set HTTP method */
359       if (http_method)
360         free(http_method);
361       http_method = strdup (optarg);
362       break;
363     case 's': /* string or substring */
364       strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
365       string_expect[MAX_INPUT_BUFFER - 1] = 0;
366       break;
367     case 'e': /* string or substring */
368       strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
369       server_expect[MAX_INPUT_BUFFER - 1] = 0;
370       server_expect_yn = 1;
371       break;
372     case 'T': /* Content-type */
373       asprintf (&http_content_type, "%s", optarg);
374       break;
375     case 'l': /* linespan */
376       cflags &= ~REG_NEWLINE;
377       break;
378     case 'R': /* regex */
379       cflags |= REG_ICASE;
380     case 'r': /* regex */
381       strncpy (regexp, optarg, MAX_RE_SIZE - 1);
382       regexp[MAX_RE_SIZE - 1] = 0;
383       errcode = regcomp (&preg, regexp, cflags);
384       if (errcode != 0) {
385         (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
386         printf (_("Could Not Compile Regular Expression: %s"), errbuf);
387         return ERROR;
388       }
389       break;
390     case INVERT_REGEX:
391       invert_regex = 1;
392       break;
393     case '4':
394       address_family = AF_INET;
395       break;
396     case '6':
397 #ifdef USE_IPV6
398       address_family = AF_INET6;
399 #else
400       usage4 (_("IPv6 support not available"));
401 #endif
402       break;
403     case 'v': /* verbose */
404       verbose = TRUE;
405       break;
406     case 'm': /* min_page_length */
407       {
408       char *tmp;
409       if (strchr(optarg, ':') != (char *)NULL) {
410         /* range, so get two values, min:max */
411         tmp = strtok(optarg, ":");
412         if (tmp == NULL) {
413           printf("Bad format: try \"-m min:max\"\n");
414           exit (STATE_WARNING);
415         } else
416           min_page_len = atoi(tmp);
418         tmp = strtok(NULL, ":");
419         if (tmp == NULL) {
420           printf("Bad format: try \"-m min:max\"\n");
421           exit (STATE_WARNING);
422         } else
423           max_page_len = atoi(tmp);
424       } else
425         min_page_len = atoi (optarg);
426       break;
427       }
428     case 'N': /* no-body */
429       no_body = TRUE;
430       break;
431     case 'M': /* max-age */
432                   {
433                     int L = strlen(optarg);
434                     if (L && optarg[L-1] == 'm')
435                       maximum_age = atoi (optarg) * 60;
436                     else if (L && optarg[L-1] == 'h')
437                       maximum_age = atoi (optarg) * 60 * 60;
438                     else if (L && optarg[L-1] == 'd')
439                       maximum_age = atoi (optarg) * 60 * 60 * 24;
440                     else if (L && (optarg[L-1] == 's' ||
441                                    isdigit (optarg[L-1])))
442                       maximum_age = atoi (optarg);
443                     else {
444                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
445                       exit (STATE_WARNING);
446                     }
447                   }
448                   break;
449     }
450   }
452   c = optind;
454   if (server_address == NULL && c < argc)
455     server_address = strdup (argv[c++]);
457   if (host_name == NULL && c < argc)
458     host_name = strdup (argv[c++]);
460   if (server_address == NULL) {
461     if (host_name == NULL)
462       usage4 (_("You must specify a server address or host name"));
463     else
464       server_address = strdup (host_name);
465   }
467   if (check_critical_time && critical_time>(double)socket_timeout)
468     socket_timeout = (int)critical_time + 1;
470   if (http_method == NULL)
471     http_method = strdup ("GET");
473   return TRUE;
478 /* Returns 1 if we're done processing the document body; 0 to keep going */
479 static int
480 document_headers_done (char *full_page)
482   const char *body;
484   for (body = full_page; *body; body++) {
485     if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
486       break;
487   }
489   if (!*body)
490     return 0;  /* haven't read end of headers yet */
492   full_page[body - full_page] = 0;
493   return 1;
496 static time_t
497 parse_time_string (const char *string)
499   struct tm tm;
500   time_t t;
501   memset (&tm, 0, sizeof(tm));
503   /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
505   if (isupper (string[0])  &&  /* Tue */
506     islower (string[1])  &&
507     islower (string[2])  &&
508     ',' ==   string[3]   &&
509     ' ' ==   string[4]   &&
510     (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
511     isdigit (string[6])  &&
512     ' ' ==   string[7]   &&
513     isupper (string[8])  &&  /* Dec */
514     islower (string[9])  &&
515     islower (string[10]) &&
516     ' ' ==   string[11]  &&
517     isdigit (string[12]) &&  /* 2001 */
518     isdigit (string[13]) &&
519     isdigit (string[14]) &&
520     isdigit (string[15]) &&
521     ' ' ==   string[16]  &&
522     isdigit (string[17]) &&  /* 02: */
523     isdigit (string[18]) &&
524     ':' ==   string[19]  &&
525     isdigit (string[20]) &&  /* 59: */
526     isdigit (string[21]) &&
527     ':' ==   string[22]  &&
528     isdigit (string[23]) &&  /* 03 */
529     isdigit (string[24]) &&
530     ' ' ==   string[25]  &&
531     'G' ==   string[26]  &&  /* GMT */
532     'M' ==   string[27]  &&  /* GMT */
533     'T' ==   string[28]) {
535     tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
536     tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
537     tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
538     tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
539     tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
540       !strncmp (string+8, "Feb", 3) ? 1 :
541       !strncmp (string+8, "Mar", 3) ? 2 :
542       !strncmp (string+8, "Apr", 3) ? 3 :
543       !strncmp (string+8, "May", 3) ? 4 :
544       !strncmp (string+8, "Jun", 3) ? 5 :
545       !strncmp (string+8, "Jul", 3) ? 6 :
546       !strncmp (string+8, "Aug", 3) ? 7 :
547       !strncmp (string+8, "Sep", 3) ? 8 :
548       !strncmp (string+8, "Oct", 3) ? 9 :
549       !strncmp (string+8, "Nov", 3) ? 10 :
550       !strncmp (string+8, "Dec", 3) ? 11 :
551       -1);
552     tm.tm_year = ((1000 * (string[12]-'0') +
553       100 * (string[13]-'0') +
554       10 * (string[14]-'0') +
555       (string[15]-'0'))
556       - 1900);
558     tm.tm_isdst = 0;  /* GMT is never in DST, right? */
560     if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
561       return 0;
563     /*
564     This is actually wrong: we need to subtract the local timezone
565     offset from GMT from this value.  But, that's ok in this usage,
566     because we only comparing these two GMT dates against each other,
567     so it doesn't matter what time zone we parse them in.
568     */
570     t = mktime (&tm);
571     if (t == (time_t) -1) t = 0;
573     if (verbose) {
574       const char *s = string;
575       while (*s && *s != '\r' && *s != '\n')
576       fputc (*s++, stdout);
577       printf (" ==> %lu\n", (unsigned long) t);
578     }
580     return t;
582   } else {
583     return 0;
584   }
587 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
588 static int
589 expected_statuscode (const char *reply, const char *statuscodes)
591   char *expected, *code;
592   int result = 0;
594   if ((expected = strdup (statuscodes)) == NULL)
595     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
597   for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
598     if (strstr (reply, code) != NULL) {
599       result = 1;
600       break;
601     }
603   free (expected);
604   return result;
607 static int
608 check_document_dates (const char *headers, char **msg)
610   const char *s;
611   char *server_date = 0;
612   char *document_date = 0;
613   int date_result = STATE_OK;
615   s = headers;
616   while (*s) {
617     const char *field = s;
618     const char *value = 0;
620     /* Find the end of the header field */
621     while (*s && !isspace(*s) && *s != ':')
622       s++;
624     /* Remember the header value, if any. */
625     if (*s == ':')
626       value = ++s;
628     /* Skip to the end of the header, including continuation lines. */
629     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
630       s++;
632     /* Avoid stepping over end-of-string marker */
633     if (*s)
634       s++;
636     /* Process this header. */
637     if (value && value > field+2) {
638       char *ff = (char *) malloc (value-field);
639       char *ss = ff;
640       while (field < value-1)
641         *ss++ = tolower(*field++);
642       *ss++ = 0;
644       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
645         const char *e;
646         while (*value && isspace (*value))
647           value++;
648         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
649           ;
650         ss = (char *) malloc (e - value + 1);
651         strncpy (ss, value, e - value);
652         ss[e - value] = 0;
653         if (!strcmp (ff, "date")) {
654           if (server_date) free (server_date);
655           server_date = ss;
656         } else {
657           if (document_date) free (document_date);
658           document_date = ss;
659         }
660       }
661       free (ff);
662     }
663   }
665   /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
666   if (!server_date || !*server_date) {
667     asprintf (msg, _("%sServer date unknown, "), *msg);
668     date_result = max_state_alt(STATE_UNKNOWN, date_result);
669   } else if (!document_date || !*document_date) {
670     asprintf (msg, _("%sDocument modification date unknown, "), *msg);
671     date_result = max_state_alt(STATE_CRITICAL, date_result);
672   } else {
673     time_t srv_data = parse_time_string (server_date);
674     time_t doc_data = parse_time_string (document_date);
676     if (srv_data <= 0) {
677       asprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
678       date_result = max_state_alt(STATE_CRITICAL, date_result);
679     } else if (doc_data <= 0) {
680       asprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
681       date_result = max_state_alt(STATE_CRITICAL, date_result);
682     } else if (doc_data > srv_data + 30) {
683       asprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
684       date_result = max_state_alt(STATE_CRITICAL, date_result);
685     } else if (doc_data < srv_data - maximum_age) {
686       int n = (srv_data - doc_data);
687       if (n > (60 * 60 * 24 * 2)) {
688         asprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
689         date_result = max_state_alt(STATE_CRITICAL, date_result);
690       } else {
691         asprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
692         date_result = max_state_alt(STATE_CRITICAL, date_result);
693       }
694     }
695     free (server_date);
696     free (document_date);
697   }
698   return date_result;
701 int
702 get_content_length (const char *headers)
704   const char *s;
705   int content_length = 0;
707   s = headers;
708   while (*s) {
709     const char *field = s;
710     const char *value = 0;
712     /* Find the end of the header field */
713     while (*s && !isspace(*s) && *s != ':')
714       s++;
716     /* Remember the header value, if any. */
717     if (*s == ':')
718       value = ++s;
720     /* Skip to the end of the header, including continuation lines. */
721     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
722       s++;
723     s++;
725     /* Process this header. */
726     if (value && value > field+2) {
727       char *ff = (char *) malloc (value-field);
728       char *ss = ff;
729       while (field < value-1)
730         *ss++ = tolower(*field++);
731       *ss++ = 0;
733       if (!strcmp (ff, "content-length")) {
734         const char *e;
735         while (*value && isspace (*value))
736           value++;
737         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
738           ;
739         ss = (char *) malloc (e - value + 1);
740         strncpy (ss, value, e - value);
741         ss[e - value] = 0;
742         content_length = atoi(ss);
743         free (ss);
744       }
745       free (ff);
746     }
747   }
748   return (content_length);
751 char *
752 prepend_slash (char *path)
754   char *newpath;
756   if (path[0] == '/')
757     return path;
759   if ((newpath = malloc (strlen(path) + 2)) == NULL)
760     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
761   newpath[0] = '/';
762   strcpy (newpath + 1, path);
763   free (path);
764   return newpath;
767 int
768 check_http (void)
770   char *msg;
771   char *status_line;
772   char *status_code;
773   char *header;
774   char *page;
775   char *auth;
776   int http_status;
777   int i = 0;
778   size_t pagesize = 0;
779   char *full_page;
780   char *buf;
781   char *pos;
782   long microsec;
783   double elapsed_time;
784   int page_len = 0;
785   int result = STATE_OK;
787   /* try to connect to the host at the given port number */
788   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
789     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
790 #ifdef HAVE_SSL
791   if (use_ssl == TRUE) {
792     np_net_ssl_init(sd);
793     if (check_cert == TRUE) {
794       result = np_net_ssl_check_cert(days_till_exp);
795       np_net_ssl_cleanup();
796       if (sd) close(sd);
797       return result;
798     }
799   }
800 #endif /* HAVE_SSL */
802   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
804   /* tell HTTP/1.1 servers not to keep the connection alive */
805   asprintf (&buf, "%sConnection: close\r\n", buf);
807   /* optionally send the host header info */
808   if (host_name) {
809     /*
810      * Specify the port only if we're using a non-default port (see RFC 2616,
811      * 14.23).  Some server applications/configurations cause trouble if the
812      * (default) port is explicitly specified in the "Host:" header line.
813      */
814     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
815         (use_ssl == TRUE && server_port == HTTPS_PORT))
816       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
817     else
818       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
819   }
821   /* optionally send any other header tag */
822   if (http_opt_headers_count) {
823     for (i = 0; i < http_opt_headers_count ; i++) {
824       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
825         asprintf (&buf, "%s%s\r\n", buf, pos);
826     }
827     /* This cannot be free'd here because a redirection will then try to access this and segfault */
828     /* Covered in a testcase in tests/check_http.t */
829     /* free(http_opt_headers); */
830   }
832   /* optionally send the authentication info */
833   if (strlen(user_auth)) {
834     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
835     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
836   }
838   /* either send http POST data (any data, not only POST)*/
839   if (http_post_data) {
840     if (http_content_type) {
841       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
842     } else {
843       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
844     }
846     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
847     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
848   }
849   else {
850     /* or just a newline so the server knows we're done with the request */
851     asprintf (&buf, "%s%s", buf, CRLF);
852   }
854   if (verbose) printf ("%s\n", buf);
855   my_send (buf, strlen (buf));
857   /* fetch the page */
858   full_page = strdup("");
859   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
860     buffer[i] = '\0';
861     asprintf (&full_page, "%s%s", full_page, buffer);
862     pagesize += i;
864                 if (no_body && document_headers_done (full_page)) {
865                   i = 0;
866                   break;
867                 }
868   }
870   if (i < 0 && errno != ECONNRESET) {
871 #ifdef HAVE_SSL
872     /*
873     if (use_ssl) {
874       sslerr=SSL_get_error(ssl, i);
875       if ( sslerr == SSL_ERROR_SSL ) {
876         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
877       } else {
878         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
879       }
880     }
881     else {
882     */
883 #endif
884       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
885 #ifdef HAVE_SSL
886       /* XXX
887     }
888     */
889 #endif
890   }
892   /* return a CRITICAL status if we couldn't read any data */
893   if (pagesize == (size_t) 0)
894     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
896   /* close the connection */
897 #ifdef HAVE_SSL
898   np_net_ssl_cleanup();
899 #endif
900   if (sd) close(sd);
902   /* Save check time */
903   microsec = deltime (tv);
904   elapsed_time = (double)microsec / 1.0e6;
906   /* leave full_page untouched so we can free it later */
907   page = full_page;
909   if (verbose)
910     printf ("%s://%s:%d%s is %d characters\n",
911       use_ssl ? "https" : "http", server_address,
912       server_port, server_url, (int)pagesize);
914   /* find status line and null-terminate it */
915   status_line = page;
916   page += (size_t) strcspn (page, "\r\n");
917   pos = page;
918   page += (size_t) strspn (page, "\r\n");
919   status_line[strcspn(status_line, "\r\n")] = 0;
920   strip (status_line);
921   if (verbose)
922     printf ("STATUS: %s\n", status_line);
924   /* find header info and null-terminate it */
925   header = page;
926   while (strcspn (page, "\r\n") > 0) {
927     page += (size_t) strcspn (page, "\r\n");
928     pos = page;
929     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
930         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
931       page += (size_t) 2;
932     else
933       page += (size_t) 1;
934   }
935   page += (size_t) strspn (page, "\r\n");
936   header[pos - header] = 0;
937   if (verbose)
938     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
939                 (no_body ? "  [[ skipped ]]" : page));
941   /* make sure the status line matches the response we are looking for */
942   if (!expected_statuscode (status_line, server_expect)) {
943     if (server_port == HTTP_PORT)
944       asprintf (&msg,
945                 _("Invalid HTTP response received from host: %s\n"),
946                 status_line);
947     else
948       asprintf (&msg,
949                 _("Invalid HTTP response received from host on port %d: %s\n"),
950                 server_port, status_line);
951     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
952   }
954   /* Bypass normal status line check if server_expect was set by user and not default */
955   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
956   if ( server_expect_yn  )  {
957     asprintf (&msg,
958               _("Status line output matched \"%s\" - "), server_expect);
959     if (verbose)
960       printf ("%s\n",msg);
961   }
962   else {
963     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
964     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
965     /* Status-Code = 3 DIGITS */
967     status_code = strchr (status_line, ' ') + sizeof (char);
968     if (strspn (status_code, "1234567890") != 3)
969       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
971     http_status = atoi (status_code);
973     /* check the return code */
975     if (http_status >= 600 || http_status < 100) {
976       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
977     }
978     /* server errors result in a critical state */
979     else if (http_status >= 500) {
980       asprintf (&msg, _("%s - "), status_line);
981       result = STATE_CRITICAL;
982     }
983     /* client errors result in a warning state */
984     else if (http_status >= 400) {
985       asprintf (&msg, _("%s - "), status_line);
986       result = max_state_alt(STATE_WARNING, result);
987     }
988     /* check redirected page if specified */
989     else if (http_status >= 300) {
991       if (onredirect == STATE_DEPENDENT)
992         redir (header, status_line);
993       else
994         result = max_state_alt(onredirect, result);
995       asprintf (&msg, _("%s - "), status_line);
996     } /* end if (http_status >= 300) */
997     else {
998       /* Print OK status anyway */
999       asprintf (&msg, _("%s - "), status_line);
1000     }
1002   } /* end else (server_expect_yn)  */
1004   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1005   alarm (0);
1007   if (maximum_age >= 0) {
1008     result = max_state_alt(check_document_dates(header, &msg), result);
1009   }
1011   /* Page and Header content checks go here */
1013   if (strlen (string_expect)) {
1014     if (!strstr (page, string_expect)) {
1015       asprintf (&msg, _("%sstring not found, "), msg);
1016       result = STATE_CRITICAL;
1017     }
1018   }
1020   if (strlen (regexp)) {
1021     errcode = regexec (&preg, page, REGS, pmatch, 0);
1022     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1023       /* OK - No-op to avoid changing the logic around it */
1024       result = max_state_alt(STATE_OK, result);
1025     }
1026     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1027       if (invert_regex == 0)
1028         asprintf (&msg, _("%spattern not found, "), msg);
1029       else
1030         asprintf (&msg, _("%spattern found, "), msg);
1031       result = STATE_CRITICAL;
1032     }
1033     else {
1034       /* FIXME: Shouldn't that be UNKNOWN? */
1035       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1036       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1037       result = STATE_CRITICAL;
1038     }
1039   }
1041   /* make sure the page is of an appropriate size */
1042   /* page_len = get_content_length(header); */
1043   /* FIXME: Will this work with -N ? IMHO we should use
1044    * get_content_length(header) and always check if it's different than the
1045    * returned pagesize
1046    */
1047   /* FIXME: IIRC pagesize returns headers - shouldn't we make
1048    * it == get_content_length(header) ??
1049    */
1050   page_len = pagesize;
1051   if ((max_page_len > 0) && (page_len > max_page_len)) {
1052     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1053     result = max_state_alt(STATE_WARNING, result);
1054   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1055     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1056     result = max_state_alt(STATE_WARNING, result);
1057   }
1059   /* Cut-off trailing characters */
1060   if(msg[strlen(msg)-2] == ',')
1061     msg[strlen(msg)-2] = '\0';
1062   else
1063     msg[strlen(msg)-3] = '\0';
1065   /* check elapsed time */
1066   asprintf (&msg,
1067             _("%s - %d bytes in %.3f second response time %s|%s %s"),
1068             msg, page_len, elapsed_time,
1069             (display_html ? "</A>" : ""),
1070             perfd_time (elapsed_time), perfd_size (page_len));
1072   if (check_critical_time == TRUE && elapsed_time > critical_time)
1073     result = STATE_CRITICAL;
1074   if (check_warning_time == TRUE && elapsed_time > warning_time)
1075     result =  max_state_alt(STATE_WARNING, result);
1077   die (result, "HTTP %s: %s\n", state_text(result), msg);
1078   /* die failed? */
1079   return STATE_UNKNOWN;
1084 /* per RFC 2396 */
1085 #define URI_HTTP "%5[HTPShtps]"
1086 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1087 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1088 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1089 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1090 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1091 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1092 #define HD4 URI_HTTP "://" URI_HOST
1093 #define HD5 URI_PATH
1095 void
1096 redir (char *pos, char *status_line)
1098   int i = 0;
1099   char *x;
1100   char xx[2];
1101   char type[6];
1102   char *addr;
1103   char *url;
1105   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1106   if (addr == NULL)
1107     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1109   url = malloc (strcspn (pos, "\r\n"));
1110   if (url == NULL)
1111     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1113   while (pos) {
1114     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1115     if (i == 0) {
1116       pos += (size_t) strcspn (pos, "\r\n");
1117       pos += (size_t) strspn (pos, "\r\n");
1118       if (strlen(pos) == 0)
1119         die (STATE_UNKNOWN,
1120              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1121              status_line, (display_html ? "</A>" : ""));
1122       continue;
1123     }
1125     pos += i;
1126     pos += strspn (pos, " \t");
1128     /*
1129      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1130      * preceding each extra line with at least one SP or HT.''
1131      */
1132     for (; (i = strspn (pos, "\r\n")); pos += i) {
1133       pos += i;
1134       if (!(i = strspn (pos, " \t"))) {
1135         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1136              display_html ? "</A>" : "");
1137       }
1138     }
1140     url = realloc (url, strcspn (pos, "\r\n") + 1);
1141     if (url == NULL)
1142       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1144     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1145     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1146       url = prepend_slash (url);
1147       use_ssl = server_type_check (type);
1148     }
1150     /* URI_HTTP URI_HOST URI_PATH */
1151     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1152       url = prepend_slash (url);
1153       use_ssl = server_type_check (type);
1154       i = server_port_check (use_ssl);
1155     }
1157     /* URI_HTTP URI_HOST URI_PORT */
1158     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1159       strcpy (url, HTTP_URL);
1160       use_ssl = server_type_check (type);
1161     }
1163     /* URI_HTTP URI_HOST */
1164     else if (sscanf (pos, HD4, type, addr) == 2) {
1165       strcpy (url, HTTP_URL);
1166       use_ssl = server_type_check (type);
1167       i = server_port_check (use_ssl);
1168     }
1170     /* URI_PATH */
1171     else if (sscanf (pos, HD5, url) == 1) {
1172       /* relative url */
1173       if ((url[0] != '/')) {
1174         if ((x = strrchr(server_url, '/')))
1175           *x = '\0';
1176         asprintf (&url, "%s/%s", server_url, url);
1177       }
1178       i = server_port;
1179       strcpy (type, server_type);
1180       strcpy (addr, host_name ? host_name : server_address);
1181     }
1183     else {
1184       die (STATE_UNKNOWN,
1185            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1186            pos, (display_html ? "</A>" : ""));
1187     }
1189     break;
1191   } /* end while (pos) */
1193   if (++redir_depth > max_depth)
1194     die (STATE_WARNING,
1195          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1196          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1198   if (server_port==i &&
1199       !strcmp(server_address, addr) &&
1200       (host_name && !strcmp(host_name, addr)) &&
1201       !strcmp(server_url, url))
1202     die (STATE_WARNING,
1203          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1204          type, addr, i, url, (display_html ? "</A>" : ""));
1206   strcpy (server_type, type);
1208   free (host_name);
1209   host_name = strdup (addr);
1211   if (!(followsticky & STICKY_HOST)) {
1212     free (server_address);
1213     server_address = strdup (addr);
1214   }
1215   if (!(followsticky & STICKY_PORT)) {
1216     server_port = i;
1217   }
1219   free (server_url);
1220   server_url = url;
1222   if (server_port > MAX_PORT)
1223     die (STATE_UNKNOWN,
1224          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1225          MAX_PORT, server_type, server_address, server_port, server_url,
1226          display_html ? "</A>" : "");
1228   if (verbose)
1229     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1230             host_name ? host_name : server_address, server_port, server_url);
1232   check_http ();
1236 int
1237 server_type_check (const char *type)
1239   if (strcmp (type, "https"))
1240     return FALSE;
1241   else
1242     return TRUE;
1245 int
1246 server_port_check (int ssl_flag)
1248   if (ssl_flag)
1249     return HTTPS_PORT;
1250   else
1251     return HTTP_PORT;
1254 char *perfd_time (double elapsed_time)
1256   return fperfdata ("time", elapsed_time, "s",
1257             check_warning_time, warning_time,
1258             check_critical_time, critical_time,
1259                    TRUE, 0, FALSE, 0);
1264 char *perfd_size (int page_len)
1266   return perfdata ("size", page_len, "B",
1267             (min_page_len>0?TRUE:FALSE), min_page_len,
1268             (min_page_len>0?TRUE:FALSE), 0,
1269             TRUE, 0, FALSE, 0);
1272 void
1273 print_help (void)
1275   print_revision (progname, NP_VERSION);
1277   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1278   printf (COPYRIGHT, copyright, email);
1280   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1281   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1282   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1283   printf ("%s\n", _("certificate expiration times."));
1285   printf ("\n\n");
1287   print_usage ();
1289   printf (_("NOTE: One or both of -H and -I must be specified"));
1291   printf ("\n");
1293   printf (_(UT_HELP_VRSN));
1294   printf (_(UT_EXTRA_OPTS));
1296   printf (" %s\n", "-H, --hostname=ADDRESS");
1297   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1298   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1299   printf (" %s\n", "-I, --IP-address=ADDRESS");
1300   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1301   printf (" %s\n", "-p, --port=INTEGER");
1302   printf ("    %s", _("Port number (default: "));
1303   printf ("%d)\n", HTTP_PORT);
1305   printf (_(UT_IPv46));
1307 #ifdef HAVE_SSL
1308   printf (" %s\n", "-S, --ssl");
1309   printf ("   %s\n", _("Connect via SSL. Port defaults to 443"));
1310   printf (" %s\n", "-C, --certificate=INTEGER");
1311   printf ("   %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1312   printf ("   %s\n", _("(when this option is used the URL is not checked.)\n"));
1313 #endif
1315   printf (" %s\n", "-e, --expect=STRING");
1316   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1317   printf ("    %s", _("the first (status) line of the server response (default: "));
1318   printf ("%s)\n", HTTP_EXPECT);
1319   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1320   printf (" %s\n", "-s, --string=STRING");
1321   printf ("    %s\n", _("String to expect in the content"));
1322   printf (" %s\n", "-u, --url=PATH");
1323   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1324   printf (" %s\n", "-P, --post=STRING");
1325   printf ("    %s\n", _("URL encoded http POST data"));
1326   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1327   printf ("    %s\n", _("Set HTTP method."));
1328   printf (" %s\n", "-N, --no-body");
1329   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1330   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1331   printf (" %s\n", "-M, --max-age=SECONDS");
1332   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1333   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1334   printf (" %s\n", "-T, --content-type=STRING");
1335   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1337   printf (" %s\n", "-l, --linespan");
1338   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1339   printf (" %s\n", "-r, --regex, --ereg=STRING");
1340   printf ("    %s\n", _("Search page for regex STRING"));
1341   printf (" %s\n", "-R, --eregi=STRING");
1342   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1343   printf (" %s\n", "--invert-regex");
1344   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1346   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1347   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1348   printf (" %s\n", "-A, --useragent=STRING");
1349   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1350   printf (" %s\n", "-k, --header=STRING");
1351   printf ("    %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1352   printf (" %s\n", "-L, --link");
1353   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1354   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1355   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1356   printf ("    %s\n", _("specified IP address. stickyport also ensure post stays the same."));
1357   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1358   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1360   printf (_(UT_WARN_CRIT));
1362   printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1364   printf (_(UT_VERBOSE));
1366   printf ("\n");
1367   printf ("%s\n", _("Notes:"));
1368   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1369   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1370   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1371   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1372   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1373   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1374   printf ("\n");
1375   printf (_(UT_EXTRA_OPTS_NOTES));
1377 #ifdef HAVE_SSL
1378   printf ("\n");
1379   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1380   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1381   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1382   printf ("\n");
1383   printf ("%s\n", _("Examples:"));
1384   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1385   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1386   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1387   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1388   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1390   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1391   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1392   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1393   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1394   printf (" %s\n", _("the certificate is expired."));
1395 #endif
1397   printf (_(UT_SUPPORT));
1403 void
1404 print_usage (void)
1406   printf (_("Usage:"));
1407   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1408   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1409   printf ("       [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n");
1410   printf ("       [-s string] [-l] [-r <regex> | -R <case-insensitive regex>] [-P string]\n");
1411   printf ("       [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>] [-A string]\n");
1412   printf ("       [-k string] [-S] [-C <age>] [-T <content-type>] [-j method]\n");