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