1 /*****************************************************************************
2 *
3 * Nagios check_http plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2014 Nagios Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_http plugin
11 *
12 * This plugin tests the HTTP service on the specified host. It can test
13 * normal (http) and secure (https) servers, follow redirects, search for
14 * strings and regular expressions, check connection times, and report on
15 * certificate expiration times.
16 *
17 *
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 *
31 *
32 *****************************************************************************/
33
34 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
35
36 const char *progname = "check_http";
37 const char *copyright = "1999-2014";
38 const char *email = "devel@nagios-plugins.org";
39
40 #include "common.h"
41 #include "netutils.h"
42 #include "utils.h"
43 #include "base64.h"
44 #include <ctype.h>
45
46 #define STICKY_NONE 0
47 #define STICKY_HOST 1
48 #define STICKY_PORT 2
49
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
56 };
57
58 #ifdef HAVE_SSL
59 int check_cert = FALSE;
60 int continue_after_check_cert = FALSE;
61 int ssl_version = 0;
62 int days_till_exp_warn, days_till_exp_crit;
63 char *randbuff;
64 X509 *server_cert;
65 # define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
66 # define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
67 #else /* ifndef HAVE_SSL */
68 # define my_recv(buf, len) read(sd, buf, len)
69 # define my_send(buf, len) send(sd, buf, len, 0)
70 #endif /* HAVE_SSL */
71 int no_body = FALSE;
72 int maximum_age = -1;
73
74 enum {
75 REGS = 2,
76 MAX_RE_SIZE = 2048
77 };
78 #include "regex.h"
79 regex_t preg;
80 regmatch_t pmatch[REGS];
81 char regexp[MAX_RE_SIZE];
82 char errbuf[MAX_INPUT_BUFFER];
83 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
84 int errcode;
85 int invert_regex = 0;
86
87 struct timeval tv;
88 struct timeval tv_temp;
89
90 #define HTTP_URL "/"
91 #define CRLF "\r\n"
92
93 int specify_port = FALSE;
94 int server_port = HTTP_PORT;
95 char server_port_text[6] = "";
96 char server_type[6] = "http";
97 char *server_address;
98 char *host_name;
99 char *server_url;
100 char *user_agent;
101 int server_url_length;
102 int server_expect_yn = 0;
103 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
104 char header_expect[MAX_INPUT_BUFFER] = "";
105 char string_expect[MAX_INPUT_BUFFER] = "";
106 char output_header_search[30] = "";
107 char output_string_search[30] = "";
108 char *warning_thresholds = NULL;
109 char *critical_thresholds = NULL;
110 thresholds *thlds;
111 char user_auth[MAX_INPUT_BUFFER] = "";
112 char proxy_auth[MAX_INPUT_BUFFER] = "";
113 int display_html = FALSE;
114 char **http_opt_headers = NULL;
115 int http_opt_headers_count = 0;
116 int have_accept = FALSE;
117 int onredirect = STATE_OK;
118 int followsticky = STICKY_NONE;
119 int use_ssl = FALSE;
120 int use_sni = FALSE;
121 int verbose = FALSE;
122 int show_extended_perfdata = FALSE;
123 int show_url = FALSE;
124 int sd;
125 int min_page_len = 0;
126 int max_page_len = 0;
127 int redir_depth = 0;
128 int max_depth = 15;
129 char *http_method;
130 char *http_post_data;
131 char *http_content_type;
132 char buffer[MAX_INPUT_BUFFER];
133 char *client_cert = NULL;
134 char *client_privkey = NULL;
135
136 int process_arguments (int, char **);
137 int check_http (void);
138 void redir (char *pos, char *status_line);
139 int server_type_check(const char *type);
140 int server_port_check(int ssl_flag);
141 char *perfd_time (double microsec);
142 char *perfd_time_connect (double microsec);
143 char *perfd_time_ssl (double microsec);
144 char *perfd_time_firstbyte (double microsec);
145 char *perfd_time_headers (double microsec);
146 char *perfd_time_transfer (double microsec);
147 char *perfd_size (int page_len);
148 void print_help (void);
149 void print_usage (void);
150
151 extern int check_hostname;
152
153
154 int
main(int argc,char ** argv)155 main (int argc, char **argv)
156 {
157 int result = STATE_UNKNOWN;
158
159 setlocale (LC_ALL, ""); setlocale(LC_NUMERIC, "C");
160 bindtextdomain (PACKAGE, LOCALEDIR);
161 textdomain (PACKAGE);
162
163 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
164 server_url = strdup(HTTP_URL);
165 server_url_length = strlen(server_url);
166 xasprintf (&user_agent, "User-Agent: check_http/v%s (nagios-plugins %s)",
167 NP_VERSION, VERSION);
168
169 /* Parse extra opts if any */
170 argv=np_extra_opts (&argc, argv, progname);
171
172 if (process_arguments (argc, argv) == ERROR)
173 usage4 (_("Could not parse arguments"));
174
175 if (display_html == TRUE)
176 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
177 use_ssl ? "https" : "http", host_name ? host_name : server_address,
178 server_port, server_url);
179
180 /* initialize alarm signal handling, set socket timeout, start timer */
181 (void) signal (SIGALRM, socket_timeout_alarm_handler);
182 (void) alarm (timeout_interval);
183 gettimeofday (&tv, NULL);
184
185 result = check_http ();
186 return result;
187 }
188
189 /* Plugin-specific wrapper for vdie() */
190 void
check_http_die(int state,char * fmt,...)191 check_http_die(int state, char* fmt, ...)
192 {
193 char* msg = malloc(4096);
194 va_list ap;
195 va_start(ap, fmt);
196 vsnprintf(msg, 4096, fmt, ap );
197 va_end(ap);
198
199 if (show_url) {
200 die (state, "HTTP %s - %s://%s:%d%s - %s", state_text(state), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url, msg);
201 }
202 else {
203 die (state, "HTTP %s - %s", state_text(state), msg);
204 }
205 }
206
207 /* check whether a file exists */
208 void
test_file(char * path)209 test_file (char *path)
210 {
211 if (access(path, R_OK) == 0)
212 return;
213 usage2 (_("file does not exist or is not readable"), path);
214 }
215
216 /* process command-line arguments */
217 int
process_arguments(int argc,char ** argv)218 process_arguments (int argc, char **argv)
219 {
220 int c = 1;
221 char *p;
222 char *temp;
223
224 enum {
225 INVERT_REGEX = CHAR_MAX + 1,
226 SNI_OPTION,
227 VERIFY_HOST,
228 CONTINUE_AFTER_CHECK_CERT
229 };
230
231 int option = 0;
232 static struct option longopts[] = {
233 STD_LONG_OPTS,
234 {"link", no_argument, 0, 'L'},
235 {"nohtml", no_argument, 0, 'n'},
236 {"ssl", optional_argument, 0, 'S'},
237 {"sni", no_argument, 0, SNI_OPTION},
238 {"verify-host", no_argument, 0, VERIFY_HOST},
239 {"post", required_argument, 0, 'P'},
240 {"method", required_argument, 0, 'j'},
241 {"IP-address", required_argument, 0, 'I'},
242 {"url", required_argument, 0, 'u'},
243 {"uri", required_argument, 0, 'u'},
244 {"port", required_argument, 0, 'p'},
245 {"authorization", required_argument, 0, 'a'},
246 {"proxy-authorization", required_argument, 0, 'b'},
247 {"header-string", required_argument, 0, 'd'},
248 {"string", required_argument, 0, 's'},
249 {"expect", required_argument, 0, 'e'},
250 {"regex", required_argument, 0, 'r'},
251 {"ereg", required_argument, 0, 'r'},
252 {"eregi", required_argument, 0, 'R'},
253 {"linespan", no_argument, 0, 'l'},
254 {"onredirect", required_argument, 0, 'f'},
255 {"certificate", required_argument, 0, 'C'},
256 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
257 {"client-cert", required_argument, 0, 'J'},
258 {"private-key", required_argument, 0, 'K'},
259 {"useragent", required_argument, 0, 'A'},
260 {"header", required_argument, 0, 'k'},
261 {"no-body", no_argument, 0, 'N'},
262 {"max-age", required_argument, 0, 'M'},
263 {"content-type", required_argument, 0, 'T'},
264 {"pagesize", required_argument, 0, 'm'},
265 {"invert-regex", no_argument, NULL, INVERT_REGEX},
266 {"use-ipv4", no_argument, 0, '4'},
267 {"use-ipv6", no_argument, 0, '6'},
268 {"extended-perfdata", no_argument, 0, 'E'},
269 {"show-url", no_argument, 0, 'U'},
270 {0, 0, 0, 0}
271 };
272
273 if (argc < 2)
274 return ERROR;
275
276 for (c = 1; c < argc; c++) {
277 if (strcmp ("-to", argv[c]) == 0)
278 strcpy (argv[c], "-t");
279 if (strcmp ("-hn", argv[c]) == 0)
280 strcpy (argv[c], "-H");
281 if (strcmp ("-wt", argv[c]) == 0)
282 strcpy (argv[c], "-w");
283 if (strcmp ("-ct", argv[c]) == 0)
284 strcpy (argv[c], "-c");
285 if (strcmp ("-nohtml", argv[c]) == 0)
286 strcpy (argv[c], "-n");
287 }
288
289 while (1) {
290 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:NEU", longopts, &option);
291 if (c == -1 || c == EOF)
292 break;
293
294 switch (c) {
295 case '?': /* usage */
296 usage5 ();
297 break;
298 case 'h': /* help */
299 print_help ();
300 exit (STATE_OK);
301 break;
302 case 'V': /* version */
303 print_revision (progname, NP_VERSION);
304 exit (STATE_OK);
305 break;
306 case 't': /* timeout period */
307 timeout_interval = parse_timeout_string(optarg);
308 break;
309 case 'c': /* critical time threshold */
310 critical_thresholds = optarg;
311 break;
312 case 'w': /* warning time threshold */
313 warning_thresholds = optarg;
314 break;
315 case 'A': /* User Agent String */
316 xasprintf (&user_agent, "User-Agent: %s", optarg);
317 break;
318 case 'k': /* Additional headers */
319 /* if (http_opt_headers_count == 0)
320 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
321 else */
322 http_opt_headers = realloc (http_opt_headers, sizeof(char *) * (++http_opt_headers_count));
323 http_opt_headers[http_opt_headers_count - 1] = optarg;
324 if (!strncmp(optarg, "Accept:", 7))
325 have_accept = TRUE;
326 break;
327 case 'L': /* show html link */
328 display_html = TRUE;
329 break;
330 case 'n': /* do not show html link */
331 display_html = FALSE;
332 break;
333 case 'C': /* Check SSL cert validity */
334 #ifdef HAVE_SSL
335 if ((temp=strchr(optarg,','))!=NULL) {
336 *temp='\0';
337 if (!is_intnonneg (optarg))
338 usage2 (_("Invalid certificate expiration period"), optarg);
339 days_till_exp_warn = atoi(optarg);
340 *temp=',';
341 temp++;
342 if (!is_intnonneg (temp))
343 usage2 (_("Invalid certificate expiration period"), temp);
344 days_till_exp_crit = atoi (temp);
345 } else {
346 days_till_exp_crit=0;
347 if (!is_intnonneg (optarg))
348 usage2 (_("Invalid certificate expiration period"), optarg);
349 days_till_exp_warn = atoi (optarg);
350 }
351 check_cert = TRUE;
352 goto enable_ssl;
353 #endif
354 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
355 #ifdef HAVE_SSL
356 continue_after_check_cert = TRUE;
357 break;
358 #endif
359 case 'J': /* use client certificate */
360 #ifdef HAVE_SSL
361 test_file(optarg);
362 client_cert = optarg;
363 goto enable_ssl;
364 #endif
365 case 'K': /* use client private key */
366 #ifdef HAVE_SSL
367 test_file(optarg);
368 client_privkey = optarg;
369 goto enable_ssl;
370 #endif
371 case 'S': /* use SSL */
372 #ifdef HAVE_SSL
373 enable_ssl:
374 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple
375 parameters, like -S and -C combinations */
376 use_ssl = TRUE;
377 if (c=='S' && optarg != NULL) {
378 int got_plus = strchr(optarg, '+') != NULL;
379
380 if (!strncmp (optarg, "1.3", 3))
381 ssl_version = got_plus ? MP_TLSv1_3_OR_NEWER : MP_TLSv1_3;
382 else if (!strncmp (optarg, "1.2", 3))
383 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2;
384 else if (!strncmp (optarg, "1.1", 3))
385 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1;
386 else if (optarg[0] == '1')
387 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1;
388 else if (optarg[0] == '3')
389 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3;
390 else if (optarg[0] == '2')
391 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2;
392 else
393 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)"));
394 }
395 if (specify_port == FALSE)
396 server_port = HTTPS_PORT;
397 #else
398 /* -C -J and -K fall through to here without SSL */
399 usage4 (_("Invalid option - SSL is not available"));
400 #endif
401 break;
402 case SNI_OPTION:
403 use_sni = TRUE;
404 break;
405 case VERIFY_HOST:
406 check_hostname = 1;
407 break;
408 case 'f': /* onredirect */
409 if (!strcmp (optarg, "stickyport"))
410 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
411 else if (!strcmp (optarg, "sticky"))
412 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
413 else if (!strcmp (optarg, "follow"))
414 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
415 else if (!strcmp (optarg, "unknown"))
416 onredirect = STATE_UNKNOWN;
417 else if (!strcmp (optarg, "ok"))
418 onredirect = STATE_OK;
419 else if (!strcmp (optarg, "warning"))
420 onredirect = STATE_WARNING;
421 else if (!strcmp (optarg, "critical"))
422 onredirect = STATE_CRITICAL;
423 else usage2 (_("Invalid onredirect option"), optarg);
424 if (verbose)
425 printf(_("option f:%d \n"), onredirect);
426 break;
427 /* Note: H, I, and u must be malloc'd or will fail on redirects */
428 case 'H': /* Host Name (virtual host) */
429 host_name = strdup(optarg);
430 if (*host_name == '[') {
431 if ((p = strstr (host_name, "]:")) != NULL) /* [IPv6]:port */ {
432 server_port = atoi (p + 2);
433 *++p = '\0'; // Set The host_name sans ":port"
434 }
435 } else if ((p = strchr (host_name, ':')) != NULL
436 && strchr (++p, ':') == NULL) /* IPv4:port or host:port */ {
437 server_port = atoi (p);
438 *--p = '\0'; // Set The host_name sans ":port"
439 }
440 break;
441 case 'I': /* Server IP-address */
442 server_address = strdup (optarg);
443 break;
444 case 'u': /* URL path */
445 /* server_url is first allocated in main() */
446 free(server_url);
447 server_url = strdup (optarg);
448 server_url_length = strlen (server_url);
449 break;
450 case 'p': /* Server port */
451 if (!is_intnonneg (optarg))
452 usage2 (_("Invalid port number"), optarg);
453 else {
454 server_port = atoi (optarg);
455 specify_port = TRUE;
456 }
457 break;
458 case 'a': /* authorization info */
459 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
460 user_auth[MAX_INPUT_BUFFER - 1] = 0;
461 break;
462 case 'b': /* proxy-authorization info */
463 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
464 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
465 break;
466 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
467 if (! http_post_data)
468 http_post_data = strdup (optarg);
469 if (! http_method)
470 http_method = strdup("POST");
471 break;
472 case 'j': /* Set HTTP method */
473 if (http_method)
474 free(http_method);
475 http_method = strdup (optarg);
476 break;
477 case 'd': /* string or substring */
478 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
479 header_expect[MAX_INPUT_BUFFER - 1] = 0;
480 break;
481 case 's': /* string or substring */
482 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
483 string_expect[MAX_INPUT_BUFFER - 1] = 0;
484 break;
485 case 'e': /* string or substring */
486 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
487 server_expect[MAX_INPUT_BUFFER - 1] = 0;
488 server_expect_yn = 1;
489 break;
490 case 'T': /* Content-type */
491 xasprintf (&http_content_type, "%s", optarg);
492 break;
493 case 'l': /* linespan */
494 cflags &= ~REG_NEWLINE;
495 break;
496 case 'R': /* regex */
497 cflags |= REG_ICASE;
498 case 'r': /* regex */
499 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
500 regexp[MAX_RE_SIZE - 1] = 0;
501 errcode = regcomp (&preg, regexp, cflags);
502 if (errcode != 0) {
503 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
504 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
505 return ERROR;
506 }
507 break;
508 case INVERT_REGEX:
509 invert_regex = 1;
510 break;
511 case '4':
512 address_family = AF_INET;
513 break;
514 case '6':
515
516 #ifdef USE_IPV6
517 address_family = AF_INET6;
518 #else
519 usage4 (_("IPv6 support not available"));
520 #endif
521
522 break;
523 case 'v': /* verbose */
524 verbose = TRUE;
525 break;
526 case 'm': { /* min_page_length */
527 char *tmp;
528 if (strchr(optarg, ':') != (char *)NULL) {
529 /* range, so get two values, min:max */
530 tmp = strtok(optarg, ":");
531 if (tmp == NULL) {
532 printf("Bad format: try \"-m min:max\"\n");
533 exit (STATE_WARNING);
534 } else
535 min_page_len = atoi(tmp);
536
537 tmp = strtok(NULL, ":");
538 if (tmp == NULL) {
539 printf("Bad format: try \"-m min:max\"\n");
540 exit (STATE_WARNING);
541 } else
542 max_page_len = atoi(tmp);
543 } else
544 min_page_len = atoi (optarg);
545 break;
546 }
547 case 'N': /* no-body */
548 no_body = TRUE;
549 break;
550 case 'M': { /* max-age */
551 int L = strlen(optarg);
552 if (L && optarg[L-1] == 'm')
553 maximum_age = atoi (optarg) * 60;
554 else if (L && optarg[L-1] == 'h')
555 maximum_age = atoi (optarg) * 60 * 60;
556 else if (L && optarg[L-1] == 'd')
557 maximum_age = atoi (optarg) * 60 * 60 * 24;
558 else if (L && (optarg[L-1] == 's' ||
559 isdigit (optarg[L-1])))
560 maximum_age = atoi (optarg);
561 else {
562 fprintf (stderr, "unparsable max-age: %s\n", optarg);
563 exit (STATE_WARNING);
564 }
565 }
566 break;
567 case 'E': /* show extended perfdata */
568 show_extended_perfdata = TRUE;
569 break;
570 case 'U': /* show checked url in output msg */
571 show_url = TRUE;
572 break;
573 }
574 }
575
576 c = optind;
577
578 if (server_address == NULL && c < argc)
579 server_address = strdup (argv[c++]);
580
581 if (host_name == NULL && c < argc)
582 host_name = strdup (argv[c++]);
583
584 if (use_sni && host_name == NULL) {
585 usage4(_("Server name indication requires that a host name is defined with -H"));
586 }
587
588 if (server_address == NULL) {
589 if (host_name == NULL)
590 usage4 (_("You must specify a server address or host name"));
591 else
592 server_address = strdup (host_name);
593 }
594
595 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
596
597 if (critical_thresholds && thlds->critical->end>(double)timeout_interval)
598 timeout_interval = (int)thlds->critical->end + 1;
599
600 if (http_method == NULL)
601 http_method = strdup ("GET");
602
603 if (client_cert && !client_privkey)
604 usage4 (_("If you use a client certificate you must also specify a private key file"));
605
606 return TRUE;
607 }
608
609
610
611 /* Returns 0 if we're still retrieving the headers.
612 * Otherwise, returns the length of the header (not including the final newlines)
613 */
614 static int
document_headers_done(const char * full_page)615 document_headers_done (const char *full_page)
616 {
617 const char *body;
618
619 for (body = full_page; *body; body++) {
620 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
621 break;
622 }
623
624 if (!*body)
625 return 0; /* haven't read end of headers yet */
626
627 return body - full_page;
628 }
629
630 static time_t
parse_time_string(const char * string)631 parse_time_string (const char *string)
632 {
633 struct tm tm;
634 time_t t;
635 memset (&tm, 0, sizeof(tm));
636
637 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
638
639 if (isupper (string[0]) && /* Tue */
640 islower (string[1]) &&
641 islower (string[2]) &&
642 ',' == string[3] &&
643 ' ' == string[4] &&
644 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
645 isdigit (string[6]) &&
646 ' ' == string[7] &&
647 isupper (string[8]) && /* Dec */
648 islower (string[9]) &&
649 islower (string[10]) &&
650 ' ' == string[11] &&
651 isdigit (string[12]) && /* 2001 */
652 isdigit (string[13]) &&
653 isdigit (string[14]) &&
654 isdigit (string[15]) &&
655 ' ' == string[16] &&
656 isdigit (string[17]) && /* 02: */
657 isdigit (string[18]) &&
658 ':' == string[19] &&
659 isdigit (string[20]) && /* 59: */
660 isdigit (string[21]) &&
661 ':' == string[22] &&
662 isdigit (string[23]) && /* 03 */
663 isdigit (string[24]) &&
664 ' ' == string[25] &&
665 'G' == string[26] && /* GMT */
666 'M' == string[27] && /* GMT */
667 'T' == string[28]) {
668
669 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
670 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
671 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
672 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
673 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
674 !strncmp (string+8, "Feb", 3) ? 1 :
675 !strncmp (string+8, "Mar", 3) ? 2 :
676 !strncmp (string+8, "Apr", 3) ? 3 :
677 !strncmp (string+8, "May", 3) ? 4 :
678 !strncmp (string+8, "Jun", 3) ? 5 :
679 !strncmp (string+8, "Jul", 3) ? 6 :
680 !strncmp (string+8, "Aug", 3) ? 7 :
681 !strncmp (string+8, "Sep", 3) ? 8 :
682 !strncmp (string+8, "Oct", 3) ? 9 :
683 !strncmp (string+8, "Nov", 3) ? 10 :
684 !strncmp (string+8, "Dec", 3) ? 11 :
685 -1);
686 tm.tm_year = ((1000 * (string[12]-'0') +
687 100 * (string[13]-'0') +
688 10 * (string[14]-'0') +
689 (string[15]-'0'))
690 - 1900);
691
692 tm.tm_isdst = 0; /* GMT is never in DST, right? */
693
694 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
695 return 0;
696
697 /*
698 This is actually wrong: we need to subtract the local timezone
699 offset from GMT from this value. But, that's ok in this usage,
700 because we only comparing these two GMT dates against each other,
701 so it doesn't matter what time zone we parse them in.
702 */
703
704 t = mktime (&tm);
705 if (t == (time_t) -1) t = 0;
706
707 if (verbose) {
708 const char *s = string;
709 while (*s && *s != '\r' && *s != '\n')
710 fputc (*s++, stdout);
711 printf (" ==> %lu\n", (unsigned long) t);
712 }
713
714 return t;
715
716 } else {
717 return 0;
718 }
719 }
720
721 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
722 static int
expected_statuscode(const char * reply,const char * statuscodes)723 expected_statuscode (const char *reply, const char *statuscodes)
724 {
725 char *expected, *code;
726 int result = 0;
727
728 if ((expected = strdup (statuscodes)) == NULL)
729 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
730
731 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
732 if (strstr (reply, code) != NULL) {
733 result = 1;
734 break;
735 }
736
737 free (expected);
738 return result;
739 }
740
chunk_header(char ** buf)741 int chunk_header(char **buf)
742 {
743 int lth = strtol(*buf, buf, 16);
744
745 if (lth <= 0)
746 return lth;
747
748 while (**buf !='\0' && **buf != '\r' && **buf != '\n')
749 ++*buf;
750
751 // soak up the leading CRLF
752 if (**buf && **buf == '\r' && *(++*buf) && **buf == '\n')
753 ++*buf;
754 else
755 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse chunked body, invalid format\n"));
756
757 return lth;
758 }
759
760 char *
decode_chunked_page(const char * raw,char * dst)761 decode_chunked_page (const char *raw, char *dst)
762 {
763 int chunksize;
764 char *raw_pos = (char*)raw;
765 char *dst_pos = (char*)dst;
766
767 for (;;) {
768 if ((chunksize = chunk_header(&raw_pos)) == 0)
769 break;
770 if (chunksize < 0)
771 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse chunked body, invalid chunk size\n"));
772
773 memmove(dst_pos, raw_pos, chunksize);
774 raw_pos += chunksize;
775 dst_pos += chunksize;
776 *dst_pos = '\0';
777
778 while (*raw_pos && (*raw_pos == '\r' || *raw_pos == '\n'))
779 raw_pos++;
780 }
781
782 return dst;
783 }
784
785 static char *
header_value(const char * headers,const char * header)786 header_value (const char *headers, const char *header)
787 {
788 char *s;
789 char *value;
790 const char *value_end;
791 int value_size;
792
793 if (!(s = strcasestr(headers, header))) {
794 return NULL;
795 }
796
797 s += strlen(header);
798
799 while (*s && (isspace(*s) || *s == ':')) s++;
800 while (*s && isspace(*s)) s++;
801
802 value_end = strchr(s, '\r');
803 if (!value_end)
804 value_end = strchr(s, '\n');
805 if (!value_end) {
806 // Turns out there's no newline after the header... So it's at the end!
807 value_end = s + strlen(s);
808 }
809
810 value_size = value_end - s;
811
812 value = malloc(value_size + 1);
813 if (!value) {
814 die (STATE_UNKNOWN, _("HTTP_UNKNOWN - Memory allocation error\n"));
815 }
816
817 if (!strncpy(value, s, value_size)) {
818 die(STATE_UNKNOWN, _("HTTP_UNKNOWN - Memory copy failure\n"));
819 }
820 value[value_size] = '\0';
821
822 return value;
823 }
824
825 static int
chunked_transfer_encoding(const char * headers)826 chunked_transfer_encoding (const char *headers)
827 {
828 int result;
829 char *encoding = header_value(headers, "Transfer-Encoding");
830 if (!encoding) {
831 return 0;
832 }
833
834 if (! strncmp(encoding, "chunked", sizeof("chunked"))) {
835 result = 1;
836 } else {
837 result = 0;
838 }
839
840 free(encoding);
841 return result;
842 }
843
844 static int
check_document_dates(const char * headers,char ** msg)845 check_document_dates (const char *headers, char **msg)
846 {
847 const char *s;
848 char *server_date = 0;
849 char *document_date = 0;
850 int date_result = STATE_OK;
851
852 s = headers;
853 while (*s) {
854 const char *field = s;
855 const char *value = 0;
856
857 /* Find the end of the header field */
858 while (*s && !isspace(*s) && *s != ':')
859 s++;
860
861 /* Remember the header value, if any. */
862 if (*s == ':')
863 value = ++s;
864
865 /* Skip to the end of the header, including continuation lines. */
866 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
867 s++;
868
869 /* Avoid stepping over end-of-string marker */
870 if (*s)
871 s++;
872
873 /* Process this header. */
874 if (value && value > field+2) {
875 char *ff = (char *) malloc (value-field);
876 char *ss = ff;
877 while (field < value-1)
878 *ss++ = tolower(*field++);
879 *ss++ = 0;
880
881 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
882 const char *e;
883 while (*value && isspace (*value))
884 value++;
885 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
886 ;
887 ss = (char *) malloc (e - value + 1);
888 strncpy (ss, value, e - value);
889 ss[e - value] = 0;
890 if (!strcmp (ff, "date")) {
891 if (server_date) free (server_date);
892 server_date = ss;
893 } else {
894 if (document_date) free (document_date);
895 document_date = ss;
896 }
897 }
898 free (ff);
899 }
900 }
901
902 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
903 if (!server_date || !*server_date) {
904 xasprintf (msg, _("%sServer date unknown, "), *msg);
905 date_result = max_state_alt(STATE_UNKNOWN, date_result);
906 } else if (!document_date || !*document_date) {
907 xasprintf (msg, _("%sDocument modification date unknown, "), *msg);
908 date_result = max_state_alt(STATE_CRITICAL, date_result);
909 } else {
910 time_t srv_data = parse_time_string (server_date);
911 time_t doc_data = parse_time_string (document_date);
912
913 if (srv_data <= 0) {
914 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
915 date_result = max_state_alt(STATE_CRITICAL, date_result);
916 } else if (doc_data <= 0) {
917 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
918 date_result = max_state_alt(STATE_CRITICAL, date_result);
919 } else if (doc_data > srv_data + 30) {
920 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
921 date_result = max_state_alt(STATE_CRITICAL, date_result);
922 } else if (doc_data < srv_data - maximum_age) {
923 int n = (srv_data - doc_data);
924 if (n > (60 * 60 * 24 * 2)) {
925 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
926 date_result = max_state_alt(STATE_CRITICAL, date_result);
927 } else {
928 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
929 date_result = max_state_alt(STATE_CRITICAL, date_result);
930 }
931 }
932 free (server_date);
933 free (document_date);
934 }
935 return date_result;
936 }
937
938 int
get_content_length(const char * headers)939 get_content_length (const char *headers)
940 {
941 const char *s;
942 int content_length = -1;
943
944 s = headers;
945 while (*s) {
946 const char *field = s;
947 const char *value = 0;
948
949 /* Find the end of the header field */
950 while (*s && !isspace(*s) && *s != ':')
951 s++;
952
953 /* Remember the header value, if any. */
954 if (*s == ':')
955 value = ++s;
956
957 /* Skip to the end of the header, including continuation lines. */
958 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
959 s++;
960
961 /* Avoid stepping over end-of-string marker */
962 if (*s)
963 s++;
964
965 /* Process this header. */
966 if (value && value > field+2) {
967 char *ff = (char *) malloc (value-field);
968 char *ss = ff;
969 while (field < value-1)
970 *ss++ = tolower(*field++);
971 *ss++ = 0;
972
973 if (!strcmp (ff, "content-length")) {
974 const char *e;
975 while (*value && isspace (*value))
976 value++;
977 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
978 ;
979 ss = (char *) malloc (e - value + 1);
980 strncpy (ss, value, e - value);
981 ss[e - value] = 0;
982 content_length = atoi(ss);
983 free (ss);
984 }
985 free (ff);
986 }
987 }
988 return (content_length);
989 }
990
991 char *
prepend_slash(char * path)992 prepend_slash (char *path)
993 {
994 char *newpath;
995
996 if (path[0] == '/')
997 return path;
998
999 if ((newpath = malloc (strlen(path) + 2)) == NULL)
1000 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
1001 newpath[0] = '/';
1002 strcpy (newpath + 1, path);
1003 free (path);
1004 return newpath;
1005 }
1006
1007 int
check_http(void)1008 check_http (void)
1009 {
1010 char *msg;
1011 char *status_line;
1012 char *status_code;
1013 char *header;
1014 char *page;
1015 char *auth;
1016 int http_status;
1017 int header_end;
1018 int content_length;
1019 int content_start;
1020 int seen_length;
1021 int i = 0;
1022 size_t pagesize = 0;
1023 char *full_page;
1024 char *full_page_new;
1025 char *buf;
1026 char *pos;
1027 long microsec = 0L;
1028 double elapsed_time = 0.0;
1029 long microsec_connect = 0L;
1030 double elapsed_time_connect = 0.0;
1031 long microsec_ssl = 0L;
1032 double elapsed_time_ssl = 0.0;
1033 long microsec_firstbyte = 0L;
1034 double elapsed_time_firstbyte = 0.0;
1035 long microsec_headers = 0L;
1036 double elapsed_time_headers = 0.0;
1037 long microsec_transfer = 0L;
1038 double elapsed_time_transfer = 0.0;
1039 int page_len = 0;
1040 int result = STATE_OK;
1041 char *force_host_header = NULL;
1042 int bad_response = FALSE;
1043 char save_char;
1044
1045 /* try to connect to the host at the given port number */
1046 gettimeofday (&tv_temp, NULL);
1047 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
1048 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
1049 microsec_connect = deltime (tv_temp);
1050
1051 /* if we are called with the -I option, the -j method is CONNECT and */
1052 /* we received -S for SSL, then we tunnel the request through a proxy*/
1053 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
1054
1055 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
1056 && host_name != NULL && use_ssl == TRUE) {
1057
1058 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT);
1059 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent);
1060 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf);
1061 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1062 /* we finished our request, send empty line with CRLF */
1063 asprintf (&buf, "%s%s", buf, CRLF);
1064 if (verbose) printf ("%s\n", buf);
1065 send(sd, buf, strlen (buf), 0);
1066 buf[0]='\0';
1067
1068 if (verbose) printf ("Receive response from proxy\n");
1069 read (sd, buffer, MAX_INPUT_BUFFER-1);
1070 if (verbose) printf ("%s", buffer);
1071 /* Here we should check if we got HTTP/1.1 200 Connection established */
1072 }
1073 #ifdef HAVE_SSL
1074 elapsed_time_connect = (double)microsec_connect / 1.0e6;
1075 if (use_ssl == TRUE) {
1076 gettimeofday (&tv_temp, NULL);
1077 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
1078 if (verbose) printf ("SSL initialized\n");
1079 if (result != STATE_OK)
1080 die (STATE_CRITICAL, NULL);
1081 microsec_ssl = deltime (tv_temp);
1082 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1083 if (check_cert == TRUE) {
1084 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1085 if (continue_after_check_cert == FALSE) {
1086
1087 if (sd) {
1088 close(sd);
1089 }
1090 np_net_ssl_cleanup();
1091 return result;
1092 }
1093 }
1094 }
1095 #endif /* HAVE_SSL */
1096
1097 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
1098 && host_name != NULL && use_ssl == TRUE)
1099 asprintf (&buf, "%s %s %s\r\n%s\r\n", "GET", server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1100 else
1101 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1102
1103 /* tell HTTP/1.1 servers not to keep the connection alive */
1104 xasprintf (&buf, "%sConnection: close\r\n", buf);
1105
1106 /* check if Host header is explicitly set in options */
1107 if (http_opt_headers_count) {
1108 for (i = 0; i < http_opt_headers_count ; i++) {
1109 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1110 force_host_header = http_opt_headers[i];
1111 }
1112 }
1113 }
1114
1115 /* optionally send the host header info */
1116 if (host_name) {
1117 if (force_host_header) {
1118 xasprintf (&buf, "%s%s\r\n", buf, force_host_header);
1119 } else {
1120 /*
1121 * Specify the port only if we're using a non-default port (see RFC 2616,
1122 * 14.23). Some server applications/configurations cause trouble if the
1123 * (default) port is explicitly specified in the "Host:" header line.
1124 */
1125 if ((use_ssl == FALSE && server_port == HTTP_PORT) ||
1126 (use_ssl == TRUE && server_port == HTTPS_PORT))
1127 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1128 else
1129 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, server_port);
1130 }
1131 }
1132
1133 /* Inform server we accept any MIME type response
1134 * TODO: Take an argument to determine what type(s) to accept,
1135 * so that we can alert if a response is of an invalid type.
1136 */
1137 if (!have_accept)
1138 xasprintf(&buf, "%sAccept: */*\r\n", buf);
1139
1140 /* optionally send any other header tag */
1141 if (http_opt_headers_count) {
1142 for (i = 0; i < http_opt_headers_count ; i++) {
1143 if (force_host_header != http_opt_headers[i]) {
1144 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1145 }
1146 }
1147 /* This cannot be free'd here because a redirection will then try to access this and segfault */
1148 /* Covered in a testcase in tests/check_http.t */
1149 /* free(http_opt_headers); */
1150 }
1151
1152 /* optionally send the authentication info */
1153 if (strlen(user_auth)) {
1154 base64_encode_alloc (user_auth, strlen (user_auth), &auth);
1155 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1156 }
1157
1158 /* optionally send the proxy authentication info */
1159 if (strlen(proxy_auth)) {
1160 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
1161 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1162 }
1163
1164 /* either send http POST data (any data, not only POST)*/
1165 if (http_post_data) {
1166 if (http_content_type) {
1167 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
1168 } else {
1169 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1170 }
1171
1172 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
1173 xasprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
1174 } else {
1175 /* or just a newline so the server knows we're done with the request */
1176 xasprintf (&buf, "%s%s", buf, CRLF);
1177 }
1178
1179 if (verbose) printf ("%s\n", buf);
1180 gettimeofday (&tv_temp, NULL);
1181 my_send (buf, strlen (buf));
1182 microsec_headers = deltime (tv_temp);
1183 elapsed_time_headers = (double)microsec_headers / 1.0e6;
1184
1185 /* fetch the page */
1186 full_page = strdup("");
1187 gettimeofday (&tv_temp, NULL);
1188 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
1189 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1190 microsec_firstbyte = deltime (tv_temp);
1191 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1192 }
1193 buffer[i] = '\0';
1194 /* xasprintf (&full_page_new, "%s%s", full_page, buffer); */
1195 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1196 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1197
1198 memmove(&full_page_new[pagesize], buffer, i + 1);
1199 /*free (full_page);*/
1200 full_page = full_page_new;
1201 pagesize += i;
1202
1203 header_end = document_headers_done(full_page);
1204 if (header_end) {
1205 i = 0;
1206 break;
1207 }
1208 }
1209
1210 if (no_body) {
1211 full_page[header_end] = '\0';
1212 }
1213 else {
1214 content_length = get_content_length(full_page);
1215
1216 content_start = header_end + 1;
1217 while (full_page[content_start] == '\n' || full_page[content_start] == '\r') {
1218 content_start += 1;
1219 }
1220 seen_length = pagesize - content_start;
1221 /* Continue receiving the body until content-length is met */
1222 while ((content_length < 0 || seen_length < content_length)
1223 && ((i = my_recv(buffer, MAX_INPUT_BUFFER-1)) > 0)) {
1224
1225 buffer[i] = '\0';
1226
1227 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1228 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1229 memmove(&full_page_new[pagesize], buffer, i + 1);
1230 full_page = full_page_new;
1231
1232 pagesize += i;
1233 seen_length = pagesize - content_start;
1234 }
1235 }
1236
1237 microsec_transfer = deltime (tv_temp);
1238 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1239
1240 if (i < 0 && errno != ECONNRESET) {
1241 #ifdef HAVE_SSL
1242 /*
1243 if (use_ssl) {
1244 sslerr=SSL_get_error(ssl, i);
1245 if ( sslerr == SSL_ERROR_SSL ) {
1246 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
1247 } else {
1248 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1249 }
1250 }
1251 else {
1252 */
1253 #endif
1254 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1255 #ifdef HAVE_SSL
1256 /* XXX
1257 }
1258 */
1259 #endif
1260 }
1261
1262 /* return a CRITICAL status if we couldn't read any data */
1263 if (pagesize == (size_t) 0)
1264 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1265
1266 /* close the connection */
1267 if (sd) close(sd);
1268 #ifdef HAVE_SSL
1269 np_net_ssl_cleanup();
1270 #endif
1271
1272 /* Save check time */
1273 microsec = deltime (tv);
1274 elapsed_time = (double)microsec / 1.0e6;
1275
1276 /* leave full_page untouched so we can free it later */
1277 pos = page = full_page;
1278
1279 if (verbose)
1280 printf ("%s://%s:%d%s is %d characters\n",
1281 use_ssl ? "https" : "http", server_address,
1282 server_port, server_url, (int)pagesize);
1283
1284 /* find status line and null-terminate it */
1285 page += (size_t) strcspn (page, "\r\n");
1286 save_char = *page;
1287 *page = '\0';
1288 status_line = strdup(pos);
1289 *page = save_char;
1290 pos = page;
1291
1292
1293 strip (status_line);
1294 if (verbose)
1295 printf ("STATUS: %s\n", status_line);
1296
1297 /* find header info and null-terminate it */
1298 header = page;
1299
1300 for (;;) {
1301
1302 if ((page == NULL) || !strncmp(page, "\r\n\r\n", 4) || !strncmp(page, "\n\n", 2) || *page == '\0' )
1303 break;
1304
1305 while (*page == '\r' || *page == '\n') {
1306 ++page;
1307 }
1308
1309 page += (size_t) strcspn (page, "\r\n");
1310 pos = page;
1311
1312 if (*page == '\0')
1313 break;
1314 }
1315
1316 page += (size_t) strspn (page, "\r\n");
1317 header[pos - header] = 0;
1318 while (*header == '\r' || *header == '\n') {
1319 ++header;
1320 }
1321
1322 if (chunked_transfer_encoding(header) && *page)
1323 page = decode_chunked_page(page, page);
1324
1325 if (verbose)
1326 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1327 (no_body ? " [[ skipped ]]" : page));
1328
1329 xasprintf(&msg, "");
1330
1331 /* make sure the status line matches the response we are looking for */
1332 if (!expected_statuscode (status_line, server_expect)) {
1333
1334 if (server_port == HTTP_PORT)
1335 xasprintf (&msg,
1336 _("Invalid HTTP response received from host: %s\n"),
1337 status_line);
1338 else
1339 xasprintf (&msg,
1340 _("Invalid HTTP response received from host on port %d: %s\n"),
1341 server_port, status_line);
1342 bad_response = TRUE;
1343 }
1344
1345 /* Bypass normal status line check if server_expect was set by user and not default */
1346 if ( server_expect_yn && !bad_response ) {
1347
1348 xasprintf (&msg,
1349 _("Status line output matched \"%s\" - "), server_expect);
1350
1351 if (verbose)
1352 printf ("%s\n",msg);
1353
1354 } else {
1355
1356 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1357 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1358 /* Status-Code = 3 DIGITS */
1359
1360 if (status_line != NULL) {
1361
1362 status_code = strchr(status_line, ' ');
1363 if (status_code != NULL)
1364 /* Normally the following line runs once, but some servers put extra whitespace between the version number and status code. */
1365 while (*status_code == ' ') { status_code += sizeof(char); }
1366
1367 if (status_code == NULL || (strspn(status_code, "1234567890") != 3))
1368 check_http_die (STATE_CRITICAL, _("Invalid Status Line (%s)\n"), status_line);
1369
1370 } else {
1371
1372 check_http_die (STATE_CRITICAL, _("No Status Line\n"));
1373 }
1374
1375 http_status = atoi (status_code);
1376
1377 /* check the return code */
1378
1379 if (http_status >= 600 || http_status < 100) {
1380 check_http_die (STATE_CRITICAL, _("Invalid Status (%s)\n"), status_line);
1381 }
1382
1383 /* server errors result in a critical state */
1384 else if (http_status >= 500) {
1385 xasprintf (&msg, _("%s%s - "), msg, status_line);
1386 if (bad_response || !server_expect_yn)
1387 result = STATE_CRITICAL;
1388 }
1389
1390 /* client errors result in a warning state */
1391 else if (http_status >= 400) {
1392 xasprintf (&msg, _("%s%s - "), msg, status_line);
1393 if (bad_response || !server_expect_yn)
1394 result = max_state_alt(STATE_WARNING, result);
1395 }
1396
1397 /* check redirected page if specified */
1398 else if (http_status >= 300) {
1399
1400 if (onredirect == STATE_DEPENDENT)
1401 redir (header, status_line);
1402 else
1403 result = max_state_alt(onredirect, result);
1404 xasprintf (&msg, _("%s%s - "), msg, status_line);
1405 }
1406
1407 /* end if (http_status >= 300) */
1408 else if (!bad_response) {
1409
1410 /* Print OK status anyway */
1411 xasprintf (&msg, _("%s%s - "), msg, status_line);
1412 }
1413
1414 } /* end else [if (server_expect_yn)] */
1415
1416 free(status_line);
1417
1418 if (bad_response)
1419 check_http_die (STATE_CRITICAL, msg);
1420
1421 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1422 alarm (0);
1423
1424 if (maximum_age >= 0) {
1425 result = max_state_alt(check_document_dates(header, &msg), result);
1426 }
1427
1428
1429 /* Page and Header content checks go here */
1430 if (strlen (header_expect)) {
1431 if (!strstr (header, header_expect)) {
1432 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
1433 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
1434 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
1435 }
1436 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);
1437 result = STATE_CRITICAL;
1438 }
1439 }
1440
1441 if (strlen (string_expect)) {
1442 if (!strstr (page, string_expect)) {
1443 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
1444 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
1445 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
1446 }
1447 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);
1448 result = STATE_CRITICAL;
1449 }
1450 }
1451
1452 if (strlen (regexp)) {
1453 errcode = regexec (&preg, page, REGS, pmatch, 0);
1454 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
1455 /* OK - No-op to avoid changing the logic around it */
1456 result = max_state_alt(STATE_OK, result);
1457 } else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1458 if (invert_regex == 0)
1459 xasprintf (&msg, _("%spattern not found, "), msg);
1460 else
1461 xasprintf (&msg, _("%spattern found, "), msg);
1462 result = STATE_CRITICAL;
1463 } else {
1464 /* FIXME: Shouldn't that be UNKNOWN? */
1465 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1466 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1467 result = STATE_CRITICAL;
1468 }
1469 }
1470
1471 /* make sure the page is of an appropriate size */
1472 /* page_len = get_content_length(header); */
1473 /* FIXME: Will this work with -N ? IMHO we should use
1474 * get_content_length(header) and always check if it's different than the
1475 * returned pagesize
1476 */
1477 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1478 * it == get_content_length(header) ??
1479 */
1480 page_len = pagesize;
1481 if ((max_page_len > 0) && (page_len > max_page_len)) {
1482 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1483 result = max_state_alt(STATE_WARNING, result);
1484 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1485 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1486 result = max_state_alt(STATE_WARNING, result);
1487 }
1488
1489 /* Cut-off trailing characters */
1490 if(msg[strlen(msg)-2] == ',') {
1491 msg[strlen(msg)-2] = '\0';
1492 }
1493 else {
1494 msg[strlen(msg)-3] = '\0';
1495 }
1496
1497 /* show checked URL */
1498 if (show_url)
1499 xasprintf (&msg, _("%s - %s://%s:%d%s"), msg, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
1500
1501
1502
1503 /* check elapsed time */
1504 if (show_extended_perfdata) {
1505 xasprintf (&msg,
1506 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1507 msg, page_len, elapsed_time,
1508 (display_html ? "</A>" : ""),
1509 perfd_time (elapsed_time),
1510 perfd_size (page_len),
1511 perfd_time_connect (elapsed_time_connect),
1512 use_ssl == TRUE ? perfd_time_ssl (elapsed_time_ssl) : "",
1513 perfd_time_headers (elapsed_time_headers),
1514 perfd_time_firstbyte (elapsed_time_firstbyte),
1515 perfd_time_transfer (elapsed_time_transfer));
1516 }
1517 else {
1518 xasprintf (&msg,
1519 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1520 msg, page_len, elapsed_time,
1521 (display_html ? "</A>" : ""),
1522 perfd_time (elapsed_time),
1523 perfd_size (page_len));
1524 }
1525
1526 result = max_state_alt(get_status(elapsed_time, thlds), result);
1527
1528 die (result, "HTTP %s: %s\n", state_text(result), msg);
1529
1530 /* die failed? */
1531 return STATE_UNKNOWN;
1532 }
1533
1534
1535
1536 /* per RFC 2396 */
1537 #define URI_HTTP "%5[HTPShtps]"
1538 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1539 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1540 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1541 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1542 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1543 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1544 #define HD4 URI_HTTP "://" URI_HOST
1545 /* HD5 - relative reference redirect like //www.site.org/test https://tools.ietf.org/html/rfc3986 */
1546 #define HD5 URI_HTTP "//" URI_HOST "/" URI_PATH
1547 #define HD6 URI_PATH
1548
1549 void
redir(char * pos,char * status_line)1550 redir (char *pos, char *status_line)
1551 {
1552 int i = 0;
1553 char *x;
1554 char xx[2];
1555 char type[6];
1556 char *addr;
1557 char *url;
1558
1559 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1560 if (addr == NULL)
1561 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1562
1563 memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1564 url = malloc (strcspn (pos, "\r\n"));
1565 if (url == NULL)
1566 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1567
1568 while (pos) {
1569 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1570 if (i == 0) {
1571 pos += (size_t) strcspn (pos, "\r\n");
1572 pos += (size_t) strspn (pos, "\r\n");
1573 if (strlen(pos) == 0)
1574 die (STATE_UNKNOWN,
1575 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1576 status_line, (display_html ? "</A>" : ""));
1577 continue;
1578 }
1579
1580 pos += i;
1581 pos += strspn (pos, " \t");
1582
1583 /*
1584 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1585 * preceding each extra line with at least one SP or HT.''
1586 */
1587 for (; (i = strspn (pos, "\r\n")); pos += i) {
1588 pos += i;
1589 if (!(i = strspn (pos, " \t"))) {
1590 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1591 display_html ? "</A>" : "");
1592 }
1593 }
1594
1595 url = realloc (url, strcspn (pos, "\r\n") + 1);
1596 if (url == NULL)
1597 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1598
1599 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1600 if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1601 url = prepend_slash (url);
1602 use_ssl = server_type_check (type);
1603 }
1604
1605 /* URI_HTTP URI_HOST URI_PATH */
1606 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1607 url = prepend_slash (url);
1608 use_ssl = server_type_check (type);
1609 i = server_port_check (use_ssl);
1610 }
1611
1612 /* URI_HTTP URI_HOST URI_PORT */
1613 else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1614 strcpy (url, HTTP_URL);
1615 use_ssl = server_type_check (type);
1616 }
1617
1618 /* URI_HTTP URI_HOST */
1619 else if (sscanf (pos, HD4, type, addr) == 2) {
1620 strcpy (url, HTTP_URL);
1621 use_ssl = server_type_check (type);
1622 i = server_port_check (use_ssl);
1623 }
1624
1625 /* URI_HTTP, URI_HOST, URI_PATH */
1626 else if (sscanf (pos, HD5, addr, url) == 2) {
1627 if(use_ssl)
1628 strcpy (type,"https");
1629 else
1630 strcpy (type,server_type);
1631 xasprintf(&url, "/%s", url);
1632 use_ssl = server_type_check (type);
1633 i = server_port_check (use_ssl);
1634 }
1635
1636 /* URI_PATH */
1637 else if (sscanf (pos, HD6, url) == 1) {
1638 /* relative url */
1639 if ((url[0] != '/')) {
1640 if ((x = strrchr(server_url, '/')))
1641 *x = '\0';
1642 xasprintf (&url, "%s/%s", server_url, url);
1643 }
1644 i = server_port;
1645 strcpy (type, server_type);
1646 strcpy (addr, host_name ? host_name : server_address);
1647 }
1648
1649 else {
1650 die (STATE_UNKNOWN,
1651 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1652 pos, (display_html ? "</A>" : ""));
1653 }
1654
1655 break;
1656
1657 } /* end while (pos) */
1658
1659 if (++redir_depth > max_depth)
1660 die (STATE_WARNING,
1661 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1662 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1663
1664 if (server_port==i &&
1665 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1666 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) &&
1667 !strcmp(server_url, url))
1668 die (STATE_WARNING,
1669 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1670 type, addr, i, url, (display_html ? "</A>" : ""));
1671
1672 strcpy (server_type, type);
1673
1674 free (host_name);
1675 host_name = strndup (addr, MAX_IPV4_HOSTLENGTH);
1676
1677 if (!(followsticky & STICKY_HOST)) {
1678 free (server_address);
1679 server_address = strndup (addr, MAX_IPV4_HOSTLENGTH);
1680 }
1681 if (!(followsticky & STICKY_PORT)) {
1682 server_port = i;
1683 }
1684
1685 free (server_url);
1686 server_url = url;
1687
1688 if (server_port > MAX_PORT)
1689 die (STATE_UNKNOWN,
1690 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1691 MAX_PORT, server_type, server_address, server_port, server_url,
1692 display_html ? "</A>" : "");
1693
1694 if (verbose)
1695 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1696 host_name ? host_name : server_address, server_port, server_url);
1697
1698 free(addr);
1699 check_http ();
1700 }
1701
1702
1703 int
server_type_check(const char * type)1704 server_type_check (const char *type)
1705 {
1706 if (strcmp (type, "https"))
1707 return FALSE;
1708 else
1709 return TRUE;
1710 }
1711
1712 int
server_port_check(int ssl_flag)1713 server_port_check (int ssl_flag)
1714 {
1715 if (ssl_flag)
1716 return HTTPS_PORT;
1717 else
1718 return HTTP_PORT;
1719 }
1720
perfd_time(double elapsed_time)1721 char *perfd_time (double elapsed_time)
1722 {
1723 return fperfdata ("time", elapsed_time, "s",
1724 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0,
1725 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0,
1726 TRUE, 0, FALSE, 0);
1727 }
1728
perfd_time_connect(double elapsed_time_connect)1729 char *perfd_time_connect (double elapsed_time_connect)
1730 {
1731 return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1732 }
1733
perfd_time_ssl(double elapsed_time_ssl)1734 char *perfd_time_ssl (double elapsed_time_ssl)
1735 {
1736 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1737 }
1738
perfd_time_headers(double elapsed_time_headers)1739 char *perfd_time_headers (double elapsed_time_headers)
1740 {
1741 return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1742 }
1743
perfd_time_firstbyte(double elapsed_time_firstbyte)1744 char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1745 {
1746 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1747 }
1748
perfd_time_transfer(double elapsed_time_transfer)1749 char *perfd_time_transfer (double elapsed_time_transfer)
1750 {
1751 return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1752 }
1753
perfd_size(int page_len)1754 char *perfd_size (int page_len)
1755 {
1756 return perfdata ("size", page_len, "B",
1757 (min_page_len>0?TRUE:FALSE), min_page_len,
1758 (min_page_len>0?TRUE:FALSE), 0,
1759 TRUE, 0, FALSE, 0);
1760 }
1761
1762 void
print_help(void)1763 print_help (void)
1764 {
1765 print_revision (progname, NP_VERSION);
1766
1767 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1768 printf (COPYRIGHT, copyright, email);
1769
1770 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1771 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1772 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1773 printf ("%s\n", _("certificate expiration times."));
1774
1775 printf ("\n\n");
1776
1777 print_usage ();
1778
1779 printf (_("NOTE: One or both of -H and -I must be specified"));
1780
1781 printf ("\n");
1782
1783 printf (UT_HELP_VRSN);
1784 printf (UT_EXTRA_OPTS);
1785
1786 printf (" %s\n", "-H, --hostname=ADDRESS");
1787 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1788 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1789 printf (" %s\n", "-I, --IP-address=ADDRESS");
1790 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1791 printf (" %s\n", "-p, --port=INTEGER");
1792 printf (" %s", _("Port number (default: "));
1793 printf ("%d)\n", HTTP_PORT);
1794
1795 printf (UT_IPv46);
1796
1797 #ifdef HAVE_SSL
1798 printf (" %s\n", "-S, --ssl=VERSION[+]");
1799 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1800 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1801 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1802 printf (" %s\n", "--sni");
1803 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1804 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1805 printf (" %s\n", "--verify-host");
1806 printf (" %s\n", _("Verify SSL certificate is for the -H hostname (with --sni and -S)"));
1807 #endif
1808 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1809 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1810 printf (" %s\n", _("(When this option is used the URL is not checked by default. You can use"));
1811 printf (" %s\n", _(" --continue-after-certificate to override this behavior)"));
1812 printf (" %s\n", "--continue-after-certificate");
1813 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
1814 printf (" %s\n", _("Does nothing unless -C is used."));
1815 printf (" %s\n", "-J, --client-cert=FILE");
1816 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1817 printf (" %s\n", _("to be used in establishing the SSL session"));
1818 printf (" %s\n", "-K, --private-key=FILE");
1819 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1820 printf (" %s\n", _("matching the client certificate"));
1821 #endif
1822
1823 printf (" %s\n", "-e, --expect=STRING");
1824 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1825 printf (" %s", _("the first (status) line of the server response (default: "));
1826 printf ("%s)\n", HTTP_EXPECT);
1827 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1828 printf (" %s\n", "-d, --header-string=STRING");
1829 printf (" %s\n", _("String to expect in the response headers"));
1830 printf (" %s\n", "-s, --string=STRING");
1831 printf (" %s\n", _("String to expect in the content"));
1832 printf (" %s\n", "-u, --uri=PATH");
1833 printf (" %s\n", _("URI to GET or POST (default: /)"));
1834 printf (" %s\n", "--url=PATH");
1835 printf (" %s\n", _("(deprecated) URL to GET or POST (default: /)"));
1836 printf (" %s\n", "-P, --post=STRING");
1837 printf (" %s\n", _("URL encoded http POST data"));
1838 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1839 printf (" %s\n", _("Set HTTP method."));
1840 printf (" %s\n", "-N, --no-body");
1841 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1842 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1843 printf (" %s\n", "-M, --max-age=SECONDS");
1844 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1845 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1846 printf (" %s\n", "-T, --content-type=STRING");
1847 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1848
1849 printf (" %s\n", "-l, --linespan");
1850 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1851 printf (" %s\n", "-r, --regex, --ereg=STRING");
1852 printf (" %s\n", _("Search page for regex STRING"));
1853 printf (" %s\n", "-R, --eregi=STRING");
1854 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1855 printf (" %s\n", "--invert-regex");
1856 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1857
1858 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1859 printf (" %s\n", _("Username:password on sites with basic authentication"));
1860 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1861 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1862 printf (" %s\n", "-A, --useragent=STRING");
1863 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1864 printf (" %s\n", "-k, --header=STRING");
1865 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1866 printf (" %s\n", "-E, --extended-perfdata");
1867 printf (" %s\n", _("Print additional performance data"));
1868 printf (" %s\n", "-U, --show-url");
1869 printf (" %s\n", _("Print URL in msg output in plain text"));
1870 printf (" %s\n", "-L, --link");
1871 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1872 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1873 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1874 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1875 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1876 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1877
1878 printf (UT_WARN_CRIT);
1879
1880 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1881
1882 printf (UT_VERBOSE);
1883
1884 printf ("\n");
1885 printf ("%s\n", _("Notes:"));
1886 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1887 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1888 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1889 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1890 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1891 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1892 printf (" %s\n", _("You may also need to give a FQDN or IP address using -I (or --IP-Address)."));
1893
1894 #ifdef HAVE_SSL
1895 printf ("\n");
1896 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1897 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1898 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1899 printf ("\n");
1900 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1901 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1902 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1903 printf ("\n");
1904 printf ("%s\n", _("Examples:"));
1905 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1906 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1907 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1908 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1909 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1910 printf ("\n");
1911 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1912 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1913 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1914 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1915 printf (" %s\n\n", _("the certificate is expired."));
1916 printf ("\n");
1917 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1918 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1919 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1920 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1921 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1922
1923 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1924 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1925 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1926 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1927 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1928 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1929
1930 #endif
1931
1932 printf (UT_SUPPORT);
1933
1934 }
1935
1936
1937
1938 void
print_usage(void)1939 print_usage (void)
1940 {
1941 printf ("%s\n", _("Usage:"));
1942 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1943 printf (" [-J <client certificate file>] [-K <private key>]\n");
1944 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-U] [-a auth]\n");
1945 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n");
1946 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1947 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1948
1949 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1950 printf (" [-A string] [-k string] [-S <version>] [--sni] [--verify-host]\n");
1951 printf (" [-C <warn_age>[,<crit_age>]] [-T <content-type>] [-j method]\n");
1952 #else
1953 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1954 printf (" [-T <content-type>] [-j method]\n");
1955 #endif
1956 }
1957