Die on SSL initialization errors
[monitoring-plugins.git] / plugins / check_http.c
blobc36d916aedb5a8a30d283c945cdcca241f33b42a
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 header_expect[MAX_INPUT_BUFFER] = "";
104 char string_expect[MAX_INPUT_BUFFER] = "";
105 char output_header_search[30] = "";
106 char output_string_search[30] = "";
107 char *warning_thresholds = NULL;
108 char *critical_thresholds = NULL;
109 thresholds *thlds;
110 char user_auth[MAX_INPUT_BUFFER] = "";
111 char proxy_auth[MAX_INPUT_BUFFER] = "";
112 int display_html = FALSE;
113 char **http_opt_headers;
114 int http_opt_headers_count = 0;
115 int onredirect = STATE_OK;
116 int followsticky = STICKY_NONE;
117 int use_ssl = FALSE;
118 int use_sni = FALSE;
119 int verbose = FALSE;
120 int show_extended_perfdata = FALSE;
121 int sd;
122 int min_page_len = 0;
123 int max_page_len = 0;
124 int redir_depth = 0;
125 int max_depth = 15;
126 char *http_method;
127 char *http_post_data;
128 char *http_content_type;
129 char buffer[MAX_INPUT_BUFFER];
130 char *client_cert = NULL;
131 char *client_privkey = NULL;
133 int process_arguments (int, char **);
134 int check_http (void);
135 void redir (char *pos, char *status_line);
136 int server_type_check(const char *type);
137 int server_port_check(int ssl_flag);
138 char *perfd_time (double microsec);
139 char *perfd_time_connect (double microsec);
140 char *perfd_time_ssl (double microsec);
141 char *perfd_time_firstbyte (double microsec);
142 char *perfd_time_headers (double microsec);
143 char *perfd_time_transfer (double microsec);
144 char *perfd_size (int page_len);
145 void print_help (void);
146 void print_usage (void);
149 main (int argc, char **argv)
151 int result = STATE_UNKNOWN;
153 setlocale (LC_ALL, "");
154 bindtextdomain (PACKAGE, LOCALEDIR);
155 textdomain (PACKAGE);
157 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
158 server_url = strdup(HTTP_URL);
159 server_url_length = strlen(server_url);
160 xasprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
161 NP_VERSION, VERSION);
163 /* Parse extra opts if any */
164 argv=np_extra_opts (&argc, argv, progname);
166 if (process_arguments (argc, argv) == ERROR)
167 usage4 (_("Could not parse arguments"));
169 if (display_html == TRUE)
170 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
171 use_ssl ? "https" : "http", host_name ? host_name : server_address,
172 server_port, server_url);
174 /* initialize alarm signal handling, set socket timeout, start timer */
175 (void) signal (SIGALRM, socket_timeout_alarm_handler);
176 (void) alarm (socket_timeout);
177 gettimeofday (&tv, NULL);
179 result = check_http ();
180 return result;
183 /* check whether a file exists */
184 void
185 test_file (char *path)
187 if (access(path, R_OK) == 0)
188 return;
189 usage2 (_("file does not exist or is not readable"), path);
192 /* process command-line arguments */
194 process_arguments (int argc, char **argv)
196 int c = 1;
197 char *p;
198 char *temp;
200 enum {
201 INVERT_REGEX = CHAR_MAX + 1,
202 SNI_OPTION
205 int option = 0;
206 static struct option longopts[] = {
207 STD_LONG_OPTS,
208 {"link", no_argument, 0, 'L'},
209 {"nohtml", no_argument, 0, 'n'},
210 {"ssl", optional_argument, 0, 'S'},
211 {"sni", no_argument, 0, SNI_OPTION},
212 {"post", required_argument, 0, 'P'},
213 {"method", required_argument, 0, 'j'},
214 {"IP-address", required_argument, 0, 'I'},
215 {"url", required_argument, 0, 'u'},
216 {"port", required_argument, 0, 'p'},
217 {"authorization", required_argument, 0, 'a'},
218 {"proxy-authorization", required_argument, 0, 'b'},
219 {"header-string", required_argument, 0, 'd'},
220 {"string", required_argument, 0, 's'},
221 {"expect", required_argument, 0, 'e'},
222 {"regex", required_argument, 0, 'r'},
223 {"ereg", required_argument, 0, 'r'},
224 {"eregi", required_argument, 0, 'R'},
225 {"linespan", no_argument, 0, 'l'},
226 {"onredirect", required_argument, 0, 'f'},
227 {"certificate", required_argument, 0, 'C'},
228 {"client-cert", required_argument, 0, 'J'},
229 {"private-key", required_argument, 0, 'K'},
230 {"useragent", required_argument, 0, 'A'},
231 {"header", required_argument, 0, 'k'},
232 {"no-body", no_argument, 0, 'N'},
233 {"max-age", required_argument, 0, 'M'},
234 {"content-type", required_argument, 0, 'T'},
235 {"pagesize", required_argument, 0, 'm'},
236 {"invert-regex", no_argument, NULL, INVERT_REGEX},
237 {"use-ipv4", no_argument, 0, '4'},
238 {"use-ipv6", no_argument, 0, '6'},
239 {"extended-perfdata", no_argument, 0, 'E'},
240 {0, 0, 0, 0}
243 if (argc < 2)
244 return ERROR;
246 for (c = 1; c < argc; c++) {
247 if (strcmp ("-to", argv[c]) == 0)
248 strcpy (argv[c], "-t");
249 if (strcmp ("-hn", argv[c]) == 0)
250 strcpy (argv[c], "-H");
251 if (strcmp ("-wt", argv[c]) == 0)
252 strcpy (argv[c], "-w");
253 if (strcmp ("-ct", argv[c]) == 0)
254 strcpy (argv[c], "-c");
255 if (strcmp ("-nohtml", argv[c]) == 0)
256 strcpy (argv[c], "-n");
259 while (1) {
260 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:N:E", longopts, &option);
261 if (c == -1 || c == EOF)
262 break;
264 switch (c) {
265 case '?': /* usage */
266 usage5 ();
267 break;
268 case 'h': /* help */
269 print_help ();
270 exit (STATE_OK);
271 break;
272 case 'V': /* version */
273 print_revision (progname, NP_VERSION);
274 exit (STATE_OK);
275 break;
276 case 't': /* timeout period */
277 if (!is_intnonneg (optarg))
278 usage2 (_("Timeout interval must be a positive integer"), optarg);
279 else
280 socket_timeout = atoi (optarg);
281 break;
282 case 'c': /* critical time threshold */
283 critical_thresholds = optarg;
284 break;
285 case 'w': /* warning time threshold */
286 warning_thresholds = optarg;
287 break;
288 case 'A': /* User Agent String */
289 xasprintf (&user_agent, "User-Agent: %s", optarg);
290 break;
291 case 'k': /* Additional headers */
292 if (http_opt_headers_count == 0)
293 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
294 else
295 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
296 http_opt_headers[http_opt_headers_count - 1] = optarg;
297 /* xasprintf (&http_opt_headers, "%s", optarg); */
298 break;
299 case 'L': /* show html link */
300 display_html = TRUE;
301 break;
302 case 'n': /* do not show html link */
303 display_html = FALSE;
304 break;
305 case 'C': /* Check SSL cert validity */
306 #ifdef HAVE_SSL
307 if ((temp=strchr(optarg,','))!=NULL) {
308 *temp='\0';
309 if (!is_intnonneg (optarg))
310 usage2 (_("Invalid certificate expiration period"), optarg);
311 days_till_exp_warn = atoi(optarg);
312 *temp=',';
313 temp++;
314 if (!is_intnonneg (temp))
315 usage2 (_("Invalid certificate expiration period"), temp);
316 days_till_exp_crit = atoi (temp);
318 else {
319 days_till_exp_crit=0;
320 if (!is_intnonneg (optarg))
321 usage2 (_("Invalid certificate expiration period"), optarg);
322 days_till_exp_warn = atoi (optarg);
324 check_cert = TRUE;
325 goto enable_ssl;
326 #endif
327 case 'J': /* use client certificate */
328 #ifdef HAVE_SSL
329 test_file(optarg);
330 client_cert = optarg;
331 goto enable_ssl;
332 #endif
333 case 'K': /* use client private key */
334 #ifdef HAVE_SSL
335 test_file(optarg);
336 client_privkey = optarg;
337 goto enable_ssl;
338 #endif
339 case 'S': /* use SSL */
340 #ifdef HAVE_SSL
341 enable_ssl:
342 use_ssl = TRUE;
343 if (optarg == NULL || c != 'S')
344 ssl_version = 0;
345 else {
346 ssl_version = atoi(optarg);
347 if (ssl_version < 1 || ssl_version > 3)
348 usage4 (_("Invalid option - Valid values for SSL Version are 1 (TLSv1), 2 (SSLv2) or 3 (SSLv3)"));
350 if (specify_port == FALSE)
351 server_port = HTTPS_PORT;
352 #else
353 /* -C -J and -K fall through to here without SSL */
354 usage4 (_("Invalid option - SSL is not available"));
355 #endif
356 break;
357 case SNI_OPTION:
358 use_sni = TRUE;
359 break;
360 case 'f': /* onredirect */
361 if (!strcmp (optarg, "stickyport"))
362 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
363 else if (!strcmp (optarg, "sticky"))
364 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
365 else if (!strcmp (optarg, "follow"))
366 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
367 else if (!strcmp (optarg, "unknown"))
368 onredirect = STATE_UNKNOWN;
369 else if (!strcmp (optarg, "ok"))
370 onredirect = STATE_OK;
371 else if (!strcmp (optarg, "warning"))
372 onredirect = STATE_WARNING;
373 else if (!strcmp (optarg, "critical"))
374 onredirect = STATE_CRITICAL;
375 else usage2 (_("Invalid onredirect option"), optarg);
376 if (verbose)
377 printf(_("option f:%d \n"), onredirect);
378 break;
379 /* Note: H, I, and u must be malloc'd or will fail on redirects */
380 case 'H': /* Host Name (virtual host) */
381 host_name = strdup (optarg);
382 if (host_name[0] == '[') {
383 if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */
384 server_port = atoi (p + 2);
385 } else if ((p = strchr (host_name, ':')) != NULL
386 && strchr (++p, ':') == NULL) /* IPv4:port or host:port */
387 server_port = atoi (p);
388 break;
389 case 'I': /* Server IP-address */
390 server_address = strdup (optarg);
391 break;
392 case 'u': /* URL path */
393 server_url = strdup (optarg);
394 server_url_length = strlen (server_url);
395 break;
396 case 'p': /* Server port */
397 if (!is_intnonneg (optarg))
398 usage2 (_("Invalid port number"), optarg);
399 else {
400 server_port = atoi (optarg);
401 specify_port = TRUE;
403 break;
404 case 'a': /* authorization info */
405 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
406 user_auth[MAX_INPUT_BUFFER - 1] = 0;
407 break;
408 case 'b': /* proxy-authorization info */
409 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
410 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
411 break;
412 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
413 if (! http_post_data)
414 http_post_data = strdup (optarg);
415 if (! http_method)
416 http_method = strdup("POST");
417 break;
418 case 'j': /* Set HTTP method */
419 if (http_method)
420 free(http_method);
421 http_method = strdup (optarg);
422 break;
423 case 'd': /* string or substring */
424 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
425 header_expect[MAX_INPUT_BUFFER - 1] = 0;
426 break;
427 case 's': /* string or substring */
428 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
429 string_expect[MAX_INPUT_BUFFER - 1] = 0;
430 break;
431 case 'e': /* string or substring */
432 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
433 server_expect[MAX_INPUT_BUFFER - 1] = 0;
434 server_expect_yn = 1;
435 break;
436 case 'T': /* Content-type */
437 xasprintf (&http_content_type, "%s", optarg);
438 break;
439 case 'l': /* linespan */
440 cflags &= ~REG_NEWLINE;
441 break;
442 case 'R': /* regex */
443 cflags |= REG_ICASE;
444 case 'r': /* regex */
445 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
446 regexp[MAX_RE_SIZE - 1] = 0;
447 errcode = regcomp (&preg, regexp, cflags);
448 if (errcode != 0) {
449 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
450 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
451 return ERROR;
453 break;
454 case INVERT_REGEX:
455 invert_regex = 1;
456 break;
457 case '4':
458 address_family = AF_INET;
459 break;
460 case '6':
461 #ifdef USE_IPV6
462 address_family = AF_INET6;
463 #else
464 usage4 (_("IPv6 support not available"));
465 #endif
466 break;
467 case 'v': /* verbose */
468 verbose = TRUE;
469 break;
470 case 'm': /* min_page_length */
472 char *tmp;
473 if (strchr(optarg, ':') != (char *)NULL) {
474 /* range, so get two values, min:max */
475 tmp = strtok(optarg, ":");
476 if (tmp == NULL) {
477 printf("Bad format: try \"-m min:max\"\n");
478 exit (STATE_WARNING);
479 } else
480 min_page_len = atoi(tmp);
482 tmp = strtok(NULL, ":");
483 if (tmp == NULL) {
484 printf("Bad format: try \"-m min:max\"\n");
485 exit (STATE_WARNING);
486 } else
487 max_page_len = atoi(tmp);
488 } else
489 min_page_len = atoi (optarg);
490 break;
492 case 'N': /* no-body */
493 no_body = TRUE;
494 break;
495 case 'M': /* max-age */
497 int L = strlen(optarg);
498 if (L && optarg[L-1] == 'm')
499 maximum_age = atoi (optarg) * 60;
500 else if (L && optarg[L-1] == 'h')
501 maximum_age = atoi (optarg) * 60 * 60;
502 else if (L && optarg[L-1] == 'd')
503 maximum_age = atoi (optarg) * 60 * 60 * 24;
504 else if (L && (optarg[L-1] == 's' ||
505 isdigit (optarg[L-1])))
506 maximum_age = atoi (optarg);
507 else {
508 fprintf (stderr, "unparsable max-age: %s\n", optarg);
509 exit (STATE_WARNING);
512 break;
513 case 'E': /* show extended perfdata */
514 show_extended_perfdata = TRUE;
515 break;
519 c = optind;
521 if (server_address == NULL && c < argc)
522 server_address = strdup (argv[c++]);
524 if (host_name == NULL && c < argc)
525 host_name = strdup (argv[c++]);
527 if (server_address == NULL) {
528 if (host_name == NULL)
529 usage4 (_("You must specify a server address or host name"));
530 else
531 server_address = strdup (host_name);
534 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
536 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
537 socket_timeout = (int)thlds->critical->end + 1;
539 if (http_method == NULL)
540 http_method = strdup ("GET");
542 if (client_cert && !client_privkey)
543 usage4 (_("If you use a client certificate you must also specify a private key file"));
545 return TRUE;
550 /* Returns 1 if we're done processing the document body; 0 to keep going */
551 static int
552 document_headers_done (char *full_page)
554 const char *body;
556 for (body = full_page; *body; body++) {
557 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
558 break;
561 if (!*body)
562 return 0; /* haven't read end of headers yet */
564 full_page[body - full_page] = 0;
565 return 1;
568 static time_t
569 parse_time_string (const char *string)
571 struct tm tm;
572 time_t t;
573 memset (&tm, 0, sizeof(tm));
575 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
577 if (isupper (string[0]) && /* Tue */
578 islower (string[1]) &&
579 islower (string[2]) &&
580 ',' == string[3] &&
581 ' ' == string[4] &&
582 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
583 isdigit (string[6]) &&
584 ' ' == string[7] &&
585 isupper (string[8]) && /* Dec */
586 islower (string[9]) &&
587 islower (string[10]) &&
588 ' ' == string[11] &&
589 isdigit (string[12]) && /* 2001 */
590 isdigit (string[13]) &&
591 isdigit (string[14]) &&
592 isdigit (string[15]) &&
593 ' ' == string[16] &&
594 isdigit (string[17]) && /* 02: */
595 isdigit (string[18]) &&
596 ':' == string[19] &&
597 isdigit (string[20]) && /* 59: */
598 isdigit (string[21]) &&
599 ':' == string[22] &&
600 isdigit (string[23]) && /* 03 */
601 isdigit (string[24]) &&
602 ' ' == string[25] &&
603 'G' == string[26] && /* GMT */
604 'M' == string[27] && /* GMT */
605 'T' == string[28]) {
607 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
608 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
609 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
610 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
611 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
612 !strncmp (string+8, "Feb", 3) ? 1 :
613 !strncmp (string+8, "Mar", 3) ? 2 :
614 !strncmp (string+8, "Apr", 3) ? 3 :
615 !strncmp (string+8, "May", 3) ? 4 :
616 !strncmp (string+8, "Jun", 3) ? 5 :
617 !strncmp (string+8, "Jul", 3) ? 6 :
618 !strncmp (string+8, "Aug", 3) ? 7 :
619 !strncmp (string+8, "Sep", 3) ? 8 :
620 !strncmp (string+8, "Oct", 3) ? 9 :
621 !strncmp (string+8, "Nov", 3) ? 10 :
622 !strncmp (string+8, "Dec", 3) ? 11 :
623 -1);
624 tm.tm_year = ((1000 * (string[12]-'0') +
625 100 * (string[13]-'0') +
626 10 * (string[14]-'0') +
627 (string[15]-'0'))
628 - 1900);
630 tm.tm_isdst = 0; /* GMT is never in DST, right? */
632 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
633 return 0;
636 This is actually wrong: we need to subtract the local timezone
637 offset from GMT from this value. But, that's ok in this usage,
638 because we only comparing these two GMT dates against each other,
639 so it doesn't matter what time zone we parse them in.
642 t = mktime (&tm);
643 if (t == (time_t) -1) t = 0;
645 if (verbose) {
646 const char *s = string;
647 while (*s && *s != '\r' && *s != '\n')
648 fputc (*s++, stdout);
649 printf (" ==> %lu\n", (unsigned long) t);
652 return t;
654 } else {
655 return 0;
659 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
660 static int
661 expected_statuscode (const char *reply, const char *statuscodes)
663 char *expected, *code;
664 int result = 0;
666 if ((expected = strdup (statuscodes)) == NULL)
667 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
669 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
670 if (strstr (reply, code) != NULL) {
671 result = 1;
672 break;
675 free (expected);
676 return result;
679 static int
680 check_document_dates (const char *headers, char **msg)
682 const char *s;
683 char *server_date = 0;
684 char *document_date = 0;
685 int date_result = STATE_OK;
687 s = headers;
688 while (*s) {
689 const char *field = s;
690 const char *value = 0;
692 /* Find the end of the header field */
693 while (*s && !isspace(*s) && *s != ':')
694 s++;
696 /* Remember the header value, if any. */
697 if (*s == ':')
698 value = ++s;
700 /* Skip to the end of the header, including continuation lines. */
701 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
702 s++;
704 /* Avoid stepping over end-of-string marker */
705 if (*s)
706 s++;
708 /* Process this header. */
709 if (value && value > field+2) {
710 char *ff = (char *) malloc (value-field);
711 char *ss = ff;
712 while (field < value-1)
713 *ss++ = tolower(*field++);
714 *ss++ = 0;
716 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
717 const char *e;
718 while (*value && isspace (*value))
719 value++;
720 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
722 ss = (char *) malloc (e - value + 1);
723 strncpy (ss, value, e - value);
724 ss[e - value] = 0;
725 if (!strcmp (ff, "date")) {
726 if (server_date) free (server_date);
727 server_date = ss;
728 } else {
729 if (document_date) free (document_date);
730 document_date = ss;
733 free (ff);
737 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
738 if (!server_date || !*server_date) {
739 xasprintf (msg, _("%sServer date unknown, "), *msg);
740 date_result = max_state_alt(STATE_UNKNOWN, date_result);
741 } else if (!document_date || !*document_date) {
742 xasprintf (msg, _("%sDocument modification date unknown, "), *msg);
743 date_result = max_state_alt(STATE_CRITICAL, date_result);
744 } else {
745 time_t srv_data = parse_time_string (server_date);
746 time_t doc_data = parse_time_string (document_date);
748 if (srv_data <= 0) {
749 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
750 date_result = max_state_alt(STATE_CRITICAL, date_result);
751 } else if (doc_data <= 0) {
752 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
753 date_result = max_state_alt(STATE_CRITICAL, date_result);
754 } else if (doc_data > srv_data + 30) {
755 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
756 date_result = max_state_alt(STATE_CRITICAL, date_result);
757 } else if (doc_data < srv_data - maximum_age) {
758 int n = (srv_data - doc_data);
759 if (n > (60 * 60 * 24 * 2)) {
760 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
761 date_result = max_state_alt(STATE_CRITICAL, date_result);
762 } else {
763 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
764 date_result = max_state_alt(STATE_CRITICAL, date_result);
767 free (server_date);
768 free (document_date);
770 return date_result;
774 get_content_length (const char *headers)
776 const char *s;
777 int content_length = 0;
779 s = headers;
780 while (*s) {
781 const char *field = s;
782 const char *value = 0;
784 /* Find the end of the header field */
785 while (*s && !isspace(*s) && *s != ':')
786 s++;
788 /* Remember the header value, if any. */
789 if (*s == ':')
790 value = ++s;
792 /* Skip to the end of the header, including continuation lines. */
793 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
794 s++;
796 /* Avoid stepping over end-of-string marker */
797 if (*s)
798 s++;
800 /* Process this header. */
801 if (value && value > field+2) {
802 char *ff = (char *) malloc (value-field);
803 char *ss = ff;
804 while (field < value-1)
805 *ss++ = tolower(*field++);
806 *ss++ = 0;
808 if (!strcmp (ff, "content-length")) {
809 const char *e;
810 while (*value && isspace (*value))
811 value++;
812 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
814 ss = (char *) malloc (e - value + 1);
815 strncpy (ss, value, e - value);
816 ss[e - value] = 0;
817 content_length = atoi(ss);
818 free (ss);
820 free (ff);
823 return (content_length);
826 char *
827 prepend_slash (char *path)
829 char *newpath;
831 if (path[0] == '/')
832 return path;
834 if ((newpath = malloc (strlen(path) + 2)) == NULL)
835 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
836 newpath[0] = '/';
837 strcpy (newpath + 1, path);
838 free (path);
839 return newpath;
843 check_http (void)
845 char *msg;
846 char *status_line;
847 char *status_code;
848 char *header;
849 char *page;
850 char *auth;
851 int http_status;
852 int i = 0;
853 size_t pagesize = 0;
854 char *full_page;
855 char *full_page_new;
856 char *buf;
857 char *pos;
858 long microsec = 0L;
859 double elapsed_time = 0.0;
860 long microsec_connect = 0L;
861 double elapsed_time_connect = 0.0;
862 long microsec_ssl = 0L;
863 double elapsed_time_ssl = 0.0;
864 long microsec_firstbyte = 0L;
865 double elapsed_time_firstbyte = 0.0;
866 long microsec_headers = 0L;
867 double elapsed_time_headers = 0.0;
868 long microsec_transfer = 0L;
869 double elapsed_time_transfer = 0.0;
870 int page_len = 0;
871 int result = STATE_OK;
873 /* try to connect to the host at the given port number */
874 gettimeofday (&tv_temp, NULL);
875 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
876 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
877 microsec_connect = deltime (tv_temp);
878 #ifdef HAVE_SSL
879 elapsed_time_connect = (double)microsec_connect / 1.0e6;
880 if (use_ssl == TRUE) {
881 gettimeofday (&tv_temp, NULL);
882 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
883 if (result != STATE_OK)
884 die (STATE_CRITICAL, NULL);
885 microsec_ssl = deltime (tv_temp);
886 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
887 if (check_cert == TRUE) {
888 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
889 np_net_ssl_cleanup();
890 if (sd) close(sd);
891 return result;
894 #endif /* HAVE_SSL */
896 xasprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
898 /* tell HTTP/1.1 servers not to keep the connection alive */
899 xasprintf (&buf, "%sConnection: close\r\n", buf);
901 /* optionally send the host header info */
902 if (host_name) {
904 * Specify the port only if we're using a non-default port (see RFC 2616,
905 * 14.23). Some server applications/configurations cause trouble if the
906 * (default) port is explicitly specified in the "Host:" header line.
908 if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
909 (use_ssl == TRUE && server_port == HTTPS_PORT))
910 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
911 else
912 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
915 /* optionally send any other header tag */
916 if (http_opt_headers_count) {
917 for (i = 0; i < http_opt_headers_count ; i++) {
918 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
920 /* This cannot be free'd here because a redirection will then try to access this and segfault */
921 /* Covered in a testcase in tests/check_http.t */
922 /* free(http_opt_headers); */
925 /* optionally send the authentication info */
926 if (strlen(user_auth)) {
927 base64_encode_alloc (user_auth, strlen (user_auth), &auth);
928 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
931 /* optionally send the proxy authentication info */
932 if (strlen(proxy_auth)) {
933 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
934 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
937 /* either send http POST data (any data, not only POST)*/
938 if (http_post_data) {
939 if (http_content_type) {
940 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
941 } else {
942 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
945 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
946 xasprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
948 else {
949 /* or just a newline so the server knows we're done with the request */
950 xasprintf (&buf, "%s%s", buf, CRLF);
953 if (verbose) printf ("%s\n", buf);
954 gettimeofday (&tv_temp, NULL);
955 my_send (buf, strlen (buf));
956 microsec_headers = deltime (tv_temp);
957 elapsed_time_headers = (double)microsec_headers / 1.0e6;
959 /* fetch the page */
960 full_page = strdup("");
961 gettimeofday (&tv_temp, NULL);
962 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
963 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
964 microsec_firstbyte = deltime (tv_temp);
965 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
967 buffer[i] = '\0';
968 xasprintf (&full_page_new, "%s%s", full_page, buffer);
969 free (full_page);
970 full_page = full_page_new;
971 pagesize += i;
973 if (no_body && document_headers_done (full_page)) {
974 i = 0;
975 break;
978 microsec_transfer = deltime (tv_temp);
979 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
981 if (i < 0 && errno != ECONNRESET) {
982 #ifdef HAVE_SSL
984 if (use_ssl) {
985 sslerr=SSL_get_error(ssl, i);
986 if ( sslerr == SSL_ERROR_SSL ) {
987 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
988 } else {
989 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
992 else {
994 #endif
995 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
996 #ifdef HAVE_SSL
997 /* XXX
1000 #endif
1003 /* return a CRITICAL status if we couldn't read any data */
1004 if (pagesize == (size_t) 0)
1005 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1007 /* close the connection */
1008 #ifdef HAVE_SSL
1009 np_net_ssl_cleanup();
1010 #endif
1011 if (sd) close(sd);
1013 /* Save check time */
1014 microsec = deltime (tv);
1015 elapsed_time = (double)microsec / 1.0e6;
1017 /* leave full_page untouched so we can free it later */
1018 page = full_page;
1020 if (verbose)
1021 printf ("%s://%s:%d%s is %d characters\n",
1022 use_ssl ? "https" : "http", server_address,
1023 server_port, server_url, (int)pagesize);
1025 /* find status line and null-terminate it */
1026 status_line = page;
1027 page += (size_t) strcspn (page, "\r\n");
1028 pos = page;
1029 page += (size_t) strspn (page, "\r\n");
1030 status_line[strcspn(status_line, "\r\n")] = 0;
1031 strip (status_line);
1032 if (verbose)
1033 printf ("STATUS: %s\n", status_line);
1035 /* find header info and null-terminate it */
1036 header = page;
1037 while (strcspn (page, "\r\n") > 0) {
1038 page += (size_t) strcspn (page, "\r\n");
1039 pos = page;
1040 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
1041 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
1042 page += (size_t) 2;
1043 else
1044 page += (size_t) 1;
1046 page += (size_t) strspn (page, "\r\n");
1047 header[pos - header] = 0;
1048 if (verbose)
1049 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1050 (no_body ? " [[ skipped ]]" : page));
1052 /* make sure the status line matches the response we are looking for */
1053 if (!expected_statuscode (status_line, server_expect)) {
1054 if (server_port == HTTP_PORT)
1055 xasprintf (&msg,
1056 _("Invalid HTTP response received from host: %s\n"),
1057 status_line);
1058 else
1059 xasprintf (&msg,
1060 _("Invalid HTTP response received from host on port %d: %s\n"),
1061 server_port, status_line);
1062 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1065 /* Bypass normal status line check if server_expect was set by user and not default */
1066 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1067 if ( server_expect_yn ) {
1068 xasprintf (&msg,
1069 _("Status line output matched \"%s\" - "), server_expect);
1070 if (verbose)
1071 printf ("%s\n",msg);
1073 else {
1074 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1075 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1076 /* Status-Code = 3 DIGITS */
1078 status_code = strchr (status_line, ' ') + sizeof (char);
1079 if (strspn (status_code, "1234567890") != 3)
1080 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1082 http_status = atoi (status_code);
1084 /* check the return code */
1086 if (http_status >= 600 || http_status < 100) {
1087 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1089 /* server errors result in a critical state */
1090 else if (http_status >= 500) {
1091 xasprintf (&msg, _("%s - "), status_line);
1092 result = STATE_CRITICAL;
1094 /* client errors result in a warning state */
1095 else if (http_status >= 400) {
1096 xasprintf (&msg, _("%s - "), status_line);
1097 result = max_state_alt(STATE_WARNING, result);
1099 /* check redirected page if specified */
1100 else if (http_status >= 300) {
1102 if (onredirect == STATE_DEPENDENT)
1103 redir (header, status_line);
1104 else
1105 result = max_state_alt(onredirect, result);
1106 xasprintf (&msg, _("%s - "), status_line);
1107 } /* end if (http_status >= 300) */
1108 else {
1109 /* Print OK status anyway */
1110 xasprintf (&msg, _("%s - "), status_line);
1113 } /* end else (server_expect_yn) */
1115 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1116 alarm (0);
1118 if (maximum_age >= 0) {
1119 result = max_state_alt(check_document_dates(header, &msg), result);
1122 /* Page and Header content checks go here */
1123 if (strlen (header_expect)) {
1124 if (!strstr (header, header_expect)) {
1125 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
1126 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
1127 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
1129 xasprintf (&msg, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
1130 result = STATE_CRITICAL;
1135 if (strlen (string_expect)) {
1136 if (!strstr (page, string_expect)) {
1137 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1138 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1139 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1141 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);
1142 result = STATE_CRITICAL;
1146 if (strlen (regexp)) {
1147 errcode = regexec (&preg, page, REGS, pmatch, 0);
1148 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1149 /* OK - No-op to avoid changing the logic around it */
1150 result = max_state_alt(STATE_OK, result);
1152 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1153 if (invert_regex == 0)
1154 xasprintf (&msg, _("%spattern not found, "), msg);
1155 else
1156 xasprintf (&msg, _("%spattern found, "), msg);
1157 result = STATE_CRITICAL;
1159 else {
1160 /* FIXME: Shouldn't that be UNKNOWN? */
1161 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1162 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1163 result = STATE_CRITICAL;
1167 /* make sure the page is of an appropriate size */
1168 /* page_len = get_content_length(header); */
1169 /* FIXME: Will this work with -N ? IMHO we should use
1170 * get_content_length(header) and always check if it's different than the
1171 * returned pagesize
1173 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1174 * it == get_content_length(header) ??
1176 page_len = pagesize;
1177 if ((max_page_len > 0) && (page_len > max_page_len)) {
1178 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1179 result = max_state_alt(STATE_WARNING, result);
1180 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1181 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1182 result = max_state_alt(STATE_WARNING, result);
1185 /* Cut-off trailing characters */
1186 if(msg[strlen(msg)-2] == ',')
1187 msg[strlen(msg)-2] = '\0';
1188 else
1189 msg[strlen(msg)-3] = '\0';
1191 /* check elapsed time */
1192 if (show_extended_perfdata)
1193 xasprintf (&msg,
1194 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1195 msg, page_len, elapsed_time,
1196 (display_html ? "</A>" : ""),
1197 perfd_time (elapsed_time),
1198 perfd_size (page_len),
1199 perfd_time_connect (elapsed_time_connect),
1200 use_ssl == TRUE ? perfd_time_ssl (elapsed_time_ssl) : "",
1201 perfd_time_headers (elapsed_time_headers),
1202 perfd_time_firstbyte (elapsed_time_firstbyte),
1203 perfd_time_transfer (elapsed_time_transfer));
1204 else
1205 xasprintf (&msg,
1206 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1207 msg, page_len, elapsed_time,
1208 (display_html ? "</A>" : ""),
1209 perfd_time (elapsed_time),
1210 perfd_size (page_len));
1212 result = max_state_alt(get_status(elapsed_time, thlds), result);
1214 die (result, "HTTP %s: %s\n", state_text(result), msg);
1215 /* die failed? */
1216 return STATE_UNKNOWN;
1221 /* per RFC 2396 */
1222 #define URI_HTTP "%5[HTPShtps]"
1223 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1224 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1225 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1226 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1227 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1228 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1229 #define HD4 URI_HTTP "://" URI_HOST
1230 #define HD5 URI_PATH
1232 void
1233 redir (char *pos, char *status_line)
1235 int i = 0;
1236 char *x;
1237 char xx[2];
1238 char type[6];
1239 char *addr;
1240 char *url;
1242 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1243 if (addr == NULL)
1244 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1246 url = malloc (strcspn (pos, "\r\n"));
1247 if (url == NULL)
1248 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1250 while (pos) {
1251 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1252 if (i == 0) {
1253 pos += (size_t) strcspn (pos, "\r\n");
1254 pos += (size_t) strspn (pos, "\r\n");
1255 if (strlen(pos) == 0)
1256 die (STATE_UNKNOWN,
1257 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1258 status_line, (display_html ? "</A>" : ""));
1259 continue;
1262 pos += i;
1263 pos += strspn (pos, " \t");
1266 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1267 * preceding each extra line with at least one SP or HT.''
1269 for (; (i = strspn (pos, "\r\n")); pos += i) {
1270 pos += i;
1271 if (!(i = strspn (pos, " \t"))) {
1272 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1273 display_html ? "</A>" : "");
1277 url = realloc (url, strcspn (pos, "\r\n") + 1);
1278 if (url == NULL)
1279 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1281 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1282 if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1283 url = prepend_slash (url);
1284 use_ssl = server_type_check (type);
1287 /* URI_HTTP URI_HOST URI_PATH */
1288 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1289 url = prepend_slash (url);
1290 use_ssl = server_type_check (type);
1291 i = server_port_check (use_ssl);
1294 /* URI_HTTP URI_HOST URI_PORT */
1295 else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1296 strcpy (url, HTTP_URL);
1297 use_ssl = server_type_check (type);
1300 /* URI_HTTP URI_HOST */
1301 else if (sscanf (pos, HD4, type, addr) == 2) {
1302 strcpy (url, HTTP_URL);
1303 use_ssl = server_type_check (type);
1304 i = server_port_check (use_ssl);
1307 /* URI_PATH */
1308 else if (sscanf (pos, HD5, url) == 1) {
1309 /* relative url */
1310 if ((url[0] != '/')) {
1311 if ((x = strrchr(server_url, '/')))
1312 *x = '\0';
1313 xasprintf (&url, "%s/%s", server_url, url);
1315 i = server_port;
1316 strcpy (type, server_type);
1317 strcpy (addr, host_name ? host_name : server_address);
1320 else {
1321 die (STATE_UNKNOWN,
1322 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1323 pos, (display_html ? "</A>" : ""));
1326 break;
1328 } /* end while (pos) */
1330 if (++redir_depth > max_depth)
1331 die (STATE_WARNING,
1332 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1333 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1335 if (server_port==i &&
1336 !strcmp(server_address, addr) &&
1337 (host_name && !strcmp(host_name, addr)) &&
1338 !strcmp(server_url, url))
1339 die (STATE_WARNING,
1340 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1341 type, addr, i, url, (display_html ? "</A>" : ""));
1343 strcpy (server_type, type);
1345 free (host_name);
1346 host_name = strdup (addr);
1348 if (!(followsticky & STICKY_HOST)) {
1349 free (server_address);
1350 server_address = strdup (addr);
1352 if (!(followsticky & STICKY_PORT)) {
1353 server_port = i;
1356 free (server_url);
1357 server_url = url;
1359 if (server_port > MAX_PORT)
1360 die (STATE_UNKNOWN,
1361 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1362 MAX_PORT, server_type, server_address, server_port, server_url,
1363 display_html ? "</A>" : "");
1365 if (verbose)
1366 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1367 host_name ? host_name : server_address, server_port, server_url);
1369 check_http ();
1374 server_type_check (const char *type)
1376 if (strcmp (type, "https"))
1377 return FALSE;
1378 else
1379 return TRUE;
1383 server_port_check (int ssl_flag)
1385 if (ssl_flag)
1386 return HTTPS_PORT;
1387 else
1388 return HTTP_PORT;
1391 char *perfd_time (double elapsed_time)
1393 return fperfdata ("time", elapsed_time, "s",
1394 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1395 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1396 TRUE, 0, FALSE, 0);
1399 char *perfd_time_connect (double elapsed_time_connect)
1401 return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1404 char *perfd_time_ssl (double elapsed_time_ssl)
1406 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1409 char *perfd_time_headers (double elapsed_time_headers)
1411 return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1414 char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1416 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1419 char *perfd_time_transfer (double elapsed_time_transfer)
1421 return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1424 char *perfd_size (int page_len)
1426 return perfdata ("size", page_len, "B",
1427 (min_page_len>0?TRUE:FALSE), min_page_len,
1428 (min_page_len>0?TRUE:FALSE), 0,
1429 TRUE, 0, FALSE, 0);
1432 void
1433 print_help (void)
1435 print_revision (progname, NP_VERSION);
1437 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1438 printf (COPYRIGHT, copyright, email);
1440 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1441 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1442 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1443 printf ("%s\n", _("certificate expiration times."));
1445 printf ("\n\n");
1447 print_usage ();
1449 printf (_("NOTE: One or both of -H and -I must be specified"));
1451 printf ("\n");
1453 printf (UT_HELP_VRSN);
1454 printf (UT_EXTRA_OPTS);
1456 printf (" %s\n", "-H, --hostname=ADDRESS");
1457 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1458 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1459 printf (" %s\n", "-I, --IP-address=ADDRESS");
1460 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1461 printf (" %s\n", "-p, --port=INTEGER");
1462 printf (" %s", _("Port number (default: "));
1463 printf ("%d)\n", HTTP_PORT);
1465 printf (UT_IPv46);
1467 #ifdef HAVE_SSL
1468 printf (" %s\n", "-S, --ssl=VERSION");
1469 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1470 printf (" %s\n", _("auto-negotiation (1 = TLSv1, 2 = SSLv2, 3 = SSLv3)."));
1471 printf (" %s\n", "--sni");
1472 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1473 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1474 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1475 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1476 printf (" %s\n", "-J, --client-cert=FILE");
1477 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1478 printf (" %s\n", _("to be used in establishing the SSL session"));
1479 printf (" %s\n", "-K, --private-key=FILE");
1480 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1481 printf (" %s\n", _("matching the client certificate"));
1482 #endif
1484 printf (" %s\n", "-e, --expect=STRING");
1485 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1486 printf (" %s", _("the first (status) line of the server response (default: "));
1487 printf ("%s)\n", HTTP_EXPECT);
1488 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1489 printf (" %s\n", "-d, --header-string=STRING");
1490 printf (" %s\n", _("String to expect in the response headers"));
1491 printf (" %s\n", "-s, --string=STRING");
1492 printf (" %s\n", _("String to expect in the content"));
1493 printf (" %s\n", "-u, --url=PATH");
1494 printf (" %s\n", _("URL to GET or POST (default: /)"));
1495 printf (" %s\n", "-P, --post=STRING");
1496 printf (" %s\n", _("URL encoded http POST data"));
1497 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE)");
1498 printf (" %s\n", _("Set HTTP method."));
1499 printf (" %s\n", "-N, --no-body");
1500 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1501 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1502 printf (" %s\n", "-M, --max-age=SECONDS");
1503 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1504 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1505 printf (" %s\n", "-T, --content-type=STRING");
1506 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1508 printf (" %s\n", "-l, --linespan");
1509 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1510 printf (" %s\n", "-r, --regex, --ereg=STRING");
1511 printf (" %s\n", _("Search page for regex STRING"));
1512 printf (" %s\n", "-R, --eregi=STRING");
1513 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1514 printf (" %s\n", "--invert-regex");
1515 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1517 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1518 printf (" %s\n", _("Username:password on sites with basic authentication"));
1519 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1520 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1521 printf (" %s\n", "-A, --useragent=STRING");
1522 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1523 printf (" %s\n", "-k, --header=STRING");
1524 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1525 printf (" %s\n", "-E, --extended-perfdata");
1526 printf (" %s\n", _("Print additional performance data"));
1527 printf (" %s\n", "-L, --link");
1528 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1529 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1530 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1531 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1532 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1533 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1535 printf (UT_WARN_CRIT);
1537 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1539 printf (UT_VERBOSE);
1541 printf ("\n");
1542 printf ("%s\n", _("Notes:"));
1543 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1544 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1545 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect reponse"));
1546 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1547 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1548 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1550 #ifdef HAVE_SSL
1551 printf ("\n");
1552 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1553 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1554 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1555 printf ("\n");
1556 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1557 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1558 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1559 printf ("\n");
1560 printf ("%s\n", _("Examples:"));
1561 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1562 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1563 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1564 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1565 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1566 printf ("\n");
1567 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1568 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1569 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1570 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1571 printf (" %s\n", _("the certificate is expired."));
1572 printf ("\n");
1573 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1574 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1575 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1576 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1577 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1579 #endif
1581 printf (UT_SUPPORT);
1587 void
1588 print_usage (void)
1590 printf ("%s\n", _("Usage:"));
1591 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1592 printf (" [-J <client certificate file>] [-K <private key>]\n");
1593 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1594 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1595 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1596 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1597 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1598 printf (" [-T <content-type>] [-j method]\n");