1 /******************************************************************************
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 $Id$
19 ******************************************************************************/
20 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
22 const char *progname = "check_http";
23 const char *revision = "$Revision$";
24 const char *copyright = "1999-2004";
25 const char *email = "nagiosplug-devel@lists.sourceforge.net";
27 #include "common.h"
28 #include "netutils.h"
29 #include "utils.h"
31 #define INPUT_DELIMITER ";"
33 #define HTTP_EXPECT "HTTP/1."
34 enum {
35 MAX_IPV4_HOSTLENGTH = 255,
36 HTTP_PORT = 80,
37 HTTPS_PORT = 443
38 };
40 #ifdef HAVE_SSL_H
41 #include <rsa.h>
42 #include <crypto.h>
43 #include <x509.h>
44 #include <pem.h>
45 #include <ssl.h>
46 #include <err.h>
47 #include <rand.h>
48 #else
49 # ifdef HAVE_OPENSSL_SSL_H
50 # include <openssl/rsa.h>
51 # include <openssl/crypto.h>
52 # include <openssl/x509.h>
53 # include <openssl/pem.h>
54 # include <openssl/ssl.h>
55 # include <openssl/err.h>
56 # include <openssl/rand.h>
57 # endif
58 #endif
60 #ifdef HAVE_SSL
61 int check_cert = FALSE;
62 int days_till_exp;
63 char *randbuff;
64 SSL_CTX *ctx;
65 SSL *ssl;
66 X509 *server_cert;
67 int connect_SSL (void);
68 # ifdef USE_OPENSSL
69 int check_certificate (X509 **);
70 # endif
71 #endif
72 int no_body = FALSE;
73 int maximum_age = -1;
75 #ifdef HAVE_REGEX_H
76 enum {
77 REGS = 2,
78 MAX_RE_SIZE = 256
79 };
80 #include <regex.h>
81 regex_t preg;
82 regmatch_t pmatch[REGS];
83 char regexp[MAX_RE_SIZE];
84 char errbuf[MAX_INPUT_BUFFER];
85 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
86 int errcode;
87 #endif
89 struct timeval tv;
91 #define HTTP_URL "/"
92 #define CRLF "\r\n"
94 char timestamp[17] = "";
95 int specify_port = FALSE;
96 int server_port = HTTP_PORT;
97 char server_port_text[6] = "";
98 char server_type[6] = "http";
99 char *server_address;
100 char *host_name;
101 char *server_url;
102 char *user_agent;
103 int server_url_length;
104 int server_expect_yn = 0;
105 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
106 char string_expect[MAX_INPUT_BUFFER] = "";
107 double warning_time = 0;
108 int check_warning_time = FALSE;
109 double critical_time = 0;
110 int check_critical_time = FALSE;
111 char user_auth[MAX_INPUT_BUFFER] = "";
112 int display_html = FALSE;
113 char *http_opt_headers;
114 int onredirect = STATE_OK;
115 int use_ssl = FALSE;
116 int verbose = FALSE;
117 int sd;
118 int min_page_len = 0;
119 int max_page_len = 0;
120 int redir_depth = 0;
121 int max_depth = 15;
122 char *http_method;
123 char *http_post_data;
124 char *http_content_type;
125 char buffer[MAX_INPUT_BUFFER];
127 int process_arguments (int, char **);
128 static char *base64 (const char *bin, size_t len);
129 int check_http (void);
130 void redir (char *pos, char *status_line);
131 int server_type_check(const char *type);
132 int server_port_check(int ssl_flag);
133 char *perfd_time (double microsec);
134 char *perfd_size (int page_len);
135 int my_recv (void);
136 int my_close (void);
137 void print_help (void);
138 void print_usage (void);
140 int
141 main (int argc, char **argv)
142 {
143 int result = STATE_UNKNOWN;
145 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
146 server_url = strdup(HTTP_URL);
147 server_url_length = strlen(server_url);
148 asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
149 clean_revstring (revision), VERSION);
151 if (process_arguments (argc, argv) == ERROR)
152 usage4 (_("Could not parse arguments"));
154 if (strstr (timestamp, ":")) {
155 if (strstr (server_url, "?"))
156 asprintf (&server_url, "%s&%s", server_url, timestamp);
157 else
158 asprintf (&server_url, "%s?%s", server_url, timestamp);
159 }
161 if (display_html == TRUE)
162 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
163 use_ssl ? "https" : "http", host_name,
164 server_port, server_url);
166 /* initialize alarm signal handling, set socket timeout, start timer */
167 (void) signal (SIGALRM, socket_timeout_alarm_handler);
168 (void) alarm (socket_timeout);
169 gettimeofday (&tv, NULL);
171 #ifdef USE_OPENSSL
172 if (use_ssl && check_cert == TRUE) {
173 if (connect_SSL () != OK)
174 die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
175 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
176 result = check_certificate (&server_cert);
177 X509_free (server_cert);
178 }
179 else {
180 printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
181 result = STATE_CRITICAL;
182 }
183 SSL_shutdown (ssl);
184 SSL_free (ssl);
185 SSL_CTX_free (ctx);
186 close (sd);
187 }
188 else {
189 result = check_http ();
190 }
191 #else
192 result = check_http ();
193 #endif
194 return result;
195 }
199 /* process command-line arguments */
200 int
201 process_arguments (int argc, char **argv)
202 {
203 int c = 1;
205 int option = 0;
206 static struct option longopts[] = {
207 STD_LONG_OPTS,
208 {"file",required_argument,0,'F'},
209 {"link", no_argument, 0, 'L'},
210 {"nohtml", no_argument, 0, 'n'},
211 {"ssl", no_argument, 0, 'S'},
212 {"verbose", no_argument, 0, 'v'},
213 {"post", required_argument, 0, 'P'},
214 {"IP-address", required_argument, 0, 'I'},
215 {"url", required_argument, 0, 'u'},
216 {"string", required_argument, 0, 's'},
217 {"regex", required_argument, 0, 'r'},
218 {"ereg", required_argument, 0, 'r'},
219 {"eregi", required_argument, 0, 'R'},
220 {"linespan", no_argument, 0, 'l'},
221 {"onredirect", required_argument, 0, 'f'},
222 {"certificate", required_argument, 0, 'C'},
223 {"useragent", required_argument, 0, 'A'},
224 {"header", required_argument, 0, 'k'},
225 {"no-body", no_argument, 0, 'N'},
226 {"max-age", required_argument, 0, 'M'},
227 {"content-type", required_argument, 0, 'T'},
228 {"pagesize", required_argument, 0, 'm'},
229 {"use-ipv4", no_argument, 0, '4'},
230 {"use-ipv6", no_argument, 0, '6'},
231 {0, 0, 0, 0}
232 };
234 if (argc < 2)
235 return ERROR;
237 for (c = 1; c < argc; c++) {
238 if (strcmp ("-to", argv[c]) == 0)
239 strcpy (argv[c], "-t");
240 if (strcmp ("-hn", argv[c]) == 0)
241 strcpy (argv[c], "-H");
242 if (strcmp ("-wt", argv[c]) == 0)
243 strcpy (argv[c], "-w");
244 if (strcmp ("-ct", argv[c]) == 0)
245 strcpy (argv[c], "-c");
246 if (strcmp ("-nohtml", argv[c]) == 0)
247 strcpy (argv[c], "-n");
248 }
250 while (1) {
251 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option);
252 if (c == -1 || c == EOF)
253 break;
255 switch (c) {
256 case '?': /* usage */
257 usage2 (_("Unknown argument"), optarg);
258 break;
259 case 'h': /* help */
260 print_help ();
261 exit (STATE_OK);
262 break;
263 case 'V': /* version */
264 print_revision (progname, revision);
265 exit (STATE_OK);
266 break;
267 case 't': /* timeout period */
268 if (!is_intnonneg (optarg))
269 usage2 (_("Timeout interval must be a positive integer"), optarg);
270 else
271 socket_timeout = atoi (optarg);
272 break;
273 case 'c': /* critical time threshold */
274 if (!is_nonnegative (optarg))
275 usage2 (_("Critical threshold must be integer"), optarg);
276 else {
277 critical_time = strtod (optarg, NULL);
278 check_critical_time = TRUE;
279 }
280 break;
281 case 'w': /* warning time threshold */
282 if (!is_nonnegative (optarg))
283 usage2 (_("Warning threshold must be integer"), optarg);
284 else {
285 warning_time = strtod (optarg, NULL);
286 check_warning_time = TRUE;
287 }
288 break;
289 case 'A': /* User Agent String */
290 asprintf (&user_agent, "User-Agent: %s", optarg);
291 break;
292 case 'k': /* Additional headers */
293 asprintf (&http_opt_headers, "%s", optarg);
294 break;
295 case 'L': /* show html link */
296 display_html = TRUE;
297 break;
298 case 'n': /* do not show html link */
299 display_html = FALSE;
300 break;
301 case 'S': /* use SSL */
302 #ifndef HAVE_SSL
303 usage4 (_("Invalid option - SSL is not available"));
304 #endif
305 use_ssl = TRUE;
306 if (specify_port == FALSE)
307 server_port = HTTPS_PORT;
308 break;
309 case 'C': /* Check SSL cert validity */
310 #ifdef USE_OPENSSL
311 if (!is_intnonneg (optarg))
312 usage2 (_("Invalid certificate expiration period"), optarg);
313 else {
314 days_till_exp = atoi (optarg);
315 check_cert = TRUE;
316 }
317 #else
318 usage4 (_("Invalid option - SSL is not available"));
319 #endif
320 break;
321 case 'f': /* onredirect */
322 if (!strcmp (optarg, "follow"))
323 onredirect = STATE_DEPENDENT;
324 if (!strcmp (optarg, "unknown"))
325 onredirect = STATE_UNKNOWN;
326 if (!strcmp (optarg, "ok"))
327 onredirect = STATE_OK;
328 if (!strcmp (optarg, "warning"))
329 onredirect = STATE_WARNING;
330 if (!strcmp (optarg, "critical"))
331 onredirect = STATE_CRITICAL;
332 if (verbose)
333 printf(_("option f:%d \n"), onredirect);
334 break;
335 /* Note: H, I, and u must be malloc'd or will fail on redirects */
336 case 'H': /* Host Name (virtual host) */
337 host_name = strdup (optarg);
338 if (strstr (optarg, ":"))
339 sscanf (optarg, "%*[^:]:%d", &server_port);
340 break;
341 case 'I': /* Server IP-address */
342 server_address = strdup (optarg);
343 break;
344 case 'u': /* URL path */
345 server_url = strdup (optarg);
346 server_url_length = strlen (server_url);
347 break;
348 case 'p': /* Server port */
349 if (!is_intnonneg (optarg))
350 usage2 (_("Invalid port number"), optarg);
351 else {
352 server_port = atoi (optarg);
353 specify_port = TRUE;
354 }
355 break;
356 case 'a': /* authorization info */
357 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
358 user_auth[MAX_INPUT_BUFFER - 1] = 0;
359 break;
360 case 'P': /* HTTP POST data in URL encoded format */
361 if (http_method || http_post_data) break;
362 http_method = strdup("POST");
363 http_post_data = strdup (optarg);
364 break;
365 case 's': /* string or substring */
366 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
367 string_expect[MAX_INPUT_BUFFER - 1] = 0;
368 break;
369 case 'e': /* string or substring */
370 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
371 server_expect[MAX_INPUT_BUFFER - 1] = 0;
372 server_expect_yn = 1;
373 break;
374 case 'T': /* Content-type */
375 asprintf (&http_content_type, "%s", optarg);
376 break;
377 #ifndef HAVE_REGEX_H
378 case 'l': /* linespan */
379 case 'r': /* linespan */
380 case 'R': /* linespan */
381 usage4 (_("Call for regex which was not a compiled option"));
382 break;
383 #else
384 case 'l': /* linespan */
385 cflags &= ~REG_NEWLINE;
386 break;
387 case 'R': /* regex */
388 cflags |= REG_ICASE;
389 case 'r': /* regex */
390 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
391 regexp[MAX_RE_SIZE - 1] = 0;
392 errcode = regcomp (&preg, regexp, cflags);
393 if (errcode != 0) {
394 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
395 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
396 return ERROR;
397 }
398 break;
399 #endif
400 case '4':
401 address_family = AF_INET;
402 break;
403 case '6':
404 #ifdef USE_IPV6
405 address_family = AF_INET6;
406 #else
407 usage4 (_("IPv6 support not available"));
408 #endif
409 break;
410 case 'v': /* verbose */
411 verbose = TRUE;
412 break;
413 case 'm': /* min_page_length */
414 {
415 char *tmp;
416 if (strchr(optarg, ':') != (char *)NULL) {
417 /* range, so get two values, min:max */
418 tmp = strtok(optarg, ":");
419 if (tmp == NULL) {
420 printf("Bad format: try \"-m min:max\"\n");
421 exit (STATE_WARNING);
422 } else
423 min_page_len = atoi(tmp);
425 tmp = strtok(NULL, ":");
426 if (tmp == NULL) {
427 printf("Bad format: try \"-m min:max\"\n");
428 exit (STATE_WARNING);
429 } else
430 max_page_len = atoi(tmp);
431 } else
432 min_page_len = atoi (optarg);
433 break;
434 }
435 case 'N': /* no-body */
436 no_body = TRUE;
437 break;
438 case 'M': /* max-age */
439 {
440 int L = strlen(optarg);
441 if (L && optarg[L-1] == 'm')
442 maximum_age = atoi (optarg) * 60;
443 else if (L && optarg[L-1] == 'h')
444 maximum_age = atoi (optarg) * 60 * 60;
445 else if (L && optarg[L-1] == 'd')
446 maximum_age = atoi (optarg) * 60 * 60 * 24;
447 else if (L && (optarg[L-1] == 's' ||
448 isdigit (optarg[L-1])))
449 maximum_age = atoi (optarg);
450 else {
451 fprintf (stderr, "unparsable max-age: %s\n", optarg);
452 exit (STATE_WARNING);
453 }
454 }
455 break;
456 }
457 }
459 c = optind;
461 if (server_address == NULL && c < argc)
462 server_address = strdup (argv[c++]);
464 if (host_name == NULL && c < argc)
465 host_name = strdup (argv[c++]);
467 if (server_address == NULL) {
468 if (host_name == NULL)
469 usage4 (_("You must specify a server address or host name"));
470 else
471 server_address = strdup (host_name);
472 }
474 if (check_critical_time && critical_time>(double)socket_timeout)
475 socket_timeout = (int)critical_time + 1;
477 if (http_method == NULL)
478 http_method = strdup ("GET");
480 return TRUE;
481 }
485 /* written by lauri alanko */
486 static char *
487 base64 (const char *bin, size_t len)
488 {
490 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
491 size_t i = 0, j = 0;
493 char BASE64_END = '=';
494 char base64_table[64];
495 strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
497 while (j < len - 2) {
498 buf[i++] = base64_table[bin[j] >> 2];
499 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
500 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
501 buf[i++] = base64_table[bin[j + 2] & 63];
502 j += 3;
503 }
505 switch (len - j) {
506 case 1:
507 buf[i++] = base64_table[bin[j] >> 2];
508 buf[i++] = base64_table[(bin[j] & 3) << 4];
509 buf[i++] = BASE64_END;
510 buf[i++] = BASE64_END;
511 break;
512 case 2:
513 buf[i++] = base64_table[bin[j] >> 2];
514 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
515 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
516 buf[i++] = BASE64_END;
517 break;
518 case 0:
519 break;
520 }
522 buf[i] = '\0';
523 return buf;
524 }
528 /* Returns 1 if we're done processing the document body; 0 to keep going */
529 static int
530 document_headers_done (char *full_page)
531 {
532 const char *body;
534 for (body = full_page; *body; body++) {
535 if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
536 break;
537 }
539 if (!*body)
540 return 0; /* haven't read end of headers yet */
542 full_page[body - full_page] = 0;
543 return 1;
544 }
546 static time_t
547 parse_time_string (const char *string)
548 {
549 struct tm tm;
550 time_t t;
551 memset (&tm, 0, sizeof(tm));
553 /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
555 if (isupper (string[0]) && /* Tue */
556 islower (string[1]) &&
557 islower (string[2]) &&
558 ',' == string[3] &&
559 ' ' == string[4] &&
560 (isdigit(string[5]) || string[5] == ' ') && /* 25 */
561 isdigit (string[6]) &&
562 ' ' == string[7] &&
563 isupper (string[8]) && /* Dec */
564 islower (string[9]) &&
565 islower (string[10]) &&
566 ' ' == string[11] &&
567 isdigit (string[12]) && /* 2001 */
568 isdigit (string[13]) &&
569 isdigit (string[14]) &&
570 isdigit (string[15]) &&
571 ' ' == string[16] &&
572 isdigit (string[17]) && /* 02: */
573 isdigit (string[18]) &&
574 ':' == string[19] &&
575 isdigit (string[20]) && /* 59: */
576 isdigit (string[21]) &&
577 ':' == string[22] &&
578 isdigit (string[23]) && /* 03 */
579 isdigit (string[24]) &&
580 ' ' == string[25] &&
581 'G' == string[26] && /* GMT */
582 'M' == string[27] && /* GMT */
583 'T' == string[28]) {
585 tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
586 tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
587 tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
588 tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
589 tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
590 !strncmp (string+8, "Feb", 3) ? 1 :
591 !strncmp (string+8, "Mar", 3) ? 2 :
592 !strncmp (string+8, "Apr", 3) ? 3 :
593 !strncmp (string+8, "May", 3) ? 4 :
594 !strncmp (string+8, "Jun", 3) ? 5 :
595 !strncmp (string+8, "Jul", 3) ? 6 :
596 !strncmp (string+8, "Aug", 3) ? 7 :
597 !strncmp (string+8, "Sep", 3) ? 8 :
598 !strncmp (string+8, "Oct", 3) ? 9 :
599 !strncmp (string+8, "Nov", 3) ? 10 :
600 !strncmp (string+8, "Dec", 3) ? 11 :
601 -1);
602 tm.tm_year = ((1000 * (string[12]-'0') +
603 100 * (string[13]-'0') +
604 10 * (string[14]-'0') +
605 (string[15]-'0'))
606 - 1900);
608 tm.tm_isdst = 0; /* GMT is never in DST, right? */
610 if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
611 return 0;
613 /*
614 This is actually wrong: we need to subtract the local timezone
615 offset from GMT from this value. But, that's ok in this usage,
616 because we only comparing these two GMT dates against each other,
617 so it doesn't matter what time zone we parse them in.
618 */
620 t = mktime (&tm);
621 if (t == (time_t) -1) t = 0;
623 if (verbose) {
624 const char *s = string;
625 while (*s && *s != '\r' && *s != '\n')
626 fputc (*s++, stdout);
627 printf (" ==> %lu\n", (unsigned long) t);
628 }
630 return t;
632 } else {
633 return 0;
634 }
635 }
639 static void
640 check_document_dates (const char *headers)
641 {
642 const char *s;
643 char *server_date = 0;
644 char *document_date = 0;
646 s = headers;
647 while (*s) {
648 const char *field = s;
649 const char *value = 0;
651 /* Find the end of the header field */
652 while (*s && !isspace(*s) && *s != ':')
653 s++;
655 /* Remember the header value, if any. */
656 if (*s == ':')
657 value = ++s;
659 /* Skip to the end of the header, including continuation lines. */
660 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
661 s++;
662 s++;
664 /* Process this header. */
665 if (value && value > field+2) {
666 char *ff = (char *) malloc (value-field);
667 char *ss = ff;
668 while (field < value-1)
669 *ss++ = tolower(*field++);
670 *ss++ = 0;
672 if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
673 const char *e;
674 while (*value && isspace (*value))
675 value++;
676 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
677 ;
678 ss = (char *) malloc (e - value + 1);
679 strncpy (ss, value, e - value);
680 ss[e - value] = 0;
681 if (!strcmp (ff, "date")) {
682 if (server_date) free (server_date);
683 server_date = ss;
684 } else {
685 if (document_date) free (document_date);
686 document_date = ss;
687 }
688 }
689 free (ff);
690 }
691 }
693 /* Done parsing the body. Now check the dates we (hopefully) parsed. */
694 if (!server_date || !*server_date) {
695 die (STATE_UNKNOWN, _("Server date unknown\n"));
696 } else if (!document_date || !*document_date) {
697 die (STATE_CRITICAL, _("Document modification date unknown\n"));
698 } else {
699 time_t srv_data = parse_time_string (server_date);
700 time_t doc_data = parse_time_string (document_date);
702 if (srv_data <= 0) {
703 die (STATE_CRITICAL, _("CRITICAL - Server date \"%100s\" unparsable"), server_date);
704 } else if (doc_data <= 0) {
705 die (STATE_CRITICAL, _("CRITICAL - Document date \"%100s\" unparsable"), document_date);
706 } else if (doc_data > srv_data + 30) {
707 die (STATE_CRITICAL, _("CRITICAL - Document is %d seconds in the future\n"), (int)doc_data - (int)srv_data);
708 } else if (doc_data < srv_data - maximum_age) {
709 int n = (srv_data - doc_data);
710 if (n > (60 * 60 * 24 * 2))
711 die (STATE_CRITICAL,
712 _("CRITICAL - Last modified %.1f days ago\n"),
713 ((float) n) / (60 * 60 * 24));
714 else
715 die (STATE_CRITICAL,
716 _("CRITICAL - Last modified %d:%02d:%02d ago\n"),
717 n / (60 * 60), (n / 60) % 60, n % 60);
718 }
720 free (server_date);
721 free (document_date);
722 }
723 }
725 int
726 get_content_length (const char *headers)
727 {
728 const char *s;
729 int content_length = 0;
731 s = headers;
732 while (*s) {
733 const char *field = s;
734 const char *value = 0;
736 /* Find the end of the header field */
737 while (*s && !isspace(*s) && *s != ':')
738 s++;
740 /* Remember the header value, if any. */
741 if (*s == ':')
742 value = ++s;
744 /* Skip to the end of the header, including continuation lines. */
745 while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
746 s++;
747 s++;
749 /* Process this header. */
750 if (value && value > field+2) {
751 char *ff = (char *) malloc (value-field);
752 char *ss = ff;
753 while (field < value-1)
754 *ss++ = tolower(*field++);
755 *ss++ = 0;
757 if (!strcmp (ff, "content-length")) {
758 const char *e;
759 while (*value && isspace (*value))
760 value++;
761 for (e = value; *e && *e != '\r' && *e != '\n'; e++)
762 ;
763 ss = (char *) malloc (e - value + 1);
764 strncpy (ss, value, e - value);
765 ss[e - value] = 0;
766 content_length = atoi(ss);
767 free (ss);
768 }
769 free (ff);
770 }
771 }
772 return (content_length);
773 }
775 int
776 check_http (void)
777 {
778 char *msg;
779 char *status_line;
780 char *status_code;
781 char *header;
782 char *page;
783 char *auth;
784 int http_status;
785 int i = 0;
786 size_t pagesize = 0;
787 char *full_page;
788 char *buf;
789 char *pos;
790 long microsec;
791 double elapsed_time;
792 int page_len = 0;
793 #ifdef HAVE_SSL
794 int sslerr;
795 #endif
797 /* try to connect to the host at the given port number */
798 #ifdef HAVE_SSL
799 if (use_ssl == TRUE) {
801 if (connect_SSL () != OK) {
802 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
803 }
804 #ifdef USE_OPENSSL
805 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
806 X509_free (server_cert);
807 }
808 #endif
809 else {
810 printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
811 return STATE_CRITICAL;
812 }
814 }
815 else {
816 #endif
817 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
818 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
819 #ifdef HAVE_SSL
820 }
821 #endif
823 asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
825 /* optionally send the host header info */
826 if (host_name)
827 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
829 /* optionally send any other header tag */
830 if (http_opt_headers) {
831 for ((pos = strtok(http_opt_headers, INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
832 asprintf (&buf, "%s%s\r\n", buf, pos);
833 }
835 /* optionally send the authentication info */
836 if (strlen(user_auth)) {
837 auth = base64 (user_auth, strlen (user_auth));
838 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
839 }
841 /* either send http POST data */
842 if (http_post_data) {
843 if (http_content_type) {
844 asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
845 } else {
846 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
847 }
849 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
850 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
851 }
852 else {
853 /* or just a newline so the server knows we're done with the request */
854 asprintf (&buf, "%s%s", buf, CRLF);
855 }
857 if (verbose)
858 printf ("%s\n", buf);
860 #ifdef HAVE_SSL
861 if (use_ssl == TRUE) {
862 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
863 # ifdef USE_OPENSSL
864 ERR_print_errors_fp (stderr);
865 # endif
866 return STATE_CRITICAL;
867 }
868 }
869 else {
870 #endif
871 send (sd, buf, strlen (buf), 0);
872 #ifdef HAVE_SSL
873 }
874 #endif
876 /* fetch the page */
877 full_page = strdup("");
878 while ((i = my_recv ()) > 0) {
879 buffer[i] = '\0';
880 asprintf (&full_page, "%s%s", full_page, buffer);
881 pagesize += i;
883 if (no_body && document_headers_done (full_page)) {
884 i = 0;
885 break;
886 }
887 }
889 if (i < 0 && errno != ECONNRESET) {
890 #ifdef HAVE_SSL
891 if (use_ssl) {
892 sslerr=SSL_get_error(ssl, i);
893 if ( sslerr == SSL_ERROR_SSL ) {
894 die (STATE_WARNING, _("Client Certificate Required\n"));
895 } else {
896 die (STATE_CRITICAL, _("Error on receive\n"));
897 }
898 }
899 else {
900 #endif
901 die (STATE_CRITICAL, _("Error on receive\n"));
902 #ifdef HAVE_SSL
903 }
904 #endif
905 }
907 /* return a CRITICAL status if we couldn't read any data */
908 if (pagesize == (size_t) 0)
909 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
911 /* close the connection */
912 my_close ();
914 /* reset the alarm */
915 alarm (0);
917 /* leave full_page untouched so we can free it later */
918 page = full_page;
920 if (verbose)
921 printf ("%s://%s:%d%s is %d characters\n",
922 use_ssl ? "https" : "http", server_address,
923 server_port, server_url, pagesize);
925 /* find status line and null-terminate it */
926 status_line = page;
927 page += (size_t) strcspn (page, "\r\n");
928 pos = page;
929 page += (size_t) strspn (page, "\r\n");
930 status_line[strcspn(status_line, "\r\n")] = 0;
931 strip (status_line);
932 if (verbose)
933 printf ("STATUS: %s\n", status_line);
935 /* find header info and null-terminate it */
936 header = page;
937 while (strcspn (page, "\r\n") > 0) {
938 page += (size_t) strcspn (page, "\r\n");
939 pos = page;
940 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
941 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
942 page += (size_t) 2;
943 else
944 page += (size_t) 1;
945 }
946 page += (size_t) strspn (page, "\r\n");
947 header[pos - header] = 0;
948 if (verbose)
949 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
950 (no_body ? " [[ skipped ]]" : page));
952 /* make sure the status line matches the response we are looking for */
953 if (!strstr (status_line, server_expect)) {
954 if (server_port == HTTP_PORT)
955 asprintf (&msg,
956 _("Invalid HTTP response received from host\n"));
957 else
958 asprintf (&msg,
959 _("Invalid HTTP response received from host on port %d\n"),
960 server_port);
961 die (STATE_CRITICAL, "%s", msg);
962 }
964 /* Exit here if server_expect was set by user and not default */
965 if ( server_expect_yn ) {
966 asprintf (&msg,
967 _("HTTP OK: Status line output matched \"%s\"\n"),
968 server_expect);
969 if (verbose)
970 printf ("%s\n",msg);
971 }
972 else {
973 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
974 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
975 /* Status-Code = 3 DIGITS */
977 status_code = strchr (status_line, ' ') + sizeof (char);
978 if (strspn (status_code, "1234567890") != 3)
979 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
981 http_status = atoi (status_code);
983 /* check the return code */
985 if (http_status >= 600 || http_status < 100)
986 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
988 /* server errors result in a critical state */
989 else if (http_status >= 500)
990 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
992 /* client errors result in a warning state */
993 else if (http_status >= 400)
994 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
996 /* check redirected page if specified */
997 else if (http_status >= 300) {
999 if (onredirect == STATE_DEPENDENT)
1000 redir (header, status_line);
1001 else if (onredirect == STATE_UNKNOWN)
1002 printf (_("UNKNOWN"));
1003 else if (onredirect == STATE_OK)
1004 printf (_("OK"));
1005 else if (onredirect == STATE_WARNING)
1006 printf (_("WARNING"));
1007 else if (onredirect == STATE_CRITICAL)
1008 printf (_("CRITICAL"));
1009 microsec = deltime (tv);
1010 elapsed_time = (double)microsec / 1.0e6;
1011 die (onredirect,
1012 _(" - %s - %.3f second response time %s%s|%s %s\n"),
1013 status_line, elapsed_time, timestamp,
1014 (display_html ? "</A>" : ""),
1015 perfd_time (elapsed_time), perfd_size (pagesize));
1016 } /* end if (http_status >= 300) */
1018 } /* end else (server_expect_yn) */
1020 if (maximum_age >= 0) {
1021 check_document_dates (header);
1022 }
1024 /* check elapsed time */
1025 microsec = deltime (tv);
1026 elapsed_time = (double)microsec / 1.0e6;
1027 asprintf (&msg,
1028 _("HTTP WARNING: %s - %.3f second response time %s%s|%s %s\n"),
1029 status_line, elapsed_time, timestamp,
1030 (display_html ? "</A>" : ""),
1031 perfd_time (elapsed_time), perfd_size (pagesize));
1032 if (check_critical_time == TRUE && elapsed_time > critical_time)
1033 die (STATE_CRITICAL, "%s", msg);
1034 if (check_warning_time == TRUE && elapsed_time > warning_time)
1035 die (STATE_WARNING, "%s", msg);
1037 /* Page and Header content checks go here */
1038 /* these checks should be last */
1040 if (strlen (string_expect)) {
1041 if (strstr (page, string_expect)) {
1042 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
1043 status_line, elapsed_time,
1044 timestamp, (display_html ? "</A>" : ""),
1045 perfd_time (elapsed_time), perfd_size (pagesize));
1046 exit (STATE_OK);
1047 }
1048 else {
1049 printf (_("CRITICAL - string not found%s|%s %s\n"),
1050 (display_html ? "</A>" : ""),
1051 perfd_time (elapsed_time), perfd_size (pagesize));
1052 exit (STATE_CRITICAL);
1053 }
1054 }
1055 #ifdef HAVE_REGEX_H
1056 if (strlen (regexp)) {
1057 errcode = regexec (&preg, page, REGS, pmatch, 0);
1058 if (errcode == 0) {
1059 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
1060 status_line, elapsed_time,
1061 timestamp, (display_html ? "</A>" : ""),
1062 perfd_time (elapsed_time), perfd_size (pagesize));
1063 exit (STATE_OK);
1064 }
1065 else {
1066 if (errcode == REG_NOMATCH) {
1067 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
1068 (display_html ? "</A>" : ""),
1069 perfd_time (elapsed_time), perfd_size (pagesize));
1070 exit (STATE_CRITICAL);
1071 }
1072 else {
1073 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1074 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
1075 exit (STATE_CRITICAL);
1076 }
1077 }
1078 }
1079 #endif
1081 /* make sure the page is of an appropriate size */
1082 /* page_len = get_content_length(header); */
1083 page_len = pagesize;
1084 if ((max_page_len > 0) && (page_len > max_page_len)) {
1085 printf (_("HTTP WARNING: page size %d too large%s|%s\n"),
1086 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1087 exit (STATE_WARNING);
1088 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
1089 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
1090 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
1091 exit (STATE_WARNING);
1092 }
1093 /* We only get here if all tests have been passed */
1094 asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
1095 status_line, page_len, elapsed_time,
1096 timestamp, (display_html ? "</A>" : ""),
1097 perfd_time (elapsed_time), perfd_size (page_len));
1098 die (STATE_OK, "%s", msg);
1099 return STATE_UNKNOWN;
1100 }
1104 /* per RFC 2396 */
1105 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
1106 #define URI_HTTP "%[HTPShtps]://"
1107 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1108 #define URI_PORT ":%[0123456789]"
1109 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
1110 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
1111 #define HD2 URI_HTTP URI_HOST URI_PATH
1112 #define HD3 URI_HTTP URI_HOST URI_PORT
1113 #define HD4 URI_HTTP URI_HOST
1114 #define HD5 URI_PATH
1116 void
1117 redir (char *pos, char *status_line)
1118 {
1119 int i = 0;
1120 char *x;
1121 char xx[2];
1122 char type[6];
1123 char *addr;
1124 char port[6];
1125 char *url;
1127 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
1128 if (addr == NULL)
1129 die (STATE_UNKNOWN, _("Could not allocate addr\n"));
1131 url = malloc (strcspn (pos, "\r\n"));
1132 if (url == NULL)
1133 die (STATE_UNKNOWN, _("Could not allocate url\n"));
1135 while (pos) {
1137 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
1139 pos += (size_t) strcspn (pos, "\r\n");
1140 pos += (size_t) strspn (pos, "\r\n");
1141 if (strlen(pos) == 0)
1142 die (STATE_UNKNOWN,
1143 _("UNKNOWN - Could not find redirect location - %s%s\n"),
1144 status_line, (display_html ? "</A>" : ""));
1145 continue;
1146 }
1148 pos += i;
1149 pos += strspn (pos, " \t\r\n");
1151 url = realloc (url, strcspn (pos, "\r\n"));
1152 if (url == NULL)
1153 die (STATE_UNKNOWN, _("could not allocate url\n"));
1155 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
1156 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
1157 use_ssl = server_type_check (type);
1158 i = atoi (port);
1159 }
1161 /* URI_HTTP URI_HOST URI_PATH */
1162 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
1163 use_ssl = server_type_check (type);
1164 i = server_port_check (use_ssl);
1165 }
1167 /* URI_HTTP URI_HOST URI_PORT */
1168 else if(sscanf (pos, HD3, type, addr, port) == 3) {
1169 strcpy (url, HTTP_URL);
1170 use_ssl = server_type_check (type);
1171 i = atoi (port);
1172 }
1174 /* URI_HTTP URI_HOST */
1175 else if(sscanf (pos, HD4, type, addr) == 2) {
1176 strcpy (url, HTTP_URL);
1177 use_ssl = server_type_check (type);
1178 i = server_port_check (use_ssl);
1179 }
1181 /* URI_PATH */
1182 else if (sscanf (pos, HD5, url) == 1) {
1183 /* relative url */
1184 if ((url[0] != '/')) {
1185 if ((x = strrchr(server_url, '/')))
1186 *x = '\0';
1187 asprintf (&url, "%s/%s", server_url, url);
1188 }
1189 i = server_port;
1190 strcpy (type, server_type);
1191 strcpy (addr, host_name);
1192 }
1194 else {
1195 die (STATE_UNKNOWN,
1196 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
1197 pos, (display_html ? "</A>" : ""));
1198 }
1200 break;
1202 } /* end while (pos) */
1204 if (++redir_depth > max_depth)
1205 die (STATE_WARNING,
1206 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
1207 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
1209 if (server_port==i &&
1210 !strcmp(server_address, addr) &&
1211 (host_name && !strcmp(host_name, addr)) &&
1212 !strcmp(server_url, url))
1213 die (STATE_WARNING,
1214 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1215 type, addr, i, url, (display_html ? "</A>" : ""));
1217 server_port = i;
1218 strcpy (server_type, type);
1220 free (host_name);
1221 host_name = strdup (addr);
1223 free (server_address);
1224 server_address = strdup (addr);
1226 free (server_url);
1227 server_url = strdup (url);
1229 check_http ();
1230 }
1234 int
1235 server_type_check (const char *type)
1236 {
1237 if (strcmp (type, "https"))
1238 return FALSE;
1239 else
1240 return TRUE;
1241 }
1243 int
1244 server_port_check (int ssl_flag)
1245 {
1246 if (ssl_flag)
1247 return HTTPS_PORT;
1248 else
1249 return HTTP_PORT;
1250 }
1254 #ifdef HAVE_SSL
1255 int connect_SSL (void)
1256 {
1257 SSL_METHOD *meth;
1259 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
1260 RAND_seed (randbuff, (int)strlen(randbuff));
1261 if (verbose)
1262 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
1264 /* Initialize SSL context */
1265 SSLeay_add_ssl_algorithms ();
1266 meth = SSLv23_client_method ();
1267 SSL_load_error_strings ();
1268 if ((ctx = SSL_CTX_new (meth)) == NULL) {
1269 printf (_("CRITICAL - Cannot create SSL context.\n"));
1270 return STATE_CRITICAL;
1271 }
1273 /* Initialize alarm signal handling */
1274 signal (SIGALRM, socket_timeout_alarm_handler);
1276 /* Set socket timeout */
1277 alarm (socket_timeout);
1279 /* Save start time */
1280 gettimeofday (&tv, NULL);
1282 /* Make TCP connection */
1283 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
1284 /* Do the SSL handshake */
1285 if ((ssl = SSL_new (ctx)) != NULL) {
1286 #ifdef USE_OPENSSL
1287 SSL_set_cipher_list(ssl, "ALL");
1288 #endif
1289 SSL_set_fd (ssl, sd);
1290 if (SSL_connect (ssl) != -1)
1291 return OK;
1292 #ifdef USE_OPENSSL
1293 ERR_print_errors_fp (stderr);
1294 #endif
1295 }
1296 else {
1297 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
1298 }
1299 SSL_free (ssl);
1300 }
1302 SSL_CTX_free (ctx);
1303 close (sd);
1305 return STATE_CRITICAL;
1306 }
1307 #endif
1311 #ifdef USE_OPENSSL
1312 int
1313 check_certificate (X509 ** certificate)
1314 {
1315 ASN1_STRING *tm;
1316 int offset;
1317 struct tm stamp;
1318 int days_left;
1321 /* Retrieve timestamp of certificate */
1322 tm = X509_get_notAfter (*certificate);
1324 /* Generate tm structure to process timestamp */
1325 if (tm->type == V_ASN1_UTCTIME) {
1326 if (tm->length < 10) {
1327 printf (_("CRITICAL - Wrong time format in certificate.\n"));
1328 return STATE_CRITICAL;
1329 }
1330 else {
1331 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
1332 if (stamp.tm_year < 50)
1333 stamp.tm_year += 100;
1334 offset = 0;
1335 }
1336 }
1337 else {
1338 if (tm->length < 12) {
1339 printf (_("CRITICAL - Wrong time format in certificate.\n"));
1340 return STATE_CRITICAL;
1341 }
1342 else {
1343 stamp.tm_year =
1344 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1345 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1346 stamp.tm_year -= 1900;
1347 offset = 2;
1348 }
1349 }
1350 stamp.tm_mon =
1351 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1352 stamp.tm_mday =
1353 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1354 stamp.tm_hour =
1355 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1356 stamp.tm_min =
1357 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1358 stamp.tm_sec = 0;
1359 stamp.tm_isdst = -1;
1361 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1362 snprintf
1363 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1364 stamp.tm_mon + 1,
1365 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1367 if (days_left > 0 && days_left <= days_till_exp) {
1368 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1369 return STATE_WARNING;
1370 }
1371 if (days_left < 0) {
1372 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1373 return STATE_CRITICAL;
1374 }
1376 if (days_left == 0) {
1377 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1378 return STATE_WARNING;
1379 }
1381 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1383 return STATE_OK;
1384 }
1385 #endif
1389 char *perfd_time (double elapsed_time)
1390 {
1391 return fperfdata ("time", elapsed_time, "s",
1392 check_warning_time, warning_time,
1393 check_critical_time, critical_time,
1394 TRUE, 0, FALSE, 0);
1395 }
1399 char *perfd_size (int page_len)
1400 {
1401 return perfdata ("size", page_len, "B",
1402 (min_page_len>0?TRUE:FALSE), min_page_len,
1403 (min_page_len>0?TRUE:FALSE), 0,
1404 TRUE, 0, FALSE, 0);
1405 }
1409 int
1410 my_recv (void)
1411 {
1412 int i;
1413 #ifdef HAVE_SSL
1414 if (use_ssl) {
1415 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1416 }
1417 else {
1418 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1419 }
1420 #else
1421 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1422 #endif
1423 return i;
1424 }
1428 int
1429 my_close (void)
1430 {
1431 #ifdef HAVE_SSL
1432 if (use_ssl == TRUE) {
1433 SSL_shutdown (ssl);
1434 SSL_free (ssl);
1435 SSL_CTX_free (ctx);
1436 return 0;
1437 }
1438 else {
1439 #endif
1440 return close (sd);
1441 #ifdef HAVE_SSL
1442 }
1443 #endif
1444 }
1448 void
1449 print_help (void)
1450 {
1451 print_revision (progname, revision);
1453 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1454 printf (COPYRIGHT, copyright, email);
1456 printf (_("\
1457 This plugin tests the HTTP service on the specified host. It can test\n\
1458 normal (http) and secure (https) servers, follow redirects, search for\n\
1459 strings and regular expressions, check connection times, and report on\n\
1460 certificate expiration times.\n\n"));
1462 print_usage ();
1464 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1466 printf (_(UT_HELP_VRSN));
1468 printf (_("\
1469 -H, --hostname=ADDRESS\n\
1470 Host name argument for servers using host headers (virtual host)\n\
1471 Append a port to include it in the header (eg: example.com:5000)\n\
1472 -I, --IP-address=ADDRESS\n\
1473 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1474 -p, --port=INTEGER\n\
1475 Port number (default: %d)\n"), HTTP_PORT);
1477 printf (_(UT_IPv46));
1479 #ifdef HAVE_SSL
1480 printf (_("\
1481 -S, --ssl\n\
1482 Connect via SSL\n\
1483 -C, --certificate=INTEGER\n\
1484 Minimum number of days a certificate has to be valid.\n\
1485 (when this option is used the url is not checked.)\n"));
1486 #endif
1488 printf (_("\
1489 -e, --expect=STRING\n\
1490 String to expect in first (status) line of server response (default: %s)\n\
1491 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1492 -s, --string=STRING\n\
1493 String to expect in the content\n\
1494 -u, --url=PATH\n\
1495 URL to GET or POST (default: /)\n\
1496 -P, --post=STRING\n\
1497 URL encoded http POST data\n\
1498 -N, --no-body\n\
1499 Don't wait for document body: stop reading after headers.\n\
1500 (Note that this still does an HTTP GET or POST, not a HEAD.)\n\
1501 -M, --max-age=SECONDS\n\
1502 Warn if document is more than SECONDS old. the number can also be of \n\
1503 the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.\n\
1504 -T, --content-type=STRING\n\
1505 specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
1507 #ifdef HAVE_REGEX_H
1508 printf (_("\
1509 -l, --linespan\n\
1510 Allow regex to span newlines (must precede -r or -R)\n\
1511 -r, --regex, --ereg=STRING\n\
1512 Search page for regex STRING\n\
1513 -R, --eregi=STRING\n\
1514 Search page for case-insensitive regex STRING\n"));
1515 #endif
1517 printf (_("\
1518 -a, --authorization=AUTH_PAIR\n\
1519 Username:password on sites with basic authentication\n\
1520 -A, --useragent=STRING\n\
1521 String to be sent in http header as \"User Agent\"\n\
1522 -k, --header=STRING\n\
1523 Any other tags to be sent in http header, separated by semicolon\n\
1524 -L, --link=URL\n\
1525 Wrap output in HTML link (obsoleted by urlize)\n\
1526 -f, --onredirect=<ok|warning|critical|follow>\n\
1527 How to handle redirected pages\n\
1528 -m, --pagesize=INTEGER<:INTEGER>\n\
1529 Minimum page size required (bytes) : Maximum page size required (bytes)\n"));
1531 printf (_(UT_WARN_CRIT));
1533 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1535 printf (_(UT_VERBOSE));
1537 printf (_("\
1538 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1539 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1540 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1541 messages from the host result in STATE_WARNING return values. If you are\n\
1542 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1543 (fully qualified domain name) as the [host_name] argument.\n"));
1545 #ifdef HAVE_SSL
1546 printf (_("\n\
1547 This plugin can also check whether an SSL enabled web server is able to\n\
1548 serve content (optionally within a specified time) or whether the X509 \n\
1549 certificate is still valid for the specified number of days.\n"));
1550 printf (_("\n\
1551 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1552 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1553 STATE_OK will be returned. When the server returns its content but exceeds\n\
1554 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1555 a STATE_CRITICAL will be returned.\n\n"));
1557 printf (_("\
1558 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1559 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1560 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1561 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1562 the certificate is expired.\n"));
1563 #endif
1565 printf (_(UT_SUPPORT));
1567 }
1571 void
1572 print_usage (void)
1573 {
1574 printf ("\
1575 Usage: %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n\
1576 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1577 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1578 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1579 [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] \n\
1580 [-M <age>] [-A string] [-k string]\n", progname);
1581 }