1 /****************************************************************************
2 *
3 * Program: HTTP plugin for Nagios
4 * License: GPL
5 *
6 * License Information:
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Id$
23 *
24 *****************************************************************************/
26 const char *progname = "check_http";
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Ethan Galstad/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
32 #include "config.h"
33 #include "common.h"
34 #include "netutils.h"
35 #include "utils.h"
37 #define SUMMARY "\
38 This plugin tests the HTTP service on the specified host. It can test\n\
39 normal (http) and secure (https) servers, follow redirects, search for\n\
40 strings and regular expressions, check connection times, and report on\n\
41 certificate expiration times.\n"
43 #define OPTIONS "\
44 \(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
45 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
46 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
47 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
48 [-P string]"
50 #define LONGOPTIONS "\
51 -H, --hostname=ADDRESS\n\
52 Host name argument for servers using host headers (virtual host)\n\
53 -I, --IP-address=ADDRESS\n\
54 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
55 -e, --expect=STRING\n\
56 String to expect in first (status) line of server response (default: %s)\n\
57 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
58 -s, --string=STRING\n\
59 String to expect in the content\n\
60 -u, --url=PATH\n\
61 URL to GET or POST (default: /)\n\
62 -p, --port=INTEGER\n\
63 Port number (default: %d)\n\
64 -P, --post=STRING\n\
65 URL encoded http POST data\n\
66 -w, --warning=INTEGER\n\
67 Response time to result in warning status (seconds)\n\
68 -c, --critical=INTEGER\n\
69 Response time to result in critical status (seconds)\n\
70 -t, --timeout=INTEGER\n\
71 Seconds before connection times out (default: %d)\n\
72 -a, --authorization=AUTH_PAIR\n\
73 Username:password on sites with basic authentication\n\
74 -L, --link=URL\n\
75 Wrap output in HTML link (obsoleted by urlize)\n\
76 -f, --onredirect=<ok|warning|critical|follow>\n\
77 How to handle redirected pages\n%s%s\
78 -v, --verbose\n\
79 Show details for command-line debugging (do not use with nagios server)\n\
80 -h, --help\n\
81 Print detailed help screen\n\
82 -V, --version\n\
83 Print version information\n"
85 #ifdef HAVE_SSL
86 #define SSLOPTIONS "\
87 -S, --ssl\n\
88 Connect via SSL\n\
89 -C, --certificate=INTEGER\n\
90 Minimum number of days a certificate has to be valid.\n\
91 (when this option is used the url is not checked.)\n"
92 #else
93 #define SSLOPTIONS ""
94 #endif
96 #ifdef HAVE_REGEX_H
97 #define REGOPTIONS "\
98 -l, --linespan\n\
99 Allow regex to span newlines (must precede -r or -R)\n\
100 -r, --regex, --ereg=STRING\n\
101 Search page for regex STRING\n\
102 -R, --eregi=STRING\n\
103 Search page for case-insensitive regex STRING\n"
104 #else
105 #define REGOPTIONS ""
106 #endif
108 #define DESCRIPTION "\
109 This plugin will attempt to open an HTTP connection with the host. Successul\n\
110 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
111 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
112 messages from the host result in STATE_WARNING return values. If you are\n\
113 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
114 \(fully qualified domain name) as the [host_name] argument.\n"
116 #define SSLDESCRIPTION "\
117 This plugin can also check whether an SSL enabled web server is able to\n\
118 serve content (optionally within a specified time) or whether the X509 \n\
119 certificate is still valid for the specified number of days.\n\n\
120 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
121 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
122 STATE_OK will be returned. When the server returns its content but exceeds\n\
123 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
124 a STATE_CRITICAL will be returned.\n\n\
125 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
126 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
127 STATE_OK is returned. When the certificate is still valid, but for less than\n\
128 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
129 the certificate is expired.\n"
131 #ifdef HAVE_SSL_H
132 #include <rsa.h>
133 #include <crypto.h>
134 #include <x509.h>
135 #include <pem.h>
136 #include <ssl.h>
137 #include <err.h>
138 #include <rand.h>
139 #endif
141 #ifdef HAVE_OPENSSL_SSL_H
142 #include <openssl/rsa.h>
143 #include <openssl/crypto.h>
144 #include <openssl/x509.h>
145 #include <openssl/pem.h>
146 #include <openssl/ssl.h>
147 #include <openssl/err.h>
148 #include <openssl/rand.h>
149 #endif
151 #ifdef HAVE_SSL
152 int check_cert = FALSE;
153 int days_till_exp;
154 unsigned char *randbuff;
155 SSL_CTX *ctx;
156 SSL *ssl;
157 X509 *server_cert;
158 int connect_SSL (void);
159 int check_certificate (X509 **);
160 #endif
162 #ifdef HAVE_REGEX_H
163 enum {
164 REGS = 2,
165 MAX_RE_SIZE = 256
166 };
167 #include <regex.h>
168 regex_t preg;
169 regmatch_t pmatch[REGS];
170 char regexp[MAX_RE_SIZE];
171 char errbuf[MAX_INPUT_BUFFER];
172 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
173 int errcode;
174 #endif
176 struct timeval tv;
178 #define server_type_check(server_type) \
179 (strcmp (server_type, "https") ? FALSE : TRUE)
181 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
183 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
184 #define URI_HTTP "%[HTPShtps]://"
185 #define URI_HOST "%[a-zA-Z0-9.-]"
186 #define URI_PORT ":%[0-9]"
187 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
189 enum {
190 MAX_IPV4_HOSTLENGTH = 64,
191 HTTP_PORT = 80,
192 HTTPS_PORT = 443
193 };
195 #define HTTP_EXPECT "HTTP/1."
196 #define HTTP_URL "/"
197 #define CRLF "\r\n"
199 char timestamp[17] = "";
200 int specify_port = FALSE;
201 int server_port = HTTP_PORT;
202 char server_port_text[6] = "";
203 char server_type[6] = "http";
204 char *server_address = "";
205 char *host_name = "";
206 char *server_url = HTTP_URL;
207 int server_url_length = 1;
208 int server_expect_yn = 0;
209 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
210 char string_expect[MAX_INPUT_BUFFER] = "";
211 double warning_time = 0;
212 int check_warning_time = FALSE;
213 double critical_time = 0;
214 int check_critical_time = FALSE;
215 char user_auth[MAX_INPUT_BUFFER] = "";
216 int display_html = FALSE;
217 int onredirect = STATE_OK;
218 int use_ssl = FALSE;
219 int verbose = FALSE;
220 int sd;
221 char *http_method = "GET";
222 char *http_post_data = "";
223 char buffer[MAX_INPUT_BUFFER];
225 void print_usage (void);
226 void print_help (void);
227 int process_arguments (int, char **);
228 static char *base64 (char *bin, int len);
229 int check_http (void);
230 int my_recv (void);
231 int my_close (void);
233 int
234 main (int argc, char **argv)
235 {
236 int result = STATE_UNKNOWN;
238 if (process_arguments (argc, argv) == ERROR)
239 usage ("check_http: could not parse arguments\n");
241 if (strstr (timestamp, ":")) {
242 if (strstr (server_url, "?"))
243 asprintf (&server_url, "%s&%s", server_url, timestamp);
244 else
245 asprintf (&server_url, "%s?%s", server_url, timestamp);
246 }
248 if (display_html == TRUE)
249 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
250 host_name, server_port, server_url);
252 /* initialize alarm signal handling, set socket timeout, start timer */
253 (void) signal (SIGALRM, socket_timeout_alarm_handler);
254 (void) alarm (socket_timeout);
255 gettimeofday (&tv, NULL);
257 #ifdef HAVE_SSL
258 if (use_ssl && check_cert == TRUE) {
259 if (connect_SSL () != OK)
260 terminate (STATE_CRITICAL,
261 "HTTP CRITICAL - Could not make SSL connection\n");
262 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
263 result = check_certificate (&server_cert);
264 X509_free (server_cert);
265 }
266 else {
267 printf ("ERROR: Cannot retrieve server certificate.\n");
268 result = STATE_CRITICAL;
269 }
270 SSL_shutdown (ssl);
271 SSL_free (ssl);
272 SSL_CTX_free (ctx);
273 close (sd);
274 }
275 else {
276 result = check_http ();
277 }
278 #else
279 result = check_http ();
280 #endif
281 return result;
282 }
283 \f
286 /* process command-line arguments */
287 int
288 process_arguments (int argc, char **argv)
289 {
290 int c = 1;
292 #ifdef HAVE_GETOPT_H
293 int option_index = 0;
294 static struct option long_options[] = {
295 STD_LONG_OPTS,
296 {"file",required_argument,0,'F'},
297 {"link", no_argument, 0, 'L'},
298 {"nohtml", no_argument, 0, 'n'},
299 {"ssl", no_argument, 0, 'S'},
300 {"verbose", no_argument, 0, 'v'},
301 {"post", required_argument, 0, 'P'},
302 {"IP-address", required_argument, 0, 'I'},
303 {"string", required_argument, 0, 's'},
304 {"regex", required_argument, 0, 'r'},
305 {"ereg", required_argument, 0, 'r'},
306 {"eregi", required_argument, 0, 'R'},
307 {"linespan", no_argument, 0, 'l'},
308 {"onredirect", required_argument, 0, 'f'},
309 {"certificate", required_argument, 0, 'C'},
310 {0, 0, 0, 0}
311 };
312 #endif
314 if (argc < 2)
315 return ERROR;
317 for (c = 1; c < argc; c++) {
318 if (strcmp ("-to", argv[c]) == 0)
319 strcpy (argv[c], "-t");
320 if (strcmp ("-hn", argv[c]) == 0)
321 strcpy (argv[c], "-H");
322 if (strcmp ("-wt", argv[c]) == 0)
323 strcpy (argv[c], "-w");
324 if (strcmp ("-ct", argv[c]) == 0)
325 strcpy (argv[c], "-c");
326 if (strcmp ("-nohtml", argv[c]) == 0)
327 strcpy (argv[c], "-n");
328 }
330 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
332 while (1) {
333 #ifdef HAVE_GETOPT_H
334 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
335 #else
336 c = getopt (argc, argv, OPTCHARS);
337 #endif
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 }
469 }
471 c = optind;
473 if (strcmp (server_address, "") == 0 && c < argc)
474 asprintf (&server_address, "%s", argv[c++]);
476 if (strcmp (host_name, "") == 0 && c < argc)
477 asprintf (&host_name, "%s", argv[c++]);
479 if (strcmp (server_address ,"") == 0) {
480 if (strcmp (host_name, "") == 0)
481 usage ("check_http: you must specify a server address or host name\n");
482 else
483 asprintf (&server_address, "%s", host_name);
484 }
486 return TRUE;
487 }
488 \f
491 /* written by lauri alanko */
492 static char *
493 base64 (char *bin, int len)
494 {
496 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
497 int i = 0, j = 0;
499 char BASE64_END = '=';
500 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
502 while (j < len - 2) {
503 buf[i++] = base64_table[bin[j] >> 2];
504 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
505 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
506 buf[i++] = base64_table[bin[j + 2] & 63];
507 j += 3;
508 }
510 switch (len - j) {
511 case 1:
512 buf[i++] = base64_table[bin[j] >> 2];
513 buf[i++] = base64_table[(bin[j] & 3) << 4];
514 buf[i++] = BASE64_END;
515 buf[i++] = BASE64_END;
516 break;
517 case 2:
518 buf[i++] = base64_table[bin[j] >> 2];
519 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
520 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
521 buf[i++] = BASE64_END;
522 break;
523 case 0:
524 break;
525 }
527 buf[i] = '\0';
528 return buf;
529 }
530 \f
533 int
534 check_http (void)
535 {
536 char *msg = NULL;
537 char *status_line = "";
538 char *header = NULL;
539 char *page = "";
540 char *auth = NULL;
541 int i = 0;
542 size_t pagesize = 0;
543 char *full_page = "";
544 char *buf = NULL;
545 char *pos = "";
546 char *x = NULL;
547 char *orig_url = NULL;
548 double elapsed_time;
549 #ifdef HAVE_SSL
550 int sslerr;
551 #endif
553 /* try to connect to the host at the given port number */
554 #ifdef HAVE_SSL
555 if (use_ssl == TRUE) {
557 if (connect_SSL () != OK) {
558 terminate (STATE_CRITICAL, "Unable to open TCP socket");
559 }
561 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
562 X509_free (server_cert);
563 }
564 else {
565 printf ("ERROR: Cannot retrieve server certificate.\n");
566 return STATE_CRITICAL;
567 }
569 }
570 else {
571 #endif
572 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
573 terminate (STATE_CRITICAL, "Unable to open TCP socket");
574 #ifdef HAVE_SSL
575 }
576 #endif
578 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
580 /* optionally send the host header info (not clear if it's usable) */
581 if (strcmp (host_name, ""))
582 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
584 /* send user agent */
585 asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
586 buf, clean_revstring (REVISION), PACKAGE_VERSION);
588 /* optionally send the authentication info */
589 if (strcmp (user_auth, "")) {
590 auth = base64 (user_auth, strlen (user_auth));
591 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
592 }
594 /* either send http POST data */
595 if (strlen (http_post_data)) {
596 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
597 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
598 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
599 }
600 else {
601 /* or just a newline so the server knows we're done with the request */
602 asprintf (&buf, "%s%s", buf, CRLF);
603 }
605 #ifdef HAVE_SSL
606 if (use_ssl == TRUE) {
607 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
608 ERR_print_errors_fp (stderr);
609 return STATE_CRITICAL;
610 }
611 }
612 else {
613 #endif
614 send (sd, buf, strlen (buf), 0);
615 #ifdef HAVE_SSL
616 }
617 #endif
619 /* fetch the page */
620 while ((i = my_recv ()) > 0) {
621 buffer[i] = '\0';
622 asprintf (&full_page, "%s%s", full_page, buffer);
623 pagesize += i;
624 }
626 if (i < 0) {
627 #ifdef HAVE_SSL
628 sslerr=SSL_get_error(ssl, i);
629 if ( sslerr == SSL_ERROR_SSL ) {
630 terminate (STATE_WARNING, "Client Certificate Required\n");
631 } else {
632 terminate (STATE_CRITICAL, "Error in recv()");
633 }
634 #else
635 terminate (STATE_CRITICAL, "Error in recv()");
636 #endif
637 }
639 /* return a CRITICAL status if we couldn't read any data */
640 if (pagesize == (size_t) 0)
641 terminate (STATE_CRITICAL, "No data received %s", timestamp);
643 /* close the connection */
644 my_close ();
646 /* reset the alarm */
647 alarm (0);
649 /* leave full_page untouched so we can free it later */
650 page = full_page;
652 if (verbose)
653 printf ("Page is %d characters\n", pagesize);
655 /* find status line and null-terminate it */
656 status_line = page;
657 page += (size_t) strcspn (page, "\r\n");
658 pos = page;
659 page += (size_t) strspn (page, "\r\n");
660 status_line[strcspn(status_line, "\r\n")] = 0;
661 strip (status_line);
662 if (verbose)
663 printf ("STATUS: %s\n", status_line);
665 /* find header info and null terminate it */
666 header = page;
667 while (strcspn (page, "\r\n") > 0) {
668 page += (size_t) strcspn (page, "\r\n");
669 pos = page;
670 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
671 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
672 page += (size_t) 2;
673 else
674 page += (size_t) 1;
675 }
676 page += (size_t) strspn (page, "\r\n");
677 header[pos - header] = 0;
678 if (verbose)
679 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
681 /* make sure the status line matches the response we are looking for */
682 if (!strstr (status_line, server_expect)) {
683 if (server_port == HTTP_PORT)
684 asprintf (&msg, "Invalid HTTP response received from host\n");
685 else
686 asprintf (&msg,
687 "Invalid HTTP response received from host on port %d\n",
688 server_port);
689 terminate (STATE_CRITICAL, msg);
690 }
693 /* Exit here if server_expect was set by user and not default */
694 if ( server_expect_yn ) {
695 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
696 server_expect);
697 if (verbose)
698 printf ("%s\n",msg);
700 }
701 else {
704 /* check the return code */
705 /* server errors result in a critical state */
706 if (strstr (status_line, "500") ||
707 strstr (status_line, "501") ||
708 strstr (status_line, "502") ||
709 strstr (status_line, "503")) {
710 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
711 }
713 /* client errors result in a warning state */
714 if (strstr (status_line, "400") ||
715 strstr (status_line, "401") ||
716 strstr (status_line, "402") ||
717 strstr (status_line, "403") ||
718 strstr (status_line, "404")) {
719 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
720 }
722 /* check redirected page if specified */
723 if (strstr (status_line, "300") ||
724 strstr (status_line, "301") ||
725 strstr (status_line, "302") ||
726 strstr (status_line, "303") ||
727 strstr (status_line, "304")) {
728 if (onredirect == STATE_DEPENDENT) {
730 asprintf (&orig_url, "%s", server_url);
731 pos = header;
732 while (pos) {
733 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
734 if (server_address == NULL)
735 terminate (STATE_UNKNOWN,
736 "HTTP UNKNOWN: could not allocate server_address");
737 if (strcspn (pos, "\r\n") > server_url_length) {
738 server_url = realloc (server_url, strcspn (pos, "\r\n"));
739 if (server_url == NULL)
740 terminate (STATE_UNKNOWN,
741 "HTTP UNKNOWN: could not allocate server_url");
742 server_url_length = strcspn (pos, "\r\n");
743 }
744 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
745 asprintf (&host_name, "%s", server_address);
746 use_ssl = server_type_check (server_type);
747 server_port = atoi (server_port_text);
748 check_http ();
749 }
750 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
751 asprintf (&host_name, "%s", server_address);
752 use_ssl = server_type_check (server_type);
753 server_port = server_port_check (use_ssl);
754 check_http ();
755 }
756 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
757 asprintf (&host_name, "%s", server_address);
758 strcpy (server_url, "/");
759 use_ssl = server_type_check (server_type);
760 server_port = atoi (server_port_text);
761 check_http ();
762 }
763 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
764 asprintf (&host_name, "%s", server_address);
765 strcpy (server_url, "/");
766 use_ssl = server_type_check (server_type);
767 server_port = server_port_check (use_ssl);
768 check_http ();
769 }
770 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
771 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
772 *x = '\0';
773 asprintf (&server_url, "%s/%s", orig_url, server_url);
774 }
775 check_http ();
776 }
777 pos += (size_t) strcspn (pos, "\r\n");
778 pos += (size_t) strspn (pos, "\r\n");
779 } /* end while (pos) */
780 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
781 status_line, (display_html ? "</A>" : ""));
782 exit (STATE_UNKNOWN);
783 } /* end if (onredirect == STATE_DEPENDENT) */
785 else if (onredirect == STATE_UNKNOWN)
786 printf ("HTTP UNKNOWN");
787 else if (onredirect == STATE_OK)
788 printf ("HTTP ok");
789 else if (onredirect == STATE_WARNING)
790 printf ("HTTP WARNING");
791 else if (onredirect == STATE_CRITICAL)
792 printf ("HTTP CRITICAL");
793 elapsed_time = delta_time (tv);
794 asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
795 status_line, elapsed_time, timestamp,
796 (display_html ? "</A>" : ""), elapsed_time);
797 terminate (onredirect, msg);
798 } /* end if (strstr (status_line, "30[0-4]") */
801 } /* end else (server_expect_yn) */
804 /* check elapsed time */
805 elapsed_time = delta_time (tv);
806 asprintf (&msg, "HTTP problem: %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 if (check_critical_time == TRUE && elapsed_time > critical_time)
810 terminate (STATE_CRITICAL, msg);
811 if (check_warning_time == TRUE && elapsed_time > warning_time)
812 terminate (STATE_WARNING, msg);
814 /* Page and Header content checks go here */
815 /* these checks should be last */
817 if (strlen (string_expect)) {
818 if (strstr (page, string_expect)) {
819 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
820 status_line, elapsed_time,
821 timestamp, (display_html ? "</A>" : ""), elapsed_time);
822 exit (STATE_OK);
823 }
824 else {
825 printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
826 (display_html ? "</A>" : ""), elapsed_time);
827 exit (STATE_CRITICAL);
828 }
829 }
830 #ifdef HAVE_REGEX_H
831 if (strlen (regexp)) {
832 errcode = regexec (&preg, page, REGS, pmatch, 0);
833 if (errcode == 0) {
834 printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
835 status_line, elapsed_time,
836 timestamp, (display_html ? "</A>" : ""), elapsed_time);
837 exit (STATE_OK);
838 }
839 else {
840 if (errcode == REG_NOMATCH) {
841 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
842 (display_html ? "</A>" : ""), elapsed_time);
843 exit (STATE_CRITICAL);
844 }
845 else {
846 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
847 printf ("Execute Error: %s\n", errbuf);
848 exit (STATE_CRITICAL);
849 }
850 }
851 }
852 #endif
854 /* We only get here if all tests have been passed */
855 asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
856 status_line, (float)elapsed_time,
857 timestamp, (display_html ? "</A>" : ""), elapsed_time);
858 terminate (STATE_OK, msg);
859 return STATE_UNKNOWN;
860 }
864 #ifdef HAVE_SSL
865 int connect_SSL (void)
866 {
867 SSL_METHOD *meth;
869 asprintf (&randbuff, "%s", "qwertyuiopasdfghjkl");
870 RAND_seed (randbuff, strlen (randbuff));
871 /* Initialize SSL context */
872 SSLeay_add_ssl_algorithms ();
873 meth = SSLv23_client_method ();
874 SSL_load_error_strings ();
875 if ((ctx = SSL_CTX_new (meth)) == NULL) {
876 printf ("ERROR: Cannot create SSL context.\n");
877 return STATE_CRITICAL;
878 }
880 /* Initialize alarm signal handling */
881 signal (SIGALRM, socket_timeout_alarm_handler);
883 /* Set socket timeout */
884 alarm (socket_timeout);
886 /* Save start time */
887 gettimeofday (&tv, NULL);
889 /* Make TCP connection */
890 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
891 /* Do the SSL handshake */
892 if ((ssl = SSL_new (ctx)) != NULL) {
893 SSL_set_cipher_list(ssl, "ALL");
894 SSL_set_fd (ssl, sd);
895 if (SSL_connect (ssl) != -1)
896 return OK;
897 ERR_print_errors_fp (stderr);
898 }
899 else {
900 printf ("ERROR: Cannot initiate SSL handshake.\n");
901 }
902 SSL_free (ssl);
903 }
905 SSL_CTX_free (ctx);
906 close (sd);
908 return STATE_CRITICAL;
909 }
910 #endif
912 #ifdef HAVE_SSL
913 int
914 check_certificate (X509 ** certificate)
915 {
916 ASN1_STRING *tm;
917 int offset;
918 struct tm stamp;
919 int days_left;
922 /* Retrieve timestamp of certificate */
923 tm = X509_get_notAfter (*certificate);
925 /* Generate tm structure to process timestamp */
926 if (tm->type == V_ASN1_UTCTIME) {
927 if (tm->length < 10) {
928 printf ("ERROR: Wrong time format in certificate.\n");
929 return STATE_CRITICAL;
930 }
931 else {
932 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
933 if (stamp.tm_year < 50)
934 stamp.tm_year += 100;
935 offset = 0;
936 }
937 }
938 else {
939 if (tm->length < 12) {
940 printf ("ERROR: Wrong time format in certificate.\n");
941 return STATE_CRITICAL;
942 }
943 else {
944 stamp.tm_year =
945 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
946 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
947 stamp.tm_year -= 1900;
948 offset = 2;
949 }
950 }
951 stamp.tm_mon =
952 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
953 stamp.tm_mday =
954 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
955 stamp.tm_hour =
956 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
957 stamp.tm_min =
958 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
959 stamp.tm_sec = 0;
960 stamp.tm_isdst = -1;
962 days_left = (mktime (&stamp) - time (NULL)) / 86400;
963 snprintf
964 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
965 stamp.tm_mon + 1,
966 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
968 if (days_left > 0 && days_left <= days_till_exp) {
969 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
970 return STATE_WARNING;
971 }
972 if (days_left < 0) {
973 printf ("Certificate expired on %s.\n", timestamp);
974 return STATE_CRITICAL;
975 }
977 if (days_left == 0) {
978 printf ("Certificate expires today (%s).\n", timestamp);
979 return STATE_WARNING;
980 }
982 printf ("Certificate will expire on %s.\n", timestamp);
984 return STATE_OK;
985 }
986 #endif
987 \f
990 int
991 my_recv (void)
992 {
993 int i;
994 #ifdef HAVE_SSL
995 if (use_ssl) {
996 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
997 }
998 else {
999 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1000 }
1001 #else
1002 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1003 #endif
1004 return i;
1005 }
1008 int
1009 my_close (void)
1010 {
1011 #ifdef HAVE_SSL
1012 if (use_ssl == TRUE) {
1013 SSL_shutdown (ssl);
1014 SSL_free (ssl);
1015 SSL_CTX_free (ctx);
1016 return 0;
1017 }
1018 else {
1019 #endif
1020 return close (sd);
1021 #ifdef HAVE_SSL
1022 }
1023 #endif
1024 }
1025 \f
1028 void
1029 print_help (void)
1030 {
1031 print_revision (progname, REVISION);
1032 printf
1033 ("Copyright (c) %s %s <%s>\n\n%s\n",
1034 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1035 print_usage ();
1036 printf ("NOTE: One or both of -H and -I must be specified\n");
1037 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1038 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1039 #ifdef HAVE_SSL
1040 printf (SSLDESCRIPTION);
1041 #endif
1042 }
1045 void
1046 print_usage (void)
1047 {
1048 printf ("Usage:\n" " %s %s\n"
1049 #ifdef HAVE_GETOPT_H
1050 " %s (-h | --help) for detailed help\n"
1051 " %s (-V | --version) for version information\n",
1052 #else
1053 " %s -h for detailed help\n"
1054 " %s -V for version information\n",
1055 #endif
1056 progname, OPTIONS, progname, progname);
1057 }