summaryrefslogtreecommitdiffstats
path: root/plugins/check_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_http.c')
-rw-r--r--plugins/check_http.c401
1 files changed, 290 insertions, 111 deletions
diff --git a/plugins/check_http.c b/plugins/check_http.c
index de59a06..8dda046 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -31,13 +31,14 @@
31* 31*
32*****************************************************************************/ 32*****************************************************************************/
33 33
34/* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
35
36const char *progname = "check_http"; 34const char *progname = "check_http";
37const char *copyright = "1999-2013"; 35const char *copyright = "1999-2022";
38const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
39 37
38// Do NOT sort those headers, it will break the build
39// TODO: Fix this
40#include "common.h" 40#include "common.h"
41#include "base64.h"
41#include "netutils.h" 42#include "netutils.h"
42#include "utils.h" 43#include "utils.h"
43#include "base64.h" 44#include "base64.h"
@@ -52,11 +53,13 @@ enum {
52 MAX_IPV4_HOSTLENGTH = 255, 53 MAX_IPV4_HOSTLENGTH = 255,
53 HTTP_PORT = 80, 54 HTTP_PORT = 80,
54 HTTPS_PORT = 443, 55 HTTPS_PORT = 443,
55 MAX_PORT = 65535 56 MAX_PORT = 65535,
57 DEFAULT_MAX_REDIRS = 15
56}; 58};
57 59
58#ifdef HAVE_SSL 60#ifdef HAVE_SSL
59int check_cert = FALSE; 61bool check_cert = false;
62bool continue_after_check_cert = false;
60int ssl_version = 0; 63int ssl_version = 0;
61int days_till_exp_warn, days_till_exp_crit; 64int days_till_exp_warn, days_till_exp_crit;
62char *randbuff; 65char *randbuff;
@@ -67,12 +70,12 @@ X509 *server_cert;
67# define my_recv(buf, len) read(sd, buf, len) 70# define my_recv(buf, len) read(sd, buf, len)
68# define my_send(buf, len) send(sd, buf, len, 0) 71# define my_send(buf, len) send(sd, buf, len, 0)
69#endif /* HAVE_SSL */ 72#endif /* HAVE_SSL */
70int no_body = FALSE; 73bool no_body = false;
71int maximum_age = -1; 74int maximum_age = -1;
72 75
73enum { 76enum {
74 REGS = 2, 77 REGS = 2,
75 MAX_RE_SIZE = 256 78 MAX_RE_SIZE = 1024
76}; 79};
77#include "regex.h" 80#include "regex.h"
78regex_t preg; 81regex_t preg;
@@ -89,7 +92,7 @@ struct timeval tv_temp;
89#define HTTP_URL "/" 92#define HTTP_URL "/"
90#define CRLF "\r\n" 93#define CRLF "\r\n"
91 94
92int specify_port = FALSE; 95bool specify_port = false;
93int server_port = HTTP_PORT; 96int server_port = HTTP_PORT;
94int virtual_port = 0; 97int virtual_port = 0;
95char server_port_text[6] = ""; 98char server_port_text[6] = "";
@@ -104,28 +107,26 @@ int server_expect_yn = 0;
104char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; 107char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
105char header_expect[MAX_INPUT_BUFFER] = ""; 108char header_expect[MAX_INPUT_BUFFER] = "";
106char string_expect[MAX_INPUT_BUFFER] = ""; 109char string_expect[MAX_INPUT_BUFFER] = "";
107char output_header_search[30] = "";
108char output_string_search[30] = "";
109char *warning_thresholds = NULL; 110char *warning_thresholds = NULL;
110char *critical_thresholds = NULL; 111char *critical_thresholds = NULL;
111thresholds *thlds; 112thresholds *thlds;
112char user_auth[MAX_INPUT_BUFFER] = ""; 113char user_auth[MAX_INPUT_BUFFER] = "";
113char proxy_auth[MAX_INPUT_BUFFER] = ""; 114char proxy_auth[MAX_INPUT_BUFFER] = "";
114int display_html = FALSE; 115bool display_html = false;
115char **http_opt_headers; 116char **http_opt_headers;
116int http_opt_headers_count = 0; 117int http_opt_headers_count = 0;
117int onredirect = STATE_OK; 118int onredirect = STATE_OK;
118int followsticky = STICKY_NONE; 119int followsticky = STICKY_NONE;
119int use_ssl = FALSE; 120bool use_ssl = false;
120int use_sni = FALSE; 121bool use_sni = false;
121int verbose = FALSE; 122bool verbose = false;
122int show_extended_perfdata = FALSE; 123bool show_extended_perfdata = false;
123int show_body = FALSE; 124bool show_body = false;
124int sd; 125int sd;
125int min_page_len = 0; 126int min_page_len = 0;
126int max_page_len = 0; 127int max_page_len = 0;
127int redir_depth = 0; 128int redir_depth = 0;
128int max_depth = 15; 129int max_depth = DEFAULT_MAX_REDIRS;
129char *http_method; 130char *http_method;
130char *http_method_proxy; 131char *http_method_proxy;
131char *http_post_data; 132char *http_post_data;
@@ -134,10 +135,11 @@ char buffer[MAX_INPUT_BUFFER];
134char *client_cert = NULL; 135char *client_cert = NULL;
135char *client_privkey = NULL; 136char *client_privkey = NULL;
136 137
137int process_arguments (int, char **); 138// Forward function declarations
139bool process_arguments (int, char **);
138int check_http (void); 140int check_http (void);
139void redir (char *pos, char *status_line); 141void redir (char *pos, char *status_line);
140int server_type_check(const char *type); 142bool server_type_check(const char *type);
141int server_port_check(int ssl_flag); 143int server_port_check(int ssl_flag);
142char *perfd_time (double microsec); 144char *perfd_time (double microsec);
143char *perfd_time_connect (double microsec); 145char *perfd_time_connect (double microsec);
@@ -148,6 +150,7 @@ char *perfd_time_transfer (double microsec);
148char *perfd_size (int page_len); 150char *perfd_size (int page_len);
149void print_help (void); 151void print_help (void);
150void print_usage (void); 152void print_usage (void);
153char *unchunk_content(const char *content);
151 154
152int 155int
153main (int argc, char **argv) 156main (int argc, char **argv)
@@ -167,10 +170,10 @@ main (int argc, char **argv)
167 /* Parse extra opts if any */ 170 /* Parse extra opts if any */
168 argv=np_extra_opts (&argc, argv, progname); 171 argv=np_extra_opts (&argc, argv, progname);
169 172
170 if (process_arguments (argc, argv) == ERROR) 173 if (process_arguments (argc, argv) == false)
171 usage4 (_("Could not parse arguments")); 174 usage4 (_("Could not parse arguments"));
172 175
173 if (display_html == TRUE) 176 if (display_html == true)
174 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 177 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
175 use_ssl ? "https" : "http", host_name ? host_name : server_address, 178 use_ssl ? "https" : "http", host_name ? host_name : server_address,
176 server_port, server_url); 179 server_port, server_url);
@@ -193,9 +196,11 @@ test_file (char *path)
193 usage2 (_("file does not exist or is not readable"), path); 196 usage2 (_("file does not exist or is not readable"), path);
194} 197}
195 198
196/* process command-line arguments */ 199/*
197int 200 * process command-line arguments
198process_arguments (int argc, char **argv) 201 * returns true on succes, false otherwise
202 */
203bool process_arguments (int argc, char **argv)
199{ 204{
200 int c = 1; 205 int c = 1;
201 char *p; 206 char *p;
@@ -203,7 +208,9 @@ process_arguments (int argc, char **argv)
203 208
204 enum { 209 enum {
205 INVERT_REGEX = CHAR_MAX + 1, 210 INVERT_REGEX = CHAR_MAX + 1,
206 SNI_OPTION 211 SNI_OPTION,
212 MAX_REDIRS_OPTION,
213 CONTINUE_AFTER_CHECK_CERT
207 }; 214 };
208 215
209 int option = 0; 216 int option = 0;
@@ -231,6 +238,7 @@ process_arguments (int argc, char **argv)
231 {"certificate", required_argument, 0, 'C'}, 238 {"certificate", required_argument, 0, 'C'},
232 {"client-cert", required_argument, 0, 'J'}, 239 {"client-cert", required_argument, 0, 'J'},
233 {"private-key", required_argument, 0, 'K'}, 240 {"private-key", required_argument, 0, 'K'},
241 {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT},
234 {"useragent", required_argument, 0, 'A'}, 242 {"useragent", required_argument, 0, 'A'},
235 {"header", required_argument, 0, 'k'}, 243 {"header", required_argument, 0, 'k'},
236 {"no-body", no_argument, 0, 'N'}, 244 {"no-body", no_argument, 0, 'N'},
@@ -242,11 +250,12 @@ process_arguments (int argc, char **argv)
242 {"use-ipv6", no_argument, 0, '6'}, 250 {"use-ipv6", no_argument, 0, '6'},
243 {"extended-perfdata", no_argument, 0, 'E'}, 251 {"extended-perfdata", no_argument, 0, 'E'},
244 {"show-body", no_argument, 0, 'B'}, 252 {"show-body", no_argument, 0, 'B'},
253 {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION},
245 {0, 0, 0, 0} 254 {0, 0, 0, 0}
246 }; 255 };
247 256
248 if (argc < 2) 257 if (argc < 2)
249 return ERROR; 258 return false;
250 259
251 for (c = 1; c < argc; c++) { 260 for (c = 1; c < argc; c++) {
252 if (strcmp ("-to", argv[c]) == 0) 261 if (strcmp ("-to", argv[c]) == 0)
@@ -302,10 +311,10 @@ process_arguments (int argc, char **argv)
302 /* xasprintf (&http_opt_headers, "%s", optarg); */ 311 /* xasprintf (&http_opt_headers, "%s", optarg); */
303 break; 312 break;
304 case 'L': /* show html link */ 313 case 'L': /* show html link */
305 display_html = TRUE; 314 display_html = true;
306 break; 315 break;
307 case 'n': /* do not show html link */ 316 case 'n': /* do not show html link */
308 display_html = FALSE; 317 display_html = false;
309 break; 318 break;
310 case 'C': /* Check SSL cert validity */ 319 case 'C': /* Check SSL cert validity */
311#ifdef HAVE_SSL 320#ifdef HAVE_SSL
@@ -326,9 +335,14 @@ process_arguments (int argc, char **argv)
326 usage2 (_("Invalid certificate expiration period"), optarg); 335 usage2 (_("Invalid certificate expiration period"), optarg);
327 days_till_exp_warn = atoi (optarg); 336 days_till_exp_warn = atoi (optarg);
328 } 337 }
329 check_cert = TRUE; 338 check_cert = true;
330 goto enable_ssl; 339 goto enable_ssl;
331#endif 340#endif
341 case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */
342#ifdef HAVE_SSL
343 continue_after_check_cert = true;
344 break;
345#endif
332 case 'J': /* use client certificate */ 346 case 'J': /* use client certificate */
333#ifdef HAVE_SSL 347#ifdef HAVE_SSL
334 test_file(optarg); 348 test_file(optarg);
@@ -346,7 +360,7 @@ process_arguments (int argc, char **argv)
346 enable_ssl: 360 enable_ssl:
347 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple 361 /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple
348 parameters, like -S and -C combinations */ 362 parameters, like -S and -C combinations */
349 use_ssl = TRUE; 363 use_ssl = true;
350 if (c=='S' && optarg != NULL) { 364 if (c=='S' && optarg != NULL) {
351 int got_plus = strchr(optarg, '+') != NULL; 365 int got_plus = strchr(optarg, '+') != NULL;
352 366
@@ -363,7 +377,7 @@ process_arguments (int argc, char **argv)
363 else 377 else
364 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); 378 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
365 } 379 }
366 if (specify_port == FALSE) 380 if (specify_port == false)
367 server_port = HTTPS_PORT; 381 server_port = HTTPS_PORT;
368#else 382#else
369 /* -C -J and -K fall through to here without SSL */ 383 /* -C -J and -K fall through to here without SSL */
@@ -371,8 +385,15 @@ process_arguments (int argc, char **argv)
371#endif 385#endif
372 break; 386 break;
373 case SNI_OPTION: 387 case SNI_OPTION:
374 use_sni = TRUE; 388 use_sni = true;
375 break; 389 break;
390 case MAX_REDIRS_OPTION:
391 if (!is_intnonneg (optarg))
392 usage2 (_("Invalid max_redirs count"), optarg);
393 else {
394 max_depth = atoi (optarg);
395 }
396 break;
376 case 'f': /* onredirect */ 397 case 'f': /* onredirect */
377 if (!strcmp (optarg, "stickyport")) 398 if (!strcmp (optarg, "stickyport"))
378 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; 399 onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT;
@@ -402,7 +423,7 @@ process_arguments (int argc, char **argv)
402 host_name_length = strlen (host_name) - strlen (p) - 1; 423 host_name_length = strlen (host_name) - strlen (p) - 1;
403 free (host_name); 424 free (host_name);
404 host_name = strndup (optarg, host_name_length); 425 host_name = strndup (optarg, host_name_length);
405 if (specify_port == FALSE) 426 if (specify_port == false)
406 server_port = virtual_port; 427 server_port = virtual_port;
407 } 428 }
408 } else if ((p = strchr (host_name, ':')) != NULL 429 } else if ((p = strchr (host_name, ':')) != NULL
@@ -412,7 +433,7 @@ process_arguments (int argc, char **argv)
412 host_name_length = strlen (host_name) - strlen (p) - 1; 433 host_name_length = strlen (host_name) - strlen (p) - 1;
413 free (host_name); 434 free (host_name);
414 host_name = strndup (optarg, host_name_length); 435 host_name = strndup (optarg, host_name_length);
415 if (specify_port == FALSE) 436 if (specify_port == false)
416 server_port = virtual_port; 437 server_port = virtual_port;
417 } 438 }
418 break; 439 break;
@@ -428,7 +449,7 @@ process_arguments (int argc, char **argv)
428 usage2 (_("Invalid port number"), optarg); 449 usage2 (_("Invalid port number"), optarg);
429 else { 450 else {
430 server_port = atoi (optarg); 451 server_port = atoi (optarg);
431 specify_port = TRUE; 452 specify_port = true;
432 } 453 }
433 break; 454 break;
434 case 'a': /* authorization info */ 455 case 'a': /* authorization info */
@@ -484,7 +505,7 @@ process_arguments (int argc, char **argv)
484 if (errcode != 0) { 505 if (errcode != 0) {
485 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); 506 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
486 printf (_("Could Not Compile Regular Expression: %s"), errbuf); 507 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
487 return ERROR; 508 return false;
488 } 509 }
489 break; 510 break;
490 case INVERT_REGEX: 511 case INVERT_REGEX:
@@ -501,7 +522,7 @@ process_arguments (int argc, char **argv)
501#endif 522#endif
502 break; 523 break;
503 case 'v': /* verbose */ 524 case 'v': /* verbose */
504 verbose = TRUE; 525 verbose = true;
505 break; 526 break;
506 case 'm': /* min_page_length */ 527 case 'm': /* min_page_length */
507 { 528 {
@@ -526,7 +547,7 @@ process_arguments (int argc, char **argv)
526 break; 547 break;
527 } 548 }
528 case 'N': /* no-body */ 549 case 'N': /* no-body */
529 no_body = TRUE; 550 no_body = true;
530 break; 551 break;
531 case 'M': /* max-age */ 552 case 'M': /* max-age */
532 { 553 {
@@ -547,10 +568,10 @@ process_arguments (int argc, char **argv)
547 } 568 }
548 break; 569 break;
549 case 'E': /* show extended perfdata */ 570 case 'E': /* show extended perfdata */
550 show_extended_perfdata = TRUE; 571 show_extended_perfdata = true;
551 break; 572 break;
552 case 'B': /* print body content after status line */ 573 case 'B': /* print body content after status line */
553 show_body = TRUE; 574 show_body = true;
554 break; 575 break;
555 } 576 }
556 } 577 }
@@ -587,7 +608,7 @@ process_arguments (int argc, char **argv)
587 if (virtual_port == 0) 608 if (virtual_port == 0)
588 virtual_port = server_port; 609 virtual_port = server_port;
589 610
590 return TRUE; 611 return true;
591} 612}
592 613
593 614
@@ -927,10 +948,25 @@ check_http (void)
927 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ 948 /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */
928 949
929 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 950 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
930 && host_name != NULL && use_ssl == TRUE) { 951 && host_name != NULL && use_ssl == true) {
931 952
932 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT); 953 if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT);
933 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent); 954 asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent);
955 if (strlen(proxy_auth)) {
956 base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth);
957 xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth);
958 }
959 /* optionally send any other header tag */
960 if (http_opt_headers_count) {
961 for (i = 0; i < http_opt_headers_count ; i++) {
962 if (force_host_header != http_opt_headers[i]) {
963 xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]);
964 }
965 }
966 /* This cannot be free'd here because a redirection will then try to access this and segfault */
967 /* Covered in a testcase in tests/check_http.t */
968 /* free(http_opt_headers); */
969 }
934 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf); 970 asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf);
935 asprintf (&buf, "%sHost: %s\r\n", buf, host_name); 971 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
936 /* we finished our request, send empty line with CRLF */ 972 /* we finished our request, send empty line with CRLF */
@@ -946,7 +982,7 @@ check_http (void)
946 } 982 }
947#ifdef HAVE_SSL 983#ifdef HAVE_SSL
948 elapsed_time_connect = (double)microsec_connect / 1.0e6; 984 elapsed_time_connect = (double)microsec_connect / 1.0e6;
949 if (use_ssl == TRUE) { 985 if (use_ssl == true) {
950 gettimeofday (&tv_temp, NULL); 986 gettimeofday (&tv_temp, NULL);
951 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey); 987 result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
952 if (verbose) printf ("SSL initialized\n"); 988 if (verbose) printf ("SSL initialized\n");
@@ -954,17 +990,19 @@ check_http (void)
954 die (STATE_CRITICAL, NULL); 990 die (STATE_CRITICAL, NULL);
955 microsec_ssl = deltime (tv_temp); 991 microsec_ssl = deltime (tv_temp);
956 elapsed_time_ssl = (double)microsec_ssl / 1.0e6; 992 elapsed_time_ssl = (double)microsec_ssl / 1.0e6;
957 if (check_cert == TRUE) { 993 if (check_cert == true) {
958 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); 994 result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
959 if (sd) close(sd); 995 if (continue_after_check_cert == false) {
960 np_net_ssl_cleanup(); 996 if (sd) close(sd);
961 return result; 997 np_net_ssl_cleanup();
998 return result;
999 }
962 } 1000 }
963 } 1001 }
964#endif /* HAVE_SSL */ 1002#endif /* HAVE_SSL */
965 1003
966 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 1004 if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0
967 && host_name != NULL && use_ssl == TRUE) 1005 && host_name != NULL && use_ssl == true)
968 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); 1006 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);
969 else 1007 else
970 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); 1008 asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent);
@@ -992,10 +1030,10 @@ check_http (void)
992 * 14.23). Some server applications/configurations cause trouble if the 1030 * 14.23). Some server applications/configurations cause trouble if the
993 * (default) port is explicitly specified in the "Host:" header line. 1031 * (default) port is explicitly specified in the "Host:" header line.
994 */ 1032 */
995 if ((use_ssl == FALSE && virtual_port == HTTP_PORT) || 1033 if ((use_ssl == false && virtual_port == HTTP_PORT) ||
996 (use_ssl == TRUE && virtual_port == HTTPS_PORT) || 1034 (use_ssl == true && virtual_port == HTTPS_PORT) ||
997 (server_address != NULL && strcmp(http_method, "CONNECT") == 0 1035 (server_address != NULL && strcmp(http_method, "CONNECT") == 0
998 && host_name != NULL && use_ssl == TRUE)) 1036 && host_name != NULL && use_ssl == true))
999 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name); 1037 xasprintf (&buf, "%sHost: %s\r\n", buf, host_name);
1000 else 1038 else
1001 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); 1039 xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port);
@@ -1035,9 +1073,8 @@ check_http (void)
1035 } 1073 }
1036 1074
1037 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data)); 1075 xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data));
1038 xasprintf (&buf, "%s%s%s", buf, http_post_data, CRLF); 1076 xasprintf (&buf, "%s%s", buf, http_post_data);
1039 } 1077 } else {
1040 else {
1041 /* or just a newline so the server knows we're done with the request */ 1078 /* or just a newline so the server knows we're done with the request */
1042 xasprintf (&buf, "%s%s", buf, CRLF); 1079 xasprintf (&buf, "%s%s", buf, CRLF);
1043 } 1080 }
@@ -1061,9 +1098,14 @@ check_http (void)
1061 *pos = ' '; 1098 *pos = ' ';
1062 } 1099 }
1063 buffer[i] = '\0'; 1100 buffer[i] = '\0';
1064 xasprintf (&full_page_new, "%s%s", full_page, buffer); 1101
1065 free (full_page); 1102 if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL)
1103 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n"));
1104
1105 memmove(&full_page_new[pagesize], buffer, i + 1);
1106
1066 full_page = full_page_new; 1107 full_page = full_page_new;
1108
1067 pagesize += i; 1109 pagesize += i;
1068 1110
1069 if (no_body && document_headers_done (full_page)) { 1111 if (no_body && document_headers_done (full_page)) {
@@ -1075,25 +1117,7 @@ check_http (void)
1075 elapsed_time_transfer = (double)microsec_transfer / 1.0e6; 1117 elapsed_time_transfer = (double)microsec_transfer / 1.0e6;
1076 1118
1077 if (i < 0 && errno != ECONNRESET) { 1119 if (i < 0 && errno != ECONNRESET) {
1078#ifdef HAVE_SSL 1120 die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1079 /*
1080 if (use_ssl) {
1081 sslerr=SSL_get_error(ssl, i);
1082 if ( sslerr == SSL_ERROR_SSL ) {
1083 die (STATE_WARNING, _("HTTP WARNING - Client Certificate Required\n"));
1084 } else {
1085 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1086 }
1087 }
1088 else {
1089 */
1090#endif
1091 die (STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n"));
1092#ifdef HAVE_SSL
1093 /* XXX
1094 }
1095 */
1096#endif
1097 } 1121 }
1098 1122
1099 /* return a CRITICAL status if we couldn't read any data */ 1123 /* return a CRITICAL status if we couldn't read any data */
@@ -1218,32 +1242,73 @@ check_http (void)
1218 } 1242 }
1219 1243
1220 /* Page and Header content checks go here */ 1244 /* Page and Header content checks go here */
1221 if (strlen (header_expect)) { 1245 if (strlen(header_expect) > 0) {
1222 if (!strstr (header, header_expect)) { 1246 if (strstr(header, header_expect) == NULL) {
1223 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search)); 1247 // We did not find the header, the rest is for building the output and setting the state
1224 if(output_header_search[sizeof(output_header_search)-1]!='\0') { 1248 char output_header_search[30] = "";
1225 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4); 1249
1250 strncpy(&output_header_search[0], header_expect,
1251 sizeof(output_header_search));
1252
1253 if (output_header_search[sizeof(output_header_search) - 1] != '\0') {
1254 bcopy("...",
1255 &output_header_search[sizeof(output_header_search) - 4],
1256 4);
1226 } 1257 }
1227 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); 1258
1259 xasprintf (&msg,
1260 _("%sheader '%s' not found on '%s://%s:%d%s', "),
1261 msg,
1262 output_header_search, use_ssl ? "https" : "http",
1263 host_name ? host_name : server_address, server_port,
1264 server_url);
1265
1228 result = STATE_CRITICAL; 1266 result = STATE_CRITICAL;
1229 } 1267 }
1230 } 1268 }
1231 1269
1270 // At this point we should test if the content is chunked and unchunk it, so
1271 // it can be searched (and possibly printed)
1272 const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *";
1273 regex_t chunked_header_regex;
1274
1275 if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) {
1276 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex");
1277 }
1278
1279 regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found
1280
1281 if (regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) {
1282 if (verbose) {
1283 printf("Found chunked content\n");
1284 }
1285 // We actually found the chunked header
1286 char *tmp = unchunk_content(page);
1287 if (tmp == NULL) {
1288 die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body");
1289 }
1290 page = tmp;
1291 }
1232 1292
1233 if (strlen (string_expect)) { 1293 if (strlen(string_expect) > 0) {
1234 if (!strstr (page, string_expect)) { 1294 if (!strstr(page, string_expect)) {
1235 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search)); 1295 // We found the string the body, the rest is for building the output
1236 if(output_string_search[sizeof(output_string_search)-1]!='\0') { 1296 char output_string_search[30] = "";
1237 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4); 1297 strncpy(&output_string_search[0], string_expect,
1298 sizeof(output_string_search));
1299 if (output_string_search[sizeof(output_string_search) - 1] != '\0') {
1300 bcopy("...", &output_string_search[sizeof(output_string_search) - 4],
1301 4);
1238 } 1302 }
1239 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); 1303 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);
1240 result = STATE_CRITICAL; 1304 result = STATE_CRITICAL;
1241 } 1305 }
1242 } 1306 }
1243 1307
1244 if (strlen (regexp)) { 1308 if (strlen(regexp) > 0) {
1245 errcode = regexec (&preg, page, REGS, pmatch, 0); 1309 errcode = regexec(&preg, page, REGS, pmatch, 0);
1246 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) { 1310 if ((errcode == 0 && invert_regex == 0) ||
1311 (errcode == REG_NOMATCH && invert_regex == 1)) {
1247 /* OK - No-op to avoid changing the logic around it */ 1312 /* OK - No-op to avoid changing the logic around it */
1248 result = max_state_alt(STATE_OK, result); 1313 result = max_state_alt(STATE_OK, result);
1249 } 1314 }
@@ -1295,7 +1360,7 @@ check_http (void)
1295 perfd_time (elapsed_time), 1360 perfd_time (elapsed_time),
1296 perfd_size (page_len), 1361 perfd_size (page_len),
1297 perfd_time_connect (elapsed_time_connect), 1362 perfd_time_connect (elapsed_time_connect),
1298 use_ssl == TRUE ? perfd_time_ssl (elapsed_time_ssl) : "", 1363 use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "",
1299 perfd_time_headers (elapsed_time_headers), 1364 perfd_time_headers (elapsed_time_headers),
1300 perfd_time_firstbyte (elapsed_time_firstbyte), 1365 perfd_time_firstbyte (elapsed_time_firstbyte),
1301 perfd_time_transfer (elapsed_time_transfer)); 1366 perfd_time_transfer (elapsed_time_transfer));
@@ -1317,7 +1382,95 @@ check_http (void)
1317 return STATE_UNKNOWN; 1382 return STATE_UNKNOWN;
1318} 1383}
1319 1384
1385/* Receivces a pointer to the beginning of the body of a HTTP message
1386 * which is chunked and returns a pointer to a freshly allocated memory
1387 * region containing the unchunked body or NULL if something failed.
1388 * The result must be freed by the caller.
1389 */
1390char *unchunk_content(const char *content) {
1391 // https://en.wikipedia.org/wiki/Chunked_transfer_encoding
1392 // https://www.rfc-editor.org/rfc/rfc7230#section-4.1
1393 char *result = NULL;
1394 size_t content_length = strlen(content);
1395 char *start_of_chunk;
1396 char* end_of_chunk;
1397 long size_of_chunk;
1398 const char *pointer = content;
1399 char *endptr;
1400 long length_of_chunk = 0;
1401 size_t overall_size = 0;
1402
1403 while (true) {
1404 size_of_chunk = strtol(pointer, &endptr, 16);
1405 if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) {
1406 // Apparently underflow or overflow, should not happen
1407 if (verbose) {
1408 printf("Got an underflow or overflow from strtol at: %u\n", __LINE__);
1409 }
1410 return NULL;
1411 }
1412 if (endptr == pointer) {
1413 // Apparently this was not a number
1414 if (verbose) {
1415 printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__);
1416 }
1417 return NULL;
1418 }
1419
1420 // So, we got the length of the chunk
1421 if (*endptr == ';') {
1422 // Chunk extension starts here
1423 while (*endptr != '\r') {
1424 endptr++;
1425 }
1426 }
1427
1428 start_of_chunk = endptr + 2;
1429 end_of_chunk = start_of_chunk + size_of_chunk;
1430 length_of_chunk = (long)(end_of_chunk - start_of_chunk);
1431 pointer = end_of_chunk + 2; //Next number should be here
1432
1433 if (length_of_chunk == 0) {
1434 // Chunk length is 0, so this is the last one
1435 break;
1436 }
1437
1438 overall_size += length_of_chunk;
1439
1440 if (result == NULL) {
1441 // Size of the chunk plus the ending NULL byte
1442 result = (char *)malloc(length_of_chunk +1);
1443 if (result == NULL) {
1444 if (verbose) {
1445 printf("Failed to allocate memory for unchunked body\n");
1446 }
1447 return NULL;
1448 }
1449 } else {
1450 // Enlarge memory to the new size plus the ending NULL byte
1451 void *tmp = realloc(result, overall_size +1);
1452 if (tmp == NULL) {
1453 if (verbose) {
1454 printf("Failed to allocate memory for unchunked body\n");
1455 }
1456 return NULL;
1457 } else {
1458 result = tmp;
1459 }
1460 }
1461
1462 memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk);
1463 }
1320 1464
1465 if (overall_size == 0 && result == NULL) {
1466 // We might just have received the end chunk without previous content, so result is never allocated
1467 result = calloc(1, sizeof(char));
1468 // No error handling here, we can only return NULL anyway
1469 } else {
1470 result[overall_size] = '\0';
1471 }
1472 return result;
1473}
1321 1474
1322/* per RFC 2396 */ 1475/* per RFC 2396 */
1323#define URI_HTTP "%5[HTPShtps]" 1476#define URI_HTTP "%5[HTPShtps]"
@@ -1328,7 +1481,9 @@ check_http (void)
1328#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH 1481#define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH
1329#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT 1482#define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT
1330#define HD4 URI_HTTP "://" URI_HOST 1483#define HD4 URI_HTTP "://" URI_HOST
1331#define HD5 URI_PATH 1484/* relative reference redirect like //www.site.org/test https://tools.ietf.org/html/rfc3986 */
1485#define HD5 "//" URI_HOST "/" URI_PATH
1486#define HD6 URI_PATH
1332 1487
1333void 1488void
1334redir (char *pos, char *status_line) 1489redir (char *pos, char *status_line)
@@ -1405,9 +1560,21 @@ redir (char *pos, char *status_line)
1405 use_ssl = server_type_check (type); 1560 use_ssl = server_type_check (type);
1406 i = server_port_check (use_ssl); 1561 i = server_port_check (use_ssl);
1407 } 1562 }
1563 /* URI_HTTP, URI_HOST, URI_PATH */
1564 else if (sscanf (pos, HD5, addr, url) == 2) {
1565 if(use_ssl){
1566 strcpy (type,"https");
1567 }
1568 else{
1569 strcpy (type, server_type);
1570 }
1571 xasprintf (&url, "/%s", url);
1572 use_ssl = server_type_check (type);
1573 i = server_port_check (use_ssl);
1574 }
1408 1575
1409 /* URI_PATH */ 1576 /* URI_PATH */
1410 else if (sscanf (pos, HD5, url) == 1) { 1577 else if (sscanf (pos, HD6, url) == 1) {
1411 /* relative url */ 1578 /* relative url */
1412 if ((url[0] != '/')) { 1579 if ((url[0] != '/')) {
1413 if ((x = strrchr(server_url, '/'))) 1580 if ((x = strrchr(server_url, '/')))
@@ -1438,8 +1605,8 @@ redir (char *pos, char *status_line)
1438 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && 1605 !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) &&
1439 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && 1606 (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) &&
1440 !strcmp(server_url, url)) 1607 !strcmp(server_url, url))
1441 die (STATE_WARNING, 1608 die (STATE_CRITICAL,
1442 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"), 1609 _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1443 type, addr, i, url, (display_html ? "</A>" : "")); 1610 type, addr, i, url, (display_html ? "</A>" : ""));
1444 1611
1445 strcpy (server_type, type); 1612 strcpy (server_type, type);
@@ -1476,13 +1643,13 @@ redir (char *pos, char *status_line)
1476} 1643}
1477 1644
1478 1645
1479int 1646bool
1480server_type_check (const char *type) 1647server_type_check (const char *type)
1481{ 1648{
1482 if (strcmp (type, "https")) 1649 if (strcmp (type, "https"))
1483 return FALSE; 1650 return false;
1484 else 1651 else
1485 return TRUE; 1652 return true;
1486} 1653}
1487 1654
1488int 1655int
@@ -1497,42 +1664,42 @@ server_port_check (int ssl_flag)
1497char *perfd_time (double elapsed_time) 1664char *perfd_time (double elapsed_time)
1498{ 1665{
1499 return fperfdata ("time", elapsed_time, "s", 1666 return fperfdata ("time", elapsed_time, "s",
1500 thlds->warning?TRUE:FALSE, thlds->warning?thlds->warning->end:0, 1667 thlds->warning?true:false, thlds->warning?thlds->warning->end:0,
1501 thlds->critical?TRUE:FALSE, thlds->critical?thlds->critical->end:0, 1668 thlds->critical?true:false, thlds->critical?thlds->critical->end:0,
1502 TRUE, 0, TRUE, socket_timeout); 1669 true, 0, true, socket_timeout);
1503} 1670}
1504 1671
1505char *perfd_time_connect (double elapsed_time_connect) 1672char *perfd_time_connect (double elapsed_time_connect)
1506{ 1673{
1507 return fperfdata ("time_connect", elapsed_time_connect, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout); 1674 return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1508} 1675}
1509 1676
1510char *perfd_time_ssl (double elapsed_time_ssl) 1677char *perfd_time_ssl (double elapsed_time_ssl)
1511{ 1678{
1512 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout); 1679 return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1513} 1680}
1514 1681
1515char *perfd_time_headers (double elapsed_time_headers) 1682char *perfd_time_headers (double elapsed_time_headers)
1516{ 1683{
1517 return fperfdata ("time_headers", elapsed_time_headers, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout); 1684 return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1518} 1685}
1519 1686
1520char *perfd_time_firstbyte (double elapsed_time_firstbyte) 1687char *perfd_time_firstbyte (double elapsed_time_firstbyte)
1521{ 1688{
1522 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout); 1689 return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1523} 1690}
1524 1691
1525char *perfd_time_transfer (double elapsed_time_transfer) 1692char *perfd_time_transfer (double elapsed_time_transfer)
1526{ 1693{
1527 return fperfdata ("time_transfer", elapsed_time_transfer, "s", FALSE, 0, FALSE, 0, FALSE, 0, TRUE, socket_timeout); 1694 return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout);
1528} 1695}
1529 1696
1530char *perfd_size (int page_len) 1697char *perfd_size (int page_len)
1531{ 1698{
1532 return perfdata ("size", page_len, "B", 1699 return perfdata ("size", page_len, "B",
1533 (min_page_len>0?TRUE:FALSE), min_page_len, 1700 (min_page_len>0?true:false), min_page_len,
1534 (min_page_len>0?TRUE:FALSE), 0, 1701 (min_page_len>0?true:false), 0,
1535 TRUE, 0, FALSE, 0); 1702 true, 0, false, 0);
1536} 1703}
1537 1704
1538void 1705void
@@ -1552,6 +1719,10 @@ print_help (void)
1552 1719
1553 print_usage (); 1720 print_usage ();
1554 1721
1722#ifdef HAVE_SSL
1723 printf (_("In the first form, make an HTTP request."));
1724 printf (_("In the second form, connect to the server and check the TLS certificate."));
1725#endif
1555 printf (_("NOTE: One or both of -H and -I must be specified")); 1726 printf (_("NOTE: One or both of -H and -I must be specified"));
1556 1727
1557 printf ("\n"); 1728 printf ("\n");
@@ -1579,7 +1750,11 @@ print_help (void)
1579 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); 1750 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1580 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); 1751 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1581 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443")); 1752 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1582 printf (" %s\n", _("(when this option is used the URL is not checked.)")); 1753 printf (" %s\n", _("(when this option is used the URL is not checked by default. You can use"));
1754 printf (" %s\n", _(" --continue-after-certificate to override this behavior)"));
1755 printf (" %s\n", "--continue-after-certificate");
1756 printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check."));
1757 printf (" %s\n", _("Does nothing unless -C is used."));
1583 printf (" %s\n", "-J, --client-cert=FILE"); 1758 printf (" %s\n", "-J, --client-cert=FILE");
1584 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); 1759 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1585 printf (" %s\n", _("to be used in establishing the SSL session")); 1760 printf (" %s\n", _("to be used in establishing the SSL session"));
@@ -1638,9 +1813,11 @@ print_help (void)
1638 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>"); 1813 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport>");
1639 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); 1814 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1640 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); 1815 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1816 printf (" %s\n", "--max-redirs=INTEGER");
1817 printf (" %s", _("Maximal number of redirects (default: "));
1818 printf ("%d)\n", DEFAULT_MAX_REDIRS);
1641 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); 1819 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1642 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); 1820 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1643
1644 printf (UT_WARN_CRIT); 1821 printf (UT_WARN_CRIT);
1645 1822
1646 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1823 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
@@ -1711,6 +1888,8 @@ print_usage (void)
1711 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n"); 1888 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
1712 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); 1889 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1713 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1890 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1714 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n"); 1891 printf (" [-A string] [-k string] [-S <version>] [--sni]\n");
1715 printf (" [-T <content-type>] [-j method]\n"); 1892 printf (" [-T <content-type>] [-j method]\n");
1893 printf (" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n",progname);
1894 printf (" [-p <port>] [-t <timeout>] [-4|-6] [--sni]\n");
1716} 1895}