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