Code

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