summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/check_http.c246
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;
63int connect_SSL (void); 63int connect_SSL (void);
64int check_certificate (X509 **); 64int check_certificate (X509 **);
65#endif 65#endif
66int no_body = FALSE;
67int maximum_age = -1;
66 68
67#ifdef HAVE_REGEX_H 69#ifdef HAVE_REGEX_H
68enum { 70enum {
@@ -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 */
493static int
494document_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
511static time_t
512parse_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
603static void
604check_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
467int 691int
468check_http (void) 692check_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}