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