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 const char *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] [-l] [-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%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 #ifdef HAVE_REGEX_H
97 #define REGOPTIONS "\
98 -l, --linespan\n\
99 Allow regex to span newlines (must precede -r or -R)\n\
100 -r, --regex, --ereg=STRING\n\
101 Search page for regex STRING\n\
102 -R, --eregi=STRING\n\
103 Search page for case-insensitive regex STRING\n"
104 #else
105 #define REGOPTIONS ""
106 #endif
108 #define DESCRIPTION "\
109 This plugin will attempt to open an HTTP connection with the host. Successul\n\
110 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
111 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
112 messages from the host result in STATE_WARNING return values. If you are\n\
113 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
114 \(fully qualified domain name) as the [host_name] argument.\n"
116 #define SSLDESCRIPTION "\
117 This plugin can also check whether an SSL enabled web server is able to\n\
118 serve content (optionally within a specified time) or whether the X509 \n\
119 certificate is still valid for the specified number of days.\n\n\
120 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
121 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
122 STATE_OK will be returned. When the server returns its content but exceeds\n\
123 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
124 a STATE_CRITICAL will be returned.\n\n\
125 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
126 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
127 STATE_OK is returned. When the certificate is still valid, but for less than\n\
128 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
129 the certificate is expired.\n"
131 #ifdef HAVE_SSL_H
132 #include <rsa.h>
133 #include <crypto.h>
134 #include <x509.h>
135 #include <pem.h>
136 #include <ssl.h>
137 #include <err.h>
138 #include <rand.h>
139 #endif
141 #ifdef HAVE_OPENSSL_SSL_H
142 #include <openssl/rsa.h>
143 #include <openssl/crypto.h>
144 #include <openssl/x509.h>
145 #include <openssl/pem.h>
146 #include <openssl/ssl.h>
147 #include <openssl/err.h>
148 #include <openssl/rand.h>
149 #endif
151 #ifdef HAVE_SSL
152 int check_cert = FALSE;
153 int days_till_exp;
154 unsigned char *randbuff;
155 SSL_CTX *ctx;
156 SSL *ssl;
157 X509 *server_cert;
158 int connect_SSL (void);
159 int check_certificate (X509 **);
160 #endif
162 #ifdef HAVE_REGEX_H
163 #define REGS 2
164 #define MAX_RE_SIZE 256
165 #include <regex.h>
166 regex_t preg;
167 regmatch_t pmatch[REGS];
168 char regexp[MAX_RE_SIZE];
169 char errbuf[MAX_INPUT_BUFFER];
170 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
171 int errcode;
172 #endif
174 struct timeval tv;
176 #define server_type_check(server_type) \
177 (strcmp (server_type, "https") ? FALSE : TRUE)
179 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
181 #define MAX_IPV4_HOSTLENGTH 64
182 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
183 #define URI_HTTP "%[HTPShtps]://"
184 #define URI_HOST "%[a-zA-Z0-9.-]"
185 #define URI_PORT ":%[0-9]"
186 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
188 #define HTTP_PORT 80
189 #define HTTPS_PORT 443
190 #define HTTP_EXPECT "HTTP/1."
191 #define HTTP_URL "/"
192 #define CRLF "\r\n"
194 char timestamp[17] = "";
195 int specify_port = FALSE;
196 int server_port = HTTP_PORT;
197 char server_port_text[6] = "";
198 char server_type[6] = "http";
199 char *server_address = "";
200 char *host_name = "";
201 char *server_url = HTTP_URL;
202 int server_url_length = 1;
203 int server_expect_yn = 0;
204 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
205 char string_expect[MAX_INPUT_BUFFER] = "";
206 double warning_time = 0;
207 int check_warning_time = FALSE;
208 double critical_time = 0;
209 int check_critical_time = FALSE;
210 char user_auth[MAX_INPUT_BUFFER] = "";
211 int display_html = FALSE;
212 int onredirect = STATE_OK;
213 int use_ssl = FALSE;
214 int verbose = FALSE;
215 int sd;
216 char *http_method = "GET";
217 char *http_post_data = "";
218 char buffer[MAX_INPUT_BUFFER];
220 void print_usage (void);
221 void print_help (void);
222 int process_arguments (int, char **);
223 static char *base64 (char *bin, int len);
224 int check_http (void);
225 int my_recv (void);
226 int my_close (void);
228 int
229 main (int argc, char **argv)
230 {
231 int result = STATE_UNKNOWN;
233 if (process_arguments (argc, argv) == ERROR)
234 usage ("check_http: could not parse arguments\n");
236 if (strstr (timestamp, ":")) {
237 if (strstr (server_url, "?"))
238 asprintf (&server_url, "%s&%s", server_url, timestamp);
239 else
240 asprintf (&server_url, "%s?%s", server_url, timestamp);
241 }
243 if (display_html == TRUE)
244 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
245 host_name, server_port, server_url);
247 /* initialize alarm signal handling, set socket timeout, start timer */
248 (void) signal (SIGALRM, socket_timeout_alarm_handler);
249 (void) alarm (socket_timeout);
250 gettimeofday (&tv, NULL);
252 #ifdef HAVE_SSL
253 if (use_ssl && check_cert == TRUE) {
254 if (connect_SSL () != OK)
255 terminate (STATE_CRITICAL,
256 "HTTP CRITICAL - Could not make SSL connection\n");
257 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
258 result = check_certificate (&server_cert);
259 X509_free (server_cert);
260 }
261 else {
262 printf ("ERROR: Cannot retrieve server certificate.\n");
263 result = STATE_CRITICAL;
264 }
265 SSL_shutdown (ssl);
266 SSL_free (ssl);
267 SSL_CTX_free (ctx);
268 close (sd);
269 }
270 else {
271 result = check_http ();
272 }
273 #else
274 result = check_http ();
275 #endif
276 return result;
277 }
278 \f
281 /* process command-line arguments */
282 int
283 process_arguments (int argc, char **argv)
284 {
285 int c = 1;
287 #ifdef HAVE_GETOPT_H
288 int option_index = 0;
289 static struct option long_options[] = {
290 STD_LONG_OPTS,
291 {"file",required_argument,0,'F'},
292 {"link", no_argument, 0, 'L'},
293 {"nohtml", no_argument, 0, 'n'},
294 {"ssl", no_argument, 0, 'S'},
295 {"verbose", no_argument, 0, 'v'},
296 {"post", required_argument, 0, 'P'},
297 {"IP-address", required_argument, 0, 'I'},
298 {"string", required_argument, 0, 's'},
299 {"regex", required_argument, 0, 'r'},
300 {"ereg", required_argument, 0, 'r'},
301 {"eregi", required_argument, 0, 'R'},
302 {"linespan", no_argument, 0, 'l'},
303 {"onredirect", required_argument, 0, 'f'},
304 {"certificate", required_argument, 0, 'C'},
305 {0, 0, 0, 0}
306 };
307 #endif
309 if (argc < 2)
310 return ERROR;
312 for (c = 1; c < argc; c++) {
313 if (strcmp ("-to", argv[c]) == 0)
314 strcpy (argv[c], "-t");
315 if (strcmp ("-hn", argv[c]) == 0)
316 strcpy (argv[c], "-H");
317 if (strcmp ("-wt", argv[c]) == 0)
318 strcpy (argv[c], "-w");
319 if (strcmp ("-ct", argv[c]) == 0)
320 strcpy (argv[c], "-c");
321 if (strcmp ("-nohtml", argv[c]) == 0)
322 strcpy (argv[c], "-n");
323 }
325 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
327 while (1) {
328 #ifdef HAVE_GETOPT_H
329 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
330 #else
331 c = getopt (argc, argv, OPTCHARS);
332 #endif
333 if (c == -1 || c == EOF)
334 break;
336 switch (c) {
337 case '?': /* usage */
338 usage2 ("unknown argument", optarg);
339 break;
340 case 'h': /* help */
341 print_help ();
342 exit (STATE_OK);
343 break;
344 case 'V': /* version */
345 print_revision (progname, REVISION);
346 exit (STATE_OK);
347 break;
348 case 't': /* timeout period */
349 if (!is_intnonneg (optarg))
350 usage2 ("timeout interval must be a non-negative integer", optarg);
351 socket_timeout = atoi (optarg);
352 break;
353 case 'c': /* critical time threshold */
354 if (!is_intnonneg (optarg))
355 usage2 ("invalid critical threshold", optarg);
356 critical_time = strtod (optarg, NULL);
357 check_critical_time = TRUE;
358 break;
359 case 'w': /* warning time threshold */
360 if (!is_intnonneg (optarg))
361 usage2 ("invalid warning threshold", optarg);
362 warning_time = strtod (optarg, NULL);
363 check_warning_time = TRUE;
364 break;
365 case 'L': /* show html link */
366 display_html = TRUE;
367 break;
368 case 'n': /* do not show html link */
369 display_html = FALSE;
370 break;
371 case 'S': /* use SSL */
372 #ifndef HAVE_SSL
373 usage ("check_http: invalid option - SSL is not available\n");
374 #endif
375 use_ssl = TRUE;
376 if (specify_port == FALSE)
377 server_port = HTTPS_PORT;
378 break;
379 case 'C': /* Check SSL cert validity */
380 #ifdef HAVE_SSL
381 if (!is_intnonneg (optarg))
382 usage2 ("invalid certificate expiration period", optarg);
383 days_till_exp = atoi (optarg);
384 check_cert = TRUE;
385 #else
386 usage ("check_http: invalid option - SSL is not available\n");
387 #endif
388 break;
389 case 'f': /* onredirect */
390 if (!strcmp (optarg, "follow"))
391 onredirect = STATE_DEPENDENT;
392 if (!strcmp (optarg, "unknown"))
393 onredirect = STATE_UNKNOWN;
394 if (!strcmp (optarg, "ok"))
395 onredirect = STATE_OK;
396 if (!strcmp (optarg, "warning"))
397 onredirect = STATE_WARNING;
398 if (!strcmp (optarg, "critical"))
399 onredirect = STATE_CRITICAL;
400 if (verbose)
401 printf("option f:%d \n", onredirect);
402 break;
403 /* Note: H, I, and u must be malloc'd or will fail on redirects */
404 case 'H': /* Host Name (virtual host) */
405 asprintf (&host_name, "%s", optarg);
406 break;
407 case 'I': /* Server IP-address */
408 asprintf (&server_address, "%s", optarg);
409 break;
410 case 'u': /* Host or server */
411 asprintf (&server_url, "%s", optarg);
412 server_url_length = strlen (server_url);
413 break;
414 case 'p': /* Host or server */
415 if (!is_intnonneg (optarg))
416 usage2 ("invalid port number", optarg);
417 server_port = atoi (optarg);
418 specify_port = TRUE;
419 break;
420 case 'a': /* authorization info */
421 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
422 user_auth[MAX_INPUT_BUFFER - 1] = 0;
423 break;
424 case 'P': /* HTTP POST data in URL encoded format */
425 asprintf (&http_method, "%s", "POST");
426 asprintf (&http_post_data, "%s", optarg);
427 break;
428 case 's': /* string or substring */
429 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
430 string_expect[MAX_INPUT_BUFFER - 1] = 0;
431 break;
432 case 'e': /* string or substring */
433 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
434 server_expect[MAX_INPUT_BUFFER - 1] = 0;
435 server_expect_yn = 1;
436 break;
437 #ifndef HAVE_REGEX_H
438 case 'l': /* linespan */
439 case 'r': /* linespan */
440 case 'R': /* linespan */
441 usage ("check_http: call for regex which was not a compiled option\n");
442 break;
443 #else
444 case 'l': /* linespan */
445 cflags &= ~REG_NEWLINE;
446 break;
447 case 'R': /* regex */
448 cflags |= REG_ICASE;
449 case 'r': /* regex */
450 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
451 regexp[MAX_RE_SIZE - 1] = 0;
452 errcode = regcomp (&preg, regexp, cflags);
453 if (errcode != 0) {
454 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
455 printf ("Could Not Compile Regular Expression: %s", errbuf);
456 return ERROR;
457 }
458 break;
459 #endif
460 case 'v': /* verbose */
461 verbose = TRUE;
462 break;
463 }
464 }
466 c = optind;
468 if (strcmp (server_address, "") == 0 && c < argc)
469 asprintf (&server_address, "%s", argv[c++]);
471 if (strcmp (host_name, "") == 0 && c < argc)
472 asprintf (&host_name, "%s", argv[c++]);
474 if (strcmp (server_address ,"") == 0) {
475 if (strcmp (host_name, "") == 0)
476 usage ("check_http: you must specify a server address or host name\n");
477 else
478 asprintf (&server_address, "%s", host_name);
479 }
481 return TRUE;
482 }
483 \f
486 /* written by lauri alanko */
487 static char *
488 base64 (char *bin, int len)
489 {
491 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
492 int i = 0, j = 0;
494 char BASE64_END = '=';
495 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
497 while (j < len - 2) {
498 buf[i++] = base64_table[bin[j] >> 2];
499 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
500 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
501 buf[i++] = base64_table[bin[j + 2] & 63];
502 j += 3;
503 }
505 switch (len - j) {
506 case 1:
507 buf[i++] = base64_table[bin[j] >> 2];
508 buf[i++] = base64_table[(bin[j] & 3) << 4];
509 buf[i++] = BASE64_END;
510 buf[i++] = BASE64_END;
511 break;
512 case 2:
513 buf[i++] = base64_table[bin[j] >> 2];
514 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
515 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
516 buf[i++] = BASE64_END;
517 break;
518 case 0:
519 break;
520 }
522 buf[i] = '\0';
523 return buf;
524 }
525 \f
528 int
529 check_http (void)
530 {
531 char *msg = NULL;
532 char *status_line = "";
533 char *header = NULL;
534 char *page = "";
535 char *auth = NULL;
536 int i = 0;
537 size_t pagesize = 0;
538 char *full_page = "";
539 char *buf = NULL;
540 char *pos = "";
541 char *x = NULL;
542 char *orig_url = NULL;
543 double elapsed_time;
545 /* try to connect to the host at the given port number */
546 #ifdef HAVE_SSL
547 if (use_ssl == TRUE) {
549 if (connect_SSL () != OK)
550 terminate (STATE_CRITICAL, "Unable to open TCP socket");
552 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
553 X509_free (server_cert);
554 }
555 else {
556 printf ("ERROR: Cannot retrieve server certificate.\n");
557 return STATE_CRITICAL;
558 }
560 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
561 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
562 ERR_print_errors_fp (stderr);
563 return STATE_CRITICAL;
564 }
566 /* optionally send the host header info (not clear if it's usable) */
567 if (strcmp (host_name, "")) {
568 asprintf (&buf, "Host: %s\r\n", host_name);
569 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
570 ERR_print_errors_fp (stderr);
571 return STATE_CRITICAL;
572 }
573 }
575 /* send user agent */
576 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
577 clean_revstring (REVISION), PACKAGE_VERSION);
578 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
579 ERR_print_errors_fp (stderr);
580 return STATE_CRITICAL;
581 }
583 /* optionally send the authentication info */
584 if (strcmp (user_auth, "")) {
585 auth = base64 (user_auth, strlen (user_auth));
586 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
587 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
588 ERR_print_errors_fp (stderr);
589 return STATE_CRITICAL;
590 }
591 }
593 /* either send http POST data */
594 if (strlen (http_post_data)) {
595 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
596 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
597 ERR_print_errors_fp (stderr);
598 return STATE_CRITICAL;
599 }
600 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
601 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
602 ERR_print_errors_fp (stderr);
603 return STATE_CRITICAL;
604 }
605 if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
606 ERR_print_errors_fp (stderr);
607 return STATE_CRITICAL;
608 }
609 asprintf (&buf, CRLF);
610 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
611 ERR_print_errors_fp (stderr);
612 return STATE_CRITICAL;
613 }
614 }
615 else {
616 /* or just a newline so the server knows we're done with the request */
617 asprintf (&buf, "\r\n");
618 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
619 ERR_print_errors_fp (stderr);
620 return STATE_CRITICAL;
621 }
622 }
624 }
625 else {
626 #endif
627 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
628 terminate (STATE_CRITICAL, "Unable to open TCP socket");
629 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
630 send (sd, buf, strlen (buf), 0);
632 /* optionally send the host header info */
633 if (strcmp (host_name, "")) {
634 asprintf (&buf, "Host: %s\r\n", host_name);
635 send (sd, buf, strlen (buf), 0);
636 }
638 /* send user agent */
639 asprintf (&buf,
640 "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
641 clean_revstring (REVISION), PACKAGE_VERSION);
642 send (sd, buf, strlen (buf), 0);
644 /* optionally send the authentication info */
645 if (strcmp (user_auth, "")) {
646 auth = base64 (user_auth, strlen (user_auth));
647 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
648 send (sd, buf, strlen (buf), 0);
649 }
651 /* either send http POST data */
652 /* written by Chris Henesy <lurker@shadowtech.org> */
653 if (strlen (http_post_data)) {
654 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
655 send (sd, buf, strlen (buf), 0);
656 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
657 send (sd, buf, strlen (buf), 0);
658 send (sd, http_post_data, strlen (http_post_data), 0);
659 send (sd, CRLF, strlen (CRLF), 0);
660 }
661 else {
662 /* or just a newline so the server knows we're done with the request */
663 asprintf (&buf, "\r\n");
664 send (sd, buf, strlen (buf), 0);
665 }
666 #ifdef HAVE_SSL
667 }
668 #endif
670 /* fetch the page */
671 while ((i = my_recv ()) > 0) {
672 buffer[i] = '\0';
673 asprintf (&full_page, "%s%s", full_page, buffer);
674 pagesize += i;
675 }
677 if (i < 0)
678 terminate (STATE_CRITICAL, "Error in recv()");
680 /* return a CRITICAL status if we couldn't read any data */
681 if (pagesize == (size_t) 0)
682 terminate (STATE_CRITICAL, "No data received %s", timestamp);
684 /* close the connection */
685 my_close ();
687 /* reset the alarm */
688 alarm (0);
690 /* leave full_page untouched so we can free it later */
691 page = full_page;
693 if (verbose)
694 printf ("Page is %d characters\n", pagesize);
696 /* find status line and null-terminate it */
697 status_line = page;
698 page += (size_t) strcspn (page, "\r\n");
699 pos = page;
700 page += (size_t) strspn (page, "\r\n");
701 status_line[strcspn(status_line, "\r\n")] = 0;
702 strip (status_line);
703 if (verbose)
704 printf ("STATUS: %s\n", status_line);
706 /* find header info and null terminate it */
707 header = page;
708 while (strcspn (page, "\r\n") > 0) {
709 page += (size_t) strcspn (page, "\r\n");
710 pos = page;
711 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
712 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
713 page += (size_t) 2;
714 else
715 page += (size_t) 1;
716 }
717 page += (size_t) strspn (page, "\r\n");
718 header[pos - header] = 0;
719 if (verbose)
720 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
722 /* make sure the status line matches the response we are looking for */
723 if (!strstr (status_line, server_expect)) {
724 if (server_port == HTTP_PORT)
725 asprintf (&msg, "Invalid HTTP response received from host\n");
726 else
727 asprintf (&msg,
728 "Invalid HTTP response received from host on port %d\n",
729 server_port);
730 terminate (STATE_CRITICAL, msg);
731 }
734 /* Exit here if server_expect was set by user and not default */
735 if ( server_expect_yn ) {
736 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
737 server_expect);
738 if (verbose)
739 printf ("%s\n",msg);
741 }
742 else {
745 /* check the return code */
746 /* server errors result in a critical state */
747 if (strstr (status_line, "500") ||
748 strstr (status_line, "501") ||
749 strstr (status_line, "502") ||
750 strstr (status_line, "503")) {
751 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
752 }
754 /* client errors result in a warning state */
755 if (strstr (status_line, "400") ||
756 strstr (status_line, "401") ||
757 strstr (status_line, "402") ||
758 strstr (status_line, "403") ||
759 strstr (status_line, "404")) {
760 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
761 }
763 /* check redirected page if specified */
764 if (strstr (status_line, "300") ||
765 strstr (status_line, "301") ||
766 strstr (status_line, "302") ||
767 strstr (status_line, "303") ||
768 strstr (status_line, "304")) {
769 if (onredirect == STATE_DEPENDENT) {
771 asprintf (&orig_url, "%s", server_url);
772 pos = header;
773 while (pos) {
774 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
775 if (server_address == NULL)
776 terminate (STATE_UNKNOWN,
777 "HTTP UNKNOWN: could not allocate server_address");
778 if (strcspn (pos, "\r\n") > server_url_length) {
779 server_url = realloc (server_url, strcspn (pos, "\r\n"));
780 if (server_url == NULL)
781 terminate (STATE_UNKNOWN,
782 "HTTP UNKNOWN: could not allocate server_url");
783 server_url_length = strcspn (pos, "\r\n");
784 }
785 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
786 asprintf (&host_name, "%s", server_address);
787 use_ssl = server_type_check (server_type);
788 server_port = atoi (server_port_text);
789 check_http ();
790 }
791 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
792 asprintf (&host_name, "%s", server_address);
793 use_ssl = server_type_check (server_type);
794 server_port = server_port_check (use_ssl);
795 check_http ();
796 }
797 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
798 asprintf (&host_name, "%s", server_address);
799 strcpy (server_url, "/");
800 use_ssl = server_type_check (server_type);
801 server_port = atoi (server_port_text);
802 check_http ();
803 }
804 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
805 asprintf (&host_name, "%s", server_address);
806 strcpy (server_url, "/");
807 use_ssl = server_type_check (server_type);
808 server_port = server_port_check (use_ssl);
809 check_http ();
810 }
811 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
812 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
813 *x = '\0';
814 asprintf (&server_url, "%s/%s", orig_url, server_url);
815 }
816 check_http ();
817 }
818 pos += (size_t) strcspn (pos, "\r\n");
819 pos += (size_t) strspn (pos, "\r\n");
820 } /* end while (pos) */
821 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
822 status_line, (display_html ? "</A>" : ""));
823 exit (STATE_UNKNOWN);
824 } /* end if (onredirect == STATE_DEPENDENT) */
826 else if (onredirect == STATE_UNKNOWN)
827 printf ("HTTP UNKNOWN");
828 else if (onredirect == STATE_OK)
829 printf ("HTTP ok");
830 else if (onredirect == STATE_WARNING)
831 printf ("HTTP WARNING");
832 else if (onredirect == STATE_CRITICAL)
833 printf ("HTTP CRITICAL");
834 elapsed_time = delta_time (tv);
835 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
836 status_line, elapsed_time, timestamp,
837 (display_html ? "</A>" : ""), elapsed_time);
838 terminate (onredirect, msg);
839 } /* end if (strstr (status_line, "30[0-4]") */
842 } /* end else (server_expect_yn) */
845 /* check elapsed time */
846 elapsed_time = delta_time (tv);
847 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
848 status_line, elapsed_time, timestamp,
849 (display_html ? "</A>" : ""), elapsed_time);
850 if (check_critical_time == TRUE && elapsed_time > critical_time)
851 terminate (STATE_CRITICAL, msg);
852 if (check_warning_time == TRUE && elapsed_time > warning_time)
853 terminate (STATE_WARNING, msg);
855 /* Page and Header content checks go here */
856 /* these checks should be last */
858 if (strlen (string_expect)) {
859 if (strstr (page, string_expect)) {
860 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
861 status_line, elapsed_time,
862 timestamp, (display_html ? "</A>" : ""), elapsed_time);
863 exit (STATE_OK);
864 }
865 else {
866 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
867 (display_html ? "</A>" : ""), elapsed_time);
868 exit (STATE_CRITICAL);
869 }
870 }
871 #ifdef HAVE_REGEX_H
872 if (strlen (regexp)) {
873 errcode = regexec (&preg, page, REGS, pmatch, 0);
874 if (errcode == 0) {
875 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
876 status_line, elapsed_time,
877 timestamp, (display_html ? "</A>" : ""), elapsed_time);
878 exit (STATE_OK);
879 }
880 else {
881 if (errcode == REG_NOMATCH) {
882 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
883 (display_html ? "</A>" : ""), elapsed_time);
884 exit (STATE_CRITICAL);
885 }
886 else {
887 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
888 printf ("Execute Error: %s\n", errbuf);
889 exit (STATE_CRITICAL);
890 }
891 }
892 }
893 #endif
895 /* We only get here if all tests have been passed */
896 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
897 status_line, (float)elapsed_time,
898 timestamp, (display_html ? "</A>" : ""), elapsed_time);
899 terminate (STATE_OK, msg);
900 return STATE_UNKNOWN;
901 }
905 #ifdef HAVE_SSL
906 int connect_SSL (void)
907 {
908 SSL_METHOD *meth;
910 asprintf (randbuff, "%s", "qwertyuiopasdfghjkl");
911 RAND_seed (randbuff, strlen (randbuff));
912 /* Initialize SSL context */
913 SSLeay_add_ssl_algorithms ();
914 meth = SSLv23_client_method ();
915 SSL_load_error_strings ();
916 if ((ctx = SSL_CTX_new (meth)) == NULL) {
917 printf ("ERROR: Cannot create SSL context.\n");
918 return STATE_CRITICAL;
919 }
921 /* Initialize alarm signal handling */
922 signal (SIGALRM, socket_timeout_alarm_handler);
924 /* Set socket timeout */
925 alarm (socket_timeout);
927 /* Save start time */
928 gettimeofday (&tv, NULL);
930 /* Make TCP connection */
931 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
932 /* Do the SSL handshake */
933 if ((ssl = SSL_new (ctx)) != NULL) {
934 SSL_set_cipher_list(ssl, "ALL");
935 SSL_set_fd (ssl, sd);
936 if (SSL_connect (ssl) != -1)
937 return OK;
938 ERR_print_errors_fp (stderr);
939 }
940 else {
941 printf ("ERROR: Cannot initiate SSL handshake.\n");
942 }
943 SSL_free (ssl);
944 }
946 SSL_CTX_free (ctx);
947 close (sd);
949 return STATE_CRITICAL;
950 }
951 #endif
953 #ifdef HAVE_SSL
954 int
955 check_certificate (X509 ** certificate)
956 {
957 ASN1_STRING *tm;
958 int offset;
959 struct tm stamp;
960 int days_left;
963 /* Retrieve timestamp of certificate */
964 tm = X509_get_notAfter (*certificate);
966 /* Generate tm structure to process timestamp */
967 if (tm->type == V_ASN1_UTCTIME) {
968 if (tm->length < 10) {
969 printf ("ERROR: Wrong time format in certificate.\n");
970 return STATE_CRITICAL;
971 }
972 else {
973 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
974 if (stamp.tm_year < 50)
975 stamp.tm_year += 100;
976 offset = 0;
977 }
978 }
979 else {
980 if (tm->length < 12) {
981 printf ("ERROR: Wrong time format in certificate.\n");
982 return STATE_CRITICAL;
983 }
984 else {
985 stamp.tm_year =
986 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
987 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
988 stamp.tm_year -= 1900;
989 offset = 2;
990 }
991 }
992 stamp.tm_mon =
993 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
994 stamp.tm_mday =
995 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
996 stamp.tm_hour =
997 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
998 stamp.tm_min =
999 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1000 stamp.tm_sec = 0;
1001 stamp.tm_isdst = -1;
1003 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1004 snprintf
1005 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1006 stamp.tm_mon + 1,
1007 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1009 if (days_left > 0 && days_left <= days_till_exp) {
1010 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1011 return STATE_WARNING;
1012 }
1013 if (days_left < 0) {
1014 printf ("Certificate expired on %s.\n", timestamp);
1015 return STATE_CRITICAL;
1016 }
1018 if (days_left == 0) {
1019 printf ("Certificate expires today (%s).\n", timestamp);
1020 return STATE_WARNING;
1021 }
1023 printf ("Certificate will expire on %s.\n", timestamp);
1025 return STATE_OK;
1026 }
1027 #endif
1028 \f
1031 int
1032 my_recv (void)
1033 {
1034 int i;
1035 #ifdef HAVE_SSL
1036 if (use_ssl) {
1037 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1038 }
1039 else {
1040 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1041 }
1042 #else
1043 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1044 #endif
1045 return i;
1046 }
1049 int
1050 my_close (void)
1051 {
1052 #ifdef HAVE_SSL
1053 if (use_ssl == TRUE) {
1054 SSL_shutdown (ssl);
1055 SSL_free (ssl);
1056 SSL_CTX_free (ctx);
1057 return 0;
1058 }
1059 else {
1060 #endif
1061 return close (sd);
1062 #ifdef HAVE_SSL
1063 }
1064 #endif
1065 }
1066 \f
1069 void
1070 print_help (void)
1071 {
1072 print_revision (progname, REVISION);
1073 printf
1074 ("Copyright (c) %s %s <%s>\n\n%s\n",
1075 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1076 print_usage ();
1077 printf ("NOTE: One or both of -H and -I must be specified\n");
1078 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1079 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1080 #ifdef HAVE_SSL
1081 printf (SSLDESCRIPTION);
1082 #endif
1083 }
1086 void
1087 print_usage (void)
1088 {
1089 printf ("Usage:\n" " %s %s\n"
1090 #ifdef HAVE_GETOPT_H
1091 " %s (-h | --help) for detailed help\n"
1092 " %s (-V | --version) for version information\n",
1093 #else
1094 " %s -h for detailed help\n"
1095 " %s -V for version information\n",
1096 #endif
1097 progname, OPTIONS, progname, progname);
1098 }