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