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;
544 #ifdef HAVE_SSL
545 int sslerr;
546 #endif
548 /* try to connect to the host at the given port number */
549 #ifdef HAVE_SSL
550 if (use_ssl == TRUE) {
552 if (connect_SSL () != OK)
553 terminate (STATE_CRITICAL, "Unable to open TCP socket");
555 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
556 X509_free (server_cert);
557 }
558 else {
559 printf ("ERROR: Cannot retrieve server certificate.\n");
560 return STATE_CRITICAL;
561 }
563 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
564 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
565 ERR_print_errors_fp (stderr);
566 return STATE_CRITICAL;
567 }
569 /* optionally send the host header info (not clear if it's usable) */
570 if (strcmp (host_name, "")) {
571 asprintf (&buf, "Host: %s\r\n", host_name);
572 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
573 ERR_print_errors_fp (stderr);
574 return STATE_CRITICAL;
575 }
576 }
578 /* send user agent */
579 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
580 clean_revstring (REVISION), PACKAGE_VERSION);
581 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
582 ERR_print_errors_fp (stderr);
583 return STATE_CRITICAL;
584 }
586 /* optionally send the authentication info */
587 if (strcmp (user_auth, "")) {
588 auth = base64 (user_auth, strlen (user_auth));
589 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
590 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
591 ERR_print_errors_fp (stderr);
592 return STATE_CRITICAL;
593 }
594 }
596 /* either send http POST data */
597 if (strlen (http_post_data)) {
598 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
599 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
600 ERR_print_errors_fp (stderr);
601 return STATE_CRITICAL;
602 }
603 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
604 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
605 ERR_print_errors_fp (stderr);
606 return STATE_CRITICAL;
607 }
608 if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
609 ERR_print_errors_fp (stderr);
610 return STATE_CRITICAL;
611 }
612 asprintf (&buf, CRLF);
613 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
614 ERR_print_errors_fp (stderr);
615 return STATE_CRITICAL;
616 }
617 }
618 else {
619 /* or just a newline so the server knows we're done with the request */
620 asprintf (&buf, "\r\n");
621 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
622 ERR_print_errors_fp (stderr);
623 return STATE_CRITICAL;
624 }
625 }
627 }
628 else {
629 #endif
630 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
631 terminate (STATE_CRITICAL, "Unable to open TCP socket");
632 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
633 send (sd, buf, strlen (buf), 0);
635 /* optionally send the host header info */
636 if (strcmp (host_name, "")) {
637 asprintf (&buf, "Host: %s\r\n", host_name);
638 send (sd, buf, strlen (buf), 0);
639 }
641 /* send user agent */
642 asprintf (&buf,
643 "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
644 clean_revstring (REVISION), PACKAGE_VERSION);
645 send (sd, buf, strlen (buf), 0);
647 /* optionally send the authentication info */
648 if (strcmp (user_auth, "")) {
649 auth = base64 (user_auth, strlen (user_auth));
650 asprintf (&buf, "Authorization: Basic %s\r\n", auth);
651 send (sd, buf, strlen (buf), 0);
652 }
654 /* either send http POST data */
655 /* written by Chris Henesy <lurker@shadowtech.org> */
656 if (strlen (http_post_data)) {
657 asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
658 send (sd, buf, strlen (buf), 0);
659 asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
660 send (sd, buf, strlen (buf), 0);
661 send (sd, http_post_data, strlen (http_post_data), 0);
662 send (sd, CRLF, strlen (CRLF), 0);
663 }
664 else {
665 /* or just a newline so the server knows we're done with the request */
666 asprintf (&buf, "\r\n");
667 send (sd, buf, strlen (buf), 0);
668 }
669 #ifdef HAVE_SSL
670 }
671 #endif
673 /* fetch the page */
674 while ((i = my_recv ()) > 0) {
675 buffer[i] = '\0';
676 asprintf (&full_page, "%s%s", full_page, buffer);
677 pagesize += i;
678 }
680 if (i < 0) {
681 #ifdef HAVE_SSL
682 sslerr=SSL_get_error(ssl, i);
683 if ( sslerr == SSL_ERROR_SSL ) {
684 terminate (STATE_WARNING, "Client Certificate Required\n");
685 } else {
686 terminate (STATE_CRITICAL, "Error in recv()");
687 }
688 #else
689 terminate (STATE_CRITICAL, "Error in recv()");
690 #endif
691 }
693 /* return a CRITICAL status if we couldn't read any data */
694 if (pagesize == (size_t) 0)
695 terminate (STATE_CRITICAL, "No data received %s", timestamp);
697 /* close the connection */
698 my_close ();
700 /* reset the alarm */
701 alarm (0);
703 /* leave full_page untouched so we can free it later */
704 page = full_page;
706 if (verbose)
707 printf ("Page is %d characters\n", pagesize);
709 /* find status line and null-terminate it */
710 status_line = page;
711 page += (size_t) strcspn (page, "\r\n");
712 pos = page;
713 page += (size_t) strspn (page, "\r\n");
714 status_line[strcspn(status_line, "\r\n")] = 0;
715 strip (status_line);
716 if (verbose)
717 printf ("STATUS: %s\n", status_line);
719 /* find header info and null terminate it */
720 header = page;
721 while (strcspn (page, "\r\n") > 0) {
722 page += (size_t) strcspn (page, "\r\n");
723 pos = page;
724 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
725 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
726 page += (size_t) 2;
727 else
728 page += (size_t) 1;
729 }
730 page += (size_t) strspn (page, "\r\n");
731 header[pos - header] = 0;
732 if (verbose)
733 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
735 /* make sure the status line matches the response we are looking for */
736 if (!strstr (status_line, server_expect)) {
737 if (server_port == HTTP_PORT)
738 asprintf (&msg, "Invalid HTTP response received from host\n");
739 else
740 asprintf (&msg,
741 "Invalid HTTP response received from host on port %d\n",
742 server_port);
743 terminate (STATE_CRITICAL, msg);
744 }
747 /* Exit here if server_expect was set by user and not default */
748 if ( server_expect_yn ) {
749 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
750 server_expect);
751 if (verbose)
752 printf ("%s\n",msg);
754 }
755 else {
758 /* check the return code */
759 /* server errors result in a critical state */
760 if (strstr (status_line, "500") ||
761 strstr (status_line, "501") ||
762 strstr (status_line, "502") ||
763 strstr (status_line, "503")) {
764 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
765 }
767 /* client errors result in a warning state */
768 if (strstr (status_line, "400") ||
769 strstr (status_line, "401") ||
770 strstr (status_line, "402") ||
771 strstr (status_line, "403") ||
772 strstr (status_line, "404")) {
773 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
774 }
776 /* check redirected page if specified */
777 if (strstr (status_line, "300") ||
778 strstr (status_line, "301") ||
779 strstr (status_line, "302") ||
780 strstr (status_line, "303") ||
781 strstr (status_line, "304")) {
782 if (onredirect == STATE_DEPENDENT) {
784 asprintf (&orig_url, "%s", server_url);
785 pos = header;
786 while (pos) {
787 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
788 if (server_address == NULL)
789 terminate (STATE_UNKNOWN,
790 "HTTP UNKNOWN: could not allocate server_address");
791 if (strcspn (pos, "\r\n") > server_url_length) {
792 server_url = realloc (server_url, strcspn (pos, "\r\n"));
793 if (server_url == NULL)
794 terminate (STATE_UNKNOWN,
795 "HTTP UNKNOWN: could not allocate server_url");
796 server_url_length = strcspn (pos, "\r\n");
797 }
798 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
799 asprintf (&host_name, "%s", server_address);
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 URI_PATH, server_type, server_address, server_url) == 3 ) {
805 asprintf (&host_name, "%s", server_address);
806 use_ssl = server_type_check (server_type);
807 server_port = server_port_check (use_ssl);
808 check_http ();
809 }
810 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
811 asprintf (&host_name, "%s", server_address);
812 strcpy (server_url, "/");
813 use_ssl = server_type_check (server_type);
814 server_port = atoi (server_port_text);
815 check_http ();
816 }
817 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
818 asprintf (&host_name, "%s", server_address);
819 strcpy (server_url, "/");
820 use_ssl = server_type_check (server_type);
821 server_port = server_port_check (use_ssl);
822 check_http ();
823 }
824 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
825 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
826 *x = '\0';
827 asprintf (&server_url, "%s/%s", orig_url, server_url);
828 }
829 check_http ();
830 }
831 pos += (size_t) strcspn (pos, "\r\n");
832 pos += (size_t) strspn (pos, "\r\n");
833 } /* end while (pos) */
834 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
835 status_line, (display_html ? "</A>" : ""));
836 exit (STATE_UNKNOWN);
837 } /* end if (onredirect == STATE_DEPENDENT) */
839 else if (onredirect == STATE_UNKNOWN)
840 printf ("HTTP UNKNOWN");
841 else if (onredirect == STATE_OK)
842 printf ("HTTP ok");
843 else if (onredirect == STATE_WARNING)
844 printf ("HTTP WARNING");
845 else if (onredirect == STATE_CRITICAL)
846 printf ("HTTP CRITICAL");
847 elapsed_time = delta_time (tv);
848 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
849 status_line, elapsed_time, timestamp,
850 (display_html ? "</A>" : ""), elapsed_time);
851 terminate (onredirect, msg);
852 } /* end if (strstr (status_line, "30[0-4]") */
855 } /* end else (server_expect_yn) */
858 /* check elapsed time */
859 elapsed_time = delta_time (tv);
860 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
861 status_line, elapsed_time, timestamp,
862 (display_html ? "</A>" : ""), elapsed_time);
863 if (check_critical_time == TRUE && elapsed_time > critical_time)
864 terminate (STATE_CRITICAL, msg);
865 if (check_warning_time == TRUE && elapsed_time > warning_time)
866 terminate (STATE_WARNING, msg);
868 /* Page and Header content checks go here */
869 /* these checks should be last */
871 if (strlen (string_expect)) {
872 if (strstr (page, string_expect)) {
873 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
874 status_line, elapsed_time,
875 timestamp, (display_html ? "</A>" : ""), elapsed_time);
876 exit (STATE_OK);
877 }
878 else {
879 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
880 (display_html ? "</A>" : ""), elapsed_time);
881 exit (STATE_CRITICAL);
882 }
883 }
884 #ifdef HAVE_REGEX_H
885 if (strlen (regexp)) {
886 errcode = regexec (&preg, page, REGS, pmatch, 0);
887 if (errcode == 0) {
888 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
889 status_line, elapsed_time,
890 timestamp, (display_html ? "</A>" : ""), elapsed_time);
891 exit (STATE_OK);
892 }
893 else {
894 if (errcode == REG_NOMATCH) {
895 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
896 (display_html ? "</A>" : ""), elapsed_time);
897 exit (STATE_CRITICAL);
898 }
899 else {
900 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
901 printf ("Execute Error: %s\n", errbuf);
902 exit (STATE_CRITICAL);
903 }
904 }
905 }
906 #endif
908 /* We only get here if all tests have been passed */
909 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
910 status_line, (float)elapsed_time,
911 timestamp, (display_html ? "</A>" : ""), elapsed_time);
912 terminate (STATE_OK, msg);
913 return STATE_UNKNOWN;
914 }
918 #ifdef HAVE_SSL
919 int connect_SSL (void)
920 {
921 SSL_METHOD *meth;
923 asprintf (randbuff, "%s", "qwertyuiopasdfghjkl");
924 RAND_seed (randbuff, strlen (randbuff));
925 /* Initialize SSL context */
926 SSLeay_add_ssl_algorithms ();
927 meth = SSLv23_client_method ();
928 SSL_load_error_strings ();
929 if ((ctx = SSL_CTX_new (meth)) == NULL) {
930 printf ("ERROR: Cannot create SSL context.\n");
931 return STATE_CRITICAL;
932 }
934 /* Initialize alarm signal handling */
935 signal (SIGALRM, socket_timeout_alarm_handler);
937 /* Set socket timeout */
938 alarm (socket_timeout);
940 /* Save start time */
941 gettimeofday (&tv, NULL);
943 /* Make TCP connection */
944 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
945 /* Do the SSL handshake */
946 if ((ssl = SSL_new (ctx)) != NULL) {
947 SSL_set_cipher_list(ssl, "ALL");
948 SSL_set_fd (ssl, sd);
949 if (SSL_connect (ssl) != -1)
950 return OK;
951 ERR_print_errors_fp (stderr);
952 }
953 else {
954 printf ("ERROR: Cannot initiate SSL handshake.\n");
955 }
956 SSL_free (ssl);
957 }
959 SSL_CTX_free (ctx);
960 close (sd);
962 return STATE_CRITICAL;
963 }
964 #endif
966 #ifdef HAVE_SSL
967 int
968 check_certificate (X509 ** certificate)
969 {
970 ASN1_STRING *tm;
971 int offset;
972 struct tm stamp;
973 int days_left;
976 /* Retrieve timestamp of certificate */
977 tm = X509_get_notAfter (*certificate);
979 /* Generate tm structure to process timestamp */
980 if (tm->type == V_ASN1_UTCTIME) {
981 if (tm->length < 10) {
982 printf ("ERROR: Wrong time format in certificate.\n");
983 return STATE_CRITICAL;
984 }
985 else {
986 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
987 if (stamp.tm_year < 50)
988 stamp.tm_year += 100;
989 offset = 0;
990 }
991 }
992 else {
993 if (tm->length < 12) {
994 printf ("ERROR: Wrong time format in certificate.\n");
995 return STATE_CRITICAL;
996 }
997 else {
998 stamp.tm_year =
999 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1000 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1001 stamp.tm_year -= 1900;
1002 offset = 2;
1003 }
1004 }
1005 stamp.tm_mon =
1006 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1007 stamp.tm_mday =
1008 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1009 stamp.tm_hour =
1010 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1011 stamp.tm_min =
1012 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1013 stamp.tm_sec = 0;
1014 stamp.tm_isdst = -1;
1016 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1017 snprintf
1018 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1019 stamp.tm_mon + 1,
1020 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1022 if (days_left > 0 && days_left <= days_till_exp) {
1023 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1024 return STATE_WARNING;
1025 }
1026 if (days_left < 0) {
1027 printf ("Certificate expired on %s.\n", timestamp);
1028 return STATE_CRITICAL;
1029 }
1031 if (days_left == 0) {
1032 printf ("Certificate expires today (%s).\n", timestamp);
1033 return STATE_WARNING;
1034 }
1036 printf ("Certificate will expire on %s.\n", timestamp);
1038 return STATE_OK;
1039 }
1040 #endif
1041 \f
1044 int
1045 my_recv (void)
1046 {
1047 int i;
1048 #ifdef HAVE_SSL
1049 if (use_ssl) {
1050 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1051 }
1052 else {
1053 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1054 }
1055 #else
1056 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1057 #endif
1058 return i;
1059 }
1062 int
1063 my_close (void)
1064 {
1065 #ifdef HAVE_SSL
1066 if (use_ssl == TRUE) {
1067 SSL_shutdown (ssl);
1068 SSL_free (ssl);
1069 SSL_CTX_free (ctx);
1070 return 0;
1071 }
1072 else {
1073 #endif
1074 return close (sd);
1075 #ifdef HAVE_SSL
1076 }
1077 #endif
1078 }
1079 \f
1082 void
1083 print_help (void)
1084 {
1085 print_revision (progname, REVISION);
1086 printf
1087 ("Copyright (c) %s %s <%s>\n\n%s\n",
1088 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1089 print_usage ();
1090 printf ("NOTE: One or both of -H and -I must be specified\n");
1091 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1092 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1093 #ifdef HAVE_SSL
1094 printf (SSLDESCRIPTION);
1095 #endif
1096 }
1099 void
1100 print_usage (void)
1101 {
1102 printf ("Usage:\n" " %s %s\n"
1103 #ifdef HAVE_GETOPT_H
1104 " %s (-h | --help) for detailed help\n"
1105 " %s (-V | --version) for version information\n",
1106 #else
1107 " %s -h for detailed help\n"
1108 " %s -V for version information\n",
1109 #endif
1110 progname, OPTIONS, progname, progname);
1111 }