diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/check_http.c | 246 |
1 files changed, 243 insertions, 3 deletions
diff --git a/plugins/check_http.c b/plugins/check_http.c index 840ba879..2281786e 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c | |||
@@ -63,6 +63,8 @@ X509 *server_cert; | |||
63 | int connect_SSL (void); | 63 | int connect_SSL (void); |
64 | int check_certificate (X509 **); | 64 | int check_certificate (X509 **); |
65 | #endif | 65 | #endif |
66 | int no_body = FALSE; | ||
67 | int maximum_age = -1; | ||
66 | 68 | ||
67 | #ifdef HAVE_REGEX_H | 69 | #ifdef HAVE_REGEX_H |
68 | enum { | 70 | enum { |
@@ -209,6 +211,8 @@ process_arguments (int argc, char **argv) | |||
209 | {"linespan", no_argument, 0, 'l'}, | 211 | {"linespan", no_argument, 0, 'l'}, |
210 | {"onredirect", required_argument, 0, 'f'}, | 212 | {"onredirect", required_argument, 0, 'f'}, |
211 | {"certificate", required_argument, 0, 'C'}, | 213 | {"certificate", required_argument, 0, 'C'}, |
214 | {"no-body", no_argument, 0, 'N'}, | ||
215 | {"max-age", required_argument, 0, 'M'}, | ||
212 | {"content-type", required_argument, 0, 'T'}, | 216 | {"content-type", required_argument, 0, 'T'}, |
213 | {"min", required_argument, 0, 'm'}, | 217 | {"min", required_argument, 0, 'm'}, |
214 | {"use-ipv4", no_argument, 0, '4'}, | 218 | {"use-ipv4", no_argument, 0, '4'}, |
@@ -233,7 +237,7 @@ process_arguments (int argc, char **argv) | |||
233 | } | 237 | } |
234 | 238 | ||
235 | while (1) { | 239 | while (1) { |
236 | c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option); | 240 | c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option); |
237 | if (c == -1 || c == EOF) | 241 | if (c == -1 || c == EOF) |
238 | break; | 242 | break; |
239 | 243 | ||
@@ -390,6 +394,27 @@ process_arguments (int argc, char **argv) | |||
390 | case 'm': /* min_page_length */ | 394 | case 'm': /* min_page_length */ |
391 | min_page_len = atoi (optarg); | 395 | min_page_len = atoi (optarg); |
392 | break; | 396 | break; |
397 | case 'N': /* no-body */ | ||
398 | no_body = TRUE; | ||
399 | break; | ||
400 | case 'M': /* max-age */ | ||
401 | { | ||
402 | int L = strlen(optarg); | ||
403 | if (L && optarg[L-1] == 'm') | ||
404 | maximum_age = atoi (optarg) * 60; | ||
405 | else if (L && optarg[L-1] == 'h') | ||
406 | maximum_age = atoi (optarg) * 60 * 60; | ||
407 | else if (L && optarg[L-1] == 'd') | ||
408 | maximum_age = atoi (optarg) * 60 * 60 * 24; | ||
409 | else if (L && (optarg[L-1] == 's' || | ||
410 | isdigit (optarg[L-1]))) | ||
411 | maximum_age = atoi (optarg); | ||
412 | else { | ||
413 | fprintf (stderr, "unparsable max-age: %s\n", optarg); | ||
414 | exit (1); | ||
415 | } | ||
416 | } | ||
417 | break; | ||
393 | } | 418 | } |
394 | } | 419 | } |
395 | 420 | ||
@@ -464,6 +489,205 @@ base64 (const char *bin, size_t len) | |||
464 | 489 | ||
465 | 490 | ||
466 | 491 | ||
492 | /* Returns 1 if we're done processing the document body; 0 to keep going */ | ||
493 | static int | ||
494 | document_headers_done (char *full_page) | ||
495 | { | ||
496 | const char *body, *s; | ||
497 | const char *end; | ||
498 | |||
499 | for (body = full_page; *body; body++) { | ||
500 | if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) | ||
501 | break; | ||
502 | } | ||
503 | |||
504 | if (!*body) | ||
505 | return 0; /* haven't read end of headers yet */ | ||
506 | |||
507 | full_page[body - full_page] = 0; | ||
508 | return 1; | ||
509 | } | ||
510 | |||
511 | static time_t | ||
512 | parse_time_string (const char *string) | ||
513 | { | ||
514 | struct tm tm; | ||
515 | time_t t; | ||
516 | memset (&tm, 0, sizeof(tm)); | ||
517 | |||
518 | /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ | ||
519 | |||
520 | if (isupper (string[0]) && /* Tue */ | ||
521 | islower (string[1]) && | ||
522 | islower (string[2]) && | ||
523 | ',' == string[3] && | ||
524 | ' ' == string[4] && | ||
525 | (isdigit(string[5]) || string[5] == ' ') && /* 25 */ | ||
526 | isdigit (string[6]) && | ||
527 | ' ' == string[7] && | ||
528 | isupper (string[8]) && /* Dec */ | ||
529 | islower (string[9]) && | ||
530 | islower (string[10]) && | ||
531 | ' ' == string[11] && | ||
532 | isdigit (string[12]) && /* 2001 */ | ||
533 | isdigit (string[13]) && | ||
534 | isdigit (string[14]) && | ||
535 | isdigit (string[15]) && | ||
536 | ' ' == string[16] && | ||
537 | isdigit (string[17]) && /* 02: */ | ||
538 | isdigit (string[18]) && | ||
539 | ':' == string[19] && | ||
540 | isdigit (string[20]) && /* 59: */ | ||
541 | isdigit (string[21]) && | ||
542 | ':' == string[22] && | ||
543 | isdigit (string[23]) && /* 03 */ | ||
544 | isdigit (string[24]) && | ||
545 | ' ' == string[25] && | ||
546 | 'G' == string[26] && /* GMT */ | ||
547 | 'M' == string[27] && /* GMT */ | ||
548 | 'T' == string[28]) { | ||
549 | |||
550 | tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); | ||
551 | tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); | ||
552 | tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); | ||
553 | tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); | ||
554 | tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : | ||
555 | !strncmp (string+8, "Feb", 3) ? 1 : | ||
556 | !strncmp (string+8, "Mar", 3) ? 2 : | ||
557 | !strncmp (string+8, "Apr", 3) ? 3 : | ||
558 | !strncmp (string+8, "May", 3) ? 4 : | ||
559 | !strncmp (string+8, "Jun", 3) ? 5 : | ||
560 | !strncmp (string+8, "Jul", 3) ? 6 : | ||
561 | !strncmp (string+8, "Aug", 3) ? 7 : | ||
562 | !strncmp (string+8, "Sep", 3) ? 8 : | ||
563 | !strncmp (string+8, "Oct", 3) ? 9 : | ||
564 | !strncmp (string+8, "Nov", 3) ? 10 : | ||
565 | !strncmp (string+8, "Dec", 3) ? 11 : | ||
566 | -1); | ||
567 | tm.tm_year = ((1000 * (string[12]-'0') + | ||
568 | 100 * (string[13]-'0') + | ||
569 | 10 * (string[14]-'0') + | ||
570 | (string[15]-'0')) | ||
571 | - 1900); | ||
572 | |||
573 | tm.tm_isdst = 0; /* GMT is never in DST, right? */ | ||
574 | |||
575 | if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) | ||
576 | return 0; | ||
577 | |||
578 | /* | ||
579 | This is actually wrong: we need to subtract the local timezone | ||
580 | offset from GMT from this value. But, that's ok in this usage, | ||
581 | because we only comparing these two GMT dates against each other, | ||
582 | so it doesn't matter what time zone we parse them in. | ||
583 | */ | ||
584 | |||
585 | t = mktime (&tm); | ||
586 | if (t == (time_t) -1) t = 0; | ||
587 | |||
588 | if (verbose) { | ||
589 | const char *s = string; | ||
590 | while (*s && *s != '\r' && *s != '\n') | ||
591 | fputc (*s++, stdout); | ||
592 | printf (" ==> %lu\n", (unsigned long) t); | ||
593 | } | ||
594 | |||
595 | return t; | ||
596 | |||
597 | } else { | ||
598 | return 0; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | |||
603 | static void | ||
604 | check_document_dates (const char *headers) | ||
605 | { | ||
606 | const char *s; | ||
607 | char *server_date = 0; | ||
608 | char *document_date = 0; | ||
609 | |||
610 | s = headers; | ||
611 | while (*s) { | ||
612 | const char *field = s; | ||
613 | const char *value = 0; | ||
614 | |||
615 | /* Find the end of the header field */ | ||
616 | while (*s && !isspace(*s) && *s != ':') | ||
617 | s++; | ||
618 | |||
619 | /* Remember the header value, if any. */ | ||
620 | if (*s == ':') | ||
621 | value = ++s; | ||
622 | |||
623 | /* Skip to the end of the header, including continuation lines. */ | ||
624 | while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) | ||
625 | s++; | ||
626 | s++; | ||
627 | |||
628 | /* Process this header. */ | ||
629 | if (value && value > field+2) { | ||
630 | char *ff = (char *) malloc (value-field); | ||
631 | char *ss = ff; | ||
632 | while (field < value-1) | ||
633 | *ss++ = tolower(*field++); | ||
634 | *ss++ = 0; | ||
635 | |||
636 | if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { | ||
637 | const char *e; | ||
638 | while (*value && isspace (*value)) | ||
639 | value++; | ||
640 | for (e = value; *e && *e != '\r' && *e != '\n'; e++) | ||
641 | ; | ||
642 | ss = (char *) malloc (e - value + 1); | ||
643 | strncpy (ss, value, e - value); | ||
644 | ss[e - value] = 0; | ||
645 | if (!strcmp (ff, "date")) { | ||
646 | if (server_date) free (server_date); | ||
647 | server_date = ss; | ||
648 | } else { | ||
649 | if (document_date) free (document_date); | ||
650 | document_date = ss; | ||
651 | } | ||
652 | } | ||
653 | free (ff); | ||
654 | } | ||
655 | } | ||
656 | |||
657 | /* Done parsing the body. Now check the dates we (hopefully) parsed. */ | ||
658 | if (!server_date || !*server_date) { | ||
659 | die (STATE_UNKNOWN, _("Server date unknown\n")); | ||
660 | } else if (!document_date || !*document_date) { | ||
661 | die (STATE_CRITICAL, _("Document modification date unknown\n")); | ||
662 | } else { | ||
663 | time_t sd = parse_time_string (server_date); | ||
664 | time_t dd = parse_time_string (document_date); | ||
665 | |||
666 | if (sd <= 0) { | ||
667 | die (STATE_CRITICAL, _("CRITICAL - Server date \"%100s\" unparsable"), server_date); | ||
668 | } else if (dd <= 0) { | ||
669 | die (STATE_CRITICAL, _("CRITICAL - Document date \"%100s\" unparsable"), document_date); | ||
670 | } else if (dd > sd + 30) { | ||
671 | die (STATE_CRITICAL, _("CRITICAL - Document is %d seconds in the future\n"), dd - sd); | ||
672 | } else if (dd < sd - maximum_age) { | ||
673 | int n = (sd - dd); | ||
674 | if (n > (60 * 60 * 24 * 2)) | ||
675 | die (STATE_CRITICAL, | ||
676 | _("CRITICAL - Last modified %.1f days ago\n"), | ||
677 | ((float) n) / (60 * 60 * 24)); | ||
678 | else | ||
679 | die (STATE_CRITICAL, | ||
680 | _("CRITICAL - Last modified %d:%02d:%02d ago\n"), | ||
681 | n / (60 * 60), (n / 60) % 60, n % 60); | ||
682 | } | ||
683 | |||
684 | free (server_date); | ||
685 | free (document_date); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | |||
690 | |||
467 | int | 691 | int |
468 | check_http (void) | 692 | check_http (void) |
469 | { | 693 | { |
@@ -561,6 +785,11 @@ check_http (void) | |||
561 | buffer[i] = '\0'; | 785 | buffer[i] = '\0'; |
562 | asprintf (&full_page, "%s%s", full_page, buffer); | 786 | asprintf (&full_page, "%s%s", full_page, buffer); |
563 | pagesize += i; | 787 | pagesize += i; |
788 | |||
789 | if (no_body && document_headers_done (full_page)) { | ||
790 | i = 0; | ||
791 | break; | ||
792 | } | ||
564 | } | 793 | } |
565 | 794 | ||
566 | if (i < 0 && errno != ECONNRESET) { | 795 | if (i < 0 && errno != ECONNRESET) { |
@@ -621,7 +850,8 @@ check_http (void) | |||
621 | page += (size_t) strspn (page, "\r\n"); | 850 | page += (size_t) strspn (page, "\r\n"); |
622 | header[pos - header] = 0; | 851 | header[pos - header] = 0; |
623 | if (verbose) | 852 | if (verbose) |
624 | printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page); | 853 | printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, |
854 | (no_body ? " [[ skipped ]]" : page)); | ||
625 | 855 | ||
626 | /* make sure the status line matches the response we are looking for */ | 856 | /* make sure the status line matches the response we are looking for */ |
627 | if (!strstr (status_line, server_expect)) { | 857 | if (!strstr (status_line, server_expect)) { |
@@ -691,6 +921,10 @@ check_http (void) | |||
691 | 921 | ||
692 | } /* end else (server_expect_yn) */ | 922 | } /* end else (server_expect_yn) */ |
693 | 923 | ||
924 | if (maximum_age >= 0) { | ||
925 | check_document_dates (header); | ||
926 | } | ||
927 | |||
694 | /* check elapsed time */ | 928 | /* check elapsed time */ |
695 | microsec = deltime (tv); | 929 | microsec = deltime (tv); |
696 | elapsed_time = (double)microsec / 1.0e6; | 930 | elapsed_time = (double)microsec / 1.0e6; |
@@ -1154,6 +1388,12 @@ certificate expiration times.\n")); | |||
1154 | URL to GET or POST (default: /)\n\ | 1388 | URL to GET or POST (default: /)\n\ |
1155 | -P, --post=STRING\n\ | 1389 | -P, --post=STRING\n\ |
1156 | URL encoded http POST data\n\ | 1390 | URL encoded http POST data\n\ |
1391 | -N, --no-body\n\ | ||
1392 | Don't wait for document body: stop reading after headers.\n\ | ||
1393 | (Note that this still does an HTTP GET or POST, not a HEAD.)\n\ | ||
1394 | -M, --max-age=SECONDS\n\ | ||
1395 | Warn if document is more than SECONDS old. the number can also be of \n\ | ||
1396 | the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.\n\ | ||
1157 | -T, --content-type=STRING\n\ | 1397 | -T, --content-type=STRING\n\ |
1158 | specify Content-Type header media type when POSTing\n"), HTTP_EXPECT); | 1398 | specify Content-Type header media type when POSTing\n"), HTTP_EXPECT); |
1159 | 1399 | ||
@@ -1226,6 +1466,6 @@ Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\ | |||
1226 | [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\ | 1466 | [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\ |
1227 | [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\ | 1467 | [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\ |
1228 | [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\ | 1468 | [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\ |
1229 | [-P string] [-m min_pg_size] [-4|-6]\n"), progname); | 1469 | [-P string] [-m min_pg_size] [-4|-6] [-N] [-M <age>]\n"), progname); |
1230 | printf (_(UT_HLP_VRS), progname, progname); | 1470 | printf (_(UT_HLP_VRS), progname, progname); |
1231 | } | 1471 | } |