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