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