Code

Some HTTP server applications/configurations cause trouble if the port is
[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 * Last Modified: $Date$
9
10 * Description:
11
12 * This file contains the check_http plugin
13
14 * This plugin tests the HTTP service on the specified host. It can test
15 * normal (http) and secure (https) servers, follow redirects, search for
16 * strings and regular expressions, check connection times, and report on
17 * certificate expiration times.
18
19
20 * This program is free software: you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation, either version 3 of the License, or
23 * (at your option) any later version.
24
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 * GNU General Public License for more details.
29
30 * You should have received a copy of the GNU General Public License
31 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
32
33 * $Id$
34
35 *****************************************************************************/
37 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
39 const char *progname = "check_http";
40 const char *revision = "$Revision$";
41 const char *copyright = "1999-2008";
42 const char *email = "nagiosplug-devel@lists.sourceforge.net";
44 #include "common.h"
45 #include "netutils.h"
46 #include "utils.h"
47 #include "base64.h"
48 #include <ctype.h>
50 #define INPUT_DELIMITER ";"
52 #define HTTP_EXPECT "HTTP/1."
53 enum {
54   MAX_IPV4_HOSTLENGTH = 255,
55   HTTP_PORT = 80,
56   HTTPS_PORT = 443,
57   MAX_PORT = 65535
58 };
60 #ifdef HAVE_SSL
61 int check_cert = FALSE;
62 int days_till_exp;
63 char *randbuff;
64 X509 *server_cert;
65 #  define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
66 #  define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
67 #else /* ifndef HAVE_SSL */
68 #  define my_recv(buf, len) read(sd, buf, len)
69 #  define my_send(buf, len) send(sd, buf, len, 0)
70 #endif /* HAVE_SSL */
71 int no_body = FALSE;
72 int maximum_age = -1;
74 enum {
75   REGS = 2,
76   MAX_RE_SIZE = 256
77 };
78 #include "regex.h"
79 regex_t preg;
80 regmatch_t pmatch[REGS];
81 char regexp[MAX_RE_SIZE];
82 char errbuf[MAX_INPUT_BUFFER];
83 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
84 int errcode;
85 int invert_regex = 0;
87 struct timeval tv;
89 #define HTTP_URL "/"
90 #define CRLF "\r\n"
92 int specify_port = FALSE;
93 int server_port = HTTP_PORT;
94 char server_port_text[6] = "";
95 char server_type[6] = "http";
96 char *server_address;
97 char *host_name;
98 char *server_url;
99 char *user_agent;
100 int server_url_length;
101 int server_expect_yn = 0;
102 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
103 char string_expect[MAX_INPUT_BUFFER] = "";
104 double warning_time = 0;
105 int check_warning_time = FALSE;
106 double critical_time = 0;
107 int check_critical_time = FALSE;
108 char user_auth[MAX_INPUT_BUFFER] = "";
109 int display_html = FALSE;
110 char **http_opt_headers;
111 int http_opt_headers_count = 0;
112 int onredirect = STATE_OK;
113 int 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/%s (nagios-plugins %s)",
148             clean_revstring (revision), 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     {"IP-address", required_argument, 0, 'I'},
191     {"url", required_argument, 0, 'u'},
192     {"port", required_argument, 0, 'p'},
193     {"authorization", required_argument, 0, 'a'},
194     {"string", required_argument, 0, 's'},
195     {"expect", required_argument, 0, 'e'},
196     {"regex", required_argument, 0, 'r'},
197     {"ereg", required_argument, 0, 'r'},
198     {"eregi", required_argument, 0, 'R'},
199     {"linespan", no_argument, 0, 'l'},
200     {"onredirect", required_argument, 0, 'f'},
201     {"certificate", required_argument, 0, 'C'},
202     {"useragent", required_argument, 0, 'A'},
203     {"header", required_argument, 0, 'k'},
204     {"no-body", no_argument, 0, 'N'},
205     {"max-age", required_argument, 0, 'M'},
206     {"content-type", required_argument, 0, 'T'},
207     {"pagesize", required_argument, 0, 'm'},
208     {"invert-regex", no_argument, NULL, INVERT_REGEX},
209     {"use-ipv4", no_argument, 0, '4'},
210     {"use-ipv6", no_argument, 0, '6'},
211     {0, 0, 0, 0}
212   };
214   if (argc < 2)
215     return ERROR;
217   for (c = 1; c < argc; c++) {
218     if (strcmp ("-to", argv[c]) == 0)
219       strcpy (argv[c], "-t");
220     if (strcmp ("-hn", argv[c]) == 0)
221       strcpy (argv[c], "-H");
222     if (strcmp ("-wt", argv[c]) == 0)
223       strcpy (argv[c], "-w");
224     if (strcmp ("-ct", argv[c]) == 0)
225       strcpy (argv[c], "-c");
226     if (strcmp ("-nohtml", argv[c]) == 0)
227       strcpy (argv[c], "-n");
228   }
230   while (1) {
231     c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option);
232     if (c == -1 || c == EOF)
233       break;
235     switch (c) {
236     case '?': /* usage */
237       usage5 ();
238       break;
239     case 'h': /* help */
240       print_help ();
241       exit (STATE_OK);
242       break;
243     case 'V': /* version */
244       print_revision (progname, revision);
245       exit (STATE_OK);
246       break;
247     case 't': /* timeout period */
248       if (!is_intnonneg (optarg))
249         usage2 (_("Timeout interval must be a positive integer"), optarg);
250       else
251         socket_timeout = atoi (optarg);
252       break;
253     case 'c': /* critical time threshold */
254       if (!is_nonnegative (optarg))
255         usage2 (_("Critical threshold must be integer"), optarg);
256       else {
257         critical_time = strtod (optarg, NULL);
258         check_critical_time = TRUE;
259       }
260       break;
261     case 'w': /* warning time threshold */
262       if (!is_nonnegative (optarg))
263         usage2 (_("Warning threshold must be integer"), optarg);
264       else {
265         warning_time = strtod (optarg, NULL);
266         check_warning_time = TRUE;
267       }
268       break;
269     case 'A': /* User Agent String */
270       asprintf (&user_agent, "User-Agent: %s", optarg);
271       break;
272     case 'k': /* Additional headers */
273       if (http_opt_headers_count == 0)
274         http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
275       else
276         http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
277       http_opt_headers[http_opt_headers_count - 1] = optarg;
278       /* asprintf (&http_opt_headers, "%s", optarg); */
279       break;
280     case 'L': /* show html link */
281       display_html = TRUE;
282       break;
283     case 'n': /* do not show html link */
284       display_html = FALSE;
285       break;
286     case 'C': /* Check SSL cert validity */
287 #ifdef HAVE_SSL
288       if (!is_intnonneg (optarg))
289         usage2 (_("Invalid certificate expiration period"), optarg);
290       else {
291         days_till_exp = atoi (optarg);
292         check_cert = TRUE;
293       }
294      /* Fall through to -S option */
295 #endif
296     case 'S': /* use SSL */
297 #ifndef HAVE_SSL
298       usage4 (_("Invalid option - SSL is not available"));
299 #endif
300       use_ssl = TRUE;
301       if (specify_port == FALSE)
302         server_port = HTTPS_PORT;
303       break;
304     case 'f': /* onredirect */
305       if (!strcmp (optarg, "follow"))
306         onredirect = STATE_DEPENDENT;
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 */
348       if (http_method || http_post_data) break;
349       http_method = strdup("POST");
350       http_post_data = strdup (optarg);
351       break;
352     case 's': /* string or substring */
353       strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
354       string_expect[MAX_INPUT_BUFFER - 1] = 0;
355       break;
356     case 'e': /* string or substring */
357       strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
358       server_expect[MAX_INPUT_BUFFER - 1] = 0;
359       server_expect_yn = 1;
360       break;
361     case 'T': /* Content-type */
362       asprintf (&http_content_type, "%s", optarg);
363       break;
364     case 'l': /* linespan */
365       cflags &= ~REG_NEWLINE;
366       break;
367     case 'R': /* regex */
368       cflags |= REG_ICASE;
369     case 'r': /* regex */
370       strncpy (regexp, optarg, MAX_RE_SIZE - 1);
371       regexp[MAX_RE_SIZE - 1] = 0;
372       errcode = regcomp (&preg, regexp, cflags);
373       if (errcode != 0) {
374         (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
375         printf (_("Could Not Compile Regular Expression: %s"), errbuf);
376         return ERROR;
377       }
378       break;
379     case INVERT_REGEX:
380       invert_regex = 1;
381       break;
382     case '4':
383       address_family = AF_INET;
384       break;
385     case '6':
386 #ifdef USE_IPV6
387       address_family = AF_INET6;
388 #else
389       usage4 (_("IPv6 support not available"));
390 #endif
391       break;
392     case 'v': /* verbose */
393       verbose = TRUE;
394       break;
395     case 'm': /* min_page_length */
396       {
397       char *tmp;
398       if (strchr(optarg, ':') != (char *)NULL) {
399         /* range, so get two values, min:max */
400         tmp = strtok(optarg, ":");
401         if (tmp == NULL) {
402           printf("Bad format: try \"-m min:max\"\n");
403           exit (STATE_WARNING);
404         } else
405           min_page_len = atoi(tmp);
407         tmp = strtok(NULL, ":");
408         if (tmp == NULL) {
409           printf("Bad format: try \"-m min:max\"\n");
410           exit (STATE_WARNING);
411         } else
412           max_page_len = atoi(tmp);
413       } else 
414         min_page_len = atoi (optarg);
415       break;
416       }
417     case 'N': /* no-body */
418       no_body = TRUE;
419       break;
420     case 'M': /* max-age */
421                   {
422                     int L = strlen(optarg);
423                     if (L && optarg[L-1] == 'm')
424                       maximum_age = atoi (optarg) * 60;
425                     else if (L && optarg[L-1] == 'h')
426                       maximum_age = atoi (optarg) * 60 * 60;
427                     else if (L && optarg[L-1] == 'd')
428                       maximum_age = atoi (optarg) * 60 * 60 * 24;
429                     else if (L && (optarg[L-1] == 's' ||
430                                    isdigit (optarg[L-1])))
431                       maximum_age = atoi (optarg);
432                     else {
433                       fprintf (stderr, "unparsable max-age: %s\n", optarg);
434                       exit (STATE_WARNING);
435                     }
436                   }
437                   break;
438     }
439   }
441   c = optind;
443   if (server_address == NULL && c < argc)
444     server_address = strdup (argv[c++]);
446   if (host_name == NULL && c < argc)
447     host_name = strdup (argv[c++]);
449   if (server_address == NULL) {
450     if (host_name == NULL)
451       usage4 (_("You must specify a server address or host name"));
452     else
453       server_address = strdup (host_name);
454   }
456   if (check_critical_time && critical_time>(double)socket_timeout)
457     socket_timeout = (int)critical_time + 1;
459   if (http_method == NULL)
460     http_method = strdup ("GET");
462   return TRUE;
467 /* Returns 1 if we're done processing the document body; 0 to keep going */
468 static int
469 document_headers_done (char *full_page)
471   const char *body;
473   for (body = full_page; *body; body++) {
474     if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
475       break;
476   }
478   if (!*body)
479     return 0;  /* haven't read end of headers yet */
481   full_page[body - full_page] = 0;
482   return 1;
485 static time_t
486 parse_time_string (const char *string)
488   struct tm tm;
489   time_t t;
490   memset (&tm, 0, sizeof(tm));
492   /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
494   if (isupper (string[0])  &&  /* Tue */
495     islower (string[1])  &&
496     islower (string[2])  &&
497     ',' ==   string[3]   &&
498     ' ' ==   string[4]   &&
499     (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
500     isdigit (string[6])  &&
501     ' ' ==   string[7]   &&
502     isupper (string[8])  &&  /* Dec */
503     islower (string[9])  &&
504     islower (string[10]) &&
505     ' ' ==   string[11]  &&
506     isdigit (string[12]) &&  /* 2001 */
507     isdigit (string[13]) &&
508     isdigit (string[14]) &&
509     isdigit (string[15]) &&
510     ' ' ==   string[16]  &&
511     isdigit (string[17]) &&  /* 02: */
512     isdigit (string[18]) &&
513     ':' ==   string[19]  &&
514     isdigit (string[20]) &&  /* 59: */
515     isdigit (string[21]) &&
516     ':' ==   string[22]  &&
517     isdigit (string[23]) &&  /* 03 */
518     isdigit (string[24]) &&
519     ' ' ==   string[25]  &&
520     'G' ==   string[26]  &&  /* GMT */
521     'M' ==   string[27]  &&  /* GMT */
522     'T' ==   string[28]) {
524     tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
525     tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
526     tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
527     tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
528     tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
529       !strncmp (string+8, "Feb", 3) ? 1 :
530       !strncmp (string+8, "Mar", 3) ? 2 :
531       !strncmp (string+8, "Apr", 3) ? 3 :
532       !strncmp (string+8, "May", 3) ? 4 :
533       !strncmp (string+8, "Jun", 3) ? 5 :
534       !strncmp (string+8, "Jul", 3) ? 6 :
535       !strncmp (string+8, "Aug", 3) ? 7 :
536       !strncmp (string+8, "Sep", 3) ? 8 :
537       !strncmp (string+8, "Oct", 3) ? 9 :
538       !strncmp (string+8, "Nov", 3) ? 10 :
539       !strncmp (string+8, "Dec", 3) ? 11 :
540       -1);
541     tm.tm_year = ((1000 * (string[12]-'0') +
542       100 * (string[13]-'0') +
543       10 * (string[14]-'0') +
544       (string[15]-'0'))
545       - 1900);
547     tm.tm_isdst = 0;  /* GMT is never in DST, right? */
549     if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
550       return 0;
552     /* 
553     This is actually wrong: we need to subtract the local timezone
554     offset from GMT from this value.  But, that's ok in this usage,
555     because we only comparing these two GMT dates against each other,
556     so it doesn't matter what time zone we parse them in.
557     */
559     t = mktime (&tm);
560     if (t == (time_t) -1) t = 0;
562     if (verbose) {
563       const char *s = string;
564       while (*s && *s != '\r' && *s != '\n')
565       fputc (*s++, stdout);
566       printf (" ==> %lu\n", (unsigned long) t);
567     }
569     return t;
571   } else {
572     return 0;
573   }
576 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
577 static int
578 expected_statuscode (const char *reply, const char *statuscodes)
580   char *expected, *code;
581   int result = 0;
583   if ((expected = strdup (statuscodes)) == NULL)
584     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
586   for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
587     if (strstr (reply, code) != NULL) {
588       result = 1;
589       break;
590     }
592   free (expected);
593   return result;
596 static void
597 check_document_dates (const char *headers)
599   const char *s;
600   char *server_date = 0;
601   char *document_date = 0;
603   s = headers;
604   while (*s) {
605     const char *field = s;
606     const char *value = 0;
608     /* Find the end of the header field */
609     while (*s && !isspace(*s) && *s != ':')
610       s++;
612     /* Remember the header value, if any. */
613     if (*s == ':')
614       value = ++s;
616     /* Skip to the end of the header, including continuation lines. */
617     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
618       s++;
620     /* Avoid stepping over end-of-string marker */
621     if (*s)
622       s++;
624     /* Process this header. */
625     if (value && value > field+2) {
626       char *ff = (char *) malloc (value-field);
627       char *ss = ff;
628       while (field < value-1)
629         *ss++ = tolower(*field++);
630       *ss++ = 0;
632       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
633         const char *e;
634         while (*value && isspace (*value))
635           value++;
636         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
637           ;
638         ss = (char *) malloc (e - value + 1);
639         strncpy (ss, value, e - value);
640         ss[e - value] = 0;
641         if (!strcmp (ff, "date")) {
642           if (server_date) free (server_date);
643           server_date = ss;
644         } else {
645           if (document_date) free (document_date);
646           document_date = ss;
647         }
648       }
649       free (ff);
650     }
651   }
653   /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
654   if (!server_date || !*server_date) {
655     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Server date unknown\n"));
656   } else if (!document_date || !*document_date) {
657     die (STATE_CRITICAL, _("HTTP CRITICAL - Document modification date unknown\n"));
658   } else {
659     time_t srv_data = parse_time_string (server_date);
660     time_t doc_data = parse_time_string (document_date);
662     if (srv_data <= 0) {
663       die (STATE_CRITICAL, _("HTTP CRITICAL - Server date \"%100s\" unparsable"), server_date);
664     } else if (doc_data <= 0) {
665       die (STATE_CRITICAL, _("HTTP CRITICAL - Document date \"%100s\" unparsable"), document_date);
666     } else if (doc_data > srv_data + 30) {
667       die (STATE_CRITICAL, _("HTTP CRITICAL - Document is %d seconds in the future\n"), (int)doc_data - (int)srv_data);
668     } else if (doc_data < srv_data - maximum_age) {
669     int n = (srv_data - doc_data);
670     if (n > (60 * 60 * 24 * 2))
671       die (STATE_CRITICAL,
672         _("HTTP CRITICAL - Last modified %.1f days ago\n"),
673         ((float) n) / (60 * 60 * 24));
674   else
675     die (STATE_CRITICAL,
676         _("HTTP CRITICAL - Last modified %d:%02d:%02d ago\n"),
677         n / (60 * 60), (n / 60) % 60, n % 60);
678     }
680     free (server_date);
681     free (document_date);
682   }
685 int
686 get_content_length (const char *headers)
688   const char *s;
689   int content_length = 0;
691   s = headers;
692   while (*s) {
693     const char *field = s;
694     const char *value = 0;
696     /* Find the end of the header field */
697     while (*s && !isspace(*s) && *s != ':')
698       s++;
700     /* Remember the header value, if any. */
701     if (*s == ':')
702       value = ++s;
704     /* Skip to the end of the header, including continuation lines. */
705     while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
706       s++;
707     s++;
709     /* Process this header. */
710     if (value && value > field+2) {
711       char *ff = (char *) malloc (value-field);
712       char *ss = ff;
713       while (field < value-1)
714         *ss++ = tolower(*field++);
715       *ss++ = 0;
717       if (!strcmp (ff, "content-length")) {
718         const char *e;
719         while (*value && isspace (*value))
720           value++;
721         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
722           ;
723         ss = (char *) malloc (e - value + 1);
724         strncpy (ss, value, e - value);
725         ss[e - value] = 0;
726         content_length = atoi(ss);
727         free (ss);
728       }
729       free (ff);
730     }
731   }
732   return (content_length);
735 char *
736 prepend_slash (char *path)
738   char *newpath;
740   if (path[0] == '/')
741     return path;
743   if ((newpath = malloc (strlen(path) + 2)) == NULL)
744     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
745   newpath[0] = '/';
746   strcpy (newpath + 1, path);
747   free (path);
748   return newpath;
751 int
752 check_http (void)
754   char *msg;
755   char *status_line;
756   char *status_code;
757   char *header;
758   char *page;
759   char *auth;
760   int http_status;
761   int i = 0;
762   size_t pagesize = 0;
763   char *full_page;
764   char *buf;
765   char *pos;
766   long microsec;
767   double elapsed_time;
768   int page_len = 0;
769   int result = STATE_UNKNOWN;
771   /* try to connect to the host at the given port number */
772   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
773     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
774 #ifdef HAVE_SSL
775   if (use_ssl == TRUE) {
776     np_net_ssl_init(sd);
777     if (check_cert == TRUE) {
778       result = np_net_ssl_check_cert(days_till_exp);
779       np_net_ssl_cleanup();
780       if (sd) close(sd);
781       return result;
782     }
783   }
784 #endif /* HAVE_SSL */
786   asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
788   /* tell HTTP/1.1 servers not to keep the connection alive */
789   asprintf (&buf, "%sConnection: close\r\n", buf);
791   /* optionally send the host header info */
792   if (host_name) {
793     /*
794      * Specify the port only if we're using a non-default port (see RFC 2616,
795      * 14.23).  Some server applications/configurations cause trouble if the
796      * (default) port is explicitly specified in the "Host:" header line.
797      */
798     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
799         (use_ssl == TRUE && server_port == HTTPS_PORT))
800       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
801     else
802       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
803   }
805   /* optionally send any other header tag */
806   if (http_opt_headers_count) {
807     for (i = 0; i < http_opt_headers_count ; i++) {
808       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
809         asprintf (&buf, "%s%s\r\n", buf, pos);
810     }
811     free(http_opt_headers);
812   }
814   /* optionally send the authentication info */
815   if (strlen(user_auth)) {
816     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
817     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
818   }
820   /* either send http POST data */
821   if (http_post_data) {
822     if (http_content_type) {
823       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
824     } else {
825       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
826     }
828     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
829     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
830   }
831   else {
832     /* or just a newline so the server knows we're done with the request */
833     asprintf (&buf, "%s%s", buf, CRLF);
834   }
836   if (verbose) printf ("%s\n", buf);
837   my_send (buf, strlen (buf));
839   /* fetch the page */
840   full_page = strdup("");
841   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
842     buffer[i] = '\0';
843     asprintf (&full_page, "%s%s", full_page, buffer);
844     pagesize += i;
846                 if (no_body && document_headers_done (full_page)) {
847                   i = 0;
848                   break;
849                 }
850   }
852   if (i < 0 && errno != ECONNRESET) {
853 #ifdef HAVE_SSL
854     /*
855     if (use_ssl) {
856       sslerr=SSL_get_error(ssl, i);
857       if ( sslerr == SSL_ERROR_SSL ) {
858         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
859       } else {
860         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
861       }
862     }
863     else {
864     */
865 #endif
866       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
867 #ifdef HAVE_SSL
868       /* XXX
869     }
870     */
871 #endif
872   }
874   /* return a CRITICAL status if we couldn't read any data */
875   if (pagesize == (size_t) 0)
876     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
878   /* close the connection */
879 #ifdef HAVE_SSL
880   np_net_ssl_cleanup();
881 #endif
882   if (sd) close(sd);
884   /* reset the alarm */
885   alarm (0);
887   /* leave full_page untouched so we can free it later */
888   page = full_page;
890   if (verbose)
891     printf ("%s://%s:%d%s is %d characters\n",
892       use_ssl ? "https" : "http", server_address,
893       server_port, server_url, (int)pagesize);
895   /* find status line and null-terminate it */
896   status_line = page;
897   page += (size_t) strcspn (page, "\r\n");
898   pos = page;
899   page += (size_t) strspn (page, "\r\n");
900   status_line[strcspn(status_line, "\r\n")] = 0;
901   strip (status_line);
902   if (verbose)
903     printf ("STATUS: %s\n", status_line);
905   /* find header info and null-terminate it */
906   header = page;
907   while (strcspn (page, "\r\n") > 0) {
908     page += (size_t) strcspn (page, "\r\n");
909     pos = page;
910     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
911         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
912       page += (size_t) 2;
913     else
914       page += (size_t) 1;
915   }
916   page += (size_t) strspn (page, "\r\n");
917   header[pos - header] = 0;
918   if (verbose)
919     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
920                 (no_body ? "  [[ skipped ]]" : page));
922   /* make sure the status line matches the response we are looking for */
923   if (!expected_statuscode (status_line, server_expect)) {
924     if (server_port == HTTP_PORT)
925       asprintf (&msg,
926                 _("Invalid HTTP response received from host: %s\n"),
927                 status_line);
928     else
929       asprintf (&msg,
930                 _("Invalid HTTP response received from host on port %d: %s\n"),
931                 server_port, status_line);
932     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
933   }
935   /* Exit here if server_expect was set by user and not default */
936   if ( server_expect_yn  )  {
937     asprintf (&msg,
938               _("HTTP OK: Status line output matched \"%s\"\n"),
939               server_expect);
940     if (verbose)
941       printf ("%s\n",msg);
942   }
943   else {
944     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
945     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
946     /* Status-Code = 3 DIGITS */
948     status_code = strchr (status_line, ' ') + sizeof (char);
949     if (strspn (status_code, "1234567890") != 3)
950       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
952     http_status = atoi (status_code);
954     /* check the return code */
956     if (http_status >= 600 || http_status < 100)
957       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
959     /* server errors result in a critical state */
960     else if (http_status >= 500)
961       die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
963     /* client errors result in a warning state */
964     else if (http_status >= 400)
965       die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
967     /* check redirected page if specified */
968     else if (http_status >= 300) {
970       if (onredirect == STATE_DEPENDENT)
971         redir (header, status_line);
972       else if (onredirect == STATE_UNKNOWN)
973         printf (_("HTTP UNKNOWN"));
974       else if (onredirect == STATE_OK)
975         printf (_("HTTP OK"));
976       else if (onredirect == STATE_WARNING)
977         printf (_("HTTP WARNING"));
978       else if (onredirect == STATE_CRITICAL)
979         printf (_("HTTP CRITICAL"));
980       microsec = deltime (tv);
981       elapsed_time = (double)microsec / 1.0e6;
982       die (onredirect,
983            _(" - %s - %.3f second response time %s|%s %s\n"),
984            status_line, elapsed_time, 
985            (display_html ? "</A>" : ""),
986            perfd_time (elapsed_time), perfd_size (pagesize));
987     } /* end if (http_status >= 300) */
989   } /* end else (server_expect_yn)  */
991         if (maximum_age >= 0) {
992           check_document_dates (header);
993         }
995   /* check elapsed time */
996   microsec = deltime (tv);
997   elapsed_time = (double)microsec / 1.0e6;
998   asprintf (&msg,
999             _(" - %s - %.3f second response time %s|%s %s\n"),
1000             status_line, elapsed_time, 
1001             (display_html ? "</A>" : ""),
1002             perfd_time (elapsed_time), perfd_size (pagesize));
1003   if (check_critical_time == TRUE && elapsed_time > critical_time)
1004     die (STATE_CRITICAL, "HTTP %s: %s", _("CRITICAL"), msg);
1005   if (check_warning_time == TRUE && elapsed_time > warning_time)
1006     die (STATE_WARNING, "HTTP %s: %s", _("WARNING"), msg);
1008   /* Page and Header content checks go here */
1009   /* these checks should be last */
1011   if (strlen (string_expect)) {
1012     if (strstr (page, string_expect)) {
1013       printf (_("HTTP OK %s - %.3f second response time %s|%s %s\n"),
1014               status_line, elapsed_time,
1015               (display_html ? "</A>" : ""),
1016               perfd_time (elapsed_time), perfd_size (pagesize));
1017       exit (STATE_OK);
1018     }
1019     else {
1020       printf (_("HTTP CRITICAL - string not found%s|%s %s\n"),
1021               (display_html ? "</A>" : ""),
1022               perfd_time (elapsed_time), perfd_size (pagesize));
1023       exit (STATE_CRITICAL);
1024     }
1025   }
1027   if (strlen (regexp)) {
1028     errcode = regexec (&preg, page, REGS, pmatch, 0);
1029     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1030       printf (_("HTTP OK %s - %.3f second response time %s|%s %s\n"),
1031               status_line, elapsed_time,
1032               (display_html ? "</A>" : ""),
1033               perfd_time (elapsed_time), perfd_size (pagesize));
1034       exit (STATE_OK);
1035     }
1036     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1037       if (invert_regex == 0) 
1038         msg = strdup(_("pattern not found"));
1039       else 
1040         msg = strdup(_("pattern found"));
1041       printf (("%s - %s%s|%s %s\n"),
1042         _("HTTP CRITICAL"),
1043         msg,
1044         (display_html ? "</A>" : ""),
1045         perfd_time (elapsed_time), perfd_size (pagesize));
1046       exit (STATE_CRITICAL);
1047     }
1048     else {
1049       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1050       printf (_("HTTP CRITICAL - Execute Error: %s\n"), errbuf);
1051       exit (STATE_CRITICAL);
1052     }
1053   }
1055   /* make sure the page is of an appropriate size */
1056   /* page_len = get_content_length(header); */
1057   page_len = pagesize;
1058   if ((max_page_len > 0) && (page_len > max_page_len)) {
1059     printf (_("HTTP WARNING: page size %d too large%s|%s\n"),
1060       page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1061     exit (STATE_WARNING);
1062   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1063     printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
1064       page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1065     exit (STATE_WARNING);
1066   }
1067   /* We only get here if all tests have been passed */
1068   asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s|%s %s\n"),
1069             status_line, page_len, elapsed_time,
1070             (display_html ? "</A>" : ""),
1071             perfd_time (elapsed_time), perfd_size (page_len));
1072   die (STATE_OK, "%s", msg);
1073   return STATE_UNKNOWN;
1078 /* per RFC 2396 */
1079 #define URI_HTTP "%5[HTPShtps]"
1080 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1081 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1082 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1083 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1084 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1085 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1086 #define HD4 URI_HTTP "://" URI_HOST
1087 #define HD5 URI_PATH
1089 void
1090 redir (char *pos, char *status_line)
1092   int i = 0;
1093   char *x;
1094   char xx[2];
1095   char type[6];
1096   char *addr;
1097   char *url;
1099   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1100   if (addr == NULL)
1101     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1103   url = malloc (strcspn (pos, "\r\n"));
1104   if (url == NULL)
1105     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate url\n"));
1107   while (pos) {
1108     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1109     if (i == 0) {
1110       pos += (size_t) strcspn (pos, "\r\n");
1111       pos += (size_t) strspn (pos, "\r\n");
1112       if (strlen(pos) == 0) 
1113         die (STATE_UNKNOWN,
1114              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1115              status_line, (display_html ? "</A>" : ""));
1116       continue;
1117     }
1119     pos += i;
1120     pos += strspn (pos, " \t");
1122     /*
1123      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1124      * preceding each extra line with at least one SP or HT.''
1125      */
1126     for (; (i = strspn (pos, "\r\n")); pos += i) {
1127       pos += i;
1128       if (!(i = strspn (pos, " \t"))) {
1129         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1130              display_html ? "</A>" : "");
1131       }
1132     }
1134     url = realloc (url, strcspn (pos, "\r\n") + 1);
1135     if (url == NULL)
1136       die (STATE_UNKNOWN, _("HTTP UNKNOWN - could not allocate url\n"));
1138     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1139     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1140       url = prepend_slash (url);
1141       use_ssl = server_type_check (type);
1142     }
1144     /* URI_HTTP URI_HOST URI_PATH */
1145     else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
1146       url = prepend_slash (url);
1147       use_ssl = server_type_check (type);
1148       i = server_port_check (use_ssl);
1149     }
1151     /* URI_HTTP URI_HOST URI_PORT */
1152     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1153       strcpy (url, HTTP_URL);
1154       use_ssl = server_type_check (type);
1155     }
1157     /* URI_HTTP URI_HOST */
1158     else if (sscanf (pos, HD4, type, addr) == 2) {
1159       strcpy (url, HTTP_URL);
1160       use_ssl = server_type_check (type);
1161       i = server_port_check (use_ssl);
1162     }
1164     /* URI_PATH */
1165     else if (sscanf (pos, HD5, url) == 1) {
1166       /* relative url */
1167       if ((url[0] != '/')) {
1168         if ((x = strrchr(server_url, '/')))
1169           *x = '\0';
1170         asprintf (&url, "%s/%s", server_url, url);
1171       }
1172       i = server_port;
1173       strcpy (type, server_type);
1174       strcpy (addr, host_name ? host_name : server_address);
1175     }           
1177     else {
1178       die (STATE_UNKNOWN,
1179            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1180            pos, (display_html ? "</A>" : ""));
1181     }
1183     break;
1185   } /* end while (pos) */
1187   if (++redir_depth > max_depth)
1188     die (STATE_WARNING,
1189          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1190          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1192   if (server_port==i &&
1193       !strcmp(server_address, addr) &&
1194       (host_name && !strcmp(host_name, addr)) &&
1195       !strcmp(server_url, url))
1196     die (STATE_WARNING,
1197          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1198          type, addr, i, url, (display_html ? "</A>" : ""));
1200   strcpy (server_type, type);
1202   free (host_name);
1203   host_name = strdup (addr);
1205   free (server_address);
1206   server_address = strdup (addr);
1208   free (server_url);
1209   server_url = url;
1211   if ((server_port = i) > MAX_PORT)
1212     die (STATE_UNKNOWN,
1213          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1214          MAX_PORT, server_type, server_address, server_port, server_url,
1215          display_html ? "</A>" : "");
1217   if (verbose)
1218     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1219             host_name ? host_name : server_address, server_port, server_url);
1221   check_http ();
1226 int
1227 server_type_check (const char *type)
1229   if (strcmp (type, "https"))
1230     return FALSE;
1231   else
1232     return TRUE;
1235 int
1236 server_port_check (int ssl_flag)
1238   if (ssl_flag)
1239     return HTTPS_PORT;
1240   else
1241     return HTTP_PORT;
1244 char *perfd_time (double elapsed_time)
1246   return fperfdata ("time", elapsed_time, "s",
1247             check_warning_time, warning_time,
1248             check_critical_time, critical_time,
1249                    TRUE, 0, FALSE, 0);
1254 char *perfd_size (int page_len)
1256   return perfdata ("size", page_len, "B",
1257             (min_page_len>0?TRUE:FALSE), min_page_len,
1258             (min_page_len>0?TRUE:FALSE), 0,
1259             TRUE, 0, FALSE, 0);
1262 void
1263 print_help (void)
1265   print_revision (progname, revision);
1267   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1268   printf (COPYRIGHT, copyright, email);
1270   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1271   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1272   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1273   printf ("%s\n", _("certificate expiration times."));
1275   printf ("\n\n");
1277   print_usage ();
1279   printf (_("NOTE: One or both of -H and -I must be specified"));
1281   printf ("\n");
1283   printf (_(UT_HELP_VRSN));
1284   printf (_(UT_EXTRA_OPTS));
1286   printf (" %s\n", "-H, --hostname=ADDRESS");
1287   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1288   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1289   printf (" %s\n", "-I, --IP-address=ADDRESS");
1290   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1291   printf (" %s\n", "-p, --port=INTEGER");
1292   printf (" %s", _("Port number (default: "));
1293   printf ("%d)\n", HTTP_PORT);
1295   printf (_(UT_IPv46));
1297 #ifdef HAVE_SSL
1298   printf (" %s\n", "-S, --ssl");
1299   printf ("   %s\n", _("Connect via SSL. Port defaults to 443"));
1300   printf (" %s\n", "-C, --certificate=INTEGER");
1301   printf ("   %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1302   printf ("   %s\n", _("(when this option is used the url is not checked.)\n"));
1303 #endif
1305   printf (" %s\n", "-e, --expect=STRING");
1306   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1307   printf ("    %s", _("the first (status) line of the server response (default: "));
1308   printf ("%s)\n", HTTP_EXPECT);
1309   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1310   printf (" %s\n", "-s, --string=STRING");
1311   printf ("    %s\n", _("String to expect in the content"));
1312   printf (" %s\n", "-u, --url=PATH");
1313   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1314   printf (" %s\n", "-P, --post=STRING");
1315   printf ("    %s\n", _("URL encoded http POST data"));
1316   printf (" %s\n", "-N, --no-body");
1317   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1318   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1319   printf (" %s\n", "-M, --max-age=SECONDS");
1320   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1321   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1322   printf (" %s\n", "-T, --content-type=STRING");
1323   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1325   printf (" %s\n", "-l, --linespan");
1326   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1327   printf (" %s\n", "-r, --regex, --ereg=STRING");
1328   printf ("    %s\n", _("Search page for regex STRING"));
1329   printf (" %s\n", "-R, --eregi=STRING");
1330   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1331   printf (" %s\n", "--invert-regex");
1332   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1334   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1335   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1336   printf (" %s\n", "-A, --useragent=STRING");
1337   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1338   printf (" %s\n", "-k, --header=STRING");
1339   printf ("    %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1340   printf (" %s\n", "-L, --link");
1341   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1342   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow>");
1343   printf ("    %s\n", _("How to handle redirected pages"));
1344   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1345   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1347   printf (_(UT_WARN_CRIT));
1349   printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1351   printf (_(UT_VERBOSE));
1353   printf ("\n");
1354   printf ("%s\n", _("Notes:"));
1355   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1356   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1357   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1358   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1359   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1360   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1361   printf ("\n");
1362   printf (_(UT_EXTRA_OPTS_NOTES));
1364 #ifdef HAVE_SSL
1365   printf ("\n");
1366   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1367   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1368   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1369   printf ("\n");
1370   printf ("%s\n", _("Examples:"));
1371   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1372   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1373   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1374   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1375   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1377   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1378   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1379   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1380   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1381   printf (" %s\n", _("the certificate is expired."));
1382 #endif
1384   printf (_(UT_SUPPORT));
1390 void
1391 print_usage (void)
1393   printf (_("Usage:"));
1394   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1395   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n");
1396   printf ("       [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n");
1397   printf ("       [-s string] [-l] [-r <regex> | -R <case-insensitive regex>] [-P string]\n");
1398   printf ("       [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>] [-A string]\n");
1399   printf ("       [-k string] [-S] [-C <age>] [-T <content-type>]\n");