c947bd8723b8569d78b2673fd135ba6cf2a4d001
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 int option_index = 0;
300 static struct option long_options[] = {
301 STD_LONG_OPTS,
302 {"file",required_argument,0,'F'},
303 {"link", no_argument, 0, 'L'},
304 {"nohtml", no_argument, 0, 'n'},
305 {"ssl", no_argument, 0, 'S'},
306 {"verbose", no_argument, 0, 'v'},
307 {"post", required_argument, 0, 'P'},
308 {"IP-address", required_argument, 0, 'I'},
309 {"string", required_argument, 0, 's'},
310 {"regex", required_argument, 0, 'r'},
311 {"ereg", required_argument, 0, 'r'},
312 {"eregi", required_argument, 0, 'R'},
313 {"linespan", no_argument, 0, 'l'},
314 {"onredirect", required_argument, 0, 'f'},
315 {"certificate", required_argument, 0, 'C'},
316 {"min", required_argument, 0, 'm'},
317 {0, 0, 0, 0}
318 };
320 if (argc < 2)
321 return ERROR;
323 for (c = 1; c < argc; c++) {
324 if (strcmp ("-to", argv[c]) == 0)
325 strcpy (argv[c], "-t");
326 if (strcmp ("-hn", argv[c]) == 0)
327 strcpy (argv[c], "-H");
328 if (strcmp ("-wt", argv[c]) == 0)
329 strcpy (argv[c], "-w");
330 if (strcmp ("-ct", argv[c]) == 0)
331 strcpy (argv[c], "-c");
332 if (strcmp ("-nohtml", argv[c]) == 0)
333 strcpy (argv[c], "-n");
334 }
336 while (1) {
337 c = getopt_long (argc, argv, "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", long_options, &option_index);
338 if (c == -1 || c == EOF)
339 break;
341 switch (c) {
342 case '?': /* usage */
343 usage2 ("unknown argument", optarg);
344 break;
345 case 'h': /* help */
346 print_help ();
347 exit (STATE_OK);
348 break;
349 case 'V': /* version */
350 print_revision (progname, REVISION);
351 exit (STATE_OK);
352 break;
353 case 't': /* timeout period */
354 if (!is_intnonneg (optarg))
355 usage2 ("timeout interval must be a non-negative integer", optarg);
356 socket_timeout = atoi (optarg);
357 break;
358 case 'c': /* critical time threshold */
359 if (!is_intnonneg (optarg))
360 usage2 ("invalid critical threshold", optarg);
361 critical_time = strtod (optarg, NULL);
362 check_critical_time = TRUE;
363 break;
364 case 'w': /* warning time threshold */
365 if (!is_intnonneg (optarg))
366 usage2 ("invalid warning threshold", optarg);
367 warning_time = strtod (optarg, NULL);
368 check_warning_time = TRUE;
369 break;
370 case 'L': /* show html link */
371 display_html = TRUE;
372 break;
373 case 'n': /* do not show html link */
374 display_html = FALSE;
375 break;
376 case 'S': /* use SSL */
377 #ifndef HAVE_SSL
378 usage ("check_http: invalid option - SSL is not available\n");
379 #endif
380 use_ssl = TRUE;
381 if (specify_port == FALSE)
382 server_port = HTTPS_PORT;
383 break;
384 case 'C': /* Check SSL cert validity */
385 #ifdef HAVE_SSL
386 if (!is_intnonneg (optarg))
387 usage2 ("invalid certificate expiration period", optarg);
388 days_till_exp = atoi (optarg);
389 check_cert = TRUE;
390 #else
391 usage ("check_http: invalid option - SSL is not available\n");
392 #endif
393 break;
394 case 'f': /* onredirect */
395 if (!strcmp (optarg, "follow"))
396 onredirect = STATE_DEPENDENT;
397 if (!strcmp (optarg, "unknown"))
398 onredirect = STATE_UNKNOWN;
399 if (!strcmp (optarg, "ok"))
400 onredirect = STATE_OK;
401 if (!strcmp (optarg, "warning"))
402 onredirect = STATE_WARNING;
403 if (!strcmp (optarg, "critical"))
404 onredirect = STATE_CRITICAL;
405 if (verbose)
406 printf("option f:%d \n", onredirect);
407 break;
408 /* Note: H, I, and u must be malloc'd or will fail on redirects */
409 case 'H': /* Host Name (virtual host) */
410 asprintf (&host_name, "%s", optarg);
411 break;
412 case 'I': /* Server IP-address */
413 asprintf (&server_address, "%s", optarg);
414 break;
415 case 'u': /* Host or server */
416 asprintf (&server_url, "%s", optarg);
417 server_url_length = strlen (server_url);
418 break;
419 case 'p': /* Host or server */
420 if (!is_intnonneg (optarg))
421 usage2 ("invalid port number", optarg);
422 server_port = atoi (optarg);
423 specify_port = TRUE;
424 break;
425 case 'a': /* authorization info */
426 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
427 user_auth[MAX_INPUT_BUFFER - 1] = 0;
428 break;
429 case 'P': /* HTTP POST data in URL encoded format */
430 asprintf (&http_method, "%s", "POST");
431 asprintf (&http_post_data, "%s", optarg);
432 break;
433 case 's': /* string or substring */
434 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
435 string_expect[MAX_INPUT_BUFFER - 1] = 0;
436 break;
437 case 'e': /* string or substring */
438 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
439 server_expect[MAX_INPUT_BUFFER - 1] = 0;
440 server_expect_yn = 1;
441 break;
442 #ifndef HAVE_REGEX_H
443 case 'l': /* linespan */
444 case 'r': /* linespan */
445 case 'R': /* linespan */
446 usage ("check_http: call for regex which was not a compiled option\n");
447 break;
448 #else
449 case 'l': /* linespan */
450 cflags &= ~REG_NEWLINE;
451 break;
452 case 'R': /* regex */
453 cflags |= REG_ICASE;
454 case 'r': /* regex */
455 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
456 regexp[MAX_RE_SIZE - 1] = 0;
457 errcode = regcomp (&preg, regexp, cflags);
458 if (errcode != 0) {
459 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
460 printf ("Could Not Compile Regular Expression: %s", errbuf);
461 return ERROR;
462 }
463 break;
464 #endif
465 case 'v': /* verbose */
466 verbose = TRUE;
467 break;
468 case 'm': /* min_page_length */
469 min_page_len = atoi (optarg);
470 break;
471 }
472 }
474 c = optind;
476 if (strcmp (server_address, "") == 0 && c < argc)
477 asprintf (&server_address, "%s", argv[c++]);
479 if (strcmp (host_name, "") == 0 && c < argc)
480 asprintf (&host_name, "%s", argv[c++]);
482 if (strcmp (server_address ,"") == 0) {
483 if (strcmp (host_name, "") == 0)
484 usage ("check_http: you must specify a server address or host name\n");
485 else
486 asprintf (&server_address, "%s", host_name);
487 }
489 return TRUE;
490 }
491 \f
494 /* written by lauri alanko */
495 static char *
496 base64 (char *bin, int len)
497 {
499 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
500 int i = 0, j = 0;
502 char BASE64_END = '=';
503 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
505 while (j < len - 2) {
506 buf[i++] = base64_table[bin[j] >> 2];
507 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
508 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
509 buf[i++] = base64_table[bin[j + 2] & 63];
510 j += 3;
511 }
513 switch (len - j) {
514 case 1:
515 buf[i++] = base64_table[bin[j] >> 2];
516 buf[i++] = base64_table[(bin[j] & 3) << 4];
517 buf[i++] = BASE64_END;
518 buf[i++] = BASE64_END;
519 break;
520 case 2:
521 buf[i++] = base64_table[bin[j] >> 2];
522 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
523 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
524 buf[i++] = BASE64_END;
525 break;
526 case 0:
527 break;
528 }
530 buf[i] = '\0';
531 return buf;
532 }
533 \f
536 int
537 check_http (void)
538 {
539 char *msg = NULL;
540 char *status_line = "";
541 char *header = NULL;
542 char *page = "";
543 char *auth = NULL;
544 int i = 0;
545 size_t pagesize = 0;
546 char *full_page = "";
547 char *buf = NULL;
548 char *pos = "";
549 char *x = NULL;
550 char *orig_url = NULL;
551 double elapsed_time;
552 int page_len = 0;
553 #ifdef HAVE_SSL
554 int sslerr;
555 #endif
557 /* try to connect to the host at the given port number */
558 #ifdef HAVE_SSL
559 if (use_ssl == TRUE) {
561 if (connect_SSL () != OK) {
562 terminate (STATE_CRITICAL, "Unable to open TCP socket");
563 }
565 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
566 X509_free (server_cert);
567 }
568 else {
569 printf ("ERROR: Cannot retrieve server certificate.\n");
570 return STATE_CRITICAL;
571 }
573 }
574 else {
575 #endif
576 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
577 terminate (STATE_CRITICAL, "Unable to open TCP socket");
578 #ifdef HAVE_SSL
579 }
580 #endif
582 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
584 /* optionally send the host header info (not clear if it's usable) */
585 if (strcmp (host_name, ""))
586 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
588 /* send user agent */
589 asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
590 buf, clean_revstring (REVISION), PACKAGE_VERSION);
592 /* optionally send the authentication info */
593 if (strcmp (user_auth, "")) {
594 auth = base64 (user_auth, strlen (user_auth));
595 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
596 }
598 /* either send http POST data */
599 if (strlen (http_post_data)) {
600 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
601 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
602 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
603 }
604 else {
605 /* or just a newline so the server knows we're done with the request */
606 asprintf (&buf, "%s%s", buf, CRLF);
607 }
609 #ifdef HAVE_SSL
610 if (use_ssl == TRUE) {
611 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
612 ERR_print_errors_fp (stderr);
613 return STATE_CRITICAL;
614 }
615 }
616 else {
617 #endif
618 send (sd, buf, strlen (buf), 0);
619 #ifdef HAVE_SSL
620 }
621 #endif
623 /* fetch the page */
624 while ((i = my_recv ()) > 0) {
625 buffer[i] = '\0';
626 asprintf (&full_page, "%s%s", full_page, buffer);
627 pagesize += i;
628 }
630 if (i < 0) {
631 #ifdef HAVE_SSL
632 sslerr=SSL_get_error(ssl, i);
633 if ( sslerr == SSL_ERROR_SSL ) {
634 terminate (STATE_WARNING, "Client Certificate Required\n");
635 } else {
636 terminate (STATE_CRITICAL, "Error in recv()");
637 }
638 #else
639 terminate (STATE_CRITICAL, "Error in recv()");
640 #endif
641 }
643 /* return a CRITICAL status if we couldn't read any data */
644 if (pagesize == (size_t) 0)
645 terminate (STATE_CRITICAL, "No data received %s", timestamp);
647 /* close the connection */
648 my_close ();
650 /* reset the alarm */
651 alarm (0);
653 /* leave full_page untouched so we can free it later */
654 page = full_page;
656 if (verbose)
657 printf ("Page is %d characters\n", pagesize);
659 /* find status line and null-terminate it */
660 status_line = page;
661 page += (size_t) strcspn (page, "\r\n");
662 pos = page;
663 page += (size_t) strspn (page, "\r\n");
664 status_line[strcspn(status_line, "\r\n")] = 0;
665 strip (status_line);
666 if (verbose)
667 printf ("STATUS: %s\n", status_line);
669 /* find header info and null terminate it */
670 header = page;
671 while (strcspn (page, "\r\n") > 0) {
672 page += (size_t) strcspn (page, "\r\n");
673 pos = page;
674 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
675 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
676 page += (size_t) 2;
677 else
678 page += (size_t) 1;
679 }
680 page += (size_t) strspn (page, "\r\n");
681 header[pos - header] = 0;
682 if (verbose)
683 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
685 /* make sure the status line matches the response we are looking for */
686 if (!strstr (status_line, server_expect)) {
687 if (server_port == HTTP_PORT)
688 asprintf (&msg, "Invalid HTTP response received from host\n");
689 else
690 asprintf (&msg,
691 "Invalid HTTP response received from host on port %d\n",
692 server_port);
693 terminate (STATE_CRITICAL, msg);
694 }
697 /* Exit here if server_expect was set by user and not default */
698 if ( server_expect_yn ) {
699 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
700 server_expect);
701 if (verbose)
702 printf ("%s\n",msg);
704 }
705 else {
708 /* check the return code */
709 /* server errors result in a critical state */
710 if (strstr (status_line, "500") ||
711 strstr (status_line, "501") ||
712 strstr (status_line, "502") ||
713 strstr (status_line, "503")) {
714 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
715 }
717 /* client errors result in a warning state */
718 if (strstr (status_line, "400") ||
719 strstr (status_line, "401") ||
720 strstr (status_line, "402") ||
721 strstr (status_line, "403") ||
722 strstr (status_line, "404")) {
723 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
724 }
726 /* check redirected page if specified */
727 if (strstr (status_line, "300") ||
728 strstr (status_line, "301") ||
729 strstr (status_line, "302") ||
730 strstr (status_line, "303") ||
731 strstr (status_line, "304")) {
732 if (onredirect == STATE_DEPENDENT) {
734 asprintf (&orig_url, "%s", server_url);
735 pos = header;
736 while (pos) {
737 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
738 if (server_address == NULL)
739 terminate (STATE_UNKNOWN,
740 "HTTP UNKNOWN: could not allocate server_address");
741 if (strcspn (pos, "\r\n") > server_url_length) {
742 server_url = realloc (server_url, strcspn (pos, "\r\n"));
743 if (server_url == NULL)
744 terminate (STATE_UNKNOWN,
745 "HTTP UNKNOWN: could not allocate server_url");
746 server_url_length = strcspn (pos, "\r\n");
747 }
748 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
749 asprintf (&host_name, "%s", server_address);
750 use_ssl = server_type_check (server_type);
751 server_port = atoi (server_port_text);
752 check_http ();
753 }
754 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
755 asprintf (&host_name, "%s", server_address);
756 use_ssl = server_type_check (server_type);
757 server_port = server_port_check (use_ssl);
758 check_http ();
759 }
760 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
761 asprintf (&host_name, "%s", server_address);
762 strcpy (server_url, "/");
763 use_ssl = server_type_check (server_type);
764 server_port = atoi (server_port_text);
765 check_http ();
766 }
767 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
768 asprintf (&host_name, "%s", server_address);
769 strcpy (server_url, "/");
770 use_ssl = server_type_check (server_type);
771 server_port = server_port_check (use_ssl);
772 check_http ();
773 }
774 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
775 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
776 *x = '\0';
777 asprintf (&server_url, "%s/%s", orig_url, server_url);
778 }
779 check_http ();
780 }
781 pos += (size_t) strcspn (pos, "\r\n");
782 pos += (size_t) strspn (pos, "\r\n");
783 } /* end while (pos) */
784 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
785 status_line, (display_html ? "</A>" : ""));
786 exit (STATE_UNKNOWN);
787 } /* end if (onredirect == STATE_DEPENDENT) */
789 else if (onredirect == STATE_UNKNOWN)
790 printf ("HTTP UNKNOWN");
791 else if (onredirect == STATE_OK)
792 printf ("HTTP ok");
793 else if (onredirect == STATE_WARNING)
794 printf ("HTTP WARNING");
795 else if (onredirect == STATE_CRITICAL)
796 printf ("HTTP CRITICAL");
797 elapsed_time = delta_time (tv);
798 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
799 status_line, elapsed_time, timestamp,
800 (display_html ? "</A>" : ""), elapsed_time);
801 terminate (onredirect, msg);
802 } /* end if (strstr (status_line, "30[0-4]") */
805 } /* end else (server_expect_yn) */
808 /* check elapsed time */
809 elapsed_time = delta_time (tv);
810 asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
811 status_line, elapsed_time, timestamp,
812 (display_html ? "</A>" : ""), elapsed_time);
813 if (check_critical_time == TRUE && elapsed_time > critical_time)
814 terminate (STATE_CRITICAL, msg);
815 if (check_warning_time == TRUE && elapsed_time > warning_time)
816 terminate (STATE_WARNING, msg);
818 /* Page and Header content checks go here */
819 /* these checks should be last */
821 if (strlen (string_expect)) {
822 if (strstr (page, string_expect)) {
823 printf ("HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
824 status_line, elapsed_time,
825 timestamp, (display_html ? "</A>" : ""), elapsed_time);
826 exit (STATE_OK);
827 }
828 else {
829 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
830 (display_html ? "</A>" : ""), elapsed_time);
831 exit (STATE_CRITICAL);
832 }
833 }
834 #ifdef HAVE_REGEX_H
835 if (strlen (regexp)) {
836 errcode = regexec (&preg, page, REGS, pmatch, 0);
837 if (errcode == 0) {
838 printf ("HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
839 status_line, elapsed_time,
840 timestamp, (display_html ? "</A>" : ""), elapsed_time);
841 exit (STATE_OK);
842 }
843 else {
844 if (errcode == REG_NOMATCH) {
845 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
846 (display_html ? "</A>" : ""), elapsed_time);
847 exit (STATE_CRITICAL);
848 }
849 else {
850 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
851 printf ("Execute Error: %s\n", errbuf);
852 exit (STATE_CRITICAL);
853 }
854 }
855 }
856 #endif
858 /* make sure the page is of an appropriate size */
859 page_len = strlen (page);
860 if ((min_page_len > 0) && (page_len < min_page_len)) {
861 printf ("HTTP WARNING: page size too small%s|size=%i\n",
862 (display_html ? "</A>" : ""), page_len );
863 exit (STATE_WARNING);
864 }
865 /* We only get here if all tests have been passed */
866 asprintf (&msg, "HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
867 status_line, (float)elapsed_time,
868 timestamp, (display_html ? "</A>" : ""), elapsed_time);
869 terminate (STATE_OK, msg);
870 return STATE_UNKNOWN;
871 }
875 #ifdef HAVE_SSL
876 int connect_SSL (void)
877 {
878 SSL_METHOD *meth;
880 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
881 RAND_seed (randbuff, strlen (randbuff));
882 if (verbose)
883 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
885 /* Initialize SSL context */
886 SSLeay_add_ssl_algorithms ();
887 meth = SSLv23_client_method ();
888 SSL_load_error_strings ();
889 if ((ctx = SSL_CTX_new (meth)) == NULL) {
890 printf ("ERROR: Cannot create SSL context.\n");
891 return STATE_CRITICAL;
892 }
894 /* Initialize alarm signal handling */
895 signal (SIGALRM, socket_timeout_alarm_handler);
897 /* Set socket timeout */
898 alarm (socket_timeout);
900 /* Save start time */
901 gettimeofday (&tv, NULL);
903 /* Make TCP connection */
904 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
905 /* Do the SSL handshake */
906 if ((ssl = SSL_new (ctx)) != NULL) {
907 SSL_set_cipher_list(ssl, "ALL");
908 SSL_set_fd (ssl, sd);
909 if (SSL_connect (ssl) != -1)
910 return OK;
911 ERR_print_errors_fp (stderr);
912 }
913 else {
914 printf ("ERROR: Cannot initiate SSL handshake.\n");
915 }
916 SSL_free (ssl);
917 }
919 SSL_CTX_free (ctx);
920 close (sd);
922 return STATE_CRITICAL;
923 }
924 #endif
926 #ifdef HAVE_SSL
927 int
928 check_certificate (X509 ** certificate)
929 {
930 ASN1_STRING *tm;
931 int offset;
932 struct tm stamp;
933 int days_left;
936 /* Retrieve timestamp of certificate */
937 tm = X509_get_notAfter (*certificate);
939 /* Generate tm structure to process timestamp */
940 if (tm->type == V_ASN1_UTCTIME) {
941 if (tm->length < 10) {
942 printf ("ERROR: Wrong time format in certificate.\n");
943 return STATE_CRITICAL;
944 }
945 else {
946 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
947 if (stamp.tm_year < 50)
948 stamp.tm_year += 100;
949 offset = 0;
950 }
951 }
952 else {
953 if (tm->length < 12) {
954 printf ("ERROR: Wrong time format in certificate.\n");
955 return STATE_CRITICAL;
956 }
957 else {
958 stamp.tm_year =
959 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
960 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
961 stamp.tm_year -= 1900;
962 offset = 2;
963 }
964 }
965 stamp.tm_mon =
966 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
967 stamp.tm_mday =
968 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
969 stamp.tm_hour =
970 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
971 stamp.tm_min =
972 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
973 stamp.tm_sec = 0;
974 stamp.tm_isdst = -1;
976 days_left = (mktime (&stamp) - time (NULL)) / 86400;
977 snprintf
978 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
979 stamp.tm_mon + 1,
980 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
982 if (days_left > 0 && days_left <= days_till_exp) {
983 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
984 return STATE_WARNING;
985 }
986 if (days_left < 0) {
987 printf ("Certificate expired on %s.\n", timestamp);
988 return STATE_CRITICAL;
989 }
991 if (days_left == 0) {
992 printf ("Certificate expires today (%s).\n", timestamp);
993 return STATE_WARNING;
994 }
996 printf ("Certificate will expire on %s.\n", timestamp);
998 return STATE_OK;
999 }
1000 #endif
1001 \f
1004 int
1005 my_recv (void)
1006 {
1007 int i;
1008 #ifdef HAVE_SSL
1009 if (use_ssl) {
1010 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1011 }
1012 else {
1013 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1014 }
1015 #else
1016 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1017 #endif
1018 return i;
1019 }
1022 int
1023 my_close (void)
1024 {
1025 #ifdef HAVE_SSL
1026 if (use_ssl == TRUE) {
1027 SSL_shutdown (ssl);
1028 SSL_free (ssl);
1029 SSL_CTX_free (ctx);
1030 return 0;
1031 }
1032 else {
1033 #endif
1034 return close (sd);
1035 #ifdef HAVE_SSL
1036 }
1037 #endif
1038 }
1039 \f
1042 void
1043 print_help (void)
1044 {
1045 print_revision (progname, REVISION);
1046 printf
1047 ("Copyright (c) %s %s <%s>\n\n%s\n",
1048 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1049 print_usage ();
1050 printf ("NOTE: One or both of -H and -I must be specified\n");
1051 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1052 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1053 #ifdef HAVE_SSL
1054 printf (SSLDESCRIPTION);
1055 #endif
1056 }
1059 void
1060 print_usage (void)
1061 {
1062 printf ("Usage:\n" " %s %s\n"
1063 " %s (-h | --help) for detailed help\n"
1064 " %s (-V | --version) for version information\n",
1065 progname, OPTIONS, progname, progname);
1066 }