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 | } |
