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