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]"
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 -m, --min=INTEGER\n\
79 Minimum page size required (bytes)\n\
80 -v, --verbose\n\
81 Show details for command-line debugging (do not use with nagios server)\n\
82 -h, --help\n\
83 Print detailed help screen\n\
84 -V, --version\n\
85 Print version information\n"
87 #ifdef HAVE_SSL
88 #define SSLOPTIONS "\
89 -S, --ssl\n\
90 Connect via SSL\n\
91 -C, --certificate=INTEGER\n\
92 Minimum number of days a certificate has to be valid.\n\
93 (when this option is used the url is not checked.)\n"
94 #else
95 #define SSLOPTIONS ""
96 #endif
98 #ifdef HAVE_REGEX_H
99 #define REGOPTIONS "\
100 -l, --linespan\n\
101 Allow regex to span newlines (must precede -r or -R)\n\
102 -r, --regex, --ereg=STRING\n\
103 Search page for regex STRING\n\
104 -R, --eregi=STRING\n\
105 Search page for case-insensitive regex STRING\n"
106 #else
107 #define REGOPTIONS ""
108 #endif
110 #define DESCRIPTION "\
111 This plugin will attempt to open an HTTP connection with the host. Successul\n\
112 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
113 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
114 messages from the host result in STATE_WARNING return values. If you are\n\
115 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
116 \(fully qualified domain name) as the [host_name] argument.\n"
118 #define SSLDESCRIPTION "\
119 This plugin can also check whether an SSL enabled web server is able to\n\
120 serve content (optionally within a specified time) or whether the X509 \n\
121 certificate is still valid for the specified number of days.\n\n\
122 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
123 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
124 STATE_OK will be returned. When the server returns its content but exceeds\n\
125 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
126 a STATE_CRITICAL will be returned.\n\n\
127 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
128 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
129 STATE_OK is returned. When the certificate is still valid, but for less than\n\
130 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
131 the certificate is expired.\n"
133 #ifdef HAVE_SSL_H
134 #include <rsa.h>
135 #include <crypto.h>
136 #include <x509.h>
137 #include <pem.h>
138 #include <ssl.h>
139 #include <err.h>
140 #include <rand.h>
141 #endif
143 #ifdef HAVE_OPENSSL_SSL_H
144 #include <openssl/rsa.h>
145 #include <openssl/crypto.h>
146 #include <openssl/x509.h>
147 #include <openssl/pem.h>
148 #include <openssl/ssl.h>
149 #include <openssl/err.h>
150 #include <openssl/rand.h>
151 #endif
153 #ifdef HAVE_SSL
154 int check_cert = FALSE;
155 int days_till_exp;
156 char *randbuff = "";
157 SSL_CTX *ctx;
158 SSL *ssl;
159 X509 *server_cert;
160 int connect_SSL (void);
161 int check_certificate (X509 **);
162 #endif
164 #ifdef HAVE_REGEX_H
165 enum {
166 REGS = 2,
167 MAX_RE_SIZE = 256
168 };
169 #include <regex.h>
170 regex_t preg;
171 regmatch_t pmatch[REGS];
172 char regexp[MAX_RE_SIZE];
173 char errbuf[MAX_INPUT_BUFFER];
174 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
175 int errcode;
176 #endif
178 struct timeval tv;
180 #define server_type_check(server_type) \
181 (strcmp (server_type, "https") ? FALSE : TRUE)
183 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
185 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
186 #define URI_HTTP "%[HTPShtps]://"
187 #define URI_HOST "%[a-zA-Z0-9.-]"
188 #define URI_PORT ":%[0-9]"
189 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
191 enum {
192 MAX_IPV4_HOSTLENGTH = 64,
193 HTTP_PORT = 80,
194 HTTPS_PORT = 443
195 };
197 #define HTTP_EXPECT "HTTP/1."
198 #define HTTP_URL "/"
199 #define CRLF "\r\n"
201 char timestamp[17] = "";
202 int specify_port = FALSE;
203 int server_port = HTTP_PORT;
204 char server_port_text[6] = "";
205 char server_type[6] = "http";
206 char *server_address = "";
207 char *host_name = "";
208 char *server_url = "";
209 int server_url_length;
210 int server_expect_yn = 0;
211 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
212 char string_expect[MAX_INPUT_BUFFER] = "";
213 double warning_time = 0;
214 int check_warning_time = FALSE;
215 double critical_time = 0;
216 int check_critical_time = FALSE;
217 char user_auth[MAX_INPUT_BUFFER] = "";
218 int display_html = FALSE;
219 int onredirect = STATE_OK;
220 int use_ssl = FALSE;
221 int verbose = FALSE;
222 int sd;
223 int min_page_len = 0;
224 char *http_method = "GET";
225 char *http_post_data = "";
226 char buffer[MAX_INPUT_BUFFER];
228 void print_usage (void);
229 void print_help (void);
230 int process_arguments (int, char **);
231 static char *base64 (char *bin, int len);
232 int check_http (void);
233 int my_recv (void);
234 int my_close (void);
236 int
237 main (int argc, char **argv)
238 {
239 int result = STATE_UNKNOWN;
241 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
242 asprintf (&server_url, "%s", HTTP_URL);
243 server_url_length = strlen(server_url);
245 if (process_arguments (argc, argv) == ERROR)
246 usage ("check_http: could not parse arguments\n");
248 if (strstr (timestamp, ":")) {
249 if (strstr (server_url, "?"))
250 asprintf (&server_url, "%s&%s", server_url, timestamp);
251 else
252 asprintf (&server_url, "%s?%s", server_url, timestamp);
253 }
255 if (display_html == TRUE)
256 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
257 host_name, server_port, server_url);
259 /* initialize alarm signal handling, set socket timeout, start timer */
260 (void) signal (SIGALRM, socket_timeout_alarm_handler);
261 (void) alarm (socket_timeout);
262 gettimeofday (&tv, NULL);
264 #ifdef HAVE_SSL
265 if (use_ssl && check_cert == TRUE) {
266 if (connect_SSL () != OK)
267 terminate (STATE_CRITICAL,
268 "HTTP CRITICAL - Could not make SSL connection\n");
269 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
270 result = check_certificate (&server_cert);
271 X509_free (server_cert);
272 }
273 else {
274 printf ("ERROR: Cannot retrieve server certificate.\n");
275 result = STATE_CRITICAL;
276 }
277 SSL_shutdown (ssl);
278 SSL_free (ssl);
279 SSL_CTX_free (ctx);
280 close (sd);
281 }
282 else {
283 result = check_http ();
284 }
285 #else
286 result = check_http ();
287 #endif
288 return result;
289 }
290 \f
293 /* process command-line arguments */
294 int
295 process_arguments (int argc, char **argv)
296 {
297 int c = 1;
299 #ifdef HAVE_GETOPT_H
300 int option_index = 0;
301 static struct option long_options[] = {
302 STD_LONG_OPTS,
303 {"file",required_argument,0,'F'},
304 {"link", no_argument, 0, 'L'},
305 {"nohtml", no_argument, 0, 'n'},
306 {"ssl", no_argument, 0, 'S'},
307 {"verbose", no_argument, 0, 'v'},
308 {"post", required_argument, 0, 'P'},
309 {"IP-address", required_argument, 0, 'I'},
310 {"string", required_argument, 0, 's'},
311 {"regex", required_argument, 0, 'r'},
312 {"ereg", required_argument, 0, 'r'},
313 {"eregi", required_argument, 0, 'R'},
314 {"linespan", no_argument, 0, 'l'},
315 {"onredirect", required_argument, 0, 'f'},
316 {"certificate", required_argument, 0, 'C'},
317 {"min", required_argument, 0, 'm'},
318 {0, 0, 0, 0}
319 };
320 #endif
322 if (argc < 2)
323 return ERROR;
325 for (c = 1; c < argc; c++) {
326 if (strcmp ("-to", argv[c]) == 0)
327 strcpy (argv[c], "-t");
328 if (strcmp ("-hn", argv[c]) == 0)
329 strcpy (argv[c], "-H");
330 if (strcmp ("-wt", argv[c]) == 0)
331 strcpy (argv[c], "-w");
332 if (strcmp ("-ct", argv[c]) == 0)
333 strcpy (argv[c], "-c");
334 if (strcmp ("-nohtml", argv[c]) == 0)
335 strcpy (argv[c], "-n");
336 }
338 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:"
340 while (1) {
341 #ifdef HAVE_GETOPT_H
342 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
343 #else
344 c = getopt (argc, argv, OPTCHARS);
345 #endif
346 if (c == -1 || c == EOF)
347 break;
349 switch (c) {
350 case '?': /* usage */
351 usage2 ("unknown argument", optarg);
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 'v': /* verbose */
474 verbose = TRUE;
475 break;
476 case 'm': /* min_page_length */
477 min_page_len = atoi (optarg);
478 break;
479 }
480 }
482 c = optind;
484 if (strcmp (server_address, "") == 0 && c < argc)
485 asprintf (&server_address, "%s", argv[c++]);
487 if (strcmp (host_name, "") == 0 && c < argc)
488 asprintf (&host_name, "%s", argv[c++]);
490 if (strcmp (server_address ,"") == 0) {
491 if (strcmp (host_name, "") == 0)
492 usage ("check_http: you must specify a server address or host name\n");
493 else
494 asprintf (&server_address, "%s", host_name);
495 }
497 return TRUE;
498 }
499 \f
502 /* written by lauri alanko */
503 static char *
504 base64 (char *bin, int len)
505 {
507 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
508 int i = 0, j = 0;
510 char BASE64_END = '=';
511 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
513 while (j < len - 2) {
514 buf[i++] = base64_table[bin[j] >> 2];
515 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
516 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
517 buf[i++] = base64_table[bin[j + 2] & 63];
518 j += 3;
519 }
521 switch (len - j) {
522 case 1:
523 buf[i++] = base64_table[bin[j] >> 2];
524 buf[i++] = base64_table[(bin[j] & 3) << 4];
525 buf[i++] = BASE64_END;
526 buf[i++] = BASE64_END;
527 break;
528 case 2:
529 buf[i++] = base64_table[bin[j] >> 2];
530 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
531 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
532 buf[i++] = BASE64_END;
533 break;
534 case 0:
535 break;
536 }
538 buf[i] = '\0';
539 return buf;
540 }
541 \f
544 int
545 check_http (void)
546 {
547 char *msg = NULL;
548 char *status_line = "";
549 char *header = NULL;
550 char *page = "";
551 char *auth = NULL;
552 int i = 0;
553 size_t pagesize = 0;
554 char *full_page = "";
555 char *buf = NULL;
556 char *pos = "";
557 char *x = NULL;
558 char *orig_url = NULL;
559 double elapsed_time;
560 int page_len = 0;
561 #ifdef HAVE_SSL
562 int sslerr;
563 #endif
565 /* try to connect to the host at the given port number */
566 #ifdef HAVE_SSL
567 if (use_ssl == TRUE) {
569 if (connect_SSL () != OK) {
570 terminate (STATE_CRITICAL, "Unable to open TCP socket");
571 }
573 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
574 X509_free (server_cert);
575 }
576 else {
577 printf ("ERROR: Cannot retrieve server certificate.\n");
578 return STATE_CRITICAL;
579 }
581 }
582 else {
583 #endif
584 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
585 terminate (STATE_CRITICAL, "Unable to open TCP socket");
586 #ifdef HAVE_SSL
587 }
588 #endif
590 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
592 /* optionally send the host header info (not clear if it's usable) */
593 if (strcmp (host_name, ""))
594 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
596 /* send user agent */
597 asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
598 buf, clean_revstring (REVISION), PACKAGE_VERSION);
600 /* optionally send the authentication info */
601 if (strcmp (user_auth, "")) {
602 auth = base64 (user_auth, strlen (user_auth));
603 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
604 }
606 /* either send http POST data */
607 if (strlen (http_post_data)) {
608 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
609 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
610 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
611 }
612 else {
613 /* or just a newline so the server knows we're done with the request */
614 asprintf (&buf, "%s%s", buf, CRLF);
615 }
617 #ifdef HAVE_SSL
618 if (use_ssl == TRUE) {
619 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
620 ERR_print_errors_fp (stderr);
621 return STATE_CRITICAL;
622 }
623 }
624 else {
625 #endif
626 send (sd, buf, strlen (buf), 0);
627 #ifdef HAVE_SSL
628 }
629 #endif
631 /* fetch the page */
632 while ((i = my_recv ()) > 0) {
633 buffer[i] = '\0';
634 asprintf (&full_page, "%s%s", full_page, buffer);
635 pagesize += i;
636 }
638 if (i < 0) {
639 #ifdef HAVE_SSL
640 sslerr=SSL_get_error(ssl, i);
641 if ( sslerr == SSL_ERROR_SSL ) {
642 terminate (STATE_WARNING, "Client Certificate Required\n");
643 } else {
644 terminate (STATE_CRITICAL, "Error in recv()");
645 }
646 #else
647 terminate (STATE_CRITICAL, "Error in recv()");
648 #endif
649 }
651 /* return a CRITICAL status if we couldn't read any data */
652 if (pagesize == (size_t) 0)
653 terminate (STATE_CRITICAL, "No data received %s", timestamp);
655 /* close the connection */
656 my_close ();
658 /* reset the alarm */
659 alarm (0);
661 /* leave full_page untouched so we can free it later */
662 page = full_page;
664 if (verbose)
665 printf ("Page is %d characters\n", pagesize);
667 /* find status line and null-terminate it */
668 status_line = page;
669 page += (size_t) strcspn (page, "\r\n");
670 pos = page;
671 page += (size_t) strspn (page, "\r\n");
672 status_line[strcspn(status_line, "\r\n")] = 0;
673 strip (status_line);
674 if (verbose)
675 printf ("STATUS: %s\n", status_line);
677 /* find header info and null terminate it */
678 header = page;
679 while (strcspn (page, "\r\n") > 0) {
680 page += (size_t) strcspn (page, "\r\n");
681 pos = page;
682 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
683 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
684 page += (size_t) 2;
685 else
686 page += (size_t) 1;
687 }
688 page += (size_t) strspn (page, "\r\n");
689 header[pos - header] = 0;
690 if (verbose)
691 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
693 /* make sure the status line matches the response we are looking for */
694 if (!strstr (status_line, server_expect)) {
695 if (server_port == HTTP_PORT)
696 asprintf (&msg, "Invalid HTTP response received from host\n");
697 else
698 asprintf (&msg,
699 "Invalid HTTP response received from host on port %d\n",
700 server_port);
701 terminate (STATE_CRITICAL, msg);
702 }
705 /* Exit here if server_expect was set by user and not default */
706 if ( server_expect_yn ) {
707 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
708 server_expect);
709 if (verbose)
710 printf ("%s\n",msg);
712 }
713 else {
716 /* check the return code */
717 /* server errors result in a critical state */
718 if (strstr (status_line, "500") ||
719 strstr (status_line, "501") ||
720 strstr (status_line, "502") ||
721 strstr (status_line, "503")) {
722 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
723 }
725 /* client errors result in a warning state */
726 if (strstr (status_line, "400") ||
727 strstr (status_line, "401") ||
728 strstr (status_line, "402") ||
729 strstr (status_line, "403") ||
730 strstr (status_line, "404")) {
731 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
732 }
734 /* check redirected page if specified */
735 if (strstr (status_line, "300") ||
736 strstr (status_line, "301") ||
737 strstr (status_line, "302") ||
738 strstr (status_line, "303") ||
739 strstr (status_line, "304")) {
740 if (onredirect == STATE_DEPENDENT) {
742 asprintf (&orig_url, "%s", server_url);
743 pos = header;
744 while (pos) {
745 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
746 if (server_address == NULL)
747 terminate (STATE_UNKNOWN,
748 "HTTP UNKNOWN: could not allocate server_address");
749 if (strcspn (pos, "\r\n") > server_url_length) {
750 server_url = realloc (server_url, strcspn (pos, "\r\n"));
751 if (server_url == NULL)
752 terminate (STATE_UNKNOWN,
753 "HTTP UNKNOWN: could not allocate server_url");
754 server_url_length = strcspn (pos, "\r\n");
755 }
756 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
757 asprintf (&host_name, "%s", server_address);
758 use_ssl = server_type_check (server_type);
759 server_port = atoi (server_port_text);
760 check_http ();
761 }
762 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
763 asprintf (&host_name, "%s", server_address);
764 use_ssl = server_type_check (server_type);
765 server_port = server_port_check (use_ssl);
766 check_http ();
767 }
768 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
769 asprintf (&host_name, "%s", server_address);
770 strcpy (server_url, "/");
771 use_ssl = server_type_check (server_type);
772 server_port = atoi (server_port_text);
773 check_http ();
774 }
775 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
776 asprintf (&host_name, "%s", server_address);
777 strcpy (server_url, "/");
778 use_ssl = server_type_check (server_type);
779 server_port = server_port_check (use_ssl);
780 check_http ();
781 }
782 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
783 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
784 *x = '\0';
785 asprintf (&server_url, "%s/%s", orig_url, server_url);
786 }
787 check_http ();
788 }
789 pos += (size_t) strcspn (pos, "\r\n");
790 pos += (size_t) strspn (pos, "\r\n");
791 } /* end while (pos) */
792 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
793 status_line, (display_html ? "</A>" : ""));
794 exit (STATE_UNKNOWN);
795 } /* end if (onredirect == STATE_DEPENDENT) */
797 else if (onredirect == STATE_UNKNOWN)
798 printf ("HTTP UNKNOWN");
799 else if (onredirect == STATE_OK)
800 printf ("HTTP ok");
801 else if (onredirect == STATE_WARNING)
802 printf ("HTTP WARNING");
803 else if (onredirect == STATE_CRITICAL)
804 printf ("HTTP CRITICAL");
805 elapsed_time = delta_time (tv);
806 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
807 status_line, elapsed_time, timestamp,
808 (display_html ? "</A>" : ""), elapsed_time);
809 terminate (onredirect, msg);
810 } /* end if (strstr (status_line, "30[0-4]") */
813 } /* end else (server_expect_yn) */
816 /* check elapsed time */
817 elapsed_time = delta_time (tv);
818 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
819 status_line, elapsed_time, timestamp,
820 (display_html ? "</A>" : ""), elapsed_time);
821 if (check_critical_time == TRUE && elapsed_time > critical_time)
822 terminate (STATE_CRITICAL, msg);
823 if (check_warning_time == TRUE && elapsed_time > warning_time)
824 terminate (STATE_WARNING, msg);
826 /* Page and Header content checks go here */
827 /* these checks should be last */
829 if (strlen (string_expect)) {
830 if (strstr (page, string_expect)) {
831 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
832 status_line, elapsed_time,
833 timestamp, (display_html ? "</A>" : ""), elapsed_time);
834 exit (STATE_OK);
835 }
836 else {
837 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
838 (display_html ? "</A>" : ""), elapsed_time);
839 exit (STATE_CRITICAL);
840 }
841 }
842 #ifdef HAVE_REGEX_H
843 if (strlen (regexp)) {
844 errcode = regexec (&preg, page, REGS, pmatch, 0);
845 if (errcode == 0) {
846 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
847 status_line, elapsed_time,
848 timestamp, (display_html ? "</A>" : ""), elapsed_time);
849 exit (STATE_OK);
850 }
851 else {
852 if (errcode == REG_NOMATCH) {
853 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
854 (display_html ? "</A>" : ""), elapsed_time);
855 exit (STATE_CRITICAL);
856 }
857 else {
858 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
859 printf ("Execute Error: %s\n", errbuf);
860 exit (STATE_CRITICAL);
861 }
862 }
863 }
864 #endif
866 /* make sure the page is of an appropriate size */
867 page_len = strlen (page);
868 if ((min_page_len > 0) && (page_len < min_page_len)) {
869 printf ("HTTP WARNING: page size too small%s|size=%i\n",
870 (display_html ? "</A>" : ""), page_len );
871 exit (STATE_WARNING);
872 }
873 /* We only get here if all tests have been passed */
874 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
875 status_line, (float)elapsed_time,
876 timestamp, (display_html ? "</A>" : ""), elapsed_time);
877 terminate (STATE_OK, msg);
878 return STATE_UNKNOWN;
879 }
883 #ifdef HAVE_SSL
884 int connect_SSL (void)
885 {
886 SSL_METHOD *meth;
888 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
889 RAND_seed (randbuff, strlen (randbuff));
890 if (verbose)
891 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
893 /* Initialize SSL context */
894 SSLeay_add_ssl_algorithms ();
895 meth = SSLv23_client_method ();
896 SSL_load_error_strings ();
897 if ((ctx = SSL_CTX_new (meth)) == NULL) {
898 printf ("ERROR: Cannot create SSL context.\n");
899 return STATE_CRITICAL;
900 }
902 /* Initialize alarm signal handling */
903 signal (SIGALRM, socket_timeout_alarm_handler);
905 /* Set socket timeout */
906 alarm (socket_timeout);
908 /* Save start time */
909 gettimeofday (&tv, NULL);
911 /* Make TCP connection */
912 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
913 /* Do the SSL handshake */
914 if ((ssl = SSL_new (ctx)) != NULL) {
915 SSL_set_cipher_list(ssl, "ALL");
916 SSL_set_fd (ssl, sd);
917 if (SSL_connect (ssl) != -1)
918 return OK;
919 ERR_print_errors_fp (stderr);
920 }
921 else {
922 printf ("ERROR: Cannot initiate SSL handshake.\n");
923 }
924 SSL_free (ssl);
925 }
927 SSL_CTX_free (ctx);
928 close (sd);
930 return STATE_CRITICAL;
931 }
932 #endif
934 #ifdef HAVE_SSL
935 int
936 check_certificate (X509 ** certificate)
937 {
938 ASN1_STRING *tm;
939 int offset;
940 struct tm stamp;
941 int days_left;
944 /* Retrieve timestamp of certificate */
945 tm = X509_get_notAfter (*certificate);
947 /* Generate tm structure to process timestamp */
948 if (tm->type == V_ASN1_UTCTIME) {
949 if (tm->length < 10) {
950 printf ("ERROR: Wrong time format in certificate.\n");
951 return STATE_CRITICAL;
952 }
953 else {
954 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
955 if (stamp.tm_year < 50)
956 stamp.tm_year += 100;
957 offset = 0;
958 }
959 }
960 else {
961 if (tm->length < 12) {
962 printf ("ERROR: Wrong time format in certificate.\n");
963 return STATE_CRITICAL;
964 }
965 else {
966 stamp.tm_year =
967 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
968 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
969 stamp.tm_year -= 1900;
970 offset = 2;
971 }
972 }
973 stamp.tm_mon =
974 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
975 stamp.tm_mday =
976 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
977 stamp.tm_hour =
978 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
979 stamp.tm_min =
980 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
981 stamp.tm_sec = 0;
982 stamp.tm_isdst = -1;
984 days_left = (mktime (&stamp) - time (NULL)) / 86400;
985 snprintf
986 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
987 stamp.tm_mon + 1,
988 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
990 if (days_left > 0 && days_left <= days_till_exp) {
991 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
992 return STATE_WARNING;
993 }
994 if (days_left < 0) {
995 printf ("Certificate expired on %s.\n", timestamp);
996 return STATE_CRITICAL;
997 }
999 if (days_left == 0) {
1000 printf ("Certificate expires today (%s).\n", timestamp);
1001 return STATE_WARNING;
1002 }
1004 printf ("Certificate will expire on %s.\n", timestamp);
1006 return STATE_OK;
1007 }
1008 #endif
1009 \f
1012 int
1013 my_recv (void)
1014 {
1015 int i;
1016 #ifdef HAVE_SSL
1017 if (use_ssl) {
1018 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1019 }
1020 else {
1021 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1022 }
1023 #else
1024 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1025 #endif
1026 return i;
1027 }
1030 int
1031 my_close (void)
1032 {
1033 #ifdef HAVE_SSL
1034 if (use_ssl == TRUE) {
1035 SSL_shutdown (ssl);
1036 SSL_free (ssl);
1037 SSL_CTX_free (ctx);
1038 return 0;
1039 }
1040 else {
1041 #endif
1042 return close (sd);
1043 #ifdef HAVE_SSL
1044 }
1045 #endif
1046 }
1047 \f
1050 void
1051 print_help (void)
1052 {
1053 print_revision (progname, REVISION);
1054 printf
1055 ("Copyright (c) %s %s <%s>\n\n%s\n",
1056 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1057 print_usage ();
1058 printf ("NOTE: One or both of -H and -I must be specified\n");
1059 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1060 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1061 #ifdef HAVE_SSL
1062 printf (SSLDESCRIPTION);
1063 #endif
1064 }
1067 void
1068 print_usage (void)
1069 {
1070 printf ("Usage:\n" " %s %s\n"
1071 #ifdef HAVE_GETOPT_H
1072 " %s (-h | --help) for detailed help\n"
1073 " %s (-V | --version) for version information\n",
1074 #else
1075 " %s -h for detailed help\n"
1076 " %s -V for version information\n",
1077 #endif
1078 progname, OPTIONS, progname, progname);
1079 }