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