Merge pull request #2023 from sni/make_rawhide_tests_optional
[monitoring-plugins.git] / plugins / check_http.c
blobcdf768c9128dab79b575803144f7c73b3ab5359c
1 /*****************************************************************************
3 * Monitoring check_http plugin
5 * License: GPL
6 * Copyright (c) 1999-2013 Monitoring 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 const char *progname = "check_http";
35 const char *copyright = "1999-2022";
36 const char *email = "devel@monitoring-plugins.org";
38 // Do NOT sort those headers, it will break the build
39 // TODO: Fix this
40 #include "common.h"
41 #include "base64.h"
42 #include "netutils.h"
43 #include "utils.h"
44 #include "base64.h"
45 #include <ctype.h>
47 #define STICKY_NONE 0
48 #define STICKY_HOST 1
49 #define STICKY_PORT 2
51 #define HTTP_EXPECT "HTTP/1."
52 enum {
53 MAX_IPV4_HOSTLENGTH = 255,
54 HTTP_PORT = 80,
55 HTTPS_PORT = 443,
56 MAX_PORT = 65535,
57 DEFAULT_MAX_REDIRS = 15
60 #ifdef HAVE_SSL
61 bool check_cert = false;
62 bool continue_after_check_cert = false;
63 int ssl_version = 0;
64 int days_till_exp_warn, days_till_exp_crit;
65 char *randbuff;
66 X509 *server_cert;
67 # define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len))
68 # define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0))
69 #else /* ifndef HAVE_SSL */
70 # define my_recv(buf, len) read(sd, buf, len)
71 # define my_send(buf, len) send(sd, buf, len, 0)
72 #endif /* HAVE_SSL */
73 bool no_body = false;
74 int maximum_age = -1;
76 enum {
77 REGS = 2,
78 MAX_RE_SIZE = 1024
80 #include "regex.h"
81 regex_t preg;
82 regmatch_t pmatch[REGS];
83 char regexp[MAX_RE_SIZE];
84 char errbuf[MAX_INPUT_BUFFER];
85 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
86 int errcode;
87 int invert_regex = 0;
88 int state_regex = STATE_CRITICAL;
90 struct timeval tv;
91 struct timeval tv_temp;
93 #define HTTP_URL "/"
94 #define CRLF "\r\n"
96 bool specify_port = false;
97 int server_port = HTTP_PORT;
98 int virtual_port = 0;
99 char server_port_text[6] = "";
100 char server_type[6] = "http";
101 char *server_address;
102 char *host_name;
103 int host_name_length;
104 char *server_url;
105 char *user_agent;
106 int server_url_length;
107 int server_expect_yn = 0;
108 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
109 char header_expect[MAX_INPUT_BUFFER] = "";
110 char string_expect[MAX_INPUT_BUFFER] = "";
111 char *warning_thresholds = NULL;
112 char *critical_thresholds = NULL;
113 thresholds *thlds;
114 char user_auth[MAX_INPUT_BUFFER] = "";
115 char proxy_auth[MAX_INPUT_BUFFER] = "";
116 bool display_html = false;
117 char **http_opt_headers;
118 int http_opt_headers_count = 0;
119 int onredirect = STATE_OK;
120 int followsticky = STICKY_NONE;
121 bool use_ssl = false;
122 bool use_sni = false;
123 bool verbose = false;
124 bool show_extended_perfdata = false;
125 bool show_body = false;
126 int sd;
127 int min_page_len = 0;
128 int max_page_len = 0;
129 int redir_depth = 0;
130 int max_depth = DEFAULT_MAX_REDIRS;
131 char *http_method;
132 char *http_method_proxy;
133 char *http_post_data;
134 char *http_content_type;
135 char buffer[MAX_INPUT_BUFFER];
136 char *client_cert = NULL;
137 char *client_privkey = NULL;
139 // Forward function declarations
140 bool process_arguments (int, char **);
141 int check_http (void);
142 void redir (char *pos, char *status_line);
143 bool server_type_check(const char *type);
144 int server_port_check(int ssl_flag);
145 char *perfd_time (double microsec);
146 char *perfd_time_connect (double microsec);
147 char *perfd_time_ssl (double microsec);
148 char *perfd_time_firstbyte (double microsec);
149 char *perfd_time_headers (double microsec);
150 char *perfd_time_transfer (double microsec);
151 char *perfd_size (int page_len);
152 void print_help (void);
153 void print_usage (void);
154 char *unchunk_content(const char *content);
157 main (int argc, char **argv)
159 int result = STATE_UNKNOWN;
161 setlocale (LC_ALL, "");
162 bindtextdomain (PACKAGE, LOCALEDIR);
163 textdomain (PACKAGE);
165 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
166 server_url = strdup(HTTP_URL);
167 server_url_length = strlen(server_url);
168 xasprintf (&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)",
169 NP_VERSION, VERSION);
171 /* Parse extra opts if any */
172 argv=np_extra_opts (&argc, argv, progname);
174 if (process_arguments (argc, argv) == false)
175 usage4 (_("Could not parse arguments"));
177 if (display_html == true)
178 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
179 use_ssl ? "https" : "http", host_name ? host_name : server_address,
180 server_port, server_url);
182 /* initialize alarm signal handling, set socket timeout, start timer */
183 (void) signal (SIGALRM, socket_timeout_alarm_handler);
184 (void) alarm (socket_timeout);
185 gettimeofday (&tv, NULL);
187 result = check_http ();
188 return result;
191 /* check whether a file exists */
192 void
193 test_file (char *path)
195 if (access(path, R_OK) == 0)
196 return;
197 usage2 (_("file does not exist or is not readable"), path);
201 * process command-line arguments
202 * returns true on success, false otherwise
204 bool process_arguments (int argc, char **argv)
206 int c = 1;
207 char *p;
208 char *temp;
210 enum {
211 INVERT_REGEX = CHAR_MAX + 1,
212 SNI_OPTION,
213 MAX_REDIRS_OPTION,
214 CONTINUE_AFTER_CHECK_CERT,
215 STATE_REGEX
218 int option = 0;
219 static struct option longopts[] = {
220 STD_LONG_OPTS,
221 {"link", no_argument, 0, 'L'},
222 {"nohtml", no_argument, 0, 'n'},
223 {"ssl", optional_argument, 0, 'S'},
224 {"sni", no_argument, 0, SNI_OPTION},
225 {"post", required_argument, 0, 'P'},
226 {"method", required_argument, 0, 'j'},
227 {"IP-address", required_argument, 0, 'I'},
228 {"url", required_argument, 0, 'u'},
229 {"port", required_argument, 0, 'p'},
230 {"authorization", required_argument, 0, 'a'},
231 {"proxy-authorization", required_argument, 0, 'b'},
232 {"header-string", required_argument, 0, 'd'},
233 {"string", required_argument, 0, 's'},
234 {"expect", required_argument, 0, 'e'},
235 {"regex", required_argument, 0, 'r'},
236 {"ereg", required_argument, 0, 'r'},
237 {"eregi", required_argument, 0, 'R'},
238 {"linespan", no_argument, 0, 'l'},
239 {"onredirect", required_argument, 0, 'f'},
240 {"certificate", required_argument, 0, 'C'},
241 {"client-cert", required_argument, 0, 'J'},
242 {"private-key", required_argument, 0, 'K'},
243 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
244 {"useragent", required_argument, 0, 'A'},
245 {"header", required_argument, 0, 'k'},
246 {"no-body", no_argument, 0, 'N'},
247 {"max-age", required_argument, 0, 'M'},
248 {"content-type", required_argument, 0, 'T'},
249 {"pagesize", required_argument, 0, 'm'},
250 {"invert-regex", no_argument, NULL, INVERT_REGEX},
251 {"state-regex", required_argument, 0, STATE_REGEX},
252 {"use-ipv4", no_argument, 0, '4'},
253 {"use-ipv6", no_argument, 0, '6'},
254 {"extended-perfdata", no_argument, 0, 'E'},
255 {"show-body", no_argument, 0, 'B'},
256 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
257 {0, 0, 0, 0}
260 if (argc < 2)
261 return false;
263 for (c = 1; c < argc; c++) {
264 if (strcmp ("-to", argv[c]) == 0)
265 strcpy (argv[c], "-t");
266 if (strcmp ("-hn", argv[c]) == 0)
267 strcpy (argv[c], "-H");
268 if (strcmp ("-wt", argv[c]) == 0)
269 strcpy (argv[c], "-w");
270 if (strcmp ("-ct", argv[c]) == 0)
271 strcpy (argv[c], "-c");
272 if (strcmp ("-nohtml", argv[c]) == 0)
273 strcpy (argv[c], "-n");
276 while (1) {
277 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:NEB", longopts, &option);
278 if (c == -1 || c == EOF)
279 break;
281 switch (c) {
282 case '?': /* usage */
283 usage5 ();
284 break;
285 case 'h': /* help */
286 print_help ();
287 exit (STATE_UNKNOWN);
288 break;
289 case 'V': /* version */
290 print_revision (progname, NP_VERSION);
291 exit (STATE_UNKNOWN);
292 break;
293 case 't': /* timeout period */
294 if (!is_intnonneg (optarg))
295 usage2 (_("Timeout interval must be a positive integer"), optarg);
296 else
297 socket_timeout = atoi (optarg);
298 break;
299 case 'c': /* critical time threshold */
300 critical_thresholds = optarg;
301 break;
302 case 'w': /* warning time threshold */
303 warning_thresholds = optarg;
304 break;
305 case 'A': /* User Agent String */
306 xasprintf (&user_agent, "User-Agent: %s", optarg);
307 break;
308 case 'k': /* Additional headers */
309 if (http_opt_headers_count == 0)
310 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
311 else
312 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
313 http_opt_headers[http_opt_headers_count - 1] = optarg;
314 /* xasprintf (&http_opt_headers, "%s", optarg); */
315 break;
316 case 'L': /* show html link */
317 display_html = true;
318 break;
319 case 'n': /* do not show html link */
320 display_html = false;
321 break;
322 case 'C': /* Check SSL cert validity */
323 #ifdef HAVE_SSL
324 if ((temp=strchr(optarg,','))!=NULL) {
325 *temp='\0';
326 if (!is_intnonneg (optarg))
327 usage2 (_("Invalid certificate expiration period"), optarg);
328 days_till_exp_warn = atoi(optarg);
329 *temp=',';
330 temp++;
331 if (!is_intnonneg (temp))
332 usage2 (_("Invalid certificate expiration period"), temp);
333 days_till_exp_crit = atoi (temp);
335 else {
336 days_till_exp_crit=0;
337 if (!is_intnonneg (optarg))
338 usage2 (_("Invalid certificate expiration period"), optarg);
339 days_till_exp_warn = atoi (optarg);
341 check_cert = true;
342 goto enable_ssl;
343 #endif
344 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
345 #ifdef HAVE_SSL
346 continue_after_check_cert = true;
347 break;
348 #endif
349 case 'J': /* use client certificate */
350 #ifdef HAVE_SSL
351 test_file(optarg);
352 client_cert = optarg;
353 goto enable_ssl;
354 #endif
355 case 'K': /* use client private key */
356 #ifdef HAVE_SSL
357 test_file(optarg);
358 client_privkey = optarg;
359 goto enable_ssl;
360 #endif
361 case 'S': /* use SSL */
362 #ifdef HAVE_SSL
363 enable_ssl:
364 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple
365 parameters, like -S and -C combinations */
366 use_ssl = true;
367 if (c=='S' && optarg != NULL) {
368 int got_plus = strchr(optarg, '+') != NULL;
370 if (!strncmp (optarg, "1.2", 3))
371 ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2;
372 else if (!strncmp (optarg, "1.1", 3))
373 ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1;
374 else if (optarg[0] == '1')
375 ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1;
376 else if (optarg[0] == '3')
377 ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3;
378 else if (optarg[0] == '2')
379 ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2;
380 else
381 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
383 if (specify_port == false)
384 server_port = HTTPS_PORT;
385 #else
386 /* -C -J and -K fall through to here without SSL */
387 usage4 (_("Invalid option - SSL is not available"));
388 #endif
389 break;
390 case SNI_OPTION:
391 use_sni = true;
392 break;
393 case MAX_REDIRS_OPTION:
394 if (!is_intnonneg (optarg))
395 usage2 (_("Invalid max_redirs count"), optarg);
396 else {
397 max_depth = atoi (optarg);
399 break;
400 case 'f': /* onredirect */
401 if (!strcmp (optarg, "stickyport"))
402 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
403 else if (!strcmp (optarg, "sticky"))
404 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST;
405 else if (!strcmp (optarg, "follow"))
406 onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE;
407 else if (!strcmp (optarg, "unknown"))
408 onredirect = STATE_UNKNOWN;
409 else if (!strcmp (optarg, "ok"))
410 onredirect = STATE_OK;
411 else if (!strcmp (optarg, "warning"))
412 onredirect = STATE_WARNING;
413 else if (!strcmp (optarg, "critical"))
414 onredirect = STATE_CRITICAL;
415 else usage2 (_("Invalid onredirect option"), optarg);
416 if (verbose)
417 printf(_("option f:%d \n"), onredirect);
418 break;
419 /* Note: H, I, and u must be malloc'd or will fail on redirects */
420 case 'H': /* Host Name (virtual host) */
421 host_name = strdup (optarg);
422 if (host_name[0] == '[') {
423 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
424 virtual_port = atoi (p + 2);
425 /* cut off the port */
426 host_name_length = strlen (host_name) - strlen (p) - 1;
427 free (host_name);
428 host_name = strndup (optarg, host_name_length);
429 if (specify_port == false)
430 server_port = virtual_port;
432 } else if ((p = strchr (host_name, ':')) != NULL
433 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
434 virtual_port = atoi (p);
435 /* cut off the port */
436 host_name_length = strlen (host_name) - strlen (p) - 1;
437 free (host_name);
438 host_name = strndup (optarg, host_name_length);
439 if (specify_port == false)
440 server_port = virtual_port;
442 break;
443 case 'I': /* Server IP-address */
444 server_address = strdup (optarg);
445 break;
446 case 'u': /* URL path */
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;
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 char *tmp;
477 if ((tmp = strstr(http_method, ":")) != NULL) {
478 tmp[0] = '\0'; // set the ":" in the middle to 0
479 http_method_proxy = ++tmp; // this points to the second part
481 break;
482 case 'd': /* string or substring */
483 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
484 header_expect[MAX_INPUT_BUFFER - 1] = 0;
485 break;
486 case 's': /* string or substring */
487 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
488 string_expect[MAX_INPUT_BUFFER - 1] = 0;
489 break;
490 case 'e': /* string or substring */
491 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
492 server_expect[MAX_INPUT_BUFFER - 1] = 0;
493 server_expect_yn = 1;
494 break;
495 case 'T': /* Content-type */
496 xasprintf (&http_content_type, "%s", optarg);
497 break;
498 case 'l': /* linespan */
499 cflags &= ~REG_NEWLINE;
500 break;
501 case 'R': /* regex */
502 cflags |= REG_ICASE;
503 // fall through
504 case 'r': /* regex */
505 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
506 regexp[MAX_RE_SIZE - 1] = 0;
507 errcode = regcomp (&preg, regexp, cflags);
508 if (errcode != 0) {
509 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
510 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
511 return false;
513 break;
514 case INVERT_REGEX:
515 invert_regex = 1;
516 break;
517 case STATE_REGEX:
518 if (!strcmp (optarg, "critical"))
519 state_regex = STATE_CRITICAL;
520 else if (!strcmp (optarg, "warning"))
521 state_regex = STATE_WARNING;
522 else usage2 (_("Invalid state-regex option"), optarg);
523 break;
524 case '4':
525 address_family = AF_INET;
526 break;
527 case '6':
528 #ifdef USE_IPV6
529 address_family = AF_INET6;
530 #else
531 usage4 (_("IPv6 support not available"));
532 #endif
533 break;
534 case 'v': /* verbose */
535 verbose = true;
536 break;
537 case 'm': /* min_page_length */
539 char *tmp;
540 if (strchr(optarg, ':') != (char *)NULL) {
541 /* range, so get two values, min:max */
542 tmp = strtok(optarg, ":");
543 if (tmp == NULL) {
544 printf("Bad format: try \"-m min:max\"\n");
545 exit (STATE_WARNING);
546 } else
547 min_page_len = atoi(tmp);
549 tmp = strtok(NULL, ":");
550 if (tmp == NULL) {
551 printf("Bad format: try \"-m min:max\"\n");
552 exit (STATE_WARNING);
553 } else
554 max_page_len = atoi(tmp);
555 } else
556 min_page_len = atoi (optarg);
557 break;
559 case 'N': /* no-body */
560 no_body = true;
561 break;
562 case 'M': /* max-age */
564 int L = strlen(optarg);
565 if (L && optarg[L-1] == 'm')
566 maximum_age = atoi (optarg) * 60;
567 else if (L && optarg[L-1] == 'h')
568 maximum_age = atoi (optarg) * 60 * 60;
569 else if (L && optarg[L-1] == 'd')
570 maximum_age = atoi (optarg) * 60 * 60 * 24;
571 else if (L && (optarg[L-1] == 's' ||
572 isdigit (optarg[L-1])))
573 maximum_age = atoi (optarg);
574 else {
575 fprintf (stderr, "unparsable max-age: %s\n", optarg);
576 exit (STATE_WARNING);
579 break;
580 case 'E': /* show extended perfdata */
581 show_extended_perfdata = true;
582 break;
583 case 'B': /* print body content after status line */
584 show_body = true;
585 break;
589 c = optind;
591 if (server_address == NULL && c < argc)
592 server_address = strdup (argv[c++]);
594 if (host_name == NULL && c < argc)
595 host_name = strdup (argv[c++]);
597 if (server_address == NULL) {
598 if (host_name == NULL)
599 usage4 (_("You must specify a server address or host name"));
600 else
601 server_address = strdup (host_name);
604 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
606 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
607 socket_timeout = (int)thlds->critical->end + 1;
609 if (http_method == NULL)
610 http_method = strdup ("GET");
612 if (http_method_proxy == NULL)
613 http_method_proxy = strdup ("GET");
615 if (client_cert && !client_privkey)
616 usage4 (_("If you use a client certificate you must also specify a private key file"));
618 if (virtual_port == 0)
619 virtual_port = server_port;
621 return true;
626 /* Returns 1 if we're done processing the document body; 0 to keep going */
627 static int
628 document_headers_done (char *full_page)
630 const char *body;
632 for (body = full_page; *body; body++) {
633 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
634 break;
637 if (!*body)
638 return 0; /* haven't read end of headers yet */
640 full_page[body - full_page] = 0;
641 return 1;
644 static time_t
645 parse_time_string (const char *string)
647 struct tm tm;
648 time_t t;
649 memset (&tm, 0, sizeof(tm));
651 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
653 if (isupper (string[0]) && /* Tue */
654 islower (string[1]) &&
655 islower (string[2]) &&
656 ',' == string[3] &&
657 ' ' == string[4] &&
658 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
659 isdigit (string[6]) &&
660 ' ' == string[7] &&
661 isupper (string[8]) && /* Dec */
662 islower (string[9]) &&
663 islower (string[10]) &&
664 ' ' == string[11] &&
665 isdigit (string[12]) && /* 2001 */
666 isdigit (string[13]) &&
667 isdigit (string[14]) &&
668 isdigit (string[15]) &&
669 ' ' == string[16] &&
670 isdigit (string[17]) && /* 02: */
671 isdigit (string[18]) &&
672 ':' == string[19] &&
673 isdigit (string[20]) && /* 59: */
674 isdigit (string[21]) &&
675 ':' == string[22] &&
676 isdigit (string[23]) && /* 03 */
677 isdigit (string[24]) &&
678 ' ' == string[25] &&
679 'G' == string[26] && /* GMT */
680 'M' == string[27] && /* GMT */
681 'T' == string[28]) {
683 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
684 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
685 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
686 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
687 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
688 !strncmp (string+8, "Feb", 3) ? 1 :
689 !strncmp (string+8, "Mar", 3) ? 2 :
690 !strncmp (string+8, "Apr", 3) ? 3 :
691 !strncmp (string+8, "May", 3) ? 4 :
692 !strncmp (string+8, "Jun", 3) ? 5 :
693 !strncmp (string+8, "Jul", 3) ? 6 :
694 !strncmp (string+8, "Aug", 3) ? 7 :
695 !strncmp (string+8, "Sep", 3) ? 8 :
696 !strncmp (string+8, "Oct", 3) ? 9 :
697 !strncmp (string+8, "Nov", 3) ? 10 :
698 !strncmp (string+8, "Dec", 3) ? 11 :
699 -1);
700 tm.tm_year = ((1000 * (string[12]-'0') +
701 100 * (string[13]-'0') +
702 10 * (string[14]-'0') +
703 (string[15]-'0'))
704 - 1900);
706 tm.tm_isdst = 0; /* GMT is never in DST, right? */
708 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
709 return 0;
712 This is actually wrong: we need to subtract the local timezone
713 offset from GMT from this value. But, that's ok in this usage,
714 because we only comparing these two GMT dates against each other,
715 so it doesn't matter what time zone we parse them in.
718 t = mktime (&tm);
719 if (t == (time_t) -1) t = 0;
721 if (verbose) {
722 const char *s = string;
723 while (*s && *s != '\r' && *s != '\n')
724 fputc (*s++, stdout);
725 printf (" ==> %lu\n", (unsigned long) t);
728 return t;
730 } else {
731 return 0;
735 /* Checks if the server 'reply' is one of the expected 'statuscodes' */
736 static int
737 expected_statuscode (const char *reply, const char *statuscodes)
739 char *expected, *code;
740 int result = 0;
742 if ((expected = strdup (statuscodes)) == NULL)
743 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
745 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
746 if (strstr (reply, code) != NULL) {
747 result = 1;
748 break;
751 free (expected);
752 return result;
755 static int
756 check_document_dates (const char *headers, char **msg)
758 const char *s;
759 char *server_date = 0;
760 char *document_date = 0;
761 int date_result = STATE_OK;
763 s = headers;
764 while (*s) {
765 const char *field = s;
766 const char *value = 0;
768 /* Find the end of the header field */
769 while (*s && !isspace(*s) && *s != ':')
770 s++;
772 /* Remember the header value, if any. */
773 if (*s == ':')
774 value = ++s;
776 /* Skip to the end of the header, including continuation lines. */
777 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
778 s++;
780 /* Avoid stepping over end-of-string marker */
781 if (*s)
782 s++;
784 /* Process this header. */
785 if (value && value > field+2) {
786 char *ff = (char *) malloc (value-field);
787 char *ss = ff;
788 while (field < value-1)
789 *ss++ = tolower(*field++);
790 *ss++ = 0;
792 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
793 const char *e;
794 while (*value && isspace (*value))
795 value++;
796 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
798 ss = (char *) malloc (e - value + 1);
799 strncpy (ss, value, e - value);
800 ss[e - value] = 0;
801 if (!strcmp (ff, "date")) {
802 if (server_date) free (server_date);
803 server_date = ss;
804 } else {
805 if (document_date) free (document_date);
806 document_date = ss;
809 free (ff);
813 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
814 if (!server_date || !*server_date) {
815 xasprintf (msg, _("%sServer date unknown, "), *msg);
816 date_result = max_state_alt(STATE_UNKNOWN, date_result);
817 } else if (!document_date || !*document_date) {
818 xasprintf (msg, _("%sDocument modification date unknown, "), *msg);
819 date_result = max_state_alt(STATE_CRITICAL, date_result);
820 } else {
821 time_t srv_data = parse_time_string (server_date);
822 time_t doc_data = parse_time_string (document_date);
824 if (srv_data <= 0) {
825 xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
826 date_result = max_state_alt(STATE_CRITICAL, date_result);
827 } else if (doc_data <= 0) {
828 xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
829 date_result = max_state_alt(STATE_CRITICAL, date_result);
830 } else if (doc_data > srv_data + 30) {
831 xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
832 date_result = max_state_alt(STATE_CRITICAL, date_result);
833 } else if (doc_data < srv_data - maximum_age) {
834 int n = (srv_data - doc_data);
835 if (n > (60 * 60 * 24 * 2)) {
836 xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
837 date_result = max_state_alt(STATE_CRITICAL, date_result);
838 } else {
839 xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
840 date_result = max_state_alt(STATE_CRITICAL, date_result);
843 free (server_date);
844 free (document_date);
846 return date_result;
850 get_content_length (const char *headers)
852 const char *s;
853 int content_length = 0;
855 s = headers;
856 while (*s) {
857 const char *field = s;
858 const char *value = 0;
860 /* Find the end of the header field */
861 while (*s && !isspace(*s) && *s != ':')
862 s++;
864 /* Remember the header value, if any. */
865 if (*s == ':')
866 value = ++s;
868 /* Skip to the end of the header, including continuation lines. */
869 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
870 s++;
872 /* Avoid stepping over end-of-string marker */
873 if (*s)
874 s++;
876 /* Process this header. */
877 if (value && value > field+2) {
878 char *ff = (char *) malloc (value-field);
879 char *ss = ff;
880 while (field < value-1)
881 *ss++ = tolower(*field++);
882 *ss++ = 0;
884 if (!strcmp (ff, "content-length")) {
885 const char *e;
886 while (*value && isspace (*value))
887 value++;
888 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
890 ss = (char *) malloc (e - value + 1);
891 strncpy (ss, value, e - value);
892 ss[e - value] = 0;
893 content_length = atoi(ss);
894 free (ss);
896 free (ff);
899 return (content_length);
902 char *
903 prepend_slash (char *path)
905 char *newpath;
907 if (path[0] == '/')
908 return path;
910 if ((newpath = malloc (strlen(path) + 2)) == NULL)
911 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
912 newpath[0] = '/';
913 strcpy (newpath + 1, path);
914 free (path);
915 return newpath;
919 check_http (void)
921 char *msg;
922 char *status_line;
923 char *status_code;
924 char *header;
925 char *page;
926 char *auth;
927 int http_status;
928 int i = 0;
929 size_t pagesize = 0;
930 char *full_page;
931 char *full_page_new;
932 char *buf;
933 char *pos;
934 long microsec = 0L;
935 double elapsed_time = 0.0;
936 long microsec_connect = 0L;
937 double elapsed_time_connect = 0.0;
938 long microsec_ssl = 0L;
939 double elapsed_time_ssl = 0.0;
940 long microsec_firstbyte = 0L;
941 double elapsed_time_firstbyte = 0.0;
942 long microsec_headers = 0L;
943 double elapsed_time_headers = 0.0;
944 long microsec_transfer = 0L;
945 double elapsed_time_transfer = 0.0;
946 int page_len = 0;
947 int result = STATE_OK;
948 char *force_host_header = NULL;
950 /* try to connect to the host at the given port number */
951 gettimeofday (&tv_temp, NULL);
952 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
953 die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n"));
954 microsec_connect = deltime (tv_temp);
956 /* if we are called with the -I option, the -j method is CONNECT and */
957 /* we received -S for SSL, then we tunnel the request through a proxy*/
958 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
960 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
961 && host_name != NULL && use_ssl == true) {
963 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT);
964 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent);
965 if (strlen(proxy_auth)) {
966 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
967 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
969 /* optionally send any other header tag */
970 if (http_opt_headers_count) {
971 for (i = 0; i < http_opt_headers_count ; i++) {
972 if (force_host_header != http_opt_headers[i]) {
973 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
976 /* This cannot be free'd here because a redirection will then try to access this and segfault */
977 /* Covered in a testcase in tests/check_http.t */
978 /* free(http_opt_headers); */
980 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf);
981 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
982 /* we finished our request, send empty line with CRLF */
983 asprintf (&buf, "%s%s", buf, CRLF);
984 if (verbose) printf ("%s\n", buf);
985 send(sd, buf, strlen (buf), 0);
986 buf[0]='\0';
988 if (verbose) printf ("Receive response from proxy\n");
989 read (sd, buffer, MAX_INPUT_BUFFER-1);
990 if (verbose) printf ("%s", buffer);
991 /* Here we should check if we got HTTP/1.1 200 Connection established */
993 #ifdef HAVE_SSL
994 elapsed_time_connect = (double)microsec_connect / 1.0e6;
995 if (use_ssl == true) {
996 gettimeofday (&tv_temp, NULL);
997 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
998 if (verbose) printf ("SSL initialized\n");
999 if (result != STATE_OK)
1000 die (STATE_CRITICAL, NULL);
1001 microsec_ssl = deltime (tv_temp);
1002 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
1003 if (check_cert == true) {
1004 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
1005 if (continue_after_check_cert == false) {
1006 if (sd) close(sd);
1007 np_net_ssl_cleanup();
1008 return result;
1012 #endif /* HAVE_SSL */
1014 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
1015 && host_name != NULL && use_ssl == true)
1016 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1017 else
1018 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
1020 /* tell HTTP/1.1 servers not to keep the connection alive */
1021 xasprintf (&buf, "%sConnection: close\r\n", buf);
1023 /* check if Host header is explicitly set in options */
1024 if (http_opt_headers_count) {
1025 for (i = 0; i < http_opt_headers_count ; i++) {
1026 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
1027 force_host_header = http_opt_headers[i];
1032 /* optionally send the host header info */
1033 if (host_name) {
1034 if (force_host_header) {
1035 xasprintf (&buf, "%s%s\r\n", buf, force_host_header);
1037 else {
1039 * Specify the port only if we're using a non-default port (see RFC 2616,
1040 * 14.23). Some server applications/configurations cause trouble if the
1041 * (default) port is explicitly specified in the "Host:" header line.
1043 if ((use_ssl == false && virtual_port == HTTP_PORT) ||
1044 (use_ssl == true && virtual_port == HTTPS_PORT) ||
1045 (server_address != NULL && strcmp(http_method, "CONNECT") == 0
1046 && host_name != NULL && use_ssl == true))
1047 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1048 else
1049 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port);
1053 /* optionally send any other header tag */
1054 if (http_opt_headers_count) {
1055 for (i = 0; i < http_opt_headers_count ; i++) {
1056 if (force_host_header != http_opt_headers[i]) {
1057 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
1060 /* This cannot be free'd here because a redirection will then try to access this and segfault */
1061 /* Covered in a testcase in tests/check_http.t */
1062 /* free(http_opt_headers); */
1065 /* optionally send the authentication info */
1066 if (strlen(user_auth)) {
1067 base64_encode_alloc (user_auth, strlen (user_auth), &auth);
1068 xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
1071 /* optionally send the proxy authentication info */
1072 if (strlen(proxy_auth)) {
1073 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
1074 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
1077 /* either send http POST data (any data, not only POST)*/
1078 if (http_post_data) {
1079 if (http_content_type) {
1080 xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
1081 } else {
1082 xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
1085 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
1086 xasprintf (&buf, "%s%s", buf, http_post_data);
1087 } else {
1088 /* or just a newline so the server knows we're done with the request */
1089 xasprintf (&buf, "%s%s", buf, CRLF);
1092 if (verbose) printf ("%s\n", buf);
1093 gettimeofday (&tv_temp, NULL);
1094 my_send (buf, strlen (buf));
1095 microsec_headers = deltime (tv_temp);
1096 elapsed_time_headers = (double)microsec_headers / 1.0e6;
1098 /* fetch the page */
1099 full_page = strdup("");
1100 gettimeofday (&tv_temp, NULL);
1101 while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) {
1102 if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) {
1103 microsec_firstbyte = deltime (tv_temp);
1104 elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6;
1106 while ((pos = memchr(buffer, '\0', i))) {
1107 /* replace nul character with a blank */
1108 *pos = ' ';
1110 buffer[i] = '\0';
1112 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1113 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1115 memmove(&full_page_new[pagesize], buffer, i + 1);
1117 full_page = full_page_new;
1119 pagesize += i;
1121 if (no_body && document_headers_done (full_page)) {
1122 i = 0;
1123 break;
1126 microsec_transfer = deltime (tv_temp);
1127 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1129 if (i < 0 && errno != ECONNRESET) {
1130 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1133 /* return a CRITICAL status if we couldn't read any data */
1134 if (pagesize == (size_t) 0)
1135 die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n"));
1137 /* close the connection */
1138 if (sd) close(sd);
1139 #ifdef HAVE_SSL
1140 np_net_ssl_cleanup();
1141 #endif
1143 /* Save check time */
1144 microsec = deltime (tv);
1145 elapsed_time = (double)microsec / 1.0e6;
1147 /* leave full_page untouched so we can free it later */
1148 page = full_page;
1150 if (verbose)
1151 printf ("%s://%s:%d%s is %d characters\n",
1152 use_ssl ? "https" : "http", server_address,
1153 server_port, server_url, (int)pagesize);
1155 /* find status line and null-terminate it */
1156 status_line = page;
1157 page += (size_t) strcspn (page, "\r\n");
1158 pos = page;
1159 page += (size_t) strspn (page, "\r\n");
1160 status_line[strcspn(status_line, "\r\n")] = 0;
1161 strip (status_line);
1162 if (verbose)
1163 printf ("STATUS: %s\n", status_line);
1165 /* find header info and null-terminate it */
1166 header = page;
1167 while (strcspn (page, "\r\n") > 0) {
1168 page += (size_t) strcspn (page, "\r\n");
1169 pos = page;
1170 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
1171 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
1172 page += (size_t) 2;
1173 else
1174 page += (size_t) 1;
1176 page += (size_t) strspn (page, "\r\n");
1177 header[pos - header] = 0;
1178 if (verbose)
1179 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
1180 (no_body ? " [[ skipped ]]" : page));
1182 /* make sure the status line matches the response we are looking for */
1183 if (!expected_statuscode (status_line, server_expect)) {
1184 if (server_port == HTTP_PORT)
1185 xasprintf (&msg,
1186 _("Invalid HTTP response received from host: %s\n"),
1187 status_line);
1188 else
1189 xasprintf (&msg,
1190 _("Invalid HTTP response received from host on port %d: %s\n"),
1191 server_port, status_line);
1192 if (show_body)
1193 xasprintf (&msg, _("%s\n%s"), msg, page);
1194 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
1197 /* Bypass normal status line check if server_expect was set by user and not default */
1198 /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */
1199 if ( server_expect_yn ) {
1200 xasprintf (&msg,
1201 _("Status line output matched \"%s\" - "), server_expect);
1202 if (verbose)
1203 printf ("%s\n",msg);
1205 else {
1206 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
1207 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
1208 /* Status-Code = 3 DIGITS */
1210 status_code = strchr (status_line, ' ') + sizeof (char);
1211 if (strspn (status_code, "1234567890") != 3)
1212 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
1214 http_status = atoi (status_code);
1216 /* check the return code */
1218 if (http_status >= 600 || http_status < 100) {
1219 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
1221 /* server errors result in a critical state */
1222 else if (http_status >= 500) {
1223 xasprintf (&msg, _("%s - "), status_line);
1224 result = STATE_CRITICAL;
1226 /* client errors result in a warning state */
1227 else if (http_status >= 400) {
1228 xasprintf (&msg, _("%s - "), status_line);
1229 result = max_state_alt(STATE_WARNING, result);
1231 /* check redirected page if specified */
1232 else if (http_status >= 300) {
1234 if (onredirect == STATE_DEPENDENT)
1235 redir (header, status_line);
1236 else
1237 result = max_state_alt(onredirect, result);
1238 xasprintf (&msg, _("%s - "), status_line);
1239 } /* end if (http_status >= 300) */
1240 else {
1241 /* Print OK status anyway */
1242 xasprintf (&msg, _("%s - "), status_line);
1245 } /* end else (server_expect_yn) */
1247 /* reset the alarm - must be called *after* redir or we'll never die on redirects! */
1248 alarm (0);
1250 if (maximum_age >= 0) {
1251 result = max_state_alt(check_document_dates(header, &msg), result);
1254 /* Page and Header content checks go here */
1255 if (strlen(header_expect) > 0) {
1256 if (strstr(header, header_expect) == NULL) {
1257 // We did not find the header, the rest is for building the output and setting the state
1258 char output_header_search[30] = "";
1260 strncpy(&output_header_search[0], header_expect,
1261 sizeof(output_header_search));
1263 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1264 bcopy("...",
1265 &output_header_search[sizeof(output_header_search) - 4],
1269 xasprintf (&msg,
1270 _("%sheader '%s' not found on '%s://%s:%d%s', "),
1271 msg,
1272 output_header_search, use_ssl ? "https" : "http",
1273 host_name ? host_name : server_address, server_port,
1274 server_url);
1276 result = STATE_CRITICAL;
1280 // At this point we should test if the content is chunked and unchunk it, so
1281 // it can be searched (and possibly printed)
1282 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *";
1283 regex_t chunked_header_regex;
1285 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) {
1286 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex");
1289 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found
1291 if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) {
1292 if (verbose) {
1293 printf("Found chunked content\n");
1295 // We actually found the chunked header
1296 char *tmp = unchunk_content(page);
1297 if (tmp == NULL) {
1298 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body");
1300 page = tmp;
1303 if (strlen(string_expect) > 0) {
1304 if (!strstr(page, string_expect)) {
1305 // We found the string the body, the rest is for building the output
1306 char output_string_search[30] = "";
1307 strncpy(&output_string_search[0], string_expect,
1308 sizeof(output_string_search));
1309 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1310 bcopy("...", &output_string_search[sizeof(output_string_search) - 4],
1313 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);
1314 result = STATE_CRITICAL;
1318 if (strlen(regexp) > 0) {
1319 errcode = regexec(&preg, page, REGS, pmatch, 0);
1320 if ((errcode == 0 && invert_regex == 0) ||
1321 (errcode == REG_NOMATCH && invert_regex == 1)) {
1322 /* OK - No-op to avoid changing the logic around it */
1323 result = max_state_alt(STATE_OK, result);
1325 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
1326 if (invert_regex == 0)
1327 xasprintf (&msg, _("%spattern not found, "), msg);
1328 else
1329 xasprintf (&msg, _("%spattern found, "), msg);
1330 result = state_regex;
1332 else {
1333 /* FIXME: Shouldn't that be UNKNOWN? */
1334 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1335 xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf);
1336 result = STATE_CRITICAL;
1340 /* make sure the page is of an appropriate size */
1341 /* page_len = get_content_length(header); */
1342 /* FIXME: Will this work with -N ? IMHO we should use
1343 * get_content_length(header) and always check if it's different than the
1344 * returned pagesize
1346 /* FIXME: IIRC pagesize returns headers - shouldn't we make
1347 * it == get_content_length(header) ??
1349 page_len = pagesize;
1350 if ((max_page_len > 0) && (page_len > max_page_len)) {
1351 xasprintf (&msg, _("%spage size %d too large, "), msg, page_len);
1352 result = max_state_alt(STATE_WARNING, result);
1353 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1354 xasprintf (&msg, _("%spage size %d too small, "), msg, page_len);
1355 result = max_state_alt(STATE_WARNING, result);
1358 /* Cut-off trailing characters */
1359 if(msg[strlen(msg)-2] == ',')
1360 msg[strlen(msg)-2] = '\0';
1361 else
1362 msg[strlen(msg)-3] = '\0';
1364 /* check elapsed time */
1365 if (show_extended_perfdata)
1366 xasprintf (&msg,
1367 _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"),
1368 msg, page_len, elapsed_time,
1369 (display_html ? "</A>" : ""),
1370 perfd_time (elapsed_time),
1371 perfd_size (page_len),
1372 perfd_time_connect (elapsed_time_connect),
1373 use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "",
1374 perfd_time_headers (elapsed_time_headers),
1375 perfd_time_firstbyte (elapsed_time_firstbyte),
1376 perfd_time_transfer (elapsed_time_transfer));
1377 else
1378 xasprintf (&msg,
1379 _("%s - %d bytes in %.3f second response time %s|%s %s"),
1380 msg, page_len, elapsed_time,
1381 (display_html ? "</A>" : ""),
1382 perfd_time (elapsed_time),
1383 perfd_size (page_len));
1385 if (show_body)
1386 xasprintf (&msg, _("%s\n%s"), msg, page);
1388 result = max_state_alt(get_status(elapsed_time, thlds), result);
1390 die (result, "HTTP %s: %s\n", state_text(result), msg);
1391 /* die failed? */
1392 return STATE_UNKNOWN;
1395 /* Receivces a pointer to the beginning of the body of a HTTP message
1396 * which is chunked and returns a pointer to a freshly allocated memory
1397 * region containing the unchunked body or NULL if something failed.
1398 * The result must be freed by the caller.
1400 char *unchunk_content(const char *content) {
1401 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding
1402 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1
1403 char *result = NULL;
1404 char *start_of_chunk;
1405 char* end_of_chunk;
1406 long size_of_chunk;
1407 const char *pointer = content;
1408 char *endptr;
1409 long length_of_chunk = 0;
1410 size_t overall_size = 0;
1412 while (true) {
1413 size_of_chunk = strtol(pointer, &endptr, 16);
1414 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) {
1415 // Apparently underflow or overflow, should not happen
1416 if (verbose) {
1417 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__);
1419 return NULL;
1421 if (endptr == pointer) {
1422 // Apparently this was not a number
1423 if (verbose) {
1424 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__);
1426 return NULL;
1429 // So, we got the length of the chunk
1430 if (*endptr == ';') {
1431 // Chunk extension starts here
1432 while (*endptr != '\r') {
1433 endptr++;
1437 start_of_chunk = endptr + 2;
1438 end_of_chunk = start_of_chunk + size_of_chunk;
1439 length_of_chunk = (long)(end_of_chunk - start_of_chunk);
1440 pointer = end_of_chunk + 2; //Next number should be here
1442 if (length_of_chunk == 0) {
1443 // Chunk length is 0, so this is the last one
1444 break;
1447 overall_size += length_of_chunk;
1449 if (result == NULL) {
1450 // Size of the chunk plus the ending NULL byte
1451 result = (char *)malloc(length_of_chunk +1);
1452 if (result == NULL) {
1453 if (verbose) {
1454 printf("Failed to allocate memory for unchunked body\n");
1456 return NULL;
1458 } else {
1459 // Enlarge memory to the new size plus the ending NULL byte
1460 void *tmp = realloc(result, overall_size +1);
1461 if (tmp == NULL) {
1462 if (verbose) {
1463 printf("Failed to allocate memory for unchunked body\n");
1465 return NULL;
1466 } else {
1467 result = tmp;
1471 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk);
1474 if (overall_size == 0 && result == NULL) {
1475 // We might just have received the end chunk without previous content, so result is never allocated
1476 result = calloc(1, sizeof(char));
1477 // No error handling here, we can only return NULL anyway
1478 } else {
1479 result[overall_size] = '\0';
1481 return result;
1484 /* per RFC 2396 */
1485 #define URI_HTTP "%5[HTPShtps]"
1486 #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1487 #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */
1488 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1489 #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH
1490 #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1491 #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1492 #define HD4 URI_HTTP "://" URI_HOST
1493 /* relative reference redirect like //www.site.org/test https://tools.ietf.org/html/rfc3986 */
1494 #define HD5 "//" URI_HOST "/" URI_PATH
1495 #define HD6 URI_PATH
1497 void
1498 redir (char *pos, char *status_line)
1500 int i = 0;
1501 char *x;
1502 char xx[2];
1503 char type[6];
1504 char *addr;
1505 char *url;
1507 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1508 if (addr == NULL)
1509 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n"));
1511 memset(addr, 0, MAX_IPV4_HOSTLENGTH);
1512 url = malloc (strcspn (pos, "\r\n"));
1513 if (url == NULL)
1514 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1516 while (pos) {
1517 sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i);
1518 if (i == 0) {
1519 pos += (size_t) strcspn (pos, "\r\n");
1520 pos += (size_t) strspn (pos, "\r\n");
1521 if (strlen(pos) == 0)
1522 die (STATE_UNKNOWN,
1523 _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"),
1524 status_line, (display_html ? "</A>" : ""));
1525 continue;
1528 pos += i;
1529 pos += strspn (pos, " \t");
1532 * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by
1533 * preceding each extra line with at least one SP or HT.''
1535 for (; (i = strspn (pos, "\r\n")); pos += i) {
1536 pos += i;
1537 if (!(i = strspn (pos, " \t"))) {
1538 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"),
1539 display_html ? "</A>" : "");
1543 url = realloc (url, strcspn (pos, "\r\n") + 1);
1544 if (url == NULL)
1545 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
1547 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1548 if (sscanf (pos, HD1, type, addr, &i, url) == 4) {
1549 url = prepend_slash (url);
1550 use_ssl = server_type_check (type);
1553 /* URI_HTTP URI_HOST URI_PATH */
1554 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1555 url = prepend_slash (url);
1556 use_ssl = server_type_check (type);
1557 i = server_port_check (use_ssl);
1560 /* URI_HTTP URI_HOST URI_PORT */
1561 else if (sscanf (pos, HD3, type, addr, &i) == 3) {
1562 strcpy (url, HTTP_URL);
1563 use_ssl = server_type_check (type);
1566 /* URI_HTTP URI_HOST */
1567 else if (sscanf (pos, HD4, type, addr) == 2) {
1568 strcpy (url, HTTP_URL);
1569 use_ssl = server_type_check (type);
1570 i = server_port_check (use_ssl);
1572 /* URI_HTTP, URI_HOST, URI_PATH */
1573 else if (sscanf (pos, HD5, addr, url) == 2) {
1574 if(use_ssl){
1575 strcpy (type,"https");
1577 else{
1578 strcpy (type, server_type);
1580 xasprintf (&url, "/%s", url);
1581 use_ssl = server_type_check (type);
1582 i = server_port_check (use_ssl);
1585 /* URI_PATH */
1586 else if (sscanf (pos, HD6, url) == 1) {
1587 /* relative url */
1588 if ((url[0] != '/')) {
1589 if ((x = strrchr(server_url, '/')))
1590 *x = '\0';
1591 xasprintf (&url, "%s/%s", server_url, url);
1593 i = server_port;
1594 strcpy (type, server_type);
1595 strcpy (addr, host_name ? host_name : server_address);
1598 else {
1599 die (STATE_UNKNOWN,
1600 _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"),
1601 pos, (display_html ? "</A>" : ""));
1604 break;
1606 } /* end while (pos) */
1608 if (++redir_depth > max_depth)
1609 die (STATE_WARNING,
1610 _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1611 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1613 if (server_port==i &&
1614 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1615 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) &&
1616 !strcmp(server_url, url))
1617 die (STATE_CRITICAL,
1618 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1619 type, addr, i, url, (display_html ? "</A>" : ""));
1621 strcpy (server_type, type);
1623 free (host_name);
1624 host_name = strndup (addr, MAX_IPV4_HOSTLENGTH);
1626 if (!(followsticky & STICKY_HOST)) {
1627 free (server_address);
1628 server_address = strndup (addr, MAX_IPV4_HOSTLENGTH);
1630 if (!(followsticky & STICKY_PORT)) {
1631 server_port = i;
1634 free (server_url);
1635 server_url = url;
1637 if (server_port > MAX_PORT)
1638 die (STATE_UNKNOWN,
1639 _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"),
1640 MAX_PORT, server_type, server_address, server_port, server_url,
1641 display_html ? "</A>" : "");
1643 /* reset virtual port */
1644 virtual_port = server_port;
1646 if (verbose)
1647 printf (_("Redirection to %s://%s:%d%s\n"), server_type,
1648 host_name ? host_name : server_address, server_port, server_url);
1650 free(addr);
1651 check_http ();
1655 bool
1656 server_type_check (const char *type)
1658 if (strcmp (type, "https"))
1659 return false;
1660 else
1661 return true;
1665 server_port_check (int ssl_flag)
1667 if (ssl_flag)
1668 return HTTPS_PORT;
1669 else
1670 return HTTP_PORT;
1673 char *perfd_time (double elapsed_time)
1675 return fperfdata ("time", elapsed_time, "s",
1676 thlds->warning?true:false, thlds->warning?thlds->warning->end:0,
1677 thlds->critical?true:false, thlds->critical?thlds->critical->end:0,
1678 true, 0, true, socket_timeout);
1681 char *perfd_time_connect (double elapsed_time_connect)
1683 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1686 char *perfd_time_ssl (double elapsed_time_ssl)
1688 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1691 char *perfd_time_headers (double elapsed_time_headers)
1693 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1696 char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1698 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1701 char *perfd_time_transfer (double elapsed_time_transfer)
1703 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1706 char *perfd_size (int page_len)
1708 return perfdata ("size", page_len, "B",
1709 (min_page_len>0?true:false), min_page_len,
1710 (min_page_len>0?true:false), 0,
1711 true, 0, false, 0);
1714 void
1715 print_help (void)
1717 print_revision (progname, NP_VERSION);
1719 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1720 printf (COPYRIGHT, copyright, email);
1722 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1723 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1724 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1725 printf ("%s\n", _("certificate expiration times."));
1727 printf ("\n\n");
1729 print_usage ();
1731 #ifdef HAVE_SSL
1732 printf (_("In the first form, make an HTTP request."));
1733 printf (_("In the second form, connect to the server and check the TLS certificate."));
1734 #endif
1735 printf (_("NOTE: One or both of -H and -I must be specified"));
1737 printf ("\n");
1739 printf (UT_HELP_VRSN);
1740 printf (UT_EXTRA_OPTS);
1742 printf (" %s\n", "-H, --hostname=ADDRESS");
1743 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1744 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1745 printf (" %s\n", "-I, --IP-address=ADDRESS");
1746 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1747 printf (" %s\n", "-p, --port=INTEGER");
1748 printf (" %s", _("Port number (default: "));
1749 printf ("%d)\n", HTTP_PORT);
1751 printf (UT_IPv46);
1753 #ifdef HAVE_SSL
1754 printf (" %s\n", "-S, --ssl=VERSION[+]");
1755 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1756 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1757 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1758 printf (" %s\n", "--sni");
1759 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1760 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1761 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1762 printf (" %s\n", _("(when this option is used the URL is not checked by default. You can use"));
1763 printf (" %s\n", _(" --continue-after-certificate to override this behavior)"));
1764 printf (" %s\n", "--continue-after-certificate");
1765 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
1766 printf (" %s\n", _("Does nothing unless -C is used."));
1767 printf (" %s\n", "-J, --client-cert=FILE");
1768 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1769 printf (" %s\n", _("to be used in establishing the SSL session"));
1770 printf (" %s\n", "-K, --private-key=FILE");
1771 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1772 printf (" %s\n", _("matching the client certificate"));
1773 #endif
1775 printf (" %s\n", "-e, --expect=STRING");
1776 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1777 printf (" %s", _("the first (status) line of the server response (default: "));
1778 printf ("%s)\n", HTTP_EXPECT);
1779 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1780 printf (" %s\n", "-d, --header-string=STRING");
1781 printf (" %s\n", _("String to expect in the response headers"));
1782 printf (" %s\n", "-s, --string=STRING");
1783 printf (" %s\n", _("String to expect in the content"));
1784 printf (" %s\n", "-u, --url=PATH");
1785 printf (" %s\n", _("URL to GET or POST (default: /)"));
1786 printf (" %s\n", "-P, --post=STRING");
1787 printf (" %s\n", _("URL decoded http POST data"));
1788 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)");
1789 printf (" %s\n", _("Set HTTP method."));
1790 printf (" %s\n", "-N, --no-body");
1791 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1792 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1793 printf (" %s\n", "-M, --max-age=SECONDS");
1794 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1795 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1796 printf (" %s\n", "-T, --content-type=STRING");
1797 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1799 printf (" %s\n", "-l, --linespan");
1800 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1801 printf (" %s\n", "-r, --regex, --ereg=STRING");
1802 printf (" %s\n", _("Search page for regex STRING"));
1803 printf (" %s\n", "-R, --eregi=STRING");
1804 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1805 printf (" %s\n", "--invert-regex");
1806 printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)"));
1807 printf (" %s\n", _("can be changed with --state--regex)"));
1808 printf (" %s\n", "--regex-state=STATE");
1809 printf (" %s\n", _("Return STATE if regex is found, OK if not\n"));
1811 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1812 printf (" %s\n", _("Username:password on sites with basic authentication"));
1813 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1814 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1815 printf (" %s\n", "-A, --useragent=STRING");
1816 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1817 printf (" %s\n", "-k, --header=STRING");
1818 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1819 printf (" %s\n", "-E, --extended-perfdata");
1820 printf (" %s\n", _("Print additional performance data"));
1821 printf (" %s\n", "-B, --show-body");
1822 printf (" %s\n", _("Print body content below status line"));
1823 printf (" %s\n", "-L, --link");
1824 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1825 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1826 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1827 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1828 printf (" %s\n", "--max-redirs=INTEGER");
1829 printf (" %s", _("Maximal number of redirects (default: "));
1830 printf ("%d)\n", DEFAULT_MAX_REDIRS);
1831 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1832 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1833 printf (UT_WARN_CRIT);
1835 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1837 printf (UT_VERBOSE);
1839 printf ("\n");
1840 printf ("%s\n", _("Notes:"));
1841 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1842 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1843 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1844 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1845 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1846 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1848 #ifdef HAVE_SSL
1849 printf ("\n");
1850 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1851 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1852 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1853 printf ("\n");
1854 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1855 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1856 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1857 printf ("\n");
1858 printf ("%s\n", _("Examples:"));
1859 printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com");
1860 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1861 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1862 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1863 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1864 printf ("\n");
1865 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14");
1866 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1867 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1868 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1869 printf (" %s\n\n", _("the certificate is expired."));
1870 printf ("\n");
1871 printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14");
1872 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1873 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1874 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1875 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1877 printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1878 printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1879 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1880 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1881 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1882 printf (" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can set the method used"));
1883 printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST"));
1885 #endif
1887 printf (UT_SUPPORT);
1893 void
1894 print_usage (void)
1896 printf ("%s\n", _("Usage:"));
1897 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1898 printf (" [-J <client certificate file>] [-K <private key>]\n");
1899 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1900 printf (" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport>]\n");
1901 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1902 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1903 printf (" [-A string] [-k string] [-S <version>] [--sni]\n");
1904 printf (" [-T <content-type>] [-j method]\n");
1905 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
1906 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");