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 char *warning_thresholds = NULL;
105 char *critical_thresholds = NULL;
106 thresholds *thlds;
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 critical_thresholds = optarg;
261 break;
262 case 'w': /* warning time threshold */
263 warning_thresholds = optarg;
264 break;
265 case 'A': /* User Agent String */
266 asprintf (&user_agent, "User-Agent: %s", optarg);
267 break;
268 case 'k': /* Additional headers */
269 if (http_opt_headers_count == 0)
270 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
271 else
272 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
273 http_opt_headers[http_opt_headers_count - 1] = optarg;
274 /* asprintf (&http_opt_headers, "%s", optarg); */
275 break;
276 case 'L': /* show html link */
277 display_html = TRUE;
278 break;
279 case 'n': /* do not show html link */
280 display_html = FALSE;
281 break;
282 case 'C': /* Check SSL cert validity */
283 #ifdef HAVE_SSL
284 if (!is_intnonneg (optarg))
285 usage2 (_("Invalid certificate expiration period"), optarg);
286 else {
287 days_till_exp = atoi (optarg);
288 check_cert = TRUE;
289 }
290 /* Fall through to -S option */
291 #endif
292 case 'S': /* use SSL */
293 #ifndef HAVE_SSL
294 usage4 (_("Invalid option - SSL is not available"));
295 #endif
296 use_ssl = TRUE;
297 if (specify_port == FALSE)
298 server_port = HTTPS_PORT;
299 break;
300 case SNI_OPTION:
301 use_sni = TRUE;
302 break;
303 case 'f': /* onredirect */
304 if (!strcmp (optarg, "stickyport"))
305 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
306 else if (!strcmp (optarg, "sticky"))
307 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
308 else if (!strcmp (optarg, "follow"))
309 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
310 else if (!strcmp (optarg, "unknown"))
311 onredirect = STATE_UNKNOWN;
312 else if (!strcmp (optarg, "ok"))
313 onredirect = STATE_OK;
314 else if (!strcmp (optarg, "warning"))
315 onredirect = STATE_WARNING;
316 else if (!strcmp (optarg, "critical"))
317 onredirect = STATE_CRITICAL;
318 else usage2 (_("Invalid onredirect option"), optarg);
319 if (verbose)
320 printf(_("option f:%d \n"), onredirect);
321 break;
322 /* Note: H, I, and u must be malloc'd or will fail on redirects */
323 case 'H': /* Host Name (virtual host) */
324 host_name = strdup (optarg);
325 if (host_name[0] == '[') {
326 if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
327 server_port = atoi (p + 2);
328 } else if ((p = strchr (host_name, ':')) != NULL
329 && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
330 server_port = atoi (p);
331 break;
332 case 'I': /* Server IP-address */
333 server_address = strdup (optarg);
334 break;
335 case 'u': /* URL path */
336 server_url = strdup (optarg);
337 server_url_length = strlen (server_url);
338 break;
339 case 'p': /* Server port */
340 if (!is_intnonneg (optarg))
341 usage2 (_("Invalid port number"), optarg);
342 else {
343 server_port = atoi (optarg);
344 specify_port = TRUE;
345 }
346 break;
347 case 'a': /* authorization info */
348 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
349 user_auth[MAX_INPUT_BUFFER - 1] = 0;
350 break;
351 case 'b': /* proxy-authorization info */
352 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
353 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
354 break;
355 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
356 if (! http_post_data)
357 http_post_data = strdup (optarg);
358 if (! http_method)
359 http_method = strdup("POST");
360 break;
361 case 'j': /* Set HTTP method */
362 if (http_method)
363 free(http_method);
364 http_method = strdup (optarg);
365 break;
366 case 's': /* string or substring */
367 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
368 string_expect[MAX_INPUT_BUFFER - 1] = 0;
369 break;
370 case 'e': /* string or substring */
371 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
372 server_expect[MAX_INPUT_BUFFER - 1] = 0;
373 server_expect_yn = 1;
374 break;
375 case 'T': /* Content-type */
376 asprintf (&http_content_type, "%s", optarg);
377 break;
378 case 'l': /* linespan */
379 cflags &= ~REG_NEWLINE;
380 break;
381 case 'R': /* regex */
382 cflags |= REG_ICASE;
383 case 'r': /* regex */
384 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
385 regexp[MAX_RE_SIZE - 1] = 0;
386 errcode = regcomp (&preg, regexp, cflags);
387 if (errcode != 0) {
388 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
389 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
390 return ERROR;
391 }
392 break;
393 case INVERT_REGEX:
394 invert_regex = 1;
395 break;
396 case '4':
397 address_family = AF_INET;
398 break;
399 case '6':
400 #ifdef USE_IPV6
401 address_family = AF_INET6;
402 #else
403 usage4 (_("IPv6 support not available"));
404 #endif
405 break;
406 case 'v': /* verbose */
407 verbose = TRUE;
408 break;
409 case 'm': /* min_page_length */
410 {
411 char *tmp;
412 if (strchr(optarg, ':') != (char *)NULL) {
413 /* range, so get two values, min:max */
414 tmp = strtok(optarg, ":");
415 if (tmp == NULL) {
416 printf("Bad format: try \"-m min:max\"\n");
417 exit (STATE_WARNING);
418 } else
419 min_page_len = atoi(tmp);
421 tmp = strtok(NULL, ":");
422 if (tmp == NULL) {
423 printf("Bad format: try \"-m min:max\"\n");
424 exit (STATE_WARNING);
425 } else
426 max_page_len = atoi(tmp);
427 } else
428 min_page_len = atoi (optarg);
429 break;
430 }
431 case 'N': /* no-body */
432 no_body = TRUE;
433 break;
434 case 'M': /* max-age */
435 {
436 int L = strlen(optarg);
437 if (L && optarg[L-1] == 'm')
438 maximum_age = atoi (optarg) * 60;
439 else if (L && optarg[L-1] == 'h')
440 maximum_age = atoi (optarg) * 60 * 60;
441 else if (L && optarg[L-1] == 'd')
442 maximum_age = atoi (optarg) * 60 * 60 * 24;
443 else if (L && (optarg[L-1] == 's' ||
444 isdigit (optarg[L-1])))
445 maximum_age = atoi (optarg);
446 else {
447 fprintf (stderr, "unparsable max-age: %s\n", optarg);
448 exit (STATE_WARNING);
449 }
450 }
451 break;
452 }
453 }
455 c = optind;
457 if (server_address == NULL && c < argc)
458 server_address = strdup (argv[c++]);
460 if (host_name == NULL && c < argc)
461 host_name = strdup (argv[c++]);
463 if (server_address == NULL) {
464 if (host_name == NULL)
465 usage4 (_("You must specify a server address or host name"));
466 else
467 server_address = strdup (host_name);
468 }
470 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
472 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
473 socket_timeout = (int)thlds->critical->end + 1;
475 if (http_method == NULL)
476 http_method = strdup ("GET");
478 return TRUE;
479 }
483 /* Returns 1 if we're done processing the document body; 0 to keep going */
484 static int
485 document_headers_done (char *full_page)
486 {
487 const char *body;
489 for (body = full_page; *body; body++) {
490 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
491 break;
492 }
494 if (!*body)
495 return 0; /* haven't read end of headers yet */
497 full_page[body - full_page] = 0;
498 return 1;
499 }
501 static time_t
502 parse_time_string (const char *string)
503 {
504 struct tm tm;
505 time_t t;
506 memset (&tm, 0, sizeof(tm));
508 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
510 if (isupper (string[0]) && /* Tue */
511 islower (string[1]) &&
512 islower (string[2]) &&
513 ',' == string[3] &&
514 ' ' == string[4] &&
515 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
516 isdigit (string[6]) &&
517 ' ' == string[7] &&
518 isupper (string[8]) && /* Dec */
519 islower (string[9]) &&
520 islower (string[10]) &&
521 ' ' == string[11] &&
522 isdigit (string[12]) && /* 2001 */
523 isdigit (string[13]) &&
524 isdigit (string[14]) &&
525 isdigit (string[15]) &&
526 ' ' == string[16] &&
527 isdigit (string[17]) && /* 02: */
528 isdigit (string[18]) &&
529 ':' == string[19] &&
530 isdigit (string[20]) && /* 59: */
531 isdigit (string[21]) &&
532 ':' == string[22] &&
533 isdigit (string[23]) && /* 03 */
534 isdigit (string[24]) &&
535 ' ' == string[25] &&
536 'G' == string[26] && /* GMT */
537 'M' == string[27] && /* GMT */
538 'T' == string[28]) {
540 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
541 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
542 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
543 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
544 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
545 !strncmp (string+8, "Feb", 3) ? 1 :
546 !strncmp (string+8, "Mar", 3) ? 2 :
547 !strncmp (string+8, "Apr", 3) ? 3 :
548 !strncmp (string+8, "May", 3) ? 4 :
549 !strncmp (string+8, "Jun", 3) ? 5 :
550 !strncmp (string+8, "Jul", 3) ? 6 :
551 !strncmp (string+8, "Aug", 3) ? 7 :
552 !strncmp (string+8, "Sep", 3) ? 8 :
553 !strncmp (string+8, "Oct", 3) ? 9 :
554 !strncmp (string+8, "Nov", 3) ? 10 :
555 !strncmp (string+8, "Dec", 3) ? 11 :
556 -1);
557 tm.tm_year = ((1000 * (string[12]-'0') +
558 100 * (string[13]-'0') +
559 10 * (string[14]-'0') +
560 (string[15]-'0'))
561 - 1900);
563 tm.tm_isdst = 0; /* GMT is never in DST, right? */
565 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
566 return 0;
568 /*
569 This is actually wrong: we need to subtract the local timezone
570 offset from GMT from this value. But, that's ok in this usage,
571 because we only comparing these two GMT dates against each other,
572 so it doesn't matter what time zone we parse them in.
573 */
575 t = mktime (&tm);
576 if (t == (time_t) -1) t = 0;
578 if (verbose) {
579 const char *s = string;
580 while (*s && *s != '\r' && *s != '\n')
581 fputc (*s++, stdout);
582 printf (" ==> %lu\n", (unsigned long) t);
583 }
585 return t;
587 } else {
588 return 0;
589 }
590 }
592 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
593 static int
594 expected_statuscode (const char *reply, const char *statuscodes)
595 {
596 char *expected, *code;
597 int result = 0;
599 if ((expected = strdup (statuscodes)) == NULL)
600 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
602 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
603 if (strstr (reply, code) != NULL) {
604 result = 1;
605 break;
606 }
608 free (expected);
609 return result;
610 }
612 static int
613 check_document_dates (const char *headers, char **msg)
614 {
615 const char *s;
616 char *server_date = 0;
617 char *document_date = 0;
618 int date_result = STATE_OK;
620 s = headers;
621 while (*s) {
622 const char *field = s;
623 const char *value = 0;
625 /* Find the end of the header field */
626 while (*s && !isspace(*s) && *s != ':')
627 s++;
629 /* Remember the header value, if any. */
630 if (*s == ':')
631 value = ++s;
633 /* Skip to the end of the header, including continuation lines. */
634 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
635 s++;
637 /* Avoid stepping over end-of-string marker */
638 if (*s)
639 s++;
641 /* Process this header. */
642 if (value && value > field+2) {
643 char *ff = (char *) malloc (value-field);
644 char *ss = ff;
645 while (field < value-1)
646 *ss++ = tolower(*field++);
647 *ss++ = 0;
649 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
650 const char *e;
651 while (*value && isspace (*value))
652 value++;
653 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
654 ;
655 ss = (char *) malloc (e - value + 1);
656 strncpy (ss, value, e - value);
657 ss[e - value] = 0;
658 if (!strcmp (ff, "date")) {
659 if (server_date) free (server_date);
660 server_date = ss;
661 } else {
662 if (document_date) free (document_date);
663 document_date = ss;
664 }
665 }
666 free (ff);
667 }
668 }
670 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
671 if (!server_date || !*server_date) {
672 asprintf (msg, _("%sServer date unknown, "), *msg);
673 date_result = max_state_alt(STATE_UNKNOWN, date_result);
674 } else if (!document_date || !*document_date) {
675 asprintf (msg, _("%sDocument modification date unknown, "), *msg);
676 date_result = max_state_alt(STATE_CRITICAL, date_result);
677 } else {
678 time_t srv_data = parse_time_string (server_date);
679 time_t doc_data = parse_time_string (document_date);
681 if (srv_data <= 0) {
682 asprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
683 date_result = max_state_alt(STATE_CRITICAL, date_result);
684 } else if (doc_data <= 0) {
685 asprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
686 date_result = max_state_alt(STATE_CRITICAL, date_result);
687 } else if (doc_data > srv_data + 30) {
688 asprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
689 date_result = max_state_alt(STATE_CRITICAL, date_result);
690 } else if (doc_data < srv_data - maximum_age) {
691 int n = (srv_data - doc_data);
692 if (n > (60 * 60 * 24 * 2)) {
693 asprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
694 date_result = max_state_alt(STATE_CRITICAL, date_result);
695 } else {
696 asprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
697 date_result = max_state_alt(STATE_CRITICAL, date_result);
698 }
699 }
700 free (server_date);
701 free (document_date);
702 }
703 return date_result;
704 }
706 int
707 get_content_length (const char *headers)
708 {
709 const char *s;
710 int content_length = 0;
712 s = headers;
713 while (*s) {
714 const char *field = s;
715 const char *value = 0;
717 /* Find the end of the header field */
718 while (*s && !isspace(*s) && *s != ':')
719 s++;
721 /* Remember the header value, if any. */
722 if (*s == ':')
723 value = ++s;
725 /* Skip to the end of the header, including continuation lines. */
726 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
727 s++;
729 /* Avoid stepping over end-of-string marker */
730 if (*s)
731 s++;
733 /* Process this header. */
734 if (value && value > field+2) {
735 char *ff = (char *) malloc (value-field);
736 char *ss = ff;
737 while (field < value-1)
738 *ss++ = tolower(*field++);
739 *ss++ = 0;
741 if (!strcmp (ff, "content-length")) {
742 const char *e;
743 while (*value && isspace (*value))
744 value++;
745 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
746 ;
747 ss = (char *) malloc (e - value + 1);
748 strncpy (ss, value, e - value);
749 ss[e - value] = 0;
750 content_length = atoi(ss);
751 free (ss);
752 }
753 free (ff);
754 }
755 }
756 return (content_length);
757 }
759 char *
760 prepend_slash (char *path)
761 {
762 char *newpath;
764 if (path[0] == '/')
765 return path;
767 if ((newpath = malloc (strlen(path) + 2)) == NULL)
768 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
769 newpath[0] = '/';
770 strcpy (newpath + 1, path);
771 free (path);
772 return newpath;
773 }
775 int
776 check_http (void)
777 {
778 char *msg;
779 char *status_line;
780 char *status_code;
781 char *header;
782 char *page;
783 char *auth;
784 int http_status;
785 int i = 0;
786 size_t pagesize = 0;
787 char *full_page;
788 char *full_page_new;
789 char *buf;
790 char *pos;
791 long microsec;
792 double elapsed_time;
793 int page_len = 0;
794 int result = STATE_OK;
796 /* try to connect to the host at the given port number */
797 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
798 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
799 #ifdef HAVE_SSL
800 if (use_ssl == TRUE) {
801 np_net_ssl_init_with_hostname(sd, (use_sni ? host_name : NULL));
802 if (check_cert == TRUE) {
803 result = np_net_ssl_check_cert(days_till_exp);
804 np_net_ssl_cleanup();
805 if (sd) close(sd);
806 return result;
807 }
808 }
809 #endif /* HAVE_SSL */
811 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
813 /* tell HTTP/1.1 servers not to keep the connection alive */
814 asprintf (&buf, "%sConnection: close\r\n", buf);
816 /* optionally send the host header info */
817 if (host_name) {
818 /*
819 * Specify the port only if we're using a non-default port (see RFC 2616,
820 * 14.23). Some server applications/configurations cause trouble if the
821 * (default) port is explicitly specified in the "Host:" header line.
822 */
823 if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
824 (use_ssl == TRUE && server_port == HTTPS_PORT))
825 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
826 else
827 asprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
828 }
830 /* optionally send any other header tag */
831 if (http_opt_headers_count) {
832 for (i = 0; i < http_opt_headers_count ; i++) {
833 for ((pos = strtok(http_opt_headers[i], INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
834 asprintf (&buf, "%s%s\r\n", buf, pos);
835 }
836 /* This cannot be free'd here because a redirection will then try to access this and segfault */
837 /* Covered in a testcase in tests/check_http.t */
838 /* free(http_opt_headers); */
839 }
841 /* optionally send the authentication info */
842 if (strlen(user_auth)) {
843 base64_encode_alloc (user_auth, strlen (user_auth), &auth);
844 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
845 }
847 /* optionally send the proxy authentication info */
848 if (strlen(proxy_auth)) {
849 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
850 asprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
851 }
853 /* either send http POST data (any data, not only POST)*/
854 if (http_post_data) {
855 if (http_content_type) {
856 asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
857 } else {
858 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
859 }
861 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
862 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
863 }
864 else {
865 /* or just a newline so the server knows we're done with the request */
866 asprintf (&buf, "%s%s", buf, CRLF);
867 }
869 if (verbose) printf ("%s\n", buf);
870 my_send (buf, strlen (buf));
872 /* fetch the page */
873 full_page = strdup("");
874 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
875 buffer[i] = '\0';
876 asprintf (&full_page_new, "%s%s", full_page, buffer);
877 free (full_page);
878 full_page = full_page_new;
879 pagesize += i;
881 if (no_body && document_headers_done (full_page)) {
882 i = 0;
883 break;
884 }
885 }
887 if (i < 0 && errno != ECONNRESET) {
888 #ifdef HAVE_SSL
889 /*
890 if (use_ssl) {
891 sslerr=SSL_get_error(ssl, i);
892 if ( sslerr == SSL_ERROR_SSL ) {
893 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
894 } else {
895 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
896 }
897 }
898 else {
899 */
900 #endif
901 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
902 #ifdef HAVE_SSL
903 /* XXX
904 }
905 */
906 #endif
907 }
909 /* return a CRITICAL status if we couldn't read any data */
910 if (pagesize == (size_t) 0)
911 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
913 /* close the connection */
914 #ifdef HAVE_SSL
915 np_net_ssl_cleanup();
916 #endif
917 if (sd) close(sd);
919 /* Save check time */
920 microsec = deltime (tv);
921 elapsed_time = (double)microsec / 1.0e6;
923 /* leave full_page untouched so we can free it later */
924 page = full_page;
926 if (verbose)
927 printf ("%s://%s:%d%s is %d characters\n",
928 use_ssl ? "https" : "http", server_address,
929 server_port, server_url, (int)pagesize);
931 /* find status line and null-terminate it */
932 status_line = page;
933 page += (size_t) strcspn (page, "\r\n");
934 pos = page;
935 page += (size_t) strspn (page, "\r\n");
936 status_line[strcspn(status_line, "\r\n")] = 0;
937 strip (status_line);
938 if (verbose)
939 printf ("STATUS: %s\n", status_line);
941 /* find header info and null-terminate it */
942 header = page;
943 while (strcspn (page, "\r\n") > 0) {
944 page += (size_t) strcspn (page, "\r\n");
945 pos = page;
946 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
947 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
948 page += (size_t) 2;
949 else
950 page += (size_t) 1;
951 }
952 page += (size_t) strspn (page, "\r\n");
953 header[pos - header] = 0;
954 if (verbose)
955 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
956 (no_body ? " [[ skipped ]]" : page));
958 /* make sure the status line matches the response we are looking for */
959 if (!expected_statuscode (status_line, server_expect)) {
960 if (server_port == HTTP_PORT)
961 asprintf (&msg,
962 _("Invalid HTTP response received from host: %s\n"),
963 status_line);
964 else
965 asprintf (&msg,
966 _("Invalid HTTP response received from host on port %d: %s\n"),
967 server_port, status_line);
968 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
969 }
971 /* Bypass normal status line check if server_expect was set by user and not default */
972 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
973 if ( server_expect_yn ) {
974 asprintf (&msg,
975 _("Status line output matched \"%s\" - "), server_expect);
976 if (verbose)
977 printf ("%s\n",msg);
978 }
979 else {
980 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
981 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
982 /* Status-Code = 3 DIGITS */
984 status_code = strchr (status_line, ' ') + sizeof (char);
985 if (strspn (status_code, "1234567890") != 3)
986 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
988 http_status = atoi (status_code);
990 /* check the return code */
992 if (http_status >= 600 || http_status < 100) {
993 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
994 }
995 /* server errors result in a critical state */
996 else if (http_status >= 500) {
997 asprintf (&msg, _("%s - "), status_line);
998 result = STATE_CRITICAL;
999 }
1000 /* client errors result in a warning state */
1001 else if (http_status >= 400) {
1002 asprintf (&msg, _("%s - "), status_line);
1003 result = max_state_alt(STATE_WARNING, result);
1004 }
1005 /* check redirected page if specified */
1006 else if (http_status >= 300) {
1008 if (onredirect == STATE_DEPENDENT)
1009 redir (header, status_line);
1010 else
1011 result = max_state_alt(onredirect, result);
1012 asprintf (&msg, _("%s - "), status_line);
1013 } /* end if (http_status >= 300) */
1014 else {
1015 /* Print OK status anyway */
1016 asprintf (&msg, _("%s - "), status_line);
1017 }
1019 } /* end else (server_expect_yn) */
1021 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1022 alarm (0);
1024 if (maximum_age >= 0) {
1025 result = max_state_alt(check_document_dates(header, &msg), result);
1026 }
1028 /* Page and Header content checks go here */
1030 if (strlen (string_expect)) {
1031 if (!strstr (page, string_expect)) {
1032 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1033 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1034 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1035 }
1036 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);
1037 result = STATE_CRITICAL;
1038 }
1039 }
1041 if (strlen (regexp)) {
1042 errcode = regexec (&preg, page, REGS, pmatch, 0);
1043 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1044 /* OK - No-op to avoid changing the logic around it */
1045 result = max_state_alt(STATE_OK, result);
1046 }
1047 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1048 if (invert_regex == 0)
1049 asprintf (&msg, _("%spattern not found, "), msg);
1050 else
1051 asprintf (&msg, _("%spattern found, "), msg);
1052 result = STATE_CRITICAL;
1053 }
1054 else {
1055 /* FIXME: Shouldn't that be UNKNOWN? */
1056 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1057 asprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1058 result = STATE_CRITICAL;
1059 }
1060 }
1062 /* make sure the page is of an appropriate size */
1063 /* page_len = get_content_length(header); */
1064 /* FIXME: Will this work with -N ? IMHO we should use
1065 * get_content_length(header) and always check if it's different than the
1066 * returned pagesize
1067 */
1068 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1069 * it == get_content_length(header) ??
1070 */
1071 page_len = pagesize;
1072 if ((max_page_len > 0) && (page_len > max_page_len)) {
1073 asprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1074 result = max_state_alt(STATE_WARNING, result);
1075 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1076 asprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1077 result = max_state_alt(STATE_WARNING, result);
1078 }
1080 /* Cut-off trailing characters */
1081 if(msg[strlen(msg)-2] == ',')
1082 msg[strlen(msg)-2] = '\0';
1083 else
1084 msg[strlen(msg)-3] = '\0';
1086 /* check elapsed time */
1087 asprintf (&msg,
1088 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1089 msg, page_len, elapsed_time,
1090 (display_html ? "</A>" : ""),
1091 perfd_time (elapsed_time), perfd_size (page_len));
1093 result = max_state_alt(get_status(elapsed_time, thlds), result);
1095 die (result, "HTTP %s: %s\n", state_text(result), msg);
1096 /* die failed? */
1097 return STATE_UNKNOWN;
1098 }
1102 /* per RFC 2396 */
1103 #define URI_HTTP "%5[HTPShtps]"
1104 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1105 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1106 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1107 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1108 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1109 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1110 #define HD4 URI_HTTP "://" URI_HOST
1111 #define HD5 URI_PATH
1113 void
1114 redir (char *pos, char *status_line)
1115 {
1116 int i = 0;
1117 char *x;
1118 char xx[2];
1119 char type[6];
1120 char *addr;
1121 char *url;
1123 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1124 if (addr == NULL)
1125 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1127 url = malloc (strcspn (pos, "\r\n"));
1128 if (url == NULL)
1129 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1131 while (pos) {
1132 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1133 if (i == 0) {
1134 pos += (size_t) strcspn (pos, "\r\n");
1135 pos += (size_t) strspn (pos, "\r\n");
1136 if (strlen(pos) == 0)
1137 die (STATE_UNKNOWN,
1138 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1139 status_line, (display_html ? "</A>" : ""));
1140 continue;
1141 }
1143 pos += i;
1144 pos += strspn (pos, " \t");
1146 /*
1147 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1148 * preceding each extra line with at least one SP or HT.''
1149 */
1150 for (; (i = strspn (pos, "\r\n")); pos += i) {
1151 pos += i;
1152 if (!(i = strspn (pos, " \t"))) {
1153 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1154 display_html ? "</A>" : "");
1155 }
1156 }
1158 url = realloc (url, strcspn (pos, "\r\n") + 1);
1159 if (url == NULL)
1160 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1162 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1163 if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1164 url = prepend_slash (url);
1165 use_ssl = server_type_check (type);
1166 }
1168 /* URI_HTTP URI_HOST URI_PATH */
1169 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1170 url = prepend_slash (url);
1171 use_ssl = server_type_check (type);
1172 i = server_port_check (use_ssl);
1173 }
1175 /* URI_HTTP URI_HOST URI_PORT */
1176 else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1177 strcpy (url, HTTP_URL);
1178 use_ssl = server_type_check (type);
1179 }
1181 /* URI_HTTP URI_HOST */
1182 else if (sscanf (pos, HD4, type, addr) == 2) {
1183 strcpy (url, HTTP_URL);
1184 use_ssl = server_type_check (type);
1185 i = server_port_check (use_ssl);
1186 }
1188 /* URI_PATH */
1189 else if (sscanf (pos, HD5, url) == 1) {
1190 /* relative url */
1191 if ((url[0] != '/')) {
1192 if ((x = strrchr(server_url, '/')))
1193 *x = '\0';
1194 asprintf (&url, "%s/%s", server_url, url);
1195 }
1196 i = server_port;
1197 strcpy (type, server_type);
1198 strcpy (addr, host_name ? host_name : server_address);
1199 }
1201 else {
1202 die (STATE_UNKNOWN,
1203 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1204 pos, (display_html ? "</A>" : ""));
1205 }
1207 break;
1209 } /* end while (pos) */
1211 if (++redir_depth > max_depth)
1212 die (STATE_WARNING,
1213 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1214 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1216 if (server_port==i &&
1217 !strcmp(server_address, addr) &&
1218 (host_name && !strcmp(host_name, addr)) &&
1219 !strcmp(server_url, url))
1220 die (STATE_WARNING,
1221 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1222 type, addr, i, url, (display_html ? "</A>" : ""));
1224 strcpy (server_type, type);
1226 free (host_name);
1227 host_name = strdup (addr);
1229 if (!(followsticky & STICKY_HOST)) {
1230 free (server_address);
1231 server_address = strdup (addr);
1232 }
1233 if (!(followsticky & STICKY_PORT)) {
1234 server_port = i;
1235 }
1237 free (server_url);
1238 server_url = url;
1240 if (server_port > MAX_PORT)
1241 die (STATE_UNKNOWN,
1242 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1243 MAX_PORT, server_type, server_address, server_port, server_url,
1244 display_html ? "</A>" : "");
1246 if (verbose)
1247 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1248 host_name ? host_name : server_address, server_port, server_url);
1250 check_http ();
1251 }
1254 int
1255 server_type_check (const char *type)
1256 {
1257 if (strcmp (type, "https"))
1258 return FALSE;
1259 else
1260 return TRUE;
1261 }
1263 int
1264 server_port_check (int ssl_flag)
1265 {
1266 if (ssl_flag)
1267 return HTTPS_PORT;
1268 else
1269 return HTTP_PORT;
1270 }
1272 char *perfd_time (double elapsed_time)
1273 {
1274 return fperfdata ("time", elapsed_time, "s",
1275 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1276 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1277 TRUE, 0, FALSE, 0);
1278 }
1282 char *perfd_size (int page_len)
1283 {
1284 return perfdata ("size", page_len, "B",
1285 (min_page_len>0?TRUE:FALSE), min_page_len,
1286 (min_page_len>0?TRUE:FALSE), 0,
1287 TRUE, 0, FALSE, 0);
1288 }
1290 void
1291 print_help (void)
1292 {
1293 print_revision (progname, NP_VERSION);
1295 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1296 printf (COPYRIGHT, copyright, email);
1298 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1299 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1300 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1301 printf ("%s\n", _("certificate expiration times."));
1303 printf ("\n\n");
1305 print_usage ();
1307 printf (_("NOTE: One or both of -H and -I must be specified"));
1309 printf ("\n");
1311 printf (UT_HELP_VRSN);
1312 printf (UT_EXTRA_OPTS);
1314 printf (" %s\n", "-H, --hostname=ADDRESS");
1315 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1316 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1317 printf (" %s\n", "-I, --IP-address=ADDRESS");
1318 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1319 printf (" %s\n", "-p, --port=INTEGER");
1320 printf (" %s", _("Port number (default: "));
1321 printf ("%d)\n", HTTP_PORT);
1323 printf (UT_IPv46);
1325 #ifdef HAVE_SSL
1326 printf (" %s\n", "-S, --ssl");
1327 printf (" %s\n", _("Connect via SSL. Port defaults to 443"));
1328 printf (" %s\n", "--sni");
1329 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1330 printf (" %s\n", "-C, --certificate=INTEGER");
1331 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1332 printf (" %s\n", _("(when this option is used the URL is not checked.)\n"));
1333 #endif
1335 printf (" %s\n", "-e, --expect=STRING");
1336 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1337 printf (" %s", _("the first (status) line of the server response (default: "));
1338 printf ("%s)\n", HTTP_EXPECT);
1339 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1340 printf (" %s\n", "-s, --string=STRING");
1341 printf (" %s\n", _("String to expect in the content"));
1342 printf (" %s\n", "-u, --url=PATH");
1343 printf (" %s\n", _("URL to GET or POST (default: /)"));
1344 printf (" %s\n", "-P, --post=STRING");
1345 printf (" %s\n", _("URL encoded http POST data"));
1346 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1347 printf (" %s\n", _("Set HTTP method."));
1348 printf (" %s\n", "-N, --no-body");
1349 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1350 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1351 printf (" %s\n", "-M, --max-age=SECONDS");
1352 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1353 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1354 printf (" %s\n", "-T, --content-type=STRING");
1355 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1357 printf (" %s\n", "-l, --linespan");
1358 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1359 printf (" %s\n", "-r, --regex, --ereg=STRING");
1360 printf (" %s\n", _("Search page for regex STRING"));
1361 printf (" %s\n", "-R, --eregi=STRING");
1362 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1363 printf (" %s\n", "--invert-regex");
1364 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1366 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1367 printf (" %s\n", _("Username:password on sites with basic authentication"));
1368 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1369 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1370 printf (" %s\n", "-A, --useragent=STRING");
1371 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1372 printf (" %s\n", "-k, --header=STRING");
1373 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1374 printf (" %s\n", "-L, --link");
1375 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1376 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1377 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1378 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1379 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1380 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1382 printf (UT_WARN_CRIT);
1384 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1386 printf (UT_VERBOSE);
1388 printf ("\n");
1389 printf ("%s\n", _("Notes:"));
1390 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1391 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1392 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1393 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1394 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1395 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1397 #ifdef HAVE_SSL
1398 printf ("\n");
1399 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1400 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1401 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1402 printf ("\n");
1403 printf ("%s\n", _("Examples:"));
1404 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1405 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1406 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1407 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1408 printf (" %s\n\n", _("a STATE_CRITICAL will be returned."));
1410 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1411 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1412 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1413 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1414 printf (" %s\n", _("the certificate is expired."));
1415 #endif
1417 printf (UT_SUPPORT);
1419 }
1423 void
1424 print_usage (void)
1425 {
1426 printf ("%s\n", _("Usage:"));
1427 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1428 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
1429 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1430 printf (" [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1431 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1432 printf (" [-A string] [-k string] [-S] [--sni] [-C <age>] [-T <content-type>]\n");
1433 printf (" [-j method]\n");
1434 }