594a368cd57ac273c54914ec7364b89802a80150
1 /****************************************************************************
2 *
3 * Program: HTTP plugin for Nagios
4 * License: GPL
5 *
6 * License Information:
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Id$
23 *
24 *****************************************************************************/
26 #define PROGNAME "check_http"
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Ethan Galstad/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
32 #include "config.h"
33 #include "common.h"
34 #include "netutils.h"
35 #include "utils.h"
37 #define SUMMARY "\
38 This plugin tests the HTTP service on the specified host. It can test\n\
39 normal (http) and secure (https) servers, follow redirects, search for\n\
40 strings and regular expressions, check connection times, and report on\n\
41 certificate expiration times.\n"
43 #define OPTIONS "\
44 \(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
45 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
46 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
47 [-s string] [-r <regex> | -R <case-insensitive regex>]\n\
48 [-P string]"
50 #define LONGOPTIONS "\
51 -H, --hostname=ADDRESS\n\
52 Host name argument for servers using host headers (virtual host)\n\
53 -I, --IP-address=ADDRESS\n\
54 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
55 -e, --expect=STRING\n\
56 String to expect in first (status) line of server response (default: %s)\n\
57 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
58 -s, --string=STRING\n\
59 String to expect in the content\n\
60 -u, --url=PATH\n\
61 URL to GET or POST (default: /)\n\
62 -p, --port=INTEGER\n\
63 Port number (default: %d)\n\
64 -P, --post=STRING\n\
65 URL encoded http POST data\n\
66 -w, --warning=INTEGER\n\
67 Response time to result in warning status (seconds)\n\
68 -c, --critical=INTEGER\n\
69 Response time to result in critical status (seconds)\n\
70 -t, --timeout=INTEGER\n\
71 Seconds before connection times out (default: %d)\n\
72 -a, --authorization=AUTH_PAIR\n\
73 Username:password on sites with basic authentication\n\
74 -L, --link=URL\n\
75 Wrap output in HTML link (obsoleted by urlize)\n\
76 -f, --onredirect=<ok|warning|critical|follow>\n\
77 How to handle redirected pages\n%s\
78 -v, --verbose\n\
79 Show details for command-line debugging (do not use with nagios server)\n\
80 -h, --help\n\
81 Print detailed help screen\n\
82 -V, --version\n\
83 Print version information\n"
85 #ifdef HAVE_SSL
86 #define SSLOPTIONS "\
87 -S, --ssl\n\
88 Connect via SSL\n\
89 -C, --certificate=INTEGER\n\
90 Minimum number of days a certificate has to be valid.\n\
91 (when this option is used the url is not checked.)\n"
92 #else
93 #define SSLOPTIONS ""
94 #endif
96 #define DESCRIPTION "\
97 This plugin will attempt to open an HTTP connection with the host. Successul\n\
98 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
99 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
100 messages from the host result in STATE_WARNING return values. If you are\n\
101 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
102 \(fully qualified domain name) as the [host_name] argument.\n"
104 #define SSLDESCRIPTION "\
105 This plugin can also check whether an SSL enabled web server is able to\n\
106 serve content (optionally within a specified time) or whether the X509 \n\
107 certificate is still valid for the specified number of days.\n\n\
108 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
109 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
110 STATE_OK will be returned. When the server returns its content but exceeds\n\
111 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
112 a STATE_CRITICAL will be returned.\n\n\
113 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
114 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
115 STATE_OK is returned. When the certificate is still valid, but for less than\n\
116 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
117 the certificate is expired.\n"
119 #ifdef HAVE_SSL_H
120 #include <rsa.h>
121 #include <crypto.h>
122 #include <x509.h>
123 #include <pem.h>
124 #include <ssl.h>
125 #include <err.h>
126 #include <rand.h>
127 #endif
129 #ifdef HAVE_OPENSSL_SSL_H
130 #include <openssl/rsa.h>
131 #include <openssl/crypto.h>
132 #include <openssl/x509.h>
133 #include <openssl/pem.h>
134 #include <openssl/ssl.h>
135 #include <openssl/err.h>
136 #include <openssl/rand.h>
137 #endif
139 #ifdef HAVE_SSL
140 int check_cert = FALSE;
141 int days_till_exp;
142 unsigned char *randbuff;
143 SSL_CTX *ctx;
144 SSL *ssl;
145 X509 *server_cert;
146 int connect_SSL (void);
147 int check_certificate (X509 **);
148 #endif
150 #ifdef HAVE_REGEX_H
151 #define REGS 2
152 #define MAX_RE_SIZE 256
153 #include <regex.h>
154 regex_t preg;
155 regmatch_t pmatch[REGS];
156 char regexp[MAX_RE_SIZE];
157 char errbuf[MAX_INPUT_BUFFER];
158 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
159 int errcode;
160 #endif
162 struct timeval tv;
164 #define server_type_check(server_type) \
165 (strcmp (server_type, "https") ? FALSE : TRUE)
167 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
169 #define MAX_IPV4_HOSTLENGTH 64
170 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
171 #define URI_HTTP "%[HTPShtps]://"
172 #define URI_HOST "%[a-zA-Z0-9.-]"
173 #define URI_PORT ":%[0-9]"
174 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
176 #define HTTP_PORT 80
177 #define HTTPS_PORT 443
178 #define HTTP_EXPECT "HTTP/1."
179 #define HTTP_URL "/"
181 char timestamp[17] = "";
182 int specify_port = FALSE;
183 int server_port = HTTP_PORT;
184 char server_port_text[6] = "";
185 char server_type[6] = "http";
186 /*@null@*/ char *server_address = NULL;
187 /*@null@*/ char *host_name = NULL;
188 /*@null@*/ char *server_url = NULL;
189 int server_url_length = 0;
190 int server_expect_yn = 0;
191 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
192 char string_expect[MAX_INPUT_BUFFER] = "";
193 double warning_time = 0;
194 int check_warning_time = FALSE;
195 double critical_time = 0;
196 int check_critical_time = FALSE;
197 char user_auth[MAX_INPUT_BUFFER] = "";
198 int display_html = FALSE;
199 int onredirect = STATE_OK;
200 int use_ssl = FALSE;
201 int verbose = FALSE;
202 int sd;
203 /*@null@*/ char *http_method = NULL;
204 /*@null@*/ char *http_post_data = NULL;
205 char buffer[MAX_INPUT_BUFFER];
207 void print_usage (void);
208 void print_help (void);
209 int process_arguments (int, char **);
210 static char *base64 (char *bin, int len);
211 int check_http (void);
212 int my_recv (void);
213 int my_close (void);
215 int
216 main (int argc, char **argv)
217 {
218 int result = STATE_UNKNOWN;
220 if (process_arguments (argc, argv) == ERROR)
221 usage ("check_http: could not parse arguments\n");
223 if (strstr (timestamp, ":")) {
224 if (strstr (server_url, "?"))
225 asprintf (&server_url, "%s&%s", server_url, timestamp);
226 else
227 asprintf (&server_url, "%s?%s", server_url, timestamp);
228 }
230 if (display_html == TRUE)
231 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
232 host_name, server_port, server_url);
234 /* initialize alarm signal handling, set socket timeout, start timer */
235 (void) signal (SIGALRM, socket_timeout_alarm_handler);
236 (void) alarm (socket_timeout);
237 gettimeofday (&tv, NULL);
239 #ifdef HAVE_SSL
240 if (use_ssl && check_cert == TRUE) {
241 if (connect_SSL () != OK)
242 terminate (STATE_CRITICAL,
243 "HTTP CRITICAL - Could not make SSL connection\n");
244 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
245 result = check_certificate (&server_cert);
246 X509_free (server_cert);
247 }
248 else {
249 printf ("ERROR: Cannot retrieve server certificate.\n");
250 result = STATE_CRITICAL;
251 }
252 SSL_shutdown (ssl);
253 SSL_free (ssl);
254 SSL_CTX_free (ctx);
255 close (sd);
256 }
257 else {
258 result = check_http ();
259 }
260 #else
261 result = check_http ();
262 #endif
263 return result;
264 }
265 \f
268 /* process command-line arguments */
269 int
270 process_arguments (int argc, char **argv)
271 {
272 int c = 1;
274 #ifdef HAVE_GETOPT_H
275 int option_index = 0;
276 static struct option long_options[] = {
277 STD_LONG_OPTS,
278 {"file",required_argument,0,'F'},
279 {"link", no_argument, 0, 'L'},
280 {"nohtml", no_argument, 0, 'n'},
281 {"ssl", no_argument, 0, 'S'},
282 {"verbose", no_argument, 0, 'v'},
283 {"post", required_argument, 0, 'P'},
284 {"IP-address", required_argument, 0, 'I'},
285 {"string", required_argument, 0, 's'},
286 {"regex", required_argument, 0, 'r'},
287 {"ereg", required_argument, 0, 'r'},
288 {"eregi", required_argument, 0, 'R'},
289 {"onredirect", required_argument, 0, 'f'},
290 {"certificate", required_argument, 0, 'C'},
291 {0, 0, 0, 0}
292 };
293 #endif
295 if (argc < 2)
296 return ERROR;
298 for (c = 1; c < argc; c++) {
299 if (strcmp ("-to", argv[c]) == 0)
300 strcpy (argv[c], "-t");
301 if (strcmp ("-hn", argv[c]) == 0)
302 strcpy (argv[c], "-H");
303 if (strcmp ("-wt", argv[c]) == 0)
304 strcpy (argv[c], "-w");
305 if (strcmp ("-ct", argv[c]) == 0)
306 strcpy (argv[c], "-c");
307 if (strcmp ("-nohtml", argv[c]) == 0)
308 strcpy (argv[c], "-n");
309 }
311 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nLS"
313 while (1) {
314 #ifdef HAVE_GETOPT_H
315 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
316 #else
317 c = getopt (argc, argv, OPTCHARS);
318 #endif
319 if (c == -1 || c == EOF)
320 break;
322 switch (c) {
323 case '?': /* usage */
324 usage2 ("unknown argument", optarg);
325 break;
326 case 'h': /* help */
327 print_help ();
328 exit (STATE_OK);
329 break;
330 case 'V': /* version */
331 print_revision (PROGNAME, REVISION);
332 exit (STATE_OK);
333 break;
334 case 't': /* timeout period */
335 if (!is_intnonneg (optarg))
336 usage2 ("timeout interval must be a non-negative integer", optarg);
337 socket_timeout = atoi (optarg);
338 break;
339 case 'c': /* critical time threshold */
340 if (!is_intnonneg (optarg))
341 usage2 ("invalid critical threshold", optarg);
342 critical_time = strtod (optarg, NULL);
343 check_critical_time = TRUE;
344 break;
345 case 'w': /* warning time threshold */
346 if (!is_intnonneg (optarg))
347 usage2 ("invalid warning threshold", optarg);
348 warning_time = strtod (optarg, NULL);
349 check_warning_time = TRUE;
350 break;
351 case 'L': /* show html link */
352 display_html = TRUE;
353 break;
354 case 'n': /* do not show html link */
355 display_html = FALSE;
356 break;
357 case 'S': /* use SSL */
358 #ifndef HAVE_SSL
359 usage ("check_http: invalid option - SSL is not available\n");
360 #endif
361 use_ssl = TRUE;
362 if (specify_port == FALSE)
363 server_port = HTTPS_PORT;
364 break;
365 case 'C': /* Check SSL cert validity */
366 #ifdef HAVE_SSL
367 if (!is_intnonneg (optarg))
368 usage2 ("invalid certificate expiration period", optarg);
369 days_till_exp = atoi (optarg);
370 check_cert = TRUE;
371 #else
372 usage ("check_http: invalid option - SSL is not available\n");
373 #endif
374 break;
375 case 'f': /* onredirect */
376 if (!strcmp (optarg, "follow"))
377 onredirect = STATE_DEPENDENT;
378 if (!strcmp (optarg, "unknown"))
379 onredirect = STATE_UNKNOWN;
380 if (!strcmp (optarg, "ok"))
381 onredirect = STATE_OK;
382 if (!strcmp (optarg, "warning"))
383 onredirect = STATE_WARNING;
384 if (!strcmp (optarg, "critical"))
385 onredirect = STATE_CRITICAL;
386 if (verbose)
387 printf("option f:%d \n", onredirect);
388 break;
389 /* Note: H, I, and u must be malloc'd or will fail on redirects */
390 case 'H': /* Host Name (virtual host) */
391 host_name = strscpy (host_name, optarg);
392 break;
393 case 'I': /* Server IP-address */
394 server_address = strscpy (server_address, optarg);
395 break;
396 case 'u': /* Host or server */
397 server_url = strscpy (server_url, optarg);
398 server_url_length = strlen (optarg);
399 break;
400 case 'p': /* Host or server */
401 if (!is_intnonneg (optarg))
402 usage2 ("invalid port number", optarg);
403 server_port = atoi (optarg);
404 specify_port = TRUE;
405 break;
406 case 'a': /* authorization info */
407 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
408 user_auth[MAX_INPUT_BUFFER - 1] = 0;
409 break;
410 case 'P': /* HTTP POST data in URL encoded format */
411 http_method = strscpy (http_method, "POST");
412 http_post_data = strscpy (http_post_data, optarg);
413 break;
414 case 's': /* string or substring */
415 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
416 string_expect[MAX_INPUT_BUFFER - 1] = 0;
417 break;
418 case 'e': /* string or substring */
419 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
420 server_expect[MAX_INPUT_BUFFER - 1] = 0;
421 server_expect_yn = 1;
422 break;
423 case 'R': /* regex */
424 #ifdef HAVE_REGEX_H
425 cflags = REG_ICASE;
426 #else
427 usage ("check_http: call for regex which was not a compiled option\n");
428 #endif
429 case 'r': /* regex */
430 #ifdef HAVE_REGEX_H
431 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
432 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
433 regexp[MAX_RE_SIZE - 1] = 0;
434 errcode = regcomp (&preg, regexp, cflags);
435 if (errcode != 0) {
436 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
437 printf ("Could Not Compile Regular Expression: %s", errbuf);
438 return ERROR;
439 }
440 #else
441 usage ("check_http: call for regex which was not a compiled option\n");
442 #endif
443 break;
444 case 'v': /* verbose */
445 verbose = TRUE;
446 break;
447 }
448 }
450 c = optind;
452 if (server_address == NULL && host_name == NULL) {
453 server_address = strscpy (NULL, argv[c]);
454 host_name = strscpy (NULL, argv[c++]);
455 }
457 if (server_address == NULL && host_name == NULL)
458 usage ("check_http: you must specify a host name\n");
460 if (server_address == NULL)
461 server_address = strscpy (NULL, host_name);
463 if (host_name == NULL)
464 host_name = strscpy (NULL, server_address);
466 if (http_method == NULL)
467 http_method = strscpy (http_method, "GET");
469 if (server_url == NULL) {
470 server_url = strscpy (NULL, "/");
471 server_url_length = strlen(HTTP_URL);
472 }
474 return TRUE;
475 }
476 \f
479 /* written by lauri alanko */
480 static char *
481 base64 (char *bin, int len)
482 {
484 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
485 int i = 0, j = 0;
487 char BASE64_END = '=';
488 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
490 while (j < len - 2) {
491 buf[i++] = base64_table[bin[j] >> 2];
492 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
493 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
494 buf[i++] = base64_table[bin[j + 2] & 63];
495 j += 3;
496 }
498 switch (len - j) {
499 case 1:
500 buf[i++] = base64_table[bin[j] >> 2];
501 buf[i++] = base64_table[(bin[j] & 3) << 4];
502 buf[i++] = BASE64_END;
503 buf[i++] = BASE64_END;
504 break;
505 case 2:
506 buf[i++] = base64_table[bin[j] >> 2];
507 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
508 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
509 buf[i++] = BASE64_END;
510 break;
511 case 0:
512 break;
513 }
515 buf[i] = '\0';
516 return buf;
517 }
518 \f
521 int
522 check_http (void)
523 {
524 char *msg = NULL;
525 char *status_line = NULL;
526 char *header = NULL;
527 char *page = NULL;
528 char *auth = NULL;
529 int i = 0;
530 size_t pagesize = 0;
531 char *full_page = NULL;
532 char *buf = NULL;
533 char *pos = NULL;
534 char *x = NULL;
535 char *orig_url = NULL;
536 double elapsed_time;
538 /* try to connect to the host at the given port number */
539 #ifdef HAVE_SSL
540 if (use_ssl == TRUE) {
542 if (connect_SSL () != OK)
543 terminate (STATE_CRITICAL, "Unable to open TCP socket");
545 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
546 X509_free (server_cert);
547 }
548 else {
549 printf ("ERROR: Cannot retrieve server certificate.\n");
550 return STATE_CRITICAL;
551 }
553 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
554 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
555 ERR_print_errors_fp (stderr);
556 return STATE_CRITICAL;
557 }
559 /* optionally send the host header info (not clear if it's usable) */
560 if (strcmp (host_name, "")) {
561 asprintf (&buf, "Host: %s\r\n", host_name);
562 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
563 ERR_print_errors_fp (stderr);
564 return STATE_CRITICAL;
565 }
566 }
568 /* send user agent */
569 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
570 clean_revstring (REVISION), PACKAGE_VERSION);
571 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
572 ERR_print_errors_fp (stderr);
573 return STATE_CRITICAL;
574 }
576 /* optionally send the authentication info */
577 if (strcmp (user_auth, "")) {
578 auth = base64 (user_auth, strlen (user_auth));
579 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
580 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
581 ERR_print_errors_fp (stderr);
582 return STATE_CRITICAL;
583 }
584 }
586 /* optionally send http POST data */
587 if (http_post_data) {
588 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
589 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
590 ERR_print_errors_fp (stderr);
591 return STATE_CRITICAL;
592 }
593 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
594 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
595 ERR_print_errors_fp (stderr);
596 return STATE_CRITICAL;
597 }
598 http_post_data = strscat (http_post_data, "\r\n");
599 if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
600 ERR_print_errors_fp (stderr);
601 return STATE_CRITICAL;
602 }
603 }
605 /* send a newline so the server knows we're done with the request */
606 asprintf (&buf, "\r\n\r\n");
607 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
608 ERR_print_errors_fp (stderr);
609 return STATE_CRITICAL;
610 }
612 }
613 else {
614 #endif
615 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
616 terminate (STATE_CRITICAL, "Unable to open TCP socket");
617 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
618 send (sd, buf, strlen (buf), 0);
622 /* optionally send the host header info */
623 if (strcmp (host_name, "")) {
624 asprintf (&buf, "Host: %s\r\n", host_name);
625 send (sd, buf, strlen (buf), 0);
626 }
628 /* send user agent */
629 asprintf (&buf,
630 "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
631 clean_revstring (REVISION), PACKAGE_VERSION);
632 send (sd, buf, strlen (buf), 0);
634 /* optionally send the authentication info */
635 if (strcmp (user_auth, "")) {
636 auth = base64 (user_auth, strlen (user_auth));
637 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
638 send (sd, buf, strlen (buf), 0);
639 }
641 /* optionally send http POST data */
642 /* written by Chris Henesy <lurker@shadowtech.org> */
643 if (http_post_data) {
644 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
645 send (sd, buf, strlen (buf), 0);
646 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
647 send (sd, buf, strlen (buf), 0);
648 http_post_data = strscat (http_post_data, "\r\n");
649 send (sd, http_post_data, strlen (http_post_data), 0);
650 }
652 /* send a newline so the server knows we're done with the request */
653 asprintf (&buf, "\r\n\r\n");
654 send (sd, buf, strlen (buf), 0);
655 #ifdef HAVE_SSL
656 }
657 #endif
659 /* fetch the page */
660 pagesize = (size_t) 1;
661 asprintf (&full_page, "");
662 while ((i = my_recv ()) > 0) {
663 buffer[i] = '\0';
664 asprintf (&full_page, "%s%s", full_page, buffer);
665 pagesize += i;
666 }
668 if (i < 0)
669 terminate (STATE_CRITICAL, "Error in recv()");
671 /* return a CRITICAL status if we couldn't read any data */
672 if (pagesize == (size_t) 0)
673 terminate (STATE_CRITICAL, "No data received %s", timestamp);
675 /* close the connection */
676 my_close ();
678 /* reset the alarm */
679 alarm (0);
681 /* leave full_page untouched so we can free it later */
682 page = full_page;
684 if (verbose)
685 printf ("Page is %d characters\n", pagesize);
687 /* find status line and null-terminate it */
688 status_line = page;
689 page += (size_t) strcspn (page, "\r\n");
690 pos = page;
691 page += (size_t) strspn (page, "\r\n");
692 status_line[pos - status_line] = 0;
693 strip (status_line);
694 if (verbose)
695 printf ("STATUS: %s\n", status_line);
697 /* find header info and null terminate it */
698 header = page;
699 while (strcspn (page, "\r\n") > 0) {
700 page += (size_t) strcspn (page, "\r\n");
701 pos = page;
702 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
703 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
704 page += (size_t) 2;
705 else
706 page += (size_t) 1;
707 }
708 page += (size_t) strspn (page, "\r\n");
709 header[pos - header] = 0;
710 if (verbose)
711 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
713 /* make sure the status line matches the response we are looking for */
714 if (!strstr (status_line, server_expect)) {
715 if (server_port == HTTP_PORT)
716 asprintf (&msg, "Invalid HTTP response received from host\n");
717 else
718 asprintf (&msg,
719 "Invalid HTTP response received from host on port %d\n",
720 server_port);
721 terminate (STATE_CRITICAL, msg);
722 }
725 /* Exit here if server_expect was set by user and not default */
726 if ( server_expect_yn ) {
727 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
728 server_expect);
729 if (verbose)
730 printf ("%s\n",msg);
732 }
733 else {
736 /* check the return code */
737 /* server errors result in a critical state */
738 if (strstr (status_line, "500") ||
739 strstr (status_line, "501") ||
740 strstr (status_line, "502") ||
741 strstr (status_line, "503")) {
742 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
743 }
745 /* client errors result in a warning state */
746 if (strstr (status_line, "400") ||
747 strstr (status_line, "401") ||
748 strstr (status_line, "402") ||
749 strstr (status_line, "403") ||
750 strstr (status_line, "404")) {
751 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
752 }
754 /* check redirected page if specified */
755 if (strstr (status_line, "300") ||
756 strstr (status_line, "301") ||
757 strstr (status_line, "302") ||
758 strstr (status_line, "303") ||
759 strstr (status_line, "304")) {
760 if (onredirect == STATE_DEPENDENT) {
762 orig_url = strscpy(NULL, server_url);
763 pos = header;
764 while (pos) {
765 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
766 if (server_address == NULL)
767 terminate (STATE_UNKNOWN,
768 "HTTP UNKNOWN: could not allocate server_address");
769 if (strcspn (pos, "\r\n") > server_url_length) {
770 server_url = realloc (server_url, strcspn (pos, "\r\n"));
771 if (server_url == NULL)
772 terminate (STATE_UNKNOWN,
773 "HTTP UNKNOWN: could not allocate server_url");
774 server_url_length = strcspn (pos, "\r\n");
775 }
776 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
777 host_name = strscpy (host_name, server_address);
778 use_ssl = server_type_check (server_type);
779 server_port = atoi (server_port_text);
780 check_http ();
781 }
782 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
783 host_name = strscpy (host_name, server_address);
784 use_ssl = server_type_check (server_type);
785 server_port = server_port_check (use_ssl);
786 check_http ();
787 }
788 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
789 host_name = strscpy (host_name, server_address);
790 strcpy (server_url, "/");
791 use_ssl = server_type_check (server_type);
792 server_port = atoi (server_port_text);
793 check_http ();
794 }
795 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
796 host_name = strscpy (host_name, server_address);
797 strcpy (server_url, "/");
798 use_ssl = server_type_check (server_type);
799 server_port = server_port_check (use_ssl);
800 check_http ();
801 }
802 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
803 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
804 *x = '\0';
805 asprintf (&server_url, "%s/%s", orig_url, server_url);
806 }
807 check_http ();
808 }
809 pos += (size_t) strcspn (pos, "\r\n");
810 pos += (size_t) strspn (pos, "\r\n");
811 } /* end while (pos) */
812 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
813 status_line, (display_html ? "</A>" : ""));
814 exit (STATE_UNKNOWN);
815 } /* end if (onredirect == STATE_DEPENDENT) */
817 else if (onredirect == STATE_UNKNOWN)
818 printf ("HTTP UNKNOWN");
819 else if (onredirect == STATE_OK)
820 printf ("HTTP ok");
821 else if (onredirect == STATE_WARNING)
822 printf ("HTTP WARNING");
823 else if (onredirect == STATE_CRITICAL)
824 printf ("HTTP CRITICAL");
825 elapsed_time = delta_time (tv);
826 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
827 status_line, elapsed_time, timestamp,
828 (display_html ? "</A>" : ""), elapsed_time);
829 terminate (onredirect, msg);
830 } /* end if (strstr (status_line, "30[0-4]") */
833 } /* end else (server_expect_yn) */
836 /* check elapsed time */
837 elapsed_time = delta_time (tv);
838 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
839 status_line, elapsed_time, timestamp,
840 (display_html ? "</A>" : ""), elapsed_time);
841 if (check_critical_time == TRUE && elapsed_time > critical_time)
842 terminate (STATE_CRITICAL, msg);
843 if (check_warning_time == TRUE && elapsed_time > warning_time)
844 terminate (STATE_WARNING, msg);
846 /* Page and Header content checks go here */
847 /* these checks should be last */
849 if (strlen (string_expect)) {
850 if (strstr (page, string_expect)) {
851 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
852 status_line, elapsed_time,
853 timestamp, (display_html ? "</A>" : ""), elapsed_time);
854 exit (STATE_OK);
855 }
856 else {
857 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
858 (display_html ? "</A>" : ""), elapsed_time);
859 exit (STATE_CRITICAL);
860 }
861 }
862 #ifdef HAVE_REGEX_H
863 if (strlen (regexp)) {
864 errcode = regexec (&preg, page, REGS, pmatch, 0);
865 if (errcode == 0) {
866 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
867 status_line, elapsed_time,
868 timestamp, (display_html ? "</A>" : ""), elapsed_time);
869 exit (STATE_OK);
870 }
871 else {
872 if (errcode == REG_NOMATCH) {
873 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
874 (display_html ? "</A>" : ""), elapsed_time);
875 exit (STATE_CRITICAL);
876 }
877 else {
878 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
879 printf ("Execute Error: %s\n", errbuf);
880 exit (STATE_CRITICAL);
881 }
882 }
883 }
884 #endif
886 /* We only get here if all tests have been passed */
887 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
888 status_line, (float)elapsed_time,
889 timestamp, (display_html ? "</A>" : ""), elapsed_time);
890 terminate (STATE_OK, msg);
891 return STATE_UNKNOWN;
892 }
896 #ifdef HAVE_SSL
897 int connect_SSL (void)
898 {
899 SSL_METHOD *meth;
901 randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
902 RAND_seed (randbuff, strlen (randbuff));
903 /* Initialize SSL context */
904 SSLeay_add_ssl_algorithms ();
905 meth = SSLv23_client_method ();
906 SSL_load_error_strings ();
907 if ((ctx = SSL_CTX_new (meth)) == NULL) {
908 printf ("ERROR: Cannot create SSL context.\n");
909 return STATE_CRITICAL;
910 }
912 /* Initialize alarm signal handling */
913 signal (SIGALRM, socket_timeout_alarm_handler);
915 /* Set socket timeout */
916 alarm (socket_timeout);
918 /* Save start time */
919 gettimeofday (&tv, NULL);
921 /* Make TCP connection */
922 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
923 /* Do the SSL handshake */
924 if ((ssl = SSL_new (ctx)) != NULL) {
925 SSL_set_cipher_list(ssl, "ALL");
926 SSL_set_fd (ssl, sd);
927 if (SSL_connect (ssl) != -1)
928 return OK;
929 ERR_print_errors_fp (stderr);
930 }
931 else {
932 printf ("ERROR: Cannot initiate SSL handshake.\n");
933 }
934 SSL_free (ssl);
935 }
937 SSL_CTX_free (ctx);
938 close (sd);
940 return STATE_CRITICAL;
941 }
942 #endif
944 #ifdef HAVE_SSL
945 int
946 check_certificate (X509 ** certificate)
947 {
948 ASN1_STRING *tm;
949 int offset;
950 struct tm stamp;
951 int days_left;
954 /* Retrieve timestamp of certificate */
955 tm = X509_get_notAfter (*certificate);
957 /* Generate tm structure to process timestamp */
958 if (tm->type == V_ASN1_UTCTIME) {
959 if (tm->length < 10) {
960 printf ("ERROR: Wrong time format in certificate.\n");
961 return STATE_CRITICAL;
962 }
963 else {
964 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
965 if (stamp.tm_year < 50)
966 stamp.tm_year += 100;
967 offset = 0;
968 }
969 }
970 else {
971 if (tm->length < 12) {
972 printf ("ERROR: Wrong time format in certificate.\n");
973 return STATE_CRITICAL;
974 }
975 else {
976 stamp.tm_year =
977 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
978 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
979 stamp.tm_year -= 1900;
980 offset = 2;
981 }
982 }
983 stamp.tm_mon =
984 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
985 stamp.tm_mday =
986 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
987 stamp.tm_hour =
988 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
989 stamp.tm_min =
990 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
991 stamp.tm_sec = 0;
992 stamp.tm_isdst = -1;
994 days_left = (mktime (&stamp) - time (NULL)) / 86400;
995 snprintf
996 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
997 stamp.tm_mon + 1,
998 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1000 if (days_left > 0 && days_left <= days_till_exp) {
1001 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1002 return STATE_WARNING;
1003 }
1004 if (days_left < 0) {
1005 printf ("Certificate expired on %s.\n", timestamp);
1006 return STATE_CRITICAL;
1007 }
1009 if (days_left == 0) {
1010 printf ("Certificate expires today (%s).\n", timestamp);
1011 return STATE_WARNING;
1012 }
1014 printf ("Certificate will expire on %s.\n", timestamp);
1016 return STATE_OK;
1017 }
1018 #endif
1019 \f
1022 int
1023 my_recv (void)
1024 {
1025 int i;
1026 #ifdef HAVE_SSL
1027 if (use_ssl) {
1028 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1029 }
1030 else {
1031 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1032 }
1033 #else
1034 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1035 #endif
1036 return i;
1037 }
1040 int
1041 my_close (void)
1042 {
1043 #ifdef HAVE_SSL
1044 if (use_ssl == TRUE) {
1045 SSL_shutdown (ssl);
1046 SSL_free (ssl);
1047 SSL_CTX_free (ctx);
1048 return 0;
1049 }
1050 else {
1051 #endif
1052 return close (sd);
1053 #ifdef HAVE_SSL
1054 }
1055 #endif
1056 }
1057 \f
1060 void
1061 print_help (void)
1062 {
1063 print_revision (PROGNAME, REVISION);
1064 printf
1065 ("Copyright (c) %s %s <%s>\n\n%s\n",
1066 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1067 print_usage ();
1068 printf ("NOTE: One or both of -H and -I must be specified\n");
1069 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1070 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1071 #ifdef HAVE_SSL
1072 printf (SSLDESCRIPTION);
1073 #endif
1074 }
1077 void
1078 print_usage (void)
1079 {
1080 printf ("Usage:\n" " %s %s\n"
1081 #ifdef HAVE_GETOPT_H
1082 " %s (-h | --help) for detailed help\n"
1083 " %s (-V | --version) for version information\n",
1084 #else
1085 " %s -h for detailed help\n"
1086 " %s -V for version information\n",
1087 #endif
1088 PROGNAME, OPTIONS, PROGNAME, PROGNAME);
1089 }