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)
140 {
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;
171 }
175 /* process command-line arguments */
176 int
177 process_arguments (int argc, char **argv)
178 {
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;
488 }
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)
495 {
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;
508 }
510 static time_t
511 parse_time_string (const char *string)
512 {
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 }
599 }
601 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
602 static int
603 expected_statuscode (const char *reply, const char *statuscodes)
604 {
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;
619 }
621 static int
622 check_document_dates (const char *headers, char **msg)
623 {
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;
713 }
715 int
716 get_content_length (const char *headers)
717 {
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);
766 }
768 char *
769 prepend_slash (char *path)
770 {
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;
782 }
784 int
785 check_http (void)
786 {
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;
1110 }
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)
1127 {
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 ();
1263 }
1266 int
1267 server_type_check (const char *type)
1268 {
1269 if (strcmp (type, "https"))
1270 return FALSE;
1271 else
1272 return TRUE;
1273 }
1275 int
1276 server_port_check (int ssl_flag)
1277 {
1278 if (ssl_flag)
1279 return HTTPS_PORT;
1280 else
1281 return HTTP_PORT;
1282 }
1284 char *perfd_time (double elapsed_time)
1285 {
1286 return fperfdata ("time", elapsed_time, "s",
1287 check_warning_time, warning_time,
1288 check_critical_time, critical_time,
1289 TRUE, 0, FALSE, 0);
1290 }
1294 char *perfd_size (int page_len)
1295 {
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);
1300 }
1302 void
1303 print_help (void)
1304 {
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 ensures port 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);
1431 }
1435 void
1436 print_usage (void)
1437 {
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");
1446 }