Code

7f773c83fd5a4aaa3f6c947133ff3d7a5eb7d7df
[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++;
737     /* Avoid stepping over end-of-string marker */
738     if (*s)
739       s++;
741     /* Process this header. */
742     if (value && value > field+2) {
743       char *ff = (char *) malloc (value-field);
744       char *ss = ff;
745       while (field < value-1)
746         *ss++ = tolower(*field++);
747       *ss++ = 0;
749       if (!strcmp (ff, "content-length")) {
750         const char *e;
751         while (*value && isspace (*value))
752           value++;
753         for (e = value; *e && *e != '\r' && *e != '\n'; e++)
754           ;
755         ss = (char *) malloc (e - value + 1);
756         strncpy (ss, value, e - value);
757         ss[e - value] = 0;
758         content_length = atoi(ss);
759         free (ss);
760       }
761       free (ff);
762     }
763   }
764   return (content_length);
767 char *
768 prepend_slash (char *path)
770   char *newpath;
772   if (path[0] == '/')
773     return path;
775   if ((newpath = malloc (strlen(path) + 2)) == NULL)
776     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
777   newpath[0] = '/';
778   strcpy (newpath + 1, path);
779   free (path);
780   return newpath;
783 int
784 check_http (void)
786   char *msg;
787   char *status_line;
788   char *status_code;
789   char *header;
790   char *page;
791   char *auth;
792   int http_status;
793   int i = 0;
794   size_t pagesize = 0;
795   char *full_page;
796   char *full_page_new;
797   char *buf;
798   char *pos;
799   long microsec;
800   double elapsed_time;
801   int page_len = 0;
802   int result = STATE_OK;
804   /* try to connect to the host at the given port number */
805   if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
806     die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
807 #ifdef HAVE_SSL
808   if (use_ssl == TRUE) {
809     np_net_ssl_init_with_hostname(sd, (use_sni ? host_name : NULL));
810     if (check_cert == TRUE) {
811       result = np_net_ssl_check_cert(days_till_exp);
812       np_net_ssl_cleanup();
813       if (sd) close(sd);
814       return result;
815     }
816   }
817 #endif /* HAVE_SSL */
819   asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
821   /* tell HTTP/1.1 servers not to keep the connection alive */
822   asprintf (&buf, "%sConnection: close\r\n", buf);
824   /* optionally send the host header info */
825   if (host_name) {
826     /*
827      * Specify the port only if we're using a non-default port (see RFC 2616,
828      * 14.23).  Some server applications/configurations cause trouble if the
829      * (default) port is explicitly specified in the "Host:" header line.
830      */
831     if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
832         (use_ssl == TRUE && server_port == HTTPS_PORT))
833       asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
834     else
835       asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
836   }
838   /* optionally send any other header tag */
839   if (http_opt_headers_count) {
840     for (i = 0; i < http_opt_headers_count ; i++) {
841       for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
842         asprintf (&buf, "%s%s\r\n", buf, pos);
843     }
844     /* This cannot be free'd here because a redirection will then try to access this and segfault */
845     /* Covered in a testcase in tests/check_http.t */
846     /* free(http_opt_headers); */
847   }
849   /* optionally send the authentication info */
850   if (strlen(user_auth)) {
851     base64_encode_alloc (user_auth, strlen (user_auth), &auth);
852     asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
853   }
855   /* optionally send the proxy authentication info */
856   if (strlen(proxy_auth)) {
857     base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
858     asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
859   }
861   /* either send http POST data (any data, not only POST)*/
862   if (http_post_data) {
863     if (http_content_type) {
864       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
865     } else {
866       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
867     }
869     asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
870     asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
871   }
872   else {
873     /* or just a newline so the server knows we're done with the request */
874     asprintf (&buf, "%s%s", buf, CRLF);
875   }
877   if (verbose) printf ("%s\n", buf);
878   my_send (buf, strlen (buf));
880   /* fetch the page */
881   full_page = strdup("");
882   while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
883     buffer[i] = '\0';
884     asprintf (&full_page_new, "%s%s", full_page, buffer);
885     free (full_page);
886     full_page = full_page_new;
887     pagesize += i;
889                 if (no_body && document_headers_done (full_page)) {
890                   i = 0;
891                   break;
892                 }
893   }
895   if (i < 0 && errno != ECONNRESET) {
896 #ifdef HAVE_SSL
897     /*
898     if (use_ssl) {
899       sslerr=SSL_get_error(ssl, i);
900       if ( sslerr == SSL_ERROR_SSL ) {
901         die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
902       } else {
903         die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
904       }
905     }
906     else {
907     */
908 #endif
909       die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
910 #ifdef HAVE_SSL
911       /* XXX
912     }
913     */
914 #endif
915   }
917   /* return a CRITICAL status if we couldn't read any data */
918   if (pagesize == (size_t) 0)
919     die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
921   /* close the connection */
922 #ifdef HAVE_SSL
923   np_net_ssl_cleanup();
924 #endif
925   if (sd) close(sd);
927   /* Save check time */
928   microsec = deltime (tv);
929   elapsed_time = (double)microsec / 1.0e6;
931   /* leave full_page untouched so we can free it later */
932   page = full_page;
934   if (verbose)
935     printf ("%s://%s:%d%s is %d characters\n",
936       use_ssl ? "https" : "http", server_address,
937       server_port, server_url, (int)pagesize);
939   /* find status line and null-terminate it */
940   status_line = page;
941   page += (size_t) strcspn (page, "\r\n");
942   pos = page;
943   page += (size_t) strspn (page, "\r\n");
944   status_line[strcspn(status_line, "\r\n")] = 0;
945   strip (status_line);
946   if (verbose)
947     printf ("STATUS: %s\n", status_line);
949   /* find header info and null-terminate it */
950   header = page;
951   while (strcspn (page, "\r\n") > 0) {
952     page += (size_t) strcspn (page, "\r\n");
953     pos = page;
954     if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
955         (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
956       page += (size_t) 2;
957     else
958       page += (size_t) 1;
959   }
960   page += (size_t) strspn (page, "\r\n");
961   header[pos - header] = 0;
962   if (verbose)
963     printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
964                 (no_body ? "  [[ skipped ]]" : page));
966   /* make sure the status line matches the response we are looking for */
967   if (!expected_statuscode (status_line, server_expect)) {
968     if (server_port == HTTP_PORT)
969       asprintf (&msg,
970                 _("Invalid HTTP response received from host: %s\n"),
971                 status_line);
972     else
973       asprintf (&msg,
974                 _("Invalid HTTP response received from host on port %d: %s\n"),
975                 server_port, status_line);
976     die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
977   }
979   /* Bypass normal status line check if server_expect was set by user and not default */
980   /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
981   if ( server_expect_yn  )  {
982     asprintf (&msg,
983               _("Status line output matched \"%s\" - "), server_expect);
984     if (verbose)
985       printf ("%s\n",msg);
986   }
987   else {
988     /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
989     /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
990     /* Status-Code = 3 DIGITS */
992     status_code = strchr (status_line, ' ') + sizeof (char);
993     if (strspn (status_code, "1234567890") != 3)
994       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
996     http_status = atoi (status_code);
998     /* check the return code */
1000     if (http_status >= 600 || http_status < 100) {
1001       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1002     }
1003     /* server errors result in a critical state */
1004     else if (http_status >= 500) {
1005       asprintf (&msg, _("%s - "), status_line);
1006       result = STATE_CRITICAL;
1007     }
1008     /* client errors result in a warning state */
1009     else if (http_status >= 400) {
1010       asprintf (&msg, _("%s - "), status_line);
1011       result = max_state_alt(STATE_WARNING, result);
1012     }
1013     /* check redirected page if specified */
1014     else if (http_status >= 300) {
1016       if (onredirect == STATE_DEPENDENT)
1017         redir (header, status_line);
1018       else
1019         result = max_state_alt(onredirect, result);
1020       asprintf (&msg, _("%s - "), status_line);
1021     } /* end if (http_status >= 300) */
1022     else {
1023       /* Print OK status anyway */
1024       asprintf (&msg, _("%s - "), status_line);
1025     }
1027   } /* end else (server_expect_yn)  */
1029   /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1030   alarm (0);
1032   if (maximum_age >= 0) {
1033     result = max_state_alt(check_document_dates(header, &msg), result);
1034   }
1036   /* Page and Header content checks go here */
1038   if (strlen (string_expect)) {
1039     if (!strstr (page, string_expect)) {
1040       asprintf (&msg, _("%sstring not found, "), msg);
1041       result = STATE_CRITICAL;
1042     }
1043   }
1045   if (strlen (regexp)) {
1046     errcode = regexec (&preg, page, REGS, pmatch, 0);
1047     if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1048       /* OK - No-op to avoid changing the logic around it */
1049       result = max_state_alt(STATE_OK, result);
1050     }
1051     else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1052       if (invert_regex == 0)
1053         asprintf (&msg, _("%spattern not found, "), msg);
1054       else
1055         asprintf (&msg, _("%spattern found, "), msg);
1056       result = STATE_CRITICAL;
1057     }
1058     else {
1059       /* FIXME: Shouldn't that be UNKNOWN? */
1060       regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1061       asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1062       result = STATE_CRITICAL;
1063     }
1064   }
1066   /* make sure the page is of an appropriate size */
1067   /* page_len = get_content_length(header); */
1068   /* FIXME: Will this work with -N ? IMHO we should use
1069    * get_content_length(header) and always check if it's different than the
1070    * returned pagesize
1071    */
1072   /* FIXME: IIRC pagesize returns headers - shouldn't we make
1073    * it == get_content_length(header) ??
1074    */
1075   page_len = pagesize;
1076   if ((max_page_len > 0) && (page_len > max_page_len)) {
1077     asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1078     result = max_state_alt(STATE_WARNING, result);
1079   } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1080     asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1081     result = max_state_alt(STATE_WARNING, result);
1082   }
1084   /* Cut-off trailing characters */
1085   if(msg[strlen(msg)-2] == ',')
1086     msg[strlen(msg)-2] = '\0';
1087   else
1088     msg[strlen(msg)-3] = '\0';
1090   /* check elapsed time */
1091   asprintf (&msg,
1092             _("%s - %d bytes in %.3f second response time %s|%s %s"),
1093             msg, page_len, elapsed_time,
1094             (display_html ? "</A>" : ""),
1095             perfd_time (elapsed_time), perfd_size (page_len));
1097   if (check_critical_time == TRUE && elapsed_time > critical_time)
1098     result = STATE_CRITICAL;
1099   if (check_warning_time == TRUE && elapsed_time > warning_time)
1100     result =  max_state_alt(STATE_WARNING, result);
1102   die (result, "HTTP %s: %s\n", state_text(result), msg);
1103   /* die failed? */
1104   return STATE_UNKNOWN;
1109 /* per RFC 2396 */
1110 #define URI_HTTP "%5[HTPShtps]"
1111 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1112 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1113 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1114 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1115 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1116 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1117 #define HD4 URI_HTTP "://" URI_HOST
1118 #define HD5 URI_PATH
1120 void
1121 redir (char *pos, char *status_line)
1123   int i = 0;
1124   char *x;
1125   char xx[2];
1126   char type[6];
1127   char *addr;
1128   char *url;
1130   addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1131   if (addr == NULL)
1132     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1134   url = malloc (strcspn (pos, "\r\n"));
1135   if (url == NULL)
1136     die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1138   while (pos) {
1139     sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1140     if (i == 0) {
1141       pos += (size_t) strcspn (pos, "\r\n");
1142       pos += (size_t) strspn (pos, "\r\n");
1143       if (strlen(pos) == 0)
1144         die (STATE_UNKNOWN,
1145              _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1146              status_line, (display_html ? "</A>" : ""));
1147       continue;
1148     }
1150     pos += i;
1151     pos += strspn (pos, " \t");
1153     /*
1154      * RFC 2616 (4.2):  ``Header fields can be extended over multiple lines by
1155      * preceding each extra line with at least one SP or HT.''
1156      */
1157     for (; (i = strspn (pos, "\r\n")); pos += i) {
1158       pos += i;
1159       if (!(i = strspn (pos, " \t"))) {
1160         die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1161              display_html ? "</A>" : "");
1162       }
1163     }
1165     url = realloc (url, strcspn (pos, "\r\n") + 1);
1166     if (url == NULL)
1167       die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1169     /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1170     if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1171       url = prepend_slash (url);
1172       use_ssl = server_type_check (type);
1173     }
1175     /* URI_HTTP URI_HOST URI_PATH */
1176     else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1177       url = prepend_slash (url);
1178       use_ssl = server_type_check (type);
1179       i = server_port_check (use_ssl);
1180     }
1182     /* URI_HTTP URI_HOST URI_PORT */
1183     else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1184       strcpy (url, HTTP_URL);
1185       use_ssl = server_type_check (type);
1186     }
1188     /* URI_HTTP URI_HOST */
1189     else if (sscanf (pos, HD4, type, addr) == 2) {
1190       strcpy (url, HTTP_URL);
1191       use_ssl = server_type_check (type);
1192       i = server_port_check (use_ssl);
1193     }
1195     /* URI_PATH */
1196     else if (sscanf (pos, HD5, url) == 1) {
1197       /* relative url */
1198       if ((url[0] != '/')) {
1199         if ((x = strrchr(server_url, '/')))
1200           *x = '\0';
1201         asprintf (&url, "%s/%s", server_url, url);
1202       }
1203       i = server_port;
1204       strcpy (type, server_type);
1205       strcpy (addr, host_name ? host_name : server_address);
1206     }
1208     else {
1209       die (STATE_UNKNOWN,
1210            _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1211            pos, (display_html ? "</A>" : ""));
1212     }
1214     break;
1216   } /* end while (pos) */
1218   if (++redir_depth > max_depth)
1219     die (STATE_WARNING,
1220          _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1221          max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1223   if (server_port==i &&
1224       !strcmp(server_address, addr) &&
1225       (host_name && !strcmp(host_name, addr)) &&
1226       !strcmp(server_url, url))
1227     die (STATE_WARNING,
1228          _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1229          type, addr, i, url, (display_html ? "</A>" : ""));
1231   strcpy (server_type, type);
1233   free (host_name);
1234   host_name = strdup (addr);
1236   if (!(followsticky & STICKY_HOST)) {
1237     free (server_address);
1238     server_address = strdup (addr);
1239   }
1240   if (!(followsticky & STICKY_PORT)) {
1241     server_port = i;
1242   }
1244   free (server_url);
1245   server_url = url;
1247   if (server_port > MAX_PORT)
1248     die (STATE_UNKNOWN,
1249          _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1250          MAX_PORT, server_type, server_address, server_port, server_url,
1251          display_html ? "</A>" : "");
1253   if (verbose)
1254     printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1255             host_name ? host_name : server_address, server_port, server_url);
1257   check_http ();
1261 int
1262 server_type_check (const char *type)
1264   if (strcmp (type, "https"))
1265     return FALSE;
1266   else
1267     return TRUE;
1270 int
1271 server_port_check (int ssl_flag)
1273   if (ssl_flag)
1274     return HTTPS_PORT;
1275   else
1276     return HTTP_PORT;
1279 char *perfd_time (double elapsed_time)
1281   return fperfdata ("time", elapsed_time, "s",
1282             check_warning_time, warning_time,
1283             check_critical_time, critical_time,
1284                    TRUE, 0, FALSE, 0);
1289 char *perfd_size (int page_len)
1291   return perfdata ("size", page_len, "B",
1292             (min_page_len>0?TRUE:FALSE), min_page_len,
1293             (min_page_len>0?TRUE:FALSE), 0,
1294             TRUE, 0, FALSE, 0);
1297 void
1298 print_help (void)
1300   print_revision (progname, NP_VERSION);
1302   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1303   printf (COPYRIGHT, copyright, email);
1305   printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1306   printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1307   printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1308   printf ("%s\n", _("certificate expiration times."));
1310   printf ("\n\n");
1312   print_usage ();
1314   printf (_("NOTE: One or both of -H and -I must be specified"));
1316   printf ("\n");
1318   printf (UT_HELP_VRSN);
1319   printf (UT_EXTRA_OPTS);
1321   printf (" %s\n", "-H, --hostname=ADDRESS");
1322   printf ("    %s\n", _("Host name argument for servers using host headers (virtual host)"));
1323   printf ("    %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1324   printf (" %s\n", "-I, --IP-address=ADDRESS");
1325   printf ("    %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1326   printf (" %s\n", "-p, --port=INTEGER");
1327   printf ("    %s", _("Port number (default: "));
1328   printf ("%d)\n", HTTP_PORT);
1330   printf (UT_IPv46);
1332 #ifdef HAVE_SSL
1333   printf (" %s\n", "-S, --ssl");
1334   printf ("   %s\n", _("Connect via SSL. Port defaults to 443"));
1335   printf (" %s\n", "--sni");
1336   printf ("   %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1337   printf (" %s\n", "-C, --certificate=INTEGER");
1338   printf ("   %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1339   printf ("   %s\n", _("(when this option is used the URL is not checked.)\n"));
1340 #endif
1342   printf (" %s\n", "-e, --expect=STRING");
1343   printf ("    %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1344   printf ("    %s", _("the first (status) line of the server response (default: "));
1345   printf ("%s)\n", HTTP_EXPECT);
1346   printf ("    %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1347   printf (" %s\n", "-s, --string=STRING");
1348   printf ("    %s\n", _("String to expect in the content"));
1349   printf (" %s\n", "-u, --url=PATH");
1350   printf ("    %s\n", _("URL to GET or POST (default: /)"));
1351   printf (" %s\n", "-P, --post=STRING");
1352   printf ("    %s\n", _("URL encoded http POST data"));
1353   printf (" %s\n", "-j, --method=STRING  (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1354   printf ("    %s\n", _("Set HTTP method."));
1355   printf (" %s\n", "-N, --no-body");
1356   printf ("    %s\n", _("Don't wait for document body: stop reading after headers."));
1357   printf ("    %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1358   printf (" %s\n", "-M, --max-age=SECONDS");
1359   printf ("    %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1360   printf ("    %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1361   printf (" %s\n", "-T, --content-type=STRING");
1362   printf ("    %s\n", _("specify Content-Type header media type when POSTing\n"));
1364   printf (" %s\n", "-l, --linespan");
1365   printf ("    %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1366   printf (" %s\n", "-r, --regex, --ereg=STRING");
1367   printf ("    %s\n", _("Search page for regex STRING"));
1368   printf (" %s\n", "-R, --eregi=STRING");
1369   printf ("    %s\n", _("Search page for case-insensitive regex STRING"));
1370   printf (" %s\n", "--invert-regex");
1371   printf ("    %s\n", _("Return CRITICAL if found, OK if not\n"));
1373   printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1374   printf ("    %s\n", _("Username:password on sites with basic authentication"));
1375   printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1376   printf ("     %s\n", _("Username:password on proxy-servers with basic authentication"));
1377   printf (" %s\n", "-A, --useragent=STRING");
1378   printf ("    %s\n", _("String to be sent in http header as \"User Agent\""));
1379   printf (" %s\n", "-k, --header=STRING");
1380   printf ("    %s\n", _(" Any other tags to be sent in http header. Use multiple times for additional headers"));
1381   printf (" %s\n", "-L, --link");
1382   printf ("    %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1383   printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1384   printf ("    %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1385   printf ("    %s\n", _("specified IP address. stickyport also ensure post stays the same."));
1386   printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1387   printf ("    %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1389   printf (UT_WARN_CRIT);
1391   printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1393   printf (UT_VERBOSE);
1395   printf ("\n");
1396   printf ("%s\n", _("Notes:"));
1397   printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1398   printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1399   printf (" %s\n", _("other errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse"));
1400   printf (" %s\n", _("messages from the host result in STATE_WARNING return values.  If you are"));
1401   printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1402   printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1404 #ifdef HAVE_SSL
1405   printf ("\n");
1406   printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1407   printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1408   printf (" %s\n", _("certificate is still valid for the specified number of days."));
1409   printf ("\n");
1410   printf ("%s\n", _("Examples:"));
1411   printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1412   printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1413   printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1414   printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1415   printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1417   printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1418   printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1419   printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1420   printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1421   printf (" %s\n", _("the certificate is expired."));
1422 #endif
1424   printf (UT_SUPPORT);
1430 void
1431 print_usage (void)
1433   printf ("%s\n", _("Usage:"));
1434   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1435   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
1436   printf ("       [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1437   printf ("       [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1438   printf ("       [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1439   printf ("       [-A string] [-k string] [-S] [--sni] [-C <age>] [-T <content-type>]\n");
1440   printf ("       [-j method]\n");