Code

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