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, i = 1;
273 char optchars[MAX_INPUT_BUFFER];
275 #ifdef HAVE_GETOPT_H
276 int option_index = 0;
277 static struct option long_options[] = {
278 STD_LONG_OPTS,
279 {"file",required_argument,0,'F'},
280 {"link", no_argument, 0, 'L'},
281 {"nohtml", no_argument, 0, 'n'},
282 {"ssl", no_argument, 0, 'S'},
283 {"verbose", no_argument, 0, 'v'},
284 {"post", required_argument, 0, 'P'},
285 {"IP-address", required_argument, 0, 'I'},
286 {"string", required_argument, 0, 's'},
287 {"regex", required_argument, 0, 'r'},
288 {"ereg", required_argument, 0, 'r'},
289 {"eregi", required_argument, 0, 'R'},
290 {"onredirect", required_argument, 0, 'f'},
291 {"certificate", required_argument, 0, 'C'},
292 {0, 0, 0, 0}
293 };
294 #endif
296 if (argc < 2)
297 return ERROR;
299 for (c = 1; c < argc; c++) {
300 if (strcmp ("-to", argv[c]) == 0)
301 strcpy (argv[c], "-t");
302 if (strcmp ("-hn", argv[c]) == 0)
303 strcpy (argv[c], "-H");
304 if (strcmp ("-wt", argv[c]) == 0)
305 strcpy (argv[c], "-w");
306 if (strcmp ("-ct", argv[c]) == 0)
307 strcpy (argv[c], "-c");
308 if (strcmp ("-nohtml", argv[c]) == 0)
309 strcpy (argv[c], "-n");
310 }
312 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nLS"
314 while (1) {
315 #ifdef HAVE_GETOPT_H
316 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
317 #else
318 c = getopt (argc, argv, OPTCHARS);
319 #endif
320 if (c == -1 || c == EOF)
321 break;
323 switch (c) {
324 case '?': /* usage */
325 usage2 ("unknown argument", optarg);
326 break;
327 case 'h': /* help */
328 print_help ();
329 exit (STATE_OK);
330 break;
331 case 'V': /* version */
332 print_revision (PROGNAME, REVISION);
333 exit (STATE_OK);
334 break;
335 case 't': /* timeout period */
336 if (!is_intnonneg (optarg))
337 usage2 ("timeout interval must be a non-negative integer", optarg);
338 socket_timeout = atoi (optarg);
339 break;
340 case 'c': /* critical time threshold */
341 if (!is_intnonneg (optarg))
342 usage2 ("invalid critical threshold", optarg);
343 critical_time = strtod (optarg, NULL);
344 check_critical_time = TRUE;
345 break;
346 case 'w': /* warning time threshold */
347 if (!is_intnonneg (optarg))
348 usage2 ("invalid warning threshold", optarg);
349 warning_time = strtod (optarg, NULL);
350 check_warning_time = TRUE;
351 break;
352 case 'L': /* show html link */
353 display_html = TRUE;
354 break;
355 case 'n': /* do not show html link */
356 display_html = FALSE;
357 break;
358 case 'S': /* use SSL */
359 #ifndef HAVE_SSL
360 usage ("check_http: invalid option - SSL is not available\n");
361 #endif
362 use_ssl = TRUE;
363 if (specify_port == FALSE)
364 server_port = HTTPS_PORT;
365 break;
366 case 'C': /* Check SSL cert validity */
367 #ifdef HAVE_SSL
368 if (!is_intnonneg (optarg))
369 usage2 ("invalid certificate expiration period", optarg);
370 days_till_exp = atoi (optarg);
371 check_cert = TRUE;
372 #else
373 usage ("check_http: invalid option - SSL is not available\n");
374 #endif
375 break;
376 case 'f': /* onredirect */
377 if (!strcmp (optarg, "follow"))
378 onredirect = STATE_DEPENDENT;
379 if (!strcmp (optarg, "unknown"))
380 onredirect = STATE_UNKNOWN;
381 if (!strcmp (optarg, "ok"))
382 onredirect = STATE_OK;
383 if (!strcmp (optarg, "warning"))
384 onredirect = STATE_WARNING;
385 if (!strcmp (optarg, "critical"))
386 onredirect = STATE_CRITICAL;
387 if (verbose)
388 printf("option f:%d \n", onredirect);
389 break;
390 /* Note: H, I, and u must be malloc'd or will fail on redirects */
391 case 'H': /* Host Name (virtual host) */
392 host_name = strscpy (host_name, optarg);
393 break;
394 case 'I': /* Server IP-address */
395 server_address = strscpy (server_address, optarg);
396 break;
397 case 'u': /* Host or server */
398 server_url = strscpy (server_url, optarg);
399 server_url_length = strlen (optarg);
400 break;
401 case 'p': /* Host or server */
402 if (!is_intnonneg (optarg))
403 usage2 ("invalid port number", optarg);
404 server_port = atoi (optarg);
405 specify_port = TRUE;
406 break;
407 case 'a': /* authorization info */
408 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
409 user_auth[MAX_INPUT_BUFFER - 1] = 0;
410 break;
411 case 'P': /* HTTP POST data in URL encoded format */
412 http_method = strscpy (http_method, "POST");
413 http_post_data = strscpy (http_post_data, optarg);
414 break;
415 case 's': /* string or substring */
416 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
417 string_expect[MAX_INPUT_BUFFER - 1] = 0;
418 break;
419 case 'e': /* string or substring */
420 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
421 server_expect[MAX_INPUT_BUFFER - 1] = 0;
422 server_expect_yn = 1;
423 break;
424 case 'R': /* regex */
425 #ifdef HAVE_REGEX_H
426 cflags = REG_ICASE;
427 #else
428 usage ("check_http: call for regex which was not a compiled option\n");
429 #endif
430 case 'r': /* regex */
431 #ifdef HAVE_REGEX_H
432 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
433 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
434 regexp[MAX_RE_SIZE - 1] = 0;
435 errcode = regcomp (&preg, regexp, cflags);
436 if (errcode != 0) {
437 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
438 printf ("Could Not Compile Regular Expression: %s", errbuf);
439 return ERROR;
440 }
441 #else
442 usage ("check_http: call for regex which was not a compiled option\n");
443 #endif
444 break;
445 case 'v': /* verbose */
446 verbose = TRUE;
447 break;
448 }
449 }
451 c = optind;
453 if (server_address == NULL && host_name == NULL) {
454 server_address = strscpy (NULL, argv[c]);
455 host_name = strscpy (NULL, argv[c++]);
456 }
458 if (server_address == NULL && host_name == NULL)
459 usage ("check_http: you must specify a host name\n");
461 if (server_address == NULL)
462 server_address = strscpy (NULL, host_name);
464 if (host_name == NULL)
465 host_name = strscpy (NULL, server_address);
467 if (http_method == NULL)
468 http_method = strscpy (http_method, "GET");
470 if (server_url == NULL) {
471 server_url = strscpy (NULL, "/");
472 server_url_length = strlen(HTTP_URL);
473 }
475 return TRUE;
476 }
477 \f
480 /* written by lauri alanko */
481 static char *
482 base64 (char *bin, int len)
483 {
485 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
486 int i = 0, j = 0;
488 char BASE64_END = '=';
489 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
491 while (j < len - 2) {
492 buf[i++] = base64_table[bin[j] >> 2];
493 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
494 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
495 buf[i++] = base64_table[bin[j + 2] & 63];
496 j += 3;
497 }
499 switch (len - j) {
500 case 1:
501 buf[i++] = base64_table[bin[j] >> 2];
502 buf[i++] = base64_table[(bin[j] & 3) << 4];
503 buf[i++] = BASE64_END;
504 buf[i++] = BASE64_END;
505 break;
506 case 2:
507 buf[i++] = base64_table[bin[j] >> 2];
508 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
509 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
510 buf[i++] = BASE64_END;
511 break;
512 case 0:
513 break;
514 }
516 buf[i] = '\0';
517 return buf;
518 }
519 \f
522 int
523 check_http (void)
524 {
525 char *msg = NULL;
526 char *status_line = NULL;
527 char *header = NULL;
528 char *page = NULL;
529 char *auth = NULL;
530 int i = 0;
531 size_t pagesize = 0;
532 char *full_page = NULL;
533 char *buf = NULL;
534 char *pos = NULL;
535 char *x = NULL;
536 char *orig_url = NULL;
537 double elapsed_time;
539 /* try to connect to the host at the given port number */
540 #ifdef HAVE_SSL
541 if (use_ssl == TRUE) {
543 if (connect_SSL () != OK)
544 terminate (STATE_CRITICAL, "Unable to open TCP socket");
546 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
547 X509_free (server_cert);
548 }
549 else {
550 printf ("ERROR: Cannot retrieve server certificate.\n");
551 return STATE_CRITICAL;
552 }
554 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
555 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
556 ERR_print_errors_fp (stderr);
557 return STATE_CRITICAL;
558 }
560 /* optionally send the host header info (not clear if it's usable) */
561 if (strcmp (host_name, "")) {
562 asprintf (&buf, "Host: %s\r\n", host_name);
563 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
564 ERR_print_errors_fp (stderr);
565 return STATE_CRITICAL;
566 }
567 }
569 /* send user agent */
570 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
571 clean_revstring (REVISION), PACKAGE_VERSION);
572 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
573 ERR_print_errors_fp (stderr);
574 return STATE_CRITICAL;
575 }
577 /* optionally send the authentication info */
578 if (strcmp (user_auth, "")) {
579 auth = base64 (user_auth, strlen (user_auth));
580 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
581 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
582 ERR_print_errors_fp (stderr);
583 return STATE_CRITICAL;
584 }
585 }
587 /* optionally send http POST data */
588 if (http_post_data) {
589 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
590 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
591 ERR_print_errors_fp (stderr);
592 return STATE_CRITICAL;
593 }
594 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
595 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
596 ERR_print_errors_fp (stderr);
597 return STATE_CRITICAL;
598 }
599 http_post_data = strscat (http_post_data, "\r\n");
600 if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
601 ERR_print_errors_fp (stderr);
602 return STATE_CRITICAL;
603 }
604 }
606 /* send a newline so the server knows we're done with the request */
607 asprintf (&buf, "\r\n\r\n");
608 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
609 ERR_print_errors_fp (stderr);
610 return STATE_CRITICAL;
611 }
613 }
614 else {
615 #endif
616 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
617 terminate (STATE_CRITICAL, "Unable to open TCP socket");
618 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
619 send (sd, buf, strlen (buf), 0);
623 /* optionally send the host header info */
624 if (strcmp (host_name, "")) {
625 asprintf (&buf, "Host: %s\r\n", host_name);
626 send (sd, buf, strlen (buf), 0);
627 }
629 /* send user agent */
630 asprintf (&buf,
631 "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
632 clean_revstring (REVISION), PACKAGE_VERSION);
633 send (sd, buf, strlen (buf), 0);
635 /* optionally send the authentication info */
636 if (strcmp (user_auth, "")) {
637 auth = base64 (user_auth, strlen (user_auth));
638 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
639 send (sd, buf, strlen (buf), 0);
640 }
642 /* optionally send http POST data */
643 /* written by Chris Henesy <lurker@shadowtech.org> */
644 if (http_post_data) {
645 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
646 send (sd, buf, strlen (buf), 0);
647 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
648 send (sd, buf, strlen (buf), 0);
649 http_post_data = strscat (http_post_data, "\r\n");
650 send (sd, http_post_data, strlen (http_post_data), 0);
651 }
653 /* send a newline so the server knows we're done with the request */
654 asprintf (&buf, "\r\n\r\n");
655 send (sd, buf, strlen (buf), 0);
656 #ifdef HAVE_SSL
657 }
658 #endif
660 /* fetch the page */
661 pagesize = (size_t) 1;
662 asprintf (&full_page, "");
663 while ((i = my_recv ()) > 0) {
664 buffer[i] = '\0';
665 asprintf (&full_page, "%s%s", full_page, buffer);
666 pagesize += i;
667 }
669 if (i < 0)
670 terminate (STATE_CRITICAL, "Error in recv()");
672 /* return a CRITICAL status if we couldn't read any data */
673 if (pagesize == (size_t) 0)
674 terminate (STATE_CRITICAL, "No data received %s", timestamp);
676 /* close the connection */
677 my_close ();
679 /* reset the alarm */
680 alarm (0);
682 /* leave full_page untouched so we can free it later */
683 page = full_page;
685 if (verbose)
686 printf ("Page is %d characters\n", pagesize);
688 /* find status line and null-terminate it */
689 status_line = page;
690 page += (size_t) strcspn (page, "\r\n");
691 pos = page;
692 page += (size_t) strspn (page, "\r\n");
693 status_line[pos - status_line] = 0;
694 strip (status_line);
695 if (verbose)
696 printf ("STATUS: %s\n", status_line);
698 /* find header info and null terminate it */
699 header = page;
700 while (strcspn (page, "\r\n") > 0) {
701 page += (size_t) strcspn (page, "\r\n");
702 pos = page;
703 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
704 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
705 page += (size_t) 2;
706 else
707 page += (size_t) 1;
708 }
709 page += (size_t) strspn (page, "\r\n");
710 header[pos - header] = 0;
711 if (verbose)
712 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
714 /* make sure the status line matches the response we are looking for */
715 if (!strstr (status_line, server_expect)) {
716 if (server_port == HTTP_PORT)
717 asprintf (&msg, "Invalid HTTP response received from host\n");
718 else
719 asprintf (&msg,
720 "Invalid HTTP response received from host on port %d\n",
721 server_port);
722 terminate (STATE_CRITICAL, msg);
723 }
726 /* Exit here if server_expect was set by user and not default */
727 if ( server_expect_yn ) {
728 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
729 server_expect);
730 if (verbose)
731 printf ("%s\n",msg);
733 }
734 else {
737 /* check the return code */
738 /* server errors result in a critical state */
739 if (strstr (status_line, "500") ||
740 strstr (status_line, "501") ||
741 strstr (status_line, "502") ||
742 strstr (status_line, "503")) {
743 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
744 }
746 /* client errors result in a warning state */
747 if (strstr (status_line, "400") ||
748 strstr (status_line, "401") ||
749 strstr (status_line, "402") ||
750 strstr (status_line, "403") ||
751 strstr (status_line, "404")) {
752 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
753 }
755 /* check redirected page if specified */
756 if (strstr (status_line, "300") ||
757 strstr (status_line, "301") ||
758 strstr (status_line, "302") ||
759 strstr (status_line, "303") ||
760 strstr (status_line, "304")) {
761 if (onredirect == STATE_DEPENDENT) {
763 orig_url = strscpy(NULL, server_url);
764 pos = header;
765 while (pos) {
766 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
767 if (server_address == NULL)
768 terminate (STATE_UNKNOWN,
769 "HTTP UNKNOWN: could not allocate server_address");
770 if (strcspn (pos, "\r\n") > server_url_length) {
771 server_url = realloc (server_url, strcspn (pos, "\r\n"));
772 if (server_url == NULL)
773 terminate (STATE_UNKNOWN,
774 "HTTP UNKNOWN: could not allocate server_url");
775 server_url_length = strcspn (pos, "\r\n");
776 }
777 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
778 host_name = strscpy (host_name, server_address);
779 use_ssl = server_type_check (server_type);
780 server_port = atoi (server_port_text);
781 check_http ();
782 }
783 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
784 host_name = strscpy (host_name, server_address);
785 use_ssl = server_type_check (server_type);
786 server_port = server_port_check (use_ssl);
787 check_http ();
788 }
789 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
790 host_name = strscpy (host_name, server_address);
791 strcpy (server_url, "/");
792 use_ssl = server_type_check (server_type);
793 server_port = atoi (server_port_text);
794 check_http ();
795 }
796 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
797 host_name = strscpy (host_name, server_address);
798 strcpy (server_url, "/");
799 use_ssl = server_type_check (server_type);
800 server_port = server_port_check (use_ssl);
801 check_http ();
802 }
803 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
804 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
805 *x = '\0';
806 asprintf (&server_url, "%s/%s", orig_url, server_url);
807 }
808 check_http ();
809 }
810 pos += (size_t) strcspn (pos, "\r\n");
811 pos += (size_t) strspn (pos, "\r\n");
812 } /* end while (pos) */
813 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
814 status_line, (display_html ? "</A>" : ""));
815 exit (STATE_UNKNOWN);
816 } /* end if (onredirect == STATE_DEPENDENT) */
818 else if (onredirect == STATE_UNKNOWN)
819 printf ("HTTP UNKNOWN");
820 else if (onredirect == STATE_OK)
821 printf ("HTTP ok");
822 else if (onredirect == STATE_WARNING)
823 printf ("HTTP WARNING");
824 else if (onredirect == STATE_CRITICAL)
825 printf ("HTTP CRITICAL");
826 elapsed_time = delta_time (tv);
827 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
828 status_line, elapsed_time, timestamp,
829 (display_html ? "</A>" : ""), elapsed_time);
830 terminate (onredirect, msg);
831 } /* end if (strstr (status_line, "30[0-4]") */
834 } /* end else (server_expect_yn) */
837 /* check elapsed time */
838 elapsed_time = delta_time (tv);
839 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
840 status_line, elapsed_time, timestamp,
841 (display_html ? "</A>" : ""), elapsed_time);
842 if (check_critical_time == TRUE && elapsed_time > critical_time)
843 terminate (STATE_CRITICAL, msg);
844 if (check_warning_time == TRUE && elapsed_time > warning_time)
845 terminate (STATE_WARNING, msg);
847 /* Page and Header content checks go here */
848 /* these checks should be last */
850 if (strlen (string_expect)) {
851 if (strstr (page, string_expect)) {
852 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
853 status_line, elapsed_time,
854 timestamp, (display_html ? "</A>" : ""), elapsed_time);
855 exit (STATE_OK);
856 }
857 else {
858 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
859 (display_html ? "</A>" : ""), elapsed_time);
860 exit (STATE_CRITICAL);
861 }
862 }
863 #ifdef HAVE_REGEX_H
864 if (strlen (regexp)) {
865 errcode = regexec (&preg, page, REGS, pmatch, 0);
866 if (errcode == 0) {
867 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
868 status_line, elapsed_time,
869 timestamp, (display_html ? "</A>" : ""), elapsed_time);
870 exit (STATE_OK);
871 }
872 else {
873 if (errcode == REG_NOMATCH) {
874 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
875 (display_html ? "</A>" : ""), elapsed_time);
876 exit (STATE_CRITICAL);
877 }
878 else {
879 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
880 printf ("Execute Error: %s\n", errbuf);
881 exit (STATE_CRITICAL);
882 }
883 }
884 }
885 #endif
887 /* We only get here if all tests have been passed */
888 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
889 status_line, (float)elapsed_time,
890 timestamp, (display_html ? "</A>" : ""), elapsed_time);
891 terminate (STATE_OK, msg);
892 return STATE_UNKNOWN;
893 }
897 #ifdef HAVE_SSL
898 int connect_SSL (void)
899 {
900 SSL_METHOD *meth;
902 randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
903 RAND_seed (randbuff, strlen (randbuff));
904 /* Initialize SSL context */
905 SSLeay_add_ssl_algorithms ();
906 meth = SSLv23_client_method ();
907 SSL_load_error_strings ();
908 if ((ctx = SSL_CTX_new (meth)) == NULL) {
909 printf ("ERROR: Cannot create SSL context.\n");
910 return STATE_CRITICAL;
911 }
913 /* Initialize alarm signal handling */
914 signal (SIGALRM, socket_timeout_alarm_handler);
916 /* Set socket timeout */
917 alarm (socket_timeout);
919 /* Save start time */
920 gettimeofday (&tv, NULL);
922 /* Make TCP connection */
923 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
924 /* Do the SSL handshake */
925 if ((ssl = SSL_new (ctx)) != NULL) {
926 SSL_set_cipher_list(ssl, "ALL");
927 SSL_set_fd (ssl, sd);
928 if (SSL_connect (ssl) != -1)
929 return OK;
930 ERR_print_errors_fp (stderr);
931 }
932 else {
933 printf ("ERROR: Cannot initiate SSL handshake.\n");
934 }
935 SSL_free (ssl);
936 }
938 SSL_CTX_free (ctx);
939 close (sd);
941 return STATE_CRITICAL;
942 }
943 #endif
945 #ifdef HAVE_SSL
946 int
947 check_certificate (X509 ** certificate)
948 {
949 ASN1_STRING *tm;
950 int offset;
951 struct tm stamp;
952 int days_left;
955 /* Retrieve timestamp of certificate */
956 tm = X509_get_notAfter (*certificate);
958 /* Generate tm structure to process timestamp */
959 if (tm->type == V_ASN1_UTCTIME) {
960 if (tm->length < 10) {
961 printf ("ERROR: Wrong time format in certificate.\n");
962 return STATE_CRITICAL;
963 }
964 else {
965 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
966 if (stamp.tm_year < 50)
967 stamp.tm_year += 100;
968 offset = 0;
969 }
970 }
971 else {
972 if (tm->length < 12) {
973 printf ("ERROR: Wrong time format in certificate.\n");
974 return STATE_CRITICAL;
975 }
976 else {
977 stamp.tm_year =
978 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
979 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
980 stamp.tm_year -= 1900;
981 offset = 2;
982 }
983 }
984 stamp.tm_mon =
985 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
986 stamp.tm_mday =
987 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
988 stamp.tm_hour =
989 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
990 stamp.tm_min =
991 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
992 stamp.tm_sec = 0;
993 stamp.tm_isdst = -1;
995 days_left = (mktime (&stamp) - time (NULL)) / 86400;
996 snprintf
997 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
998 stamp.tm_mon + 1,
999 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1001 if (days_left > 0 && days_left <= days_till_exp) {
1002 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1003 return STATE_WARNING;
1004 }
1005 if (days_left < 0) {
1006 printf ("Certificate expired on %s.\n", timestamp);
1007 return STATE_CRITICAL;
1008 }
1010 if (days_left == 0) {
1011 printf ("Certificate expires today (%s).\n", timestamp);
1012 return STATE_WARNING;
1013 }
1015 printf ("Certificate will expire on %s.\n", timestamp);
1017 return STATE_OK;
1018 }
1019 #endif
1020 \f
1023 int
1024 my_recv (void)
1025 {
1026 int i;
1027 #ifdef HAVE_SSL
1028 if (use_ssl) {
1029 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1030 }
1031 else {
1032 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1033 }
1034 #else
1035 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1036 #endif
1037 return i;
1038 }
1041 int
1042 my_close (void)
1043 {
1044 #ifdef HAVE_SSL
1045 if (use_ssl == TRUE) {
1046 SSL_shutdown (ssl);
1047 SSL_free (ssl);
1048 SSL_CTX_free (ctx);
1049 return 0;
1050 }
1051 else {
1052 #endif
1053 return close (sd);
1054 #ifdef HAVE_SSL
1055 }
1056 #endif
1057 }
1058 \f
1061 void
1062 print_help (void)
1063 {
1064 print_revision (PROGNAME, REVISION);
1065 printf
1066 ("Copyright (c) %s %s <%s>\n\n%s\n",
1067 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1068 print_usage ();
1069 printf ("NOTE: One or both of -H and -I must be specified\n");
1070 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1071 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1072 #ifdef HAVE_SSL
1073 printf (SSLDESCRIPTION);
1074 #endif
1075 }
1078 void
1079 print_usage (void)
1080 {
1081 printf ("Usage:\n" " %s %s\n"
1082 #ifdef HAVE_GETOPT_H
1083 " %s (-h | --help) for detailed help\n"
1084 " %s (-V | --version) for version information\n",
1085 #else
1086 " %s -h for detailed help\n"
1087 " %s -V for version information\n",
1088 #endif
1089 PROGNAME, OPTIONS, PROGNAME, PROGNAME);
1090 }