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