1 /******************************************************************************
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ******************************************************************************/
18 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
20 const char *progname = "check_http";
21 const char *revision = "$Revision$";
22 const char *copyright = "1999-2001";
23 const char *email = "nagiosplug-devel@lists.sourceforge.net";
25 #include "common.h"
26 #include "netutils.h"
27 #include "utils.h"
29 #define HTTP_EXPECT "HTTP/1."
30 enum {
31 MAX_IPV4_HOSTLENGTH = 255,
32 HTTP_PORT = 80,
33 HTTPS_PORT = 443
34 };
36 #ifdef HAVE_SSL_H
37 #include <rsa.h>
38 #include <crypto.h>
39 #include <x509.h>
40 #include <pem.h>
41 #include <ssl.h>
42 #include <err.h>
43 #include <rand.h>
44 #else
45 # ifdef HAVE_OPENSSL_SSL_H
46 # include <openssl/rsa.h>
47 # include <openssl/crypto.h>
48 # include <openssl/x509.h>
49 # include <openssl/pem.h>
50 # include <openssl/ssl.h>
51 # include <openssl/err.h>
52 # include <openssl/rand.h>
53 # endif
54 #endif
56 #ifdef HAVE_SSL
57 int check_cert = FALSE;
58 int days_till_exp;
59 char *randbuff;
60 SSL_CTX *ctx;
61 SSL *ssl;
62 X509 *server_cert;
63 int connect_SSL (void);
64 int check_certificate (X509 **);
65 #endif
67 #ifdef HAVE_REGEX_H
68 enum {
69 REGS = 2,
70 MAX_RE_SIZE = 256
71 };
72 #include <regex.h>
73 regex_t preg;
74 regmatch_t pmatch[REGS];
75 char regexp[MAX_RE_SIZE];
76 char errbuf[MAX_INPUT_BUFFER];
77 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
78 int errcode;
79 #endif
81 struct timeval tv;
83 #define HTTP_URL "/"
84 #define CRLF "\r\n"
86 char timestamp[17] = "";
87 int specify_port = FALSE;
88 int server_port = HTTP_PORT;
89 char server_port_text[6] = "";
90 char server_type[6] = "http";
91 char *server_address;
92 char *host_name;
93 char *server_url;
94 char *user_agent;
95 int server_url_length;
96 int server_expect_yn = 0;
97 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
98 char string_expect[MAX_INPUT_BUFFER] = "";
99 double warning_time = 0;
100 int check_warning_time = FALSE;
101 double critical_time = 0;
102 int check_critical_time = FALSE;
103 char user_auth[MAX_INPUT_BUFFER] = "";
104 int display_html = FALSE;
105 int onredirect = STATE_OK;
106 int use_ssl = FALSE;
107 int verbose = FALSE;
108 int sd;
109 int min_page_len = 0;
110 int redir_depth = 0;
111 int max_depth = 15;
112 char *http_method;
113 char *http_post_data;
114 char *http_content_type;
115 char buffer[MAX_INPUT_BUFFER];
117 int process_arguments (int, char **);
118 static char *base64 (const char *bin, size_t len);
119 int check_http (void);
120 void redir (char *pos, char *status_line);
121 int server_type_check(const char *type);
122 int server_port_check(int ssl_flag);
123 char *perfd_time (double microsec);
124 char *perfd_size (int page_len);
125 int my_recv (void);
126 int my_close (void);
127 void print_help (void);
128 void print_usage (void);
130 int
131 main (int argc, char **argv)
132 {
133 int result = STATE_UNKNOWN;
135 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
136 server_url = strdup(HTTP_URL);
137 server_url_length = strlen(server_url);
138 asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
139 clean_revstring (revision), VERSION);
141 if (process_arguments (argc, argv) == ERROR)
142 usage (_("check_http: could not parse arguments\n"));
144 if (strstr (timestamp, ":")) {
145 if (strstr (server_url, "?"))
146 asprintf (&server_url, "%s&%s", server_url, timestamp);
147 else
148 asprintf (&server_url, "%s?%s", server_url, timestamp);
149 }
151 if (display_html == TRUE)
152 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
153 host_name, server_port, server_url);
155 /* initialize alarm signal handling, set socket timeout, start timer */
156 (void) signal (SIGALRM, socket_timeout_alarm_handler);
157 (void) alarm (socket_timeout);
158 gettimeofday (&tv, NULL);
160 #ifdef HAVE_SSL
161 if (use_ssl && check_cert == TRUE) {
162 if (connect_SSL () != OK)
163 die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
164 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
165 result = check_certificate (&server_cert);
166 X509_free (server_cert);
167 }
168 else {
169 printf (_("ERROR: Cannot retrieve server certificate.\n"));
170 result = STATE_CRITICAL;
171 }
172 SSL_shutdown (ssl);
173 SSL_free (ssl);
174 SSL_CTX_free (ctx);
175 close (sd);
176 }
177 else {
178 result = check_http ();
179 }
180 #else
181 result = check_http ();
182 #endif
183 return result;
184 }
185 \f
188 /* process command-line arguments */
189 int
190 process_arguments (int argc, char **argv)
191 {
192 int c = 1;
194 int option = 0;
195 static struct option longopts[] = {
196 STD_LONG_OPTS,
197 {"file",required_argument,0,'F'},
198 {"link", no_argument, 0, 'L'},
199 {"nohtml", no_argument, 0, 'n'},
200 {"ssl", no_argument, 0, 'S'},
201 {"verbose", no_argument, 0, 'v'},
202 {"post", required_argument, 0, 'P'},
203 {"IP-address", required_argument, 0, 'I'},
204 {"url", required_argument, 0, 'u'},
205 {"string", required_argument, 0, 's'},
206 {"regex", required_argument, 0, 'r'},
207 {"ereg", required_argument, 0, 'r'},
208 {"eregi", required_argument, 0, 'R'},
209 {"linespan", no_argument, 0, 'l'},
210 {"onredirect", required_argument, 0, 'f'},
211 {"certificate", required_argument, 0, 'C'},
212 {"content-type", required_argument, 0, 'T'},
213 {"min", required_argument, 0, 'm'},
214 {"use-ipv4", no_argument, 0, '4'},
215 {"use-ipv6", no_argument, 0, '6'},
216 {0, 0, 0, 0}
217 };
219 if (argc < 2)
220 return ERROR;
222 for (c = 1; c < argc; c++) {
223 if (strcmp ("-to", argv[c]) == 0)
224 strcpy (argv[c], "-t");
225 if (strcmp ("-hn", argv[c]) == 0)
226 strcpy (argv[c], "-H");
227 if (strcmp ("-wt", argv[c]) == 0)
228 strcpy (argv[c], "-w");
229 if (strcmp ("-ct", argv[c]) == 0)
230 strcpy (argv[c], "-c");
231 if (strcmp ("-nohtml", argv[c]) == 0)
232 strcpy (argv[c], "-n");
233 }
235 while (1) {
236 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
237 if (c == -1 || c == EOF)
238 break;
240 switch (c) {
241 case '?': /* usage */
242 usage3 (_("unknown argument"), optopt);
243 break;
244 case 'h': /* help */
245 print_help ();
246 exit (STATE_OK);
247 break;
248 case 'V': /* version */
249 print_revision (progname, revision);
250 exit (STATE_OK);
251 break;
252 case 't': /* timeout period */
253 if (!is_intnonneg (optarg))
254 usage2 (_("timeout interval must be a non-negative integer"), optarg);
255 else
256 socket_timeout = atoi (optarg);
257 break;
258 case 'c': /* critical time threshold */
259 if (!is_nonnegative (optarg))
260 usage2 (_("invalid critical threshold"), optarg);
261 else {
262 critical_time = strtod (optarg, NULL);
263 check_critical_time = TRUE;
264 }
265 break;
266 case 'w': /* warning time threshold */
267 if (!is_nonnegative (optarg))
268 usage2 (_("invalid warning threshold"), optarg);
269 else {
270 warning_time = strtod (optarg, NULL);
271 check_warning_time = TRUE;
272 }
273 break;
274 case 'L': /* show html link */
275 display_html = TRUE;
276 break;
277 case 'n': /* do not show html link */
278 display_html = FALSE;
279 break;
280 case 'S': /* use SSL */
281 #ifndef HAVE_SSL
282 usage (_("check_http: invalid option - SSL is not available\n"));
283 #endif
284 use_ssl = TRUE;
285 if (specify_port == FALSE)
286 server_port = HTTPS_PORT;
287 break;
288 case 'C': /* Check SSL cert validity */
289 #ifdef HAVE_SSL
290 if (!is_intnonneg (optarg))
291 usage2 (_("invalid certificate expiration period"), optarg);
292 else {
293 days_till_exp = atoi (optarg);
294 check_cert = TRUE;
295 }
296 #else
297 usage (_("check_http: invalid option - SSL is not available\n"));
298 #endif
299 break;
300 case 'f': /* onredirect */
301 if (!strcmp (optarg, "follow"))
302 onredirect = STATE_DEPENDENT;
303 if (!strcmp (optarg, "unknown"))
304 onredirect = STATE_UNKNOWN;
305 if (!strcmp (optarg, "ok"))
306 onredirect = STATE_OK;
307 if (!strcmp (optarg, "warning"))
308 onredirect = STATE_WARNING;
309 if (!strcmp (optarg, "critical"))
310 onredirect = STATE_CRITICAL;
311 if (verbose)
312 printf(_("option f:%d \n"), onredirect);
313 break;
314 /* Note: H, I, and u must be malloc'd or will fail on redirects */
315 case 'H': /* Host Name (virtual host) */
316 host_name = strdup (optarg);
317 break;
318 case 'I': /* Server IP-address */
319 server_address = strdup (optarg);
320 break;
321 case 'u': /* URL path */
322 server_url = strdup (optarg);
323 server_url_length = strlen (server_url);
324 break;
325 case 'p': /* Server port */
326 if (!is_intnonneg (optarg))
327 usage2 (_("invalid port number"), optarg);
328 else {
329 server_port = atoi (optarg);
330 specify_port = TRUE;
331 }
332 break;
333 case 'a': /* authorization info */
334 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
335 user_auth[MAX_INPUT_BUFFER - 1] = 0;
336 break;
337 case 'P': /* HTTP POST data in URL encoded format */
338 if (http_method || http_post_data) break;
339 http_method = strdup("POST");
340 http_post_data = strdup (optarg);
341 break;
342 case 's': /* string or substring */
343 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
344 string_expect[MAX_INPUT_BUFFER - 1] = 0;
345 break;
346 case 'e': /* string or substring */
347 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
348 server_expect[MAX_INPUT_BUFFER - 1] = 0;
349 server_expect_yn = 1;
350 break;
351 case 'T': /* Content-type */
352 asprintf (&http_content_type, "%s", optarg);
353 break;
354 #ifndef HAVE_REGEX_H
355 case 'l': /* linespan */
356 case 'r': /* linespan */
357 case 'R': /* linespan */
358 usage (_("check_http: call for regex which was not a compiled option\n"));
359 break;
360 #else
361 case 'l': /* linespan */
362 cflags &= ~REG_NEWLINE;
363 break;
364 case 'R': /* regex */
365 cflags |= REG_ICASE;
366 case 'r': /* regex */
367 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
368 regexp[MAX_RE_SIZE - 1] = 0;
369 errcode = regcomp (&preg, regexp, cflags);
370 if (errcode != 0) {
371 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
372 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
373 return ERROR;
374 }
375 break;
376 #endif
377 case '4':
378 address_family = AF_INET;
379 break;
380 case '6':
381 #ifdef USE_IPV6
382 address_family = AF_INET6;
383 #else
384 usage (_("IPv6 support not available\n"));
385 #endif
386 break;
387 case 'v': /* verbose */
388 verbose = TRUE;
389 break;
390 case 'm': /* min_page_length */
391 min_page_len = atoi (optarg);
392 break;
393 }
394 }
396 c = optind;
398 if (server_address == NULL && c < argc)
399 server_address = strdup (argv[c++]);
401 if (host_name == NULL && c < argc)
402 host_name = strdup (argv[c++]);
404 if (server_address == NULL) {
405 if (host_name == NULL)
406 usage (_("check_http: you must specify a server address or host name\n"));
407 else
408 server_address = strdup (host_name);
409 }
411 if (check_critical_time && critical_time>(double)socket_timeout)
412 socket_timeout = (int)critical_time + 1;
414 if (http_method == NULL)
415 http_method = strdup ("GET");
417 return TRUE;
418 }
419 \f
422 /* written by lauri alanko */
423 static char *
424 base64 (const char *bin, size_t len)
425 {
427 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
428 size_t i = 0, j = 0;
430 char BASE64_END = '=';
431 char base64_table[64];
432 strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
434 while (j < len - 2) {
435 buf[i++] = base64_table[bin[j] >> 2];
436 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
437 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
438 buf[i++] = base64_table[bin[j + 2] & 63];
439 j += 3;
440 }
442 switch (len - j) {
443 case 1:
444 buf[i++] = base64_table[bin[j] >> 2];
445 buf[i++] = base64_table[(bin[j] & 3) << 4];
446 buf[i++] = BASE64_END;
447 buf[i++] = BASE64_END;
448 break;
449 case 2:
450 buf[i++] = base64_table[bin[j] >> 2];
451 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
452 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
453 buf[i++] = BASE64_END;
454 break;
455 case 0:
456 break;
457 }
459 buf[i] = '\0';
460 return buf;
461 }
462 \f
467 int
468 check_http (void)
469 {
470 char *msg;
471 char *status_line;
472 char *status_code;
473 char *header;
474 char *page;
475 char *auth;
476 int http_status;
477 int i = 0;
478 size_t pagesize = 0;
479 char *full_page;
480 char *buf;
481 char *pos;
482 long microsec;
483 double elapsed_time;
484 int page_len = 0;
485 #ifdef HAVE_SSL
486 int sslerr;
487 #endif
489 /* try to connect to the host at the given port number */
490 #ifdef HAVE_SSL
491 if (use_ssl == TRUE) {
493 if (connect_SSL () != OK) {
494 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
495 }
497 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
498 X509_free (server_cert);
499 }
500 else {
501 printf (_("ERROR: Cannot retrieve server certificate.\n"));
502 return STATE_CRITICAL;
503 }
505 }
506 else {
507 #endif
508 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
509 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
510 #ifdef HAVE_SSL
511 }
512 #endif
514 asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
516 /* optionally send the host header info */
517 if (host_name)
518 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
520 /* optionally send the authentication info */
521 if (strlen(user_auth)) {
522 auth = base64 (user_auth, strlen (user_auth));
523 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
524 }
526 /* either send http POST data */
527 if (http_post_data) {
528 if (http_content_type) {
529 asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
530 } else {
531 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
532 }
533 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
534 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
535 }
536 else {
537 /* or just a newline so the server knows we're done with the request */
538 asprintf (&buf, "%s%s", buf, CRLF);
539 }
541 if (verbose)
542 printf ("%s\n", buf);
544 #ifdef HAVE_SSL
545 if (use_ssl == TRUE) {
546 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
547 ERR_print_errors_fp (stderr);
548 return STATE_CRITICAL;
549 }
550 }
551 else {
552 #endif
553 send (sd, buf, strlen (buf), 0);
554 #ifdef HAVE_SSL
555 }
556 #endif
558 /* fetch the page */
559 full_page = strdup("");
560 while ((i = my_recv ()) > 0) {
561 buffer[i] = '\0';
562 asprintf (&full_page, "%s%s", full_page, buffer);
563 pagesize += i;
564 }
566 if (i < 0 && errno != ECONNRESET) {
567 #ifdef HAVE_SSL
568 if (use_ssl) {
569 sslerr=SSL_get_error(ssl, i);
570 if ( sslerr == SSL_ERROR_SSL ) {
571 die (STATE_WARNING, _("Client Certificate Required\n"));
572 } else {
573 die (STATE_CRITICAL, _("Error in recv()\n"));
574 }
575 }
576 else {
577 #endif
578 die (STATE_CRITICAL, _("Error in recv()\n"));
579 #ifdef HAVE_SSL
580 }
581 #endif
582 }
584 /* return a CRITICAL status if we couldn't read any data */
585 if (pagesize == (size_t) 0)
586 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
588 /* close the connection */
589 my_close ();
591 /* reset the alarm */
592 alarm (0);
594 /* leave full_page untouched so we can free it later */
595 page = full_page;
597 if (verbose)
598 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
600 /* find status line and null-terminate it */
601 status_line = page;
602 page += (size_t) strcspn (page, "\r\n");
603 pos = page;
604 page += (size_t) strspn (page, "\r\n");
605 status_line[strcspn(status_line, "\r\n")] = 0;
606 strip (status_line);
607 if (verbose)
608 printf ("STATUS: %s\n", status_line);
610 /* find header info and null-terminate it */
611 header = page;
612 while (strcspn (page, "\r\n") > 0) {
613 page += (size_t) strcspn (page, "\r\n");
614 pos = page;
615 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
616 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
617 page += (size_t) 2;
618 else
619 page += (size_t) 1;
620 }
621 page += (size_t) strspn (page, "\r\n");
622 header[pos - header] = 0;
623 if (verbose)
624 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
626 /* make sure the status line matches the response we are looking for */
627 if (!strstr (status_line, server_expect)) {
628 if (server_port == HTTP_PORT)
629 asprintf (&msg,
630 _("Invalid HTTP response received from host\n"));
631 else
632 asprintf (&msg,
633 _("Invalid HTTP response received from host on port %d\n"),
634 server_port);
635 die (STATE_CRITICAL, "%s", msg);
636 }
638 /* Exit here if server_expect was set by user and not default */
639 if ( server_expect_yn ) {
640 asprintf (&msg,
641 _("HTTP OK: Status line output matched \"%s\"\n"),
642 server_expect);
643 if (verbose)
644 printf ("%s\n",msg);
645 }
646 else {
647 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
648 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
649 /* Status-Code = 3 DIGITS */
651 status_code = strchr (status_line, ' ') + sizeof (char);
652 if (strspn (status_code, "1234567890") != 3)
653 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
655 http_status = atoi (status_code);
657 /* check the return code */
659 if (http_status >= 600 || http_status < 100)
660 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
662 /* server errors result in a critical state */
663 else if (http_status >= 500)
664 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
666 /* client errors result in a warning state */
667 else if (http_status >= 400)
668 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
670 /* check redirected page if specified */
671 else if (http_status >= 300) {
673 if (onredirect == STATE_DEPENDENT)
674 redir (header, status_line);
675 else if (onredirect == STATE_UNKNOWN)
676 printf (_("UNKNOWN"));
677 else if (onredirect == STATE_OK)
678 printf (_("OK"));
679 else if (onredirect == STATE_WARNING)
680 printf (_("WARNING"));
681 else if (onredirect == STATE_CRITICAL)
682 printf (_("CRITICAL"));
683 microsec = deltime (tv);
684 elapsed_time = (double)microsec / 1.0e6;
685 die (onredirect,
686 _(" - %s - %.3f second response time %s%s|%s %s\n"),
687 status_line, elapsed_time, timestamp,
688 (display_html ? "</A>" : ""),
689 perfd_time (elapsed_time), perfd_size (pagesize));
690 } /* end if (http_status >= 300) */
692 } /* end else (server_expect_yn) */
694 /* check elapsed time */
695 microsec = deltime (tv);
696 elapsed_time = (double)microsec / 1.0e6;
697 asprintf (&msg,
698 _("HTTP problem: %s - %.3f second response time %s%s|%s %s\n"),
699 status_line, elapsed_time, timestamp,
700 (display_html ? "</A>" : ""),
701 perfd_time (elapsed_time), perfd_size (pagesize));
702 if (check_critical_time == TRUE && elapsed_time > critical_time)
703 die (STATE_CRITICAL, "%s", msg);
704 if (check_warning_time == TRUE && elapsed_time > warning_time)
705 die (STATE_WARNING, "%s", msg);
707 /* Page and Header content checks go here */
708 /* these checks should be last */
710 if (strlen (string_expect)) {
711 if (strstr (page, string_expect)) {
712 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
713 status_line, elapsed_time,
714 timestamp, (display_html ? "</A>" : ""),
715 perfd_time (elapsed_time), perfd_size (pagesize));
716 exit (STATE_OK);
717 }
718 else {
719 printf (_("CRITICAL - string not found%s|%s %s\n"),
720 (display_html ? "</A>" : ""),
721 perfd_time (elapsed_time), perfd_size (pagesize));
722 exit (STATE_CRITICAL);
723 }
724 }
725 #ifdef HAVE_REGEX_H
726 if (strlen (regexp)) {
727 errcode = regexec (&preg, page, REGS, pmatch, 0);
728 if (errcode == 0) {
729 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
730 status_line, elapsed_time,
731 timestamp, (display_html ? "</A>" : ""),
732 perfd_time (elapsed_time), perfd_size (pagesize));
733 exit (STATE_OK);
734 }
735 else {
736 if (errcode == REG_NOMATCH) {
737 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
738 (display_html ? "</A>" : ""),
739 perfd_time (elapsed_time), perfd_size (pagesize));
740 exit (STATE_CRITICAL);
741 }
742 else {
743 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
744 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
745 exit (STATE_CRITICAL);
746 }
747 }
748 }
749 #endif
751 /* make sure the page is of an appropriate size */
752 page_len = strlen (page);
753 if ((min_page_len > 0) && (page_len < min_page_len)) {
754 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
755 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
756 exit (STATE_WARNING);
757 }
758 /* We only get here if all tests have been passed */
759 asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
760 status_line, page_len, elapsed_time,
761 timestamp, (display_html ? "</A>" : ""),
762 perfd_time (elapsed_time), perfd_size (page_len));
763 die (STATE_OK, "%s", msg);
764 return STATE_UNKNOWN;
765 }
770 /* per RFC 2396 */
771 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
772 #define URI_HTTP "%[HTPShtps]://"
773 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
774 #define URI_PORT ":%[0123456789]"
775 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
776 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
777 #define HD2 URI_HTTP URI_HOST URI_PATH
778 #define HD3 URI_HTTP URI_HOST URI_PORT
779 #define HD4 URI_HTTP URI_HOST
780 #define HD5 URI_PATH
782 void
783 redir (char *pos, char *status_line)
784 {
785 int i = 0;
786 char *x;
787 char xx[2];
788 char type[6];
789 char *addr;
790 char port[6];
791 char *url;
793 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
794 if (addr == NULL)
795 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
797 url = malloc (strcspn (pos, "\r\n"));
798 if (url == NULL)
799 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
801 while (pos) {
803 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
805 pos += (size_t) strcspn (pos, "\r\n");
806 pos += (size_t) strspn (pos, "\r\n");
807 if (strlen(pos) == 0)
808 die (STATE_UNKNOWN,
809 _("UNKNOWN - Could not find redirect location - %s%s\n"),
810 status_line, (display_html ? "</A>" : ""));
811 continue;
812 }
814 pos += i;
815 pos += strspn (pos, " \t\r\n");
817 url = realloc (url, strcspn (pos, "\r\n"));
818 if (url == NULL)
819 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
821 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
822 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
823 use_ssl = server_type_check (type);
824 i = atoi (port);
825 }
827 /* URI_HTTP URI_HOST URI_PATH */
828 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
829 use_ssl = server_type_check (type);
830 i = server_port_check (use_ssl);
831 }
833 /* URI_HTTP URI_HOST URI_PORT */
834 else if(sscanf (pos, HD3, type, addr, port) == 3) {
835 strcpy (url, HTTP_URL);
836 use_ssl = server_type_check (type);
837 i = atoi (port);
838 }
840 /* URI_HTTP URI_HOST */
841 else if(sscanf (pos, HD4, type, addr) == 2) {
842 strcpy (url, HTTP_URL);
843 use_ssl = server_type_check (type);
844 i = server_port_check (use_ssl);
845 }
847 /* URI_PATH */
848 else if (sscanf (pos, HD5, url) == 1) {
849 /* relative url */
850 if ((url[0] != '/')) {
851 if ((x = strrchr(server_url, '/')))
852 *x = '\0';
853 asprintf (&url, "%s/%s", server_url, url);
854 }
855 i = server_port;
856 strcpy (type, server_type);
857 strcpy (addr, host_name);
858 }
860 else {
861 die (STATE_UNKNOWN,
862 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
863 pos, (display_html ? "</A>" : ""));
864 }
866 break;
868 } /* end while (pos) */
870 if (++redir_depth > max_depth)
871 die (STATE_WARNING,
872 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
873 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
875 if (server_port==i &&
876 !strcmp(server_address, addr) &&
877 (host_name && !strcmp(host_name, addr)) &&
878 !strcmp(server_url, url))
879 die (STATE_WARNING,
880 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
881 type, addr, i, url, (display_html ? "</A>" : ""));
883 server_port = i;
884 strcpy (server_type, type);
886 free (host_name);
887 host_name = strdup (addr);
889 free (server_address);
890 server_address = strdup (addr);
892 free (server_url);
893 server_url = strdup (url);
895 check_http ();
896 }
900 int
901 server_type_check (const char *type)
902 {
903 if (strcmp (type, "https"))
904 return FALSE;
905 else
906 return TRUE;
907 }
909 int
910 server_port_check (int ssl_flag)
911 {
912 if (ssl_flag)
913 return HTTPS_PORT;
914 else
915 return HTTP_PORT;
916 }
920 #ifdef HAVE_SSL
921 int connect_SSL (void)
922 {
923 SSL_METHOD *meth;
925 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
926 RAND_seed (randbuff, (int)strlen(randbuff));
927 if (verbose)
928 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
930 /* Initialize SSL context */
931 SSLeay_add_ssl_algorithms ();
932 meth = SSLv23_client_method ();
933 SSL_load_error_strings ();
934 if ((ctx = SSL_CTX_new (meth)) == NULL) {
935 printf (_("CRITICAL - Cannot create SSL context.\n"));
936 return STATE_CRITICAL;
937 }
939 /* Initialize alarm signal handling */
940 signal (SIGALRM, socket_timeout_alarm_handler);
942 /* Set socket timeout */
943 alarm (socket_timeout);
945 /* Save start time */
946 gettimeofday (&tv, NULL);
948 /* Make TCP connection */
949 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
950 /* Do the SSL handshake */
951 if ((ssl = SSL_new (ctx)) != NULL) {
952 SSL_set_cipher_list(ssl, "ALL");
953 SSL_set_fd (ssl, sd);
954 if (SSL_connect (ssl) != -1)
955 return OK;
956 ERR_print_errors_fp (stderr);
957 }
958 else {
959 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
960 }
961 SSL_free (ssl);
962 }
964 SSL_CTX_free (ctx);
965 close (sd);
967 return STATE_CRITICAL;
968 }
969 #endif
971 #ifdef HAVE_SSL
972 int
973 check_certificate (X509 ** certificate)
974 {
975 ASN1_STRING *tm;
976 int offset;
977 struct tm stamp;
978 int days_left;
981 /* Retrieve timestamp of certificate */
982 tm = X509_get_notAfter (*certificate);
984 /* Generate tm structure to process timestamp */
985 if (tm->type == V_ASN1_UTCTIME) {
986 if (tm->length < 10) {
987 printf (_("CRITICAL - Wrong time format in certificate.\n"));
988 return STATE_CRITICAL;
989 }
990 else {
991 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
992 if (stamp.tm_year < 50)
993 stamp.tm_year += 100;
994 offset = 0;
995 }
996 }
997 else {
998 if (tm->length < 12) {
999 printf (_("CRITICAL - Wrong time format in certificate.\n"));
1000 return STATE_CRITICAL;
1001 }
1002 else {
1003 stamp.tm_year =
1004 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1005 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1006 stamp.tm_year -= 1900;
1007 offset = 2;
1008 }
1009 }
1010 stamp.tm_mon =
1011 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1012 stamp.tm_mday =
1013 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1014 stamp.tm_hour =
1015 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1016 stamp.tm_min =
1017 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1018 stamp.tm_sec = 0;
1019 stamp.tm_isdst = -1;
1021 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1022 snprintf
1023 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1024 stamp.tm_mon + 1,
1025 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1027 if (days_left > 0 && days_left <= days_till_exp) {
1028 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1029 return STATE_WARNING;
1030 }
1031 if (days_left < 0) {
1032 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1033 return STATE_CRITICAL;
1034 }
1036 if (days_left == 0) {
1037 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1038 return STATE_WARNING;
1039 }
1041 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1043 return STATE_OK;
1044 }
1045 #endif
1046 \f
1049 char *perfd_time (double elapsed_time)
1050 {
1051 return fperfdata ("time", elapsed_time, "s",
1052 check_warning_time, warning_time,
1053 check_critical_time, critical_time,
1054 TRUE, 0, FALSE, 0);
1055 }
1058 char *perfd_size (int page_len)
1059 {
1060 return perfdata ("size", page_len, "B",
1061 (min_page_len>0?TRUE:FALSE), min_page_len,
1062 (min_page_len>0?TRUE:FALSE), 0,
1063 TRUE, 0, FALSE, 0);
1064 }
1067 int
1068 my_recv (void)
1069 {
1070 int i;
1071 #ifdef HAVE_SSL
1072 if (use_ssl) {
1073 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1074 }
1075 else {
1076 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1077 }
1078 #else
1079 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1080 #endif
1081 return i;
1082 }
1085 int
1086 my_close (void)
1087 {
1088 #ifdef HAVE_SSL
1089 if (use_ssl == TRUE) {
1090 SSL_shutdown (ssl);
1091 SSL_free (ssl);
1092 SSL_CTX_free (ctx);
1093 return 0;
1094 }
1095 else {
1096 #endif
1097 return close (sd);
1098 #ifdef HAVE_SSL
1099 }
1100 #endif
1101 }
1107 \f
1108 void
1109 print_help (void)
1110 {
1111 print_revision (progname, revision);
1113 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1114 printf (_(COPYRIGHT), copyright, email);
1116 printf (_("\
1117 This plugin tests the HTTP service on the specified host. It can test\n\
1118 normal (http) and secure (https) servers, follow redirects, search for\n\
1119 strings and regular expressions, check connection times, and report on\n\
1120 certificate expiration times.\n"));
1122 print_usage ();
1124 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1126 printf (_(UT_HELP_VRSN));
1128 printf (_("\
1129 -H, --hostname=ADDRESS\n\
1130 Host name argument for servers using host headers (virtual host)\n\
1131 -I, --IP-address=ADDRESS\n\
1132 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1133 -p, --port=INTEGER\n\
1134 Port number (default: %d)\n"), HTTP_PORT);
1136 printf (_(UT_IPv46));
1138 #ifdef HAVE_SSL
1139 printf (_("\
1140 -S, --ssl\n\
1141 Connect via SSL\n\
1142 -C, --certificate=INTEGER\n\
1143 Minimum number of days a certificate has to be valid.\n\
1144 (when this option is used the url is not checked.)\n"));
1145 #endif
1147 printf (_("\
1148 -e, --expect=STRING\n\
1149 String to expect in first (status) line of server response (default: %s)\n\
1150 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1151 -s, --string=STRING\n\
1152 String to expect in the content\n\
1153 -u, --url=PATH\n\
1154 URL to GET or POST (default: /)\n\
1155 -P, --post=STRING\n\
1156 URL encoded http POST data\n\
1157 -T, --content-type=STRING\n\
1158 specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
1160 #ifdef HAVE_REGEX_H
1161 printf (_("\
1162 -l, --linespan\n\
1163 Allow regex to span newlines (must precede -r or -R)\n\
1164 -r, --regex, --ereg=STRING\n\
1165 Search page for regex STRING\n\
1166 -R, --eregi=STRING\n\
1167 Search page for case-insensitive regex STRING\n"));
1168 #endif
1170 printf (_("\
1171 -a, --authorization=AUTH_PAIR\n\
1172 Username:password on sites with basic authentication\n\
1173 -L, --link=URL\n\
1174 Wrap output in HTML link (obsoleted by urlize)\n\
1175 -f, --onredirect=<ok|warning|critical|follow>\n\
1176 How to handle redirected pages\n\
1177 -m, --min=INTEGER\n\
1178 Minimum page size required (bytes)\n"));
1180 printf (_(UT_WARN_CRIT));
1182 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1184 printf (_(UT_VERBOSE));
1186 printf (_("\
1187 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1188 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1189 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1190 messages from the host result in STATE_WARNING return values. If you are\n\
1191 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1192 (fully qualified domain name) as the [host_name] argument.\n"));
1194 #ifdef HAVE_SSL
1195 printf (_("\n\
1196 This plugin can also check whether an SSL enabled web server is able to\n\
1197 serve content (optionally within a specified time) or whether the X509 \n\
1198 certificate is still valid for the specified number of days.\n"));
1199 printf (_("\n\
1200 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1201 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1202 STATE_OK will be returned. When the server returns its content but exceeds\n\
1203 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1204 a STATE_CRITICAL will be returned.\n\n"));
1206 printf (_("\
1207 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1208 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1209 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1210 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1211 the certificate is expired.\n"));
1212 #endif
1214 printf (_(UT_SUPPORT));
1216 }
1221 void
1222 print_usage (void)
1223 {
1224 printf (_("\
1225 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1226 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1227 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1228 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1229 [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1230 printf (_(UT_HLP_VRS), progname, progname);
1231 }