Code

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