check_procs: Assume we have stat()
[monitoring-plugins.git] / plugins / check_http.c
blob6db38e8c6707baaa79be1c48b97cde49542e9357
1 /*****************************************************************************
3 * Nagios check_http plugin
5 * License: GPL
6 * Copyright (c) 1999-2013 Nagios Plugins Development Team
8 * Description:
10 * This file contains the check_http plugin
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.
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.
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.
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/>.
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-2013";
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 STICKY_NONE 0
47 #define STICKY_HOST 1
48 #define STICKY_PORT 2
50 #define HTTP_EXPECT "HTTP/1."
51 enum {
52 MAX_IPV4_HOSTLENGTH = 255,
53 HTTP_PORT = 80,
54 HTTPS_PORT = 443,
55 MAX_PORT = 65535
58 #ifdef HAVE_SSL
59 int check_cert = FALSE;
60 int ssl_version;
61 int days_till_exp_warn, days_till_exp_crit;
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
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;
87 struct timeval tv_temp;
89 #define HTTP_URL "/"
90 #define CRLF "\r\n"
92 int specify_port = FALSE;
93 int server_port = HTTP_PORT;
94 char server_port_text[6] = "";
95 char server_type[6] = "http";
96 char *server_address;
97 char *host_name;
98 char *server_url;
99 char *user_agent;
100 int server_url_length;
101 int server_expect_yn = 0;
102 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
103 char string_expect[MAX_INPUT_BUFFER] = "";
104 char output_string_search[30] = "";
105 char *warning_thresholds = NULL;
106 char *critical_thresholds = NULL;
107 thresholds *thlds;
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 show_extended_perfdata = FALSE;
119 int sd;
120 int min_page_len = 0;
121 int max_page_len = 0;
122 int redir_depth = 0;
123 int max_depth = 15;
124 char *http_method;
125 char *http_post_data;
126 char *http_content_type;
127 char buffer[MAX_INPUT_BUFFER];
129 int process_arguments (int, char **);
130 int check_http (void);
131 void redir (char *pos, char *status_line);
132 int server_type_check(const char *type);
133 int server_port_check(int ssl_flag);
134 char *perfd_time (double microsec);
135 char *perfd_time_connect (double microsec);
136 char *perfd_time_ssl (double microsec);
137 char *perfd_time_firstbyte (double microsec);
138 char *perfd_time_headers (double microsec);
139 char *perfd_time_transfer (double microsec);
140 char *perfd_size (int page_len);
141 void print_help (void);
142 void print_usage (void);
145 main (int argc, char **argv)
147 int result = STATE_UNKNOWN;
149 setlocale (LC_ALL, "");
150 bindtextdomain (PACKAGE, LOCALEDIR);
151 textdomain (PACKAGE);
153 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
154 server_url = strdup(HTTP_URL);
155 server_url_length = strlen(server_url);
156 xasprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
157 NP_VERSION, VERSION);
159 /* Parse extra opts if any */
160 argv=np_extra_opts (&argc, argv, progname);
162 if (process_arguments (argc, argv) == ERROR)
163 usage4 (_("Could not parse arguments"));
165 if (display_html == TRUE)
166 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
167 use_ssl ? "https" : "http", host_name ? host_name : server_address,
168 server_port, server_url);
170 /* initialize alarm signal handling, set socket timeout, start timer */
171 (void) signal (SIGALRM, socket_timeout_alarm_handler);
172 (void) alarm (socket_timeout);
173 gettimeofday (&tv, NULL);
175 result = check_http ();
176 return result;
181 /* process command-line arguments */
183 process_arguments (int argc, char **argv)
185 int c = 1;
186 char *p;
187 char *temp;
189 enum {
190 INVERT_REGEX = CHAR_MAX + 1,
191 SNI_OPTION
194 int option = 0;
195 static struct option longopts[] = {
196 STD_LONG_OPTS,
197 {"link", no_argument, 0, 'L'},
198 {"nohtml", no_argument, 0, 'n'},
199 {"ssl", optional_argument, 0, 'S'},
200 {"sni", no_argument, 0, SNI_OPTION},
201 {"post", required_argument, 0, 'P'},
202 {"method", required_argument, 0, 'j'},
203 {"IP-address", required_argument, 0, 'I'},
204 {"url", required_argument, 0, 'u'},
205 {"port", required_argument, 0, 'p'},
206 {"authorization", required_argument, 0, 'a'},
207 {"proxy_authorization", required_argument, 0, 'b'},
208 {"string", required_argument, 0, 's'},
209 {"expect", required_argument, 0, 'e'},
210 {"regex", required_argument, 0, 'r'},
211 {"ereg", required_argument, 0, 'r'},
212 {"eregi", required_argument, 0, 'R'},
213 {"linespan", no_argument, 0, 'l'},
214 {"onredirect", required_argument, 0, 'f'},
215 {"certificate", required_argument, 0, 'C'},
216 {"useragent", required_argument, 0, 'A'},
217 {"header", required_argument, 0, 'k'},
218 {"no-body", no_argument, 0, 'N'},
219 {"max-age", required_argument, 0, 'M'},
220 {"content-type", required_argument, 0, 'T'},
221 {"pagesize", required_argument, 0, 'm'},
222 {"invert-regex", no_argument, NULL, INVERT_REGEX},
223 {"use-ipv4", no_argument, 0, '4'},
224 {"use-ipv6", no_argument, 0, '6'},
225 {"extended-perfdata", no_argument, 0, 'E'},
226 {0, 0, 0, 0}
229 if (argc < 2)
230 return ERROR;
232 for (c = 1; c < argc; c++) {
233 if (strcmp ("-to", argv[c]) == 0)
234 strcpy (argv[c], "-t");
235 if (strcmp ("-hn", argv[c]) == 0)
236 strcpy (argv[c], "-H");
237 if (strcmp ("-wt", argv[c]) == 0)
238 strcpy (argv[c], "-w");
239 if (strcmp ("-ct", argv[c]) == 0)
240 strcpy (argv[c], "-c");
241 if (strcmp ("-nohtml", argv[c]) == 0)
242 strcpy (argv[c], "-n");
245 while (1) {
246 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:nlLS::m:M:N:E", longopts, &option);
247 if (c == -1 || c == EOF)
248 break;
250 switch (c) {
251 case '?': /* usage */
252 usage5 ();
253 break;
254 case 'h': /* help */
255 print_help ();
256 exit (STATE_OK);
257 break;
258 case 'V': /* version */
259 print_revision (progname, NP_VERSION);
260 exit (STATE_OK);
261 break;
262 case 't': /* timeout period */
263 if (!is_intnonneg (optarg))
264 usage2 (_("Timeout interval must be a positive integer"), optarg);
265 else
266 socket_timeout = atoi (optarg);
267 break;
268 case 'c': /* critical time threshold */
269 critical_thresholds = optarg;
270 break;
271 case 'w': /* warning time threshold */
272 warning_thresholds = optarg;
273 break;
274 case 'A': /* User Agent String */
275 xasprintf (&user_agent, "User-Agent: %s", optarg);
276 break;
277 case 'k': /* Additional headers */
278 if (http_opt_headers_count == 0)
279 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
280 else
281 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
282 http_opt_headers[http_opt_headers_count - 1] = optarg;
283 /* xasprintf (&http_opt_headers, "%s", optarg); */
284 break;
285 case 'L': /* show html link */
286 display_html = TRUE;
287 break;
288 case 'n': /* do not show html link */
289 display_html = FALSE;
290 break;
291 case 'C': /* Check SSL cert validity */
292 #ifdef HAVE_SSL
293 if ((temp=strchr(optarg,','))!=NULL) {
294 *temp='\0';
295 if (!is_intnonneg (optarg))
296 usage2 (_("Invalid certificate expiration period"), optarg);
297 days_till_exp_warn = atoi(optarg);
298 *temp=',';
299 temp++;
300 if (!is_intnonneg (temp))
301 usage2 (_("Invalid certificate expiration period"), temp);
302 days_till_exp_crit = atoi (temp);
304 else {
305 days_till_exp_crit=0;
306 if (!is_intnonneg (optarg))
307 usage2 (_("Invalid certificate expiration period"), optarg);
308 days_till_exp_warn = atoi (optarg);
310 check_cert = TRUE;
311 /* Fall through to -S option */
312 #endif
313 case 'S': /* use SSL */
314 #ifdef HAVE_SSL
315 use_ssl = TRUE;
316 if (optarg == NULL || c != 'S')
317 ssl_version = 0;
318 else {
319 ssl_version = atoi(optarg);
320 if (ssl_version < 1 || ssl_version > 3)
321 usage4 (_("Invalid option - Valid values for SSL Version are 1 (TLSv1), 2 (SSLv2) or 3 (SSLv3)"));
323 if (specify_port == FALSE)
324 server_port = HTTPS_PORT;
325 #else
326 usage4 (_("Invalid option - SSL is not available"));
327 #endif
328 break;
329 case SNI_OPTION:
330 use_sni = TRUE;
331 break;
332 case 'f': /* onredirect */
333 if (!strcmp (optarg, "stickyport"))
334 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
335 else if (!strcmp (optarg, "sticky"))
336 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
337 else if (!strcmp (optarg, "follow"))
338 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
339 else if (!strcmp (optarg, "unknown"))
340 onredirect = STATE_UNKNOWN;
341 else if (!strcmp (optarg, "ok"))
342 onredirect = STATE_OK;
343 else if (!strcmp (optarg, "warning"))
344 onredirect = STATE_WARNING;
345 else if (!strcmp (optarg, "critical"))
346 onredirect = STATE_CRITICAL;
347 else usage2 (_("Invalid onredirect option"), optarg);
348 if (verbose)
349 printf(_("option f:%d \n"), onredirect);
350 break;
351 /* Note: H, I, and u must be malloc'd or will fail on redirects */
352 case 'H': /* Host Name (virtual host) */
353 host_name = strdup (optarg);
354 if (host_name[0] == '[') {
355 if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
356 server_port = atoi (p + 2);
357 } else if ((p = strchr (host_name, ':')) != NULL
358 && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
359 server_port = atoi (p);
360 break;
361 case 'I': /* Server IP-address */
362 server_address = strdup (optarg);
363 break;
364 case 'u': /* URL path */
365 server_url = strdup (optarg);
366 server_url_length = strlen (server_url);
367 break;
368 case 'p': /* Server port */
369 if (!is_intnonneg (optarg))
370 usage2 (_("Invalid port number"), optarg);
371 else {
372 server_port = atoi (optarg);
373 specify_port = TRUE;
375 break;
376 case 'a': /* authorization info */
377 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
378 user_auth[MAX_INPUT_BUFFER - 1] = 0;
379 break;
380 case 'b': /* proxy-authorization info */
381 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
382 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
383 break;
384 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
385 if (! http_post_data)
386 http_post_data = strdup (optarg);
387 if (! http_method)
388 http_method = strdup("POST");
389 break;
390 case 'j': /* Set HTTP method */
391 if (http_method)
392 free(http_method);
393 http_method = strdup (optarg);
394 break;
395 case 's': /* string or substring */
396 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
397 string_expect[MAX_INPUT_BUFFER - 1] = 0;
398 break;
399 case 'e': /* string or substring */
400 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
401 server_expect[MAX_INPUT_BUFFER - 1] = 0;
402 server_expect_yn = 1;
403 break;
404 case 'T': /* Content-type */
405 xasprintf (&http_content_type, "%s", optarg);
406 break;
407 case 'l': /* linespan */
408 cflags &= ~REG_NEWLINE;
409 break;
410 case 'R': /* regex */
411 cflags |= REG_ICASE;
412 case 'r': /* regex */
413 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
414 regexp[MAX_RE_SIZE - 1] = 0;
415 errcode = regcomp (&preg, regexp, cflags);
416 if (errcode != 0) {
417 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
418 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
419 return ERROR;
421 break;
422 case INVERT_REGEX:
423 invert_regex = 1;
424 break;
425 case '4':
426 address_family = AF_INET;
427 break;
428 case '6':
429 #ifdef USE_IPV6
430 address_family = AF_INET6;
431 #else
432 usage4 (_("IPv6 support not available"));
433 #endif
434 break;
435 case 'v': /* verbose */
436 verbose = TRUE;
437 break;
438 case 'm': /* min_page_length */
440 char *tmp;
441 if (strchr(optarg, ':') != (char *)NULL) {
442 /* range, so get two values, min:max */
443 tmp = strtok(optarg, ":");
444 if (tmp == NULL) {
445 printf("Bad format: try \"-m min:max\"\n");
446 exit (STATE_WARNING);
447 } else
448 min_page_len = atoi(tmp);
450 tmp = strtok(NULL, ":");
451 if (tmp == NULL) {
452 printf("Bad format: try \"-m min:max\"\n");
453 exit (STATE_WARNING);
454 } else
455 max_page_len = atoi(tmp);
456 } else
457 min_page_len = atoi (optarg);
458 break;
460 case 'N': /* no-body */
461 no_body = TRUE;
462 break;
463 case 'M': /* max-age */
465 int L = strlen(optarg);
466 if (L && optarg[L-1] == 'm')
467 maximum_age = atoi (optarg) * 60;
468 else if (L && optarg[L-1] == 'h')
469 maximum_age = atoi (optarg) * 60 * 60;
470 else if (L && optarg[L-1] == 'd')
471 maximum_age = atoi (optarg) * 60 * 60 * 24;
472 else if (L && (optarg[L-1] == 's' ||
473 isdigit (optarg[L-1])))
474 maximum_age = atoi (optarg);
475 else {
476 fprintf (stderr, "unparsable max-age: %s\n", optarg);
477 exit (STATE_WARNING);
480 break;
481 case 'E': /* show extended perfdata */
482 show_extended_perfdata = TRUE;
483 break;
487 c = optind;
489 if (server_address == NULL && c < argc)
490 server_address = strdup (argv[c++]);
492 if (host_name == NULL && c < argc)
493 host_name = strdup (argv[c++]);
495 if (server_address == NULL) {
496 if (host_name == NULL)
497 usage4 (_("You must specify a server address or host name"));
498 else
499 server_address = strdup (host_name);
502 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
504 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
505 socket_timeout = (int)thlds->critical->end + 1;
507 if (http_method == NULL)
508 http_method = strdup ("GET");
510 return TRUE;
515 /* Returns 1 if we're done processing the document body; 0 to keep going */
516 static int
517 document_headers_done (char *full_page)
519 const char *body;
521 for (body = full_page; *body; body++) {
522 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
523 break;
526 if (!*body)
527 return 0; /* haven't read end of headers yet */
529 full_page[body - full_page] = 0;
530 return 1;
533 static time_t
534 parse_time_string (const char *string)
536 struct tm tm;
537 time_t t;
538 memset (&tm, 0, sizeof(tm));
540 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
542 if (isupper (string[0]) && /* Tue */
543 islower (string[1]) &&
544 islower (string[2]) &&
545 ',' == string[3] &&
546 ' ' == string[4] &&
547 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
548 isdigit (string[6]) &&
549 ' ' == string[7] &&
550 isupper (string[8]) && /* Dec */
551 islower (string[9]) &&
552 islower (string[10]) &&
553 ' ' == string[11] &&
554 isdigit (string[12]) && /* 2001 */
555 isdigit (string[13]) &&
556 isdigit (string[14]) &&
557 isdigit (string[15]) &&
558 ' ' == string[16] &&
559 isdigit (string[17]) && /* 02: */
560 isdigit (string[18]) &&
561 ':' == string[19] &&
562 isdigit (string[20]) && /* 59: */
563 isdigit (string[21]) &&
564 ':' == string[22] &&
565 isdigit (string[23]) && /* 03 */
566 isdigit (string[24]) &&
567 ' ' == string[25] &&
568 'G' == string[26] && /* GMT */
569 'M' == string[27] && /* GMT */
570 'T' == string[28]) {
572 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
573 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
574 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
575 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
576 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
577 !strncmp (string+8, "Feb", 3) ? 1 :
578 !strncmp (string+8, "Mar", 3) ? 2 :
579 !strncmp (string+8, "Apr", 3) ? 3 :
580 !strncmp (string+8, "May", 3) ? 4 :
581 !strncmp (string+8, "Jun", 3) ? 5 :
582 !strncmp (string+8, "Jul", 3) ? 6 :
583 !strncmp (string+8, "Aug", 3) ? 7 :
584 !strncmp (string+8, "Sep", 3) ? 8 :
585 !strncmp (string+8, "Oct", 3) ? 9 :
586 !strncmp (string+8, "Nov", 3) ? 10 :
587 !strncmp (string+8, "Dec", 3) ? 11 :
588 -1);
589 tm.tm_year = ((1000 * (string[12]-'0') +
590 100 * (string[13]-'0') +
591 10 * (string[14]-'0') +
592 (string[15]-'0'))
593 - 1900);
595 tm.tm_isdst = 0; /* GMT is never in DST, right? */
597 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
598 return 0;
601 This is actually wrong: we need to subtract the local timezone
602 offset from GMT from this value. But, that's ok in this usage,
603 because we only comparing these two GMT dates against each other,
604 so it doesn't matter what time zone we parse them in.
607 t = mktime (&tm);
608 if (t == (time_t) -1) t = 0;
610 if (verbose) {
611 const char *s = string;
612 while (*s && *s != '\r' && *s != '\n')
613 fputc (*s++, stdout);
614 printf (" ==> %lu\n", (unsigned long) t);
617 return t;
619 } else {
620 return 0;
624 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
625 static int
626 expected_statuscode (const char *reply, const char *statuscodes)
628 char *expected, *code;
629 int result = 0;
631 if ((expected = strdup (statuscodes)) == NULL)
632 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
634 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
635 if (strstr (reply, code) != NULL) {
636 result = 1;
637 break;
640 free (expected);
641 return result;
644 static int
645 check_document_dates (const char *headers, char **msg)
647 const char *s;
648 char *server_date = 0;
649 char *document_date = 0;
650 int date_result = STATE_OK;
652 s = headers;
653 while (*s) {
654 const char *field = s;
655 const char *value = 0;
657 /* Find the end of the header field */
658 while (*s && !isspace(*s) && *s != ':')
659 s++;
661 /* Remember the header value, if any. */
662 if (*s == ':')
663 value = ++s;
665 /* Skip to the end of the header, including continuation lines. */
666 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
667 s++;
669 /* Avoid stepping over end-of-string marker */
670 if (*s)
671 s++;
673 /* Process this header. */
674 if (value && value > field+2) {
675 char *ff = (char *) malloc (value-field);
676 char *ss = ff;
677 while (field < value-1)
678 *ss++ = tolower(*field++);
679 *ss++ = 0;
681 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
682 const char *e;
683 while (*value && isspace (*value))
684 value++;
685 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
687 ss = (char *) malloc (e - value + 1);
688 strncpy (ss, value, e - value);
689 ss[e - value] = 0;
690 if (!strcmp (ff, "date")) {
691 if (server_date) free (server_date);
692 server_date = ss;
693 } else {
694 if (document_date) free (document_date);
695 document_date = ss;
698 free (ff);
702 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
703 if (!server_date || !*server_date) {
704 xasprintf (msg, _("%sServer date unknown, "), *msg);
705 date_result = max_state_alt(STATE_UNKNOWN, date_result);
706 } else if (!document_date || !*document_date) {
707 xasprintf (msg, _("%sDocument modification date unknown, "), *msg);
708 date_result = max_state_alt(STATE_CRITICAL, date_result);
709 } else {
710 time_t srv_data = parse_time_string (server_date);
711 time_t doc_data = parse_time_string (document_date);
713 if (srv_data <= 0) {
714 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
715 date_result = max_state_alt(STATE_CRITICAL, date_result);
716 } else if (doc_data <= 0) {
717 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
718 date_result = max_state_alt(STATE_CRITICAL, date_result);
719 } else if (doc_data > srv_data + 30) {
720 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
721 date_result = max_state_alt(STATE_CRITICAL, date_result);
722 } else if (doc_data < srv_data - maximum_age) {
723 int n = (srv_data - doc_data);
724 if (n > (60 * 60 * 24 * 2)) {
725 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
726 date_result = max_state_alt(STATE_CRITICAL, date_result);
727 } else {
728 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
729 date_result = max_state_alt(STATE_CRITICAL, date_result);
732 free (server_date);
733 free (document_date);
735 return date_result;
739 get_content_length (const char *headers)
741 const char *s;
742 int content_length = 0;
744 s = headers;
745 while (*s) {
746 const char *field = s;
747 const char *value = 0;
749 /* Find the end of the header field */
750 while (*s && !isspace(*s) && *s != ':')
751 s++;
753 /* Remember the header value, if any. */
754 if (*s == ':')
755 value = ++s;
757 /* Skip to the end of the header, including continuation lines. */
758 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
759 s++;
761 /* Avoid stepping over end-of-string marker */
762 if (*s)
763 s++;
765 /* Process this header. */
766 if (value && value > field+2) {
767 char *ff = (char *) malloc (value-field);
768 char *ss = ff;
769 while (field < value-1)
770 *ss++ = tolower(*field++);
771 *ss++ = 0;
773 if (!strcmp (ff, "content-length")) {
774 const char *e;
775 while (*value && isspace (*value))
776 value++;
777 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
779 ss = (char *) malloc (e - value + 1);
780 strncpy (ss, value, e - value);
781 ss[e - value] = 0;
782 content_length = atoi(ss);
783 free (ss);
785 free (ff);
788 return (content_length);
791 char *
792 prepend_slash (char *path)
794 char *newpath;
796 if (path[0] == '/')
797 return path;
799 if ((newpath = malloc (strlen(path) + 2)) == NULL)
800 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
801 newpath[0] = '/';
802 strcpy (newpath + 1, path);
803 free (path);
804 return newpath;
808 check_http (void)
810 char *msg;
811 char *status_line;
812 char *status_code;
813 char *header;
814 char *page;
815 char *auth;
816 int http_status;
817 int i = 0;
818 size_t pagesize = 0;
819 char *full_page;
820 char *full_page_new;
821 char *buf;
822 char *pos;
823 long microsec;
824 double elapsed_time;
825 long microsec_connect;
826 double elapsed_time_connect;
827 long microsec_ssl;
828 double elapsed_time_ssl;
829 long microsec_firstbyte;
830 double elapsed_time_firstbyte;
831 long microsec_headers;
832 double elapsed_time_headers;
833 long microsec_transfer;
834 double elapsed_time_transfer;
835 int page_len = 0;
836 int result = STATE_OK;
838 /* try to connect to the host at the given port number */
839 gettimeofday (&tv_temp, NULL);
840 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
841 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
842 microsec_connect = deltime (tv_temp);
843 #ifdef HAVE_SSL
844 elapsed_time_connect = (double)microsec_connect / 1.0e6;
845 if (use_ssl == TRUE) {
846 gettimeofday (&tv_temp, NULL);
847 result = np_net_ssl_init_with_hostname_and_version(sd, (use_sni ? host_name : NULL), ssl_version);
848 if (result != STATE_OK)
849 return result;
850 microsec_ssl = deltime (tv_temp);
851 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
852 if (check_cert == TRUE) {
853 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
854 np_net_ssl_cleanup();
855 if (sd) close(sd);
856 return result;
859 #endif /* HAVE_SSL */
861 xasprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
863 /* tell HTTP/1.1 servers not to keep the connection alive */
864 xasprintf (&buf, "%sConnection: close\r\n", buf);
866 /* optionally send the host header info */
867 if (host_name) {
869 * Specify the port only if we're using a non-default port (see RFC 2616,
870 * 14.23). Some server applications/configurations cause trouble if the
871 * (default) port is explicitly specified in the "Host:" header line.
873 if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
874 (use_ssl == TRUE && server_port == HTTPS_PORT))
875 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
876 else
877 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
880 /* optionally send any other header tag */
881 if (http_opt_headers_count) {
882 for (i = 0; i < http_opt_headers_count ; i++) {
883 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
885 /* This cannot be free'd here because a redirection will then try to access this and segfault */
886 /* Covered in a testcase in tests/check_http.t */
887 /* free(http_opt_headers); */
890 /* optionally send the authentication info */
891 if (strlen(user_auth)) {
892 base64_encode_alloc (user_auth, strlen (user_auth), &auth);
893 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
896 /* optionally send the proxy authentication info */
897 if (strlen(proxy_auth)) {
898 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
899 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
902 /* either send http POST data (any data, not only POST)*/
903 if (http_post_data) {
904 if (http_content_type) {
905 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
906 } else {
907 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
910 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
911 xasprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
913 else {
914 /* or just a newline so the server knows we're done with the request */
915 xasprintf (&buf, "%s%s", buf, CRLF);
918 if (verbose) printf ("%s\n", buf);
919 gettimeofday (&tv_temp, NULL);
920 my_send (buf, strlen (buf));
921 microsec_headers = deltime (tv_temp);
922 elapsed_time_headers = (double)microsec_headers / 1.0e6;
924 /* fetch the page */
925 full_page = strdup("");
926 gettimeofday (&tv_temp, NULL);
927 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
928 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
929 microsec_firstbyte = deltime (tv_temp);
930 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
932 buffer[i] = '\0';
933 xasprintf (&full_page_new, "%s%s", full_page, buffer);
934 free (full_page);
935 full_page = full_page_new;
936 pagesize += i;
938 if (no_body && document_headers_done (full_page)) {
939 i = 0;
940 break;
943 microsec_transfer = deltime (tv_temp);
944 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
946 if (i < 0 && errno != ECONNRESET) {
947 #ifdef HAVE_SSL
949 if (use_ssl) {
950 sslerr=SSL_get_error(ssl, i);
951 if ( sslerr == SSL_ERROR_SSL ) {
952 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
953 } else {
954 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
957 else {
959 #endif
960 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
961 #ifdef HAVE_SSL
962 /* XXX
965 #endif
968 /* return a CRITICAL status if we couldn't read any data */
969 if (pagesize == (size_t) 0)
970 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
972 /* close the connection */
973 #ifdef HAVE_SSL
974 np_net_ssl_cleanup();
975 #endif
976 if (sd) close(sd);
978 /* Save check time */
979 microsec = deltime (tv);
980 elapsed_time = (double)microsec / 1.0e6;
982 /* leave full_page untouched so we can free it later */
983 page = full_page;
985 if (verbose)
986 printf ("%s://%s:%d%s is %d characters\n",
987 use_ssl ? "https" : "http", server_address,
988 server_port, server_url, (int)pagesize);
990 /* find status line and null-terminate it */
991 status_line = page;
992 page += (size_t) strcspn (page, "\r\n");
993 pos = page;
994 page += (size_t) strspn (page, "\r\n");
995 status_line[strcspn(status_line, "\r\n")] = 0;
996 strip (status_line);
997 if (verbose)
998 printf ("STATUS: %s\n", status_line);
1000 /* find header info and null-terminate it */
1001 header = page;
1002 while (strcspn (page, "\r\n") > 0) {
1003 page += (size_t) strcspn (page, "\r\n");
1004 pos = page;
1005 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
1006 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
1007 page += (size_t) 2;
1008 else
1009 page += (size_t) 1;
1011 page += (size_t) strspn (page, "\r\n");
1012 header[pos - header] = 0;
1013 if (verbose)
1014 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1015 (no_body ? " [[ skipped ]]" : page));
1017 /* make sure the status line matches the response we are looking for */
1018 if (!expected_statuscode (status_line, server_expect)) {
1019 if (server_port == HTTP_PORT)
1020 xasprintf (&msg,
1021 _("Invalid HTTP response received from host: %s\n"),
1022 status_line);
1023 else
1024 xasprintf (&msg,
1025 _("Invalid HTTP response received from host on port %d: %s\n"),
1026 server_port, status_line);
1027 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1030 /* Bypass normal status line check if server_expect was set by user and not default */
1031 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1032 if ( server_expect_yn ) {
1033 xasprintf (&msg,
1034 _("Status line output matched \"%s\" - "), server_expect);
1035 if (verbose)
1036 printf ("%s\n",msg);
1038 else {
1039 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1040 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1041 /* Status-Code = 3 DIGITS */
1043 status_code = strchr (status_line, ' ') + sizeof (char);
1044 if (strspn (status_code, "1234567890") != 3)
1045 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1047 http_status = atoi (status_code);
1049 /* check the return code */
1051 if (http_status >= 600 || http_status < 100) {
1052 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1054 /* server errors result in a critical state */
1055 else if (http_status >= 500) {
1056 xasprintf (&msg, _("%s - "), status_line);
1057 result = STATE_CRITICAL;
1059 /* client errors result in a warning state */
1060 else if (http_status >= 400) {
1061 xasprintf (&msg, _("%s - "), status_line);
1062 result = max_state_alt(STATE_WARNING, result);
1064 /* check redirected page if specified */
1065 else if (http_status >= 300) {
1067 if (onredirect == STATE_DEPENDENT)
1068 redir (header, status_line);
1069 else
1070 result = max_state_alt(onredirect, result);
1071 xasprintf (&msg, _("%s - "), status_line);
1072 } /* end if (http_status >= 300) */
1073 else {
1074 /* Print OK status anyway */
1075 xasprintf (&msg, _("%s - "), status_line);
1078 } /* end else (server_expect_yn) */
1080 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1081 alarm (0);
1083 if (maximum_age >= 0) {
1084 result = max_state_alt(check_document_dates(header, &msg), result);
1087 /* Page and Header content checks go here */
1089 if (strlen (string_expect)) {
1090 if (!strstr (page, string_expect)) {
1091 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1092 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1093 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1095 xasprintf (&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);
1096 result = STATE_CRITICAL;
1100 if (strlen (regexp)) {
1101 errcode = regexec (&preg, page, REGS, pmatch, 0);
1102 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1103 /* OK - No-op to avoid changing the logic around it */
1104 result = max_state_alt(STATE_OK, result);
1106 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1107 if (invert_regex == 0)
1108 xasprintf (&msg, _("%spattern not found, "), msg);
1109 else
1110 xasprintf (&msg, _("%spattern found, "), msg);
1111 result = STATE_CRITICAL;
1113 else {
1114 /* FIXME: Shouldn't that be UNKNOWN? */
1115 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1116 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1117 result = STATE_CRITICAL;
1121 /* make sure the page is of an appropriate size */
1122 /* page_len = get_content_length(header); */
1123 /* FIXME: Will this work with -N ? IMHO we should use
1124 * get_content_length(header) and always check if it's different than the
1125 * returned pagesize
1127 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1128 * it == get_content_length(header) ??
1130 page_len = pagesize;
1131 if ((max_page_len > 0) && (page_len > max_page_len)) {
1132 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1133 result = max_state_alt(STATE_WARNING, result);
1134 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1135 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1136 result = max_state_alt(STATE_WARNING, result);
1139 /* Cut-off trailing characters */
1140 if(msg[strlen(msg)-2] == ',')
1141 msg[strlen(msg)-2] = '\0';
1142 else
1143 msg[strlen(msg)-3] = '\0';
1145 /* check elapsed time */
1146 if (show_extended_perfdata)
1147 xasprintf (&msg,
1148 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1149 msg, page_len, elapsed_time,
1150 (display_html ? "</A>" : ""),
1151 perfd_time (elapsed_time),
1152 perfd_size (page_len),
1153 perfd_time_connect (elapsed_time_connect),
1154 use_ssl == TRUE ? perfd_time_ssl (elapsed_time_ssl) : "",
1155 perfd_time_headers (elapsed_time_headers),
1156 perfd_time_firstbyte (elapsed_time_firstbyte),
1157 perfd_time_transfer (elapsed_time_transfer));
1158 else
1159 xasprintf (&msg,
1160 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1161 msg, page_len, elapsed_time,
1162 (display_html ? "</A>" : ""),
1163 perfd_time (elapsed_time),
1164 perfd_size (page_len));
1166 result = max_state_alt(get_status(elapsed_time, thlds), result);
1168 die (result, "HTTP %s: %s\n", state_text(result), msg);
1169 /* die failed? */
1170 return STATE_UNKNOWN;
1175 /* per RFC 2396 */
1176 #define URI_HTTP "%5[HTPShtps]"
1177 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1178 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1179 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1180 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1181 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1182 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1183 #define HD4 URI_HTTP "://" URI_HOST
1184 #define HD5 URI_PATH
1186 void
1187 redir (char *pos, char *status_line)
1189 int i = 0;
1190 char *x;
1191 char xx[2];
1192 char type[6];
1193 char *addr;
1194 char *url;
1196 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1197 if (addr == NULL)
1198 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1200 url = malloc (strcspn (pos, "\r\n"));
1201 if (url == NULL)
1202 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1204 while (pos) {
1205 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1206 if (i == 0) {
1207 pos += (size_t) strcspn (pos, "\r\n");
1208 pos += (size_t) strspn (pos, "\r\n");
1209 if (strlen(pos) == 0)
1210 die (STATE_UNKNOWN,
1211 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1212 status_line, (display_html ? "</A>" : ""));
1213 continue;
1216 pos += i;
1217 pos += strspn (pos, " \t");
1220 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1221 * preceding each extra line with at least one SP or HT.''
1223 for (; (i = strspn (pos, "\r\n")); pos += i) {
1224 pos += i;
1225 if (!(i = strspn (pos, " \t"))) {
1226 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1227 display_html ? "</A>" : "");
1231 url = realloc (url, strcspn (pos, "\r\n") + 1);
1232 if (url == NULL)
1233 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1235 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1236 if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1237 url = prepend_slash (url);
1238 use_ssl = server_type_check (type);
1241 /* URI_HTTP URI_HOST URI_PATH */
1242 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1243 url = prepend_slash (url);
1244 use_ssl = server_type_check (type);
1245 i = server_port_check (use_ssl);
1248 /* URI_HTTP URI_HOST URI_PORT */
1249 else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1250 strcpy (url, HTTP_URL);
1251 use_ssl = server_type_check (type);
1254 /* URI_HTTP URI_HOST */
1255 else if (sscanf (pos, HD4, type, addr) == 2) {
1256 strcpy (url, HTTP_URL);
1257 use_ssl = server_type_check (type);
1258 i = server_port_check (use_ssl);
1261 /* URI_PATH */
1262 else if (sscanf (pos, HD5, url) == 1) {
1263 /* relative url */
1264 if ((url[0] != '/')) {
1265 if ((x = strrchr(server_url, '/')))
1266 *x = '\0';
1267 xasprintf (&url, "%s/%s", server_url, url);
1269 i = server_port;
1270 strcpy (type, server_type);
1271 strcpy (addr, host_name ? host_name : server_address);
1274 else {
1275 die (STATE_UNKNOWN,
1276 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1277 pos, (display_html ? "</A>" : ""));
1280 break;
1282 } /* end while (pos) */
1284 if (++redir_depth > max_depth)
1285 die (STATE_WARNING,
1286 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1287 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1289 if (server_port==i &&
1290 !strcmp(server_address, addr) &&
1291 (host_name && !strcmp(host_name, addr)) &&
1292 !strcmp(server_url, url))
1293 die (STATE_WARNING,
1294 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1295 type, addr, i, url, (display_html ? "</A>" : ""));
1297 strcpy (server_type, type);
1299 free (host_name);
1300 host_name = strdup (addr);
1302 if (!(followsticky & STICKY_HOST)) {
1303 free (server_address);
1304 server_address = strdup (addr);
1306 if (!(followsticky & STICKY_PORT)) {
1307 server_port = i;
1310 free (server_url);
1311 server_url = url;
1313 if (server_port > MAX_PORT)
1314 die (STATE_UNKNOWN,
1315 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1316 MAX_PORT, server_type, server_address, server_port, server_url,
1317 display_html ? "</A>" : "");
1319 if (verbose)
1320 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1321 host_name ? host_name : server_address, server_port, server_url);
1323 check_http ();
1328 server_type_check (const char *type)
1330 if (strcmp (type, "https"))
1331 return FALSE;
1332 else
1333 return TRUE;
1337 server_port_check (int ssl_flag)
1339 if (ssl_flag)
1340 return HTTPS_PORT;
1341 else
1342 return HTTP_PORT;
1345 char *perfd_time (double elapsed_time)
1347 return fperfdata ("time", elapsed_time, "s",
1348 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1349 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1350 TRUE, 0, FALSE, 0);
1353 char *perfd_time_connect (double elapsed_time_connect)
1355 return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1358 char *perfd_time_ssl (double elapsed_time_ssl)
1360 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1363 char *perfd_time_headers (double elapsed_time_headers)
1365 return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1368 char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1370 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1373 char *perfd_time_transfer (double elapsed_time_transfer)
1375 return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1378 char *perfd_size (int page_len)
1380 return perfdata ("size", page_len, "B",
1381 (min_page_len>0?TRUE:FALSE), min_page_len,
1382 (min_page_len>0?TRUE:FALSE), 0,
1383 TRUE, 0, FALSE, 0);
1386 void
1387 print_help (void)
1389 print_revision (progname, NP_VERSION);
1391 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1392 printf (COPYRIGHT, copyright, email);
1394 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1395 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1396 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1397 printf ("%s\n", _("certificate expiration times."));
1399 printf ("\n\n");
1401 print_usage ();
1403 printf (_("NOTE: One or both of -H and -I must be specified"));
1405 printf ("\n");
1407 printf (UT_HELP_VRSN);
1408 printf (UT_EXTRA_OPTS);
1410 printf (" %s\n", "-H, --hostname=ADDRESS");
1411 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1412 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1413 printf (" %s\n", "-I, --IP-address=ADDRESS");
1414 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1415 printf (" %s\n", "-p, --port=INTEGER");
1416 printf (" %s", _("Port number (default: "));
1417 printf ("%d)\n", HTTP_PORT);
1419 printf (UT_IPv46);
1421 #ifdef HAVE_SSL
1422 printf (" %s\n", "-S, --ssl=VERSION");
1423 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1424 printf (" %s\n", _("auto-negotiation (1 = TLSv1, 2 = SSLv2, 3 = SSLv3)."));
1425 printf (" %s\n", "--sni");
1426 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1427 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1428 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1429 printf (" %s\n", _("(when this option is used the URL is not checked.)\n"));
1430 #endif
1432 printf (" %s\n", "-e, --expect=STRING");
1433 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1434 printf (" %s", _("the first (status) line of the server response (default: "));
1435 printf ("%s)\n", HTTP_EXPECT);
1436 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1437 printf (" %s\n", "-s, --string=STRING");
1438 printf (" %s\n", _("String to expect in the content"));
1439 printf (" %s\n", "-u, --url=PATH");
1440 printf (" %s\n", _("URL to GET or POST (default: /)"));
1441 printf (" %s\n", "-P, --post=STRING");
1442 printf (" %s\n", _("URL encoded http POST data"));
1443 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1444 printf (" %s\n", _("Set HTTP method."));
1445 printf (" %s\n", "-N, --no-body");
1446 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1447 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1448 printf (" %s\n", "-M, --max-age=SECONDS");
1449 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1450 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1451 printf (" %s\n", "-T, --content-type=STRING");
1452 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1454 printf (" %s\n", "-l, --linespan");
1455 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1456 printf (" %s\n", "-r, --regex, --ereg=STRING");
1457 printf (" %s\n", _("Search page for regex STRING"));
1458 printf (" %s\n", "-R, --eregi=STRING");
1459 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1460 printf (" %s\n", "--invert-regex");
1461 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1463 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1464 printf (" %s\n", _("Username:password on sites with basic authentication"));
1465 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1466 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1467 printf (" %s\n", "-A, --useragent=STRING");
1468 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1469 printf (" %s\n", "-k, --header=STRING");
1470 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1471 printf (" %s\n", "-L, --link");
1472 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1473 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1474 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1475 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1476 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1477 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1479 printf (UT_WARN_CRIT);
1481 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1483 printf (UT_VERBOSE);
1485 printf ("\n");
1486 printf ("%s\n", _("Notes:"));
1487 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1488 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1489 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1490 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1491 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1492 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1494 #ifdef HAVE_SSL
1495 printf ("\n");
1496 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1497 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1498 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1499 printf ("\n");
1500 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1501 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1502 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1503 printf ("\n");
1504 printf ("%s\n", _("Examples:"));
1505 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1506 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1507 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1508 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1509 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1510 printf ("\n");
1511 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1512 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1513 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1514 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1515 printf (" %s\n", _("the certificate is expired."));
1516 printf ("\n");
1517 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1518 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1519 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1520 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1521 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1523 #endif
1525 printf (UT_SUPPORT);
1531 void
1532 print_usage (void)
1534 printf ("%s\n", _("Usage:"));
1535 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1536 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-a auth]\n");
1537 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1538 printf (" [-e <expect>] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1539 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1540 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1541 printf (" [-T <content-type>] [-j method]\n");