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 buffer[MAX_INPUT_BUFFER];
116 int process_arguments (int, char **);
117 static char *base64 (const char *bin, size_t len);
118 int check_http (void);
119 void redir (char *pos, char *status_line);
120 int server_type_check(const char *type);
121 int server_port_check(int ssl_flag);
122 char *perfd_time (long microsec);
123 char *perfd_size (int page_len);
124 int my_recv (void);
125 int my_close (void);
126 void print_help (void);
127 void print_usage (void);
129 int
130 main (int argc, char **argv)
131 {
132 int result = STATE_UNKNOWN;
134 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
135 server_url = strdup(HTTP_URL);
136 server_url_length = strlen(server_url);
137 asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
138 clean_revstring (revision), VERSION);
140 if (process_arguments (argc, argv) == ERROR)
141 usage (_("check_http: could not parse arguments\n"));
143 if (strstr (timestamp, ":")) {
144 if (strstr (server_url, "?"))
145 asprintf (&server_url, "%s&%s", server_url, timestamp);
146 else
147 asprintf (&server_url, "%s?%s", server_url, timestamp);
148 }
150 if (display_html == TRUE)
151 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
152 host_name, server_port, server_url);
154 /* initialize alarm signal handling, set socket timeout, start timer */
155 (void) signal (SIGALRM, socket_timeout_alarm_handler);
156 (void) alarm (socket_timeout);
157 gettimeofday (&tv, NULL);
159 #ifdef HAVE_SSL
160 if (use_ssl && check_cert == TRUE) {
161 if (connect_SSL () != OK)
162 die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
163 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
164 result = check_certificate (&server_cert);
165 X509_free (server_cert);
166 }
167 else {
168 printf (_("ERROR: Cannot retrieve server certificate.\n"));
169 result = STATE_CRITICAL;
170 }
171 SSL_shutdown (ssl);
172 SSL_free (ssl);
173 SSL_CTX_free (ctx);
174 close (sd);
175 }
176 else {
177 result = check_http ();
178 }
179 #else
180 result = check_http ();
181 #endif
182 return result;
183 }
184 \f
187 /* process command-line arguments */
188 int
189 process_arguments (int argc, char **argv)
190 {
191 int c = 1;
193 int option = 0;
194 static struct option longopts[] = {
195 STD_LONG_OPTS,
196 {"file",required_argument,0,'F'},
197 {"link", no_argument, 0, 'L'},
198 {"nohtml", no_argument, 0, 'n'},
199 {"ssl", no_argument, 0, 'S'},
200 {"verbose", no_argument, 0, 'v'},
201 {"post", required_argument, 0, 'P'},
202 {"IP-address", required_argument, 0, 'I'},
203 {"string", required_argument, 0, 's'},
204 {"regex", required_argument, 0, 'r'},
205 {"ereg", required_argument, 0, 'r'},
206 {"eregi", required_argument, 0, 'R'},
207 {"linespan", no_argument, 0, 'l'},
208 {"onredirect", required_argument, 0, 'f'},
209 {"certificate", required_argument, 0, 'C'},
210 {"min", required_argument, 0, 'm'},
211 {"use-ipv4", no_argument, 0, '4'},
212 {"use-ipv6", no_argument, 0, '6'},
213 {0, 0, 0, 0}
214 };
216 if (argc < 2)
217 return ERROR;
219 for (c = 1; c < argc; c++) {
220 if (strcmp ("-to", argv[c]) == 0)
221 strcpy (argv[c], "-t");
222 if (strcmp ("-hn", argv[c]) == 0)
223 strcpy (argv[c], "-H");
224 if (strcmp ("-wt", argv[c]) == 0)
225 strcpy (argv[c], "-w");
226 if (strcmp ("-ct", argv[c]) == 0)
227 strcpy (argv[c], "-c");
228 if (strcmp ("-nohtml", argv[c]) == 0)
229 strcpy (argv[c], "-n");
230 }
232 while (1) {
233 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
234 if (c == -1 || c == EOF)
235 break;
237 switch (c) {
238 case '?': /* usage */
239 usage3 (_("unknown argument"), optopt);
240 break;
241 case 'h': /* help */
242 print_help ();
243 exit (STATE_OK);
244 break;
245 case 'V': /* version */
246 print_revision (progname, revision);
247 exit (STATE_OK);
248 break;
249 case 't': /* timeout period */
250 if (!is_intnonneg (optarg))
251 usage2 (_("timeout interval must be a non-negative integer"), optarg);
252 else
253 socket_timeout = atoi (optarg);
254 break;
255 case 'c': /* critical time threshold */
256 if (!is_intnonneg (optarg))
257 usage2 (_("invalid critical threshold"), optarg);
258 else {
259 critical_time = strtod (optarg, NULL);
260 check_critical_time = TRUE;
261 }
262 break;
263 case 'w': /* warning time threshold */
264 if (!is_intnonneg (optarg))
265 usage2 (_("invalid warning threshold"), optarg);
266 else {
267 warning_time = strtod (optarg, NULL);
268 check_warning_time = TRUE;
269 }
270 break;
271 case 'L': /* show html link */
272 display_html = TRUE;
273 break;
274 case 'n': /* do not show html link */
275 display_html = FALSE;
276 break;
277 case 'S': /* use SSL */
278 #ifndef HAVE_SSL
279 usage (_("check_http: invalid option - SSL is not available\n"));
280 #endif
281 use_ssl = TRUE;
282 if (specify_port == FALSE)
283 server_port = HTTPS_PORT;
284 break;
285 case 'C': /* Check SSL cert validity */
286 #ifdef HAVE_SSL
287 if (!is_intnonneg (optarg))
288 usage2 (_("invalid certificate expiration period"), optarg);
289 else {
290 days_till_exp = atoi (optarg);
291 check_cert = TRUE;
292 }
293 #else
294 usage (_("check_http: invalid option - SSL is not available\n"));
295 #endif
296 break;
297 case 'f': /* onredirect */
298 if (!strcmp (optarg, "follow"))
299 onredirect = STATE_DEPENDENT;
300 if (!strcmp (optarg, "unknown"))
301 onredirect = STATE_UNKNOWN;
302 if (!strcmp (optarg, "ok"))
303 onredirect = STATE_OK;
304 if (!strcmp (optarg, "warning"))
305 onredirect = STATE_WARNING;
306 if (!strcmp (optarg, "critical"))
307 onredirect = STATE_CRITICAL;
308 if (verbose)
309 printf(_("option f:%d \n"), onredirect);
310 break;
311 /* Note: H, I, and u must be malloc'd or will fail on redirects */
312 case 'H': /* Host Name (virtual host) */
313 host_name = strdup (optarg);
314 break;
315 case 'I': /* Server IP-address */
316 server_address = strdup (optarg);
317 break;
318 case 'u': /* URL path */
319 server_url = strdup (optarg);
320 server_url_length = strlen (server_url);
321 break;
322 case 'p': /* Server port */
323 if (!is_intnonneg (optarg))
324 usage2 (_("invalid port number"), optarg);
325 else {
326 server_port = atoi (optarg);
327 specify_port = TRUE;
328 }
329 break;
330 case 'a': /* authorization info */
331 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
332 user_auth[MAX_INPUT_BUFFER - 1] = 0;
333 break;
334 case 'P': /* HTTP POST data in URL encoded format */
335 if (http_method || http_post_data) break;
336 http_method = strdup("POST");
337 http_post_data = strdup (optarg);
338 break;
339 case 's': /* string or substring */
340 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
341 string_expect[MAX_INPUT_BUFFER - 1] = 0;
342 break;
343 case 'e': /* string or substring */
344 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
345 server_expect[MAX_INPUT_BUFFER - 1] = 0;
346 server_expect_yn = 1;
347 break;
348 #ifndef HAVE_REGEX_H
349 case 'l': /* linespan */
350 case 'r': /* linespan */
351 case 'R': /* linespan */
352 usage (_("check_http: call for regex which was not a compiled option\n"));
353 break;
354 #else
355 case 'l': /* linespan */
356 cflags &= ~REG_NEWLINE;
357 break;
358 case 'R': /* regex */
359 cflags |= REG_ICASE;
360 case 'r': /* regex */
361 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
362 regexp[MAX_RE_SIZE - 1] = 0;
363 errcode = regcomp (&preg, regexp, cflags);
364 if (errcode != 0) {
365 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
366 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
367 return ERROR;
368 }
369 break;
370 #endif
371 case '4':
372 address_family = AF_INET;
373 break;
374 case '6':
375 #ifdef USE_IPV6
376 address_family = AF_INET6;
377 #else
378 usage (_("IPv6 support not available\n"));
379 #endif
380 break;
381 case 'v': /* verbose */
382 verbose = TRUE;
383 break;
384 case 'm': /* min_page_length */
385 min_page_len = atoi (optarg);
386 break;
387 }
388 }
390 c = optind;
392 if (server_address == NULL && c < argc)
393 server_address = strdup (argv[c++]);
395 if (host_name == NULL && c < argc)
396 host_name = strdup (argv[c++]);
398 if (server_address == NULL) {
399 if (host_name == NULL)
400 usage (_("check_http: you must specify a server address or host name\n"));
401 else
402 server_address = strdup (host_name);
403 }
405 if (check_critical_time && critical_time>(double)socket_timeout)
406 socket_timeout = (int)critical_time + 1;
408 if (http_method == NULL)
409 http_method = strdup ("GET");
411 return TRUE;
412 }
413 \f
416 /* written by lauri alanko */
417 static char *
418 base64 (const char *bin, size_t len)
419 {
421 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
422 size_t i = 0, j = 0;
424 char BASE64_END = '=';
425 char base64_table[64];
426 strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
428 while (j < len - 2) {
429 buf[i++] = base64_table[bin[j] >> 2];
430 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
431 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
432 buf[i++] = base64_table[bin[j + 2] & 63];
433 j += 3;
434 }
436 switch (len - j) {
437 case 1:
438 buf[i++] = base64_table[bin[j] >> 2];
439 buf[i++] = base64_table[(bin[j] & 3) << 4];
440 buf[i++] = BASE64_END;
441 buf[i++] = BASE64_END;
442 break;
443 case 2:
444 buf[i++] = base64_table[bin[j] >> 2];
445 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
446 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
447 buf[i++] = BASE64_END;
448 break;
449 case 0:
450 break;
451 }
453 buf[i] = '\0';
454 return buf;
455 }
456 \f
461 int
462 check_http (void)
463 {
464 char *msg;
465 char *status_line;
466 char *status_code;
467 char *header;
468 char *page;
469 char *auth;
470 int http_status;
471 int i = 0;
472 size_t pagesize = 0;
473 char *full_page;
474 char *buf;
475 char *pos;
476 long microsec;
477 double elapsed_time;
478 int page_len = 0;
479 #ifdef HAVE_SSL
480 int sslerr;
481 #endif
483 /* try to connect to the host at the given port number */
484 #ifdef HAVE_SSL
485 if (use_ssl == TRUE) {
487 if (connect_SSL () != OK) {
488 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
489 }
491 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
492 X509_free (server_cert);
493 }
494 else {
495 printf (_("ERROR: Cannot retrieve server certificate.\n"));
496 return STATE_CRITICAL;
497 }
499 }
500 else {
501 #endif
502 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
503 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
504 #ifdef HAVE_SSL
505 }
506 #endif
508 asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
510 /* optionally send the host header info */
511 if (host_name)
512 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
514 /* optionally send the authentication info */
515 if (strlen(user_auth)) {
516 auth = base64 (user_auth, strlen (user_auth));
517 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
518 }
520 /* either send http POST data */
521 if (http_post_data) {
522 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
523 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
524 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
525 }
526 else {
527 /* or just a newline so the server knows we're done with the request */
528 asprintf (&buf, "%s%s", buf, CRLF);
529 }
531 if (verbose)
532 printf ("%s\n", buf);
534 #ifdef HAVE_SSL
535 if (use_ssl == TRUE) {
536 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
537 ERR_print_errors_fp (stderr);
538 return STATE_CRITICAL;
539 }
540 }
541 else {
542 #endif
543 send (sd, buf, strlen (buf), 0);
544 #ifdef HAVE_SSL
545 }
546 #endif
548 /* fetch the page */
549 full_page = strdup("");
550 while ((i = my_recv ()) > 0) {
551 buffer[i] = '\0';
552 asprintf (&full_page, "%s%s", full_page, buffer);
553 pagesize += i;
554 }
556 if (i < 0 && errno != ECONNRESET) {
557 #ifdef HAVE_SSL
558 if (use_ssl) {
559 sslerr=SSL_get_error(ssl, i);
560 if ( sslerr == SSL_ERROR_SSL ) {
561 die (STATE_WARNING, _("Client Certificate Required\n"));
562 } else {
563 die (STATE_CRITICAL, _("Error in recv()\n"));
564 }
565 }
566 else {
567 #endif
568 die (STATE_CRITICAL, _("Error in recv()\n"));
569 #ifdef HAVE_SSL
570 }
571 #endif
572 }
574 /* return a CRITICAL status if we couldn't read any data */
575 if (pagesize == (size_t) 0)
576 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
578 /* close the connection */
579 my_close ();
581 /* reset the alarm */
582 alarm (0);
584 /* leave full_page untouched so we can free it later */
585 page = full_page;
587 if (verbose)
588 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
590 /* find status line and null-terminate it */
591 status_line = page;
592 page += (size_t) strcspn (page, "\r\n");
593 pos = page;
594 page += (size_t) strspn (page, "\r\n");
595 status_line[strcspn(status_line, "\r\n")] = 0;
596 strip (status_line);
597 if (verbose)
598 printf ("STATUS: %s\n", status_line);
600 /* find header info and null-terminate it */
601 header = page;
602 while (strcspn (page, "\r\n") > 0) {
603 page += (size_t) strcspn (page, "\r\n");
604 pos = page;
605 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
606 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
607 page += (size_t) 2;
608 else
609 page += (size_t) 1;
610 }
611 page += (size_t) strspn (page, "\r\n");
612 header[pos - header] = 0;
613 if (verbose)
614 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
616 /* make sure the status line matches the response we are looking for */
617 if (!strstr (status_line, server_expect)) {
618 if (server_port == HTTP_PORT)
619 asprintf (&msg,
620 _("Invalid HTTP response received from host\n"));
621 else
622 asprintf (&msg,
623 _("Invalid HTTP response received from host on port %d\n"),
624 server_port);
625 die (STATE_CRITICAL, "%s", msg);
626 }
628 /* Exit here if server_expect was set by user and not default */
629 if ( server_expect_yn ) {
630 asprintf (&msg,
631 _("HTTP OK: Status line output matched \"%s\"\n"),
632 server_expect);
633 if (verbose)
634 printf ("%s\n",msg);
635 }
636 else {
637 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
638 /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
639 /* Status-Code = 3 DIGITS */
641 status_code = strchr (status_line, ' ') + sizeof (char);
642 if (strspn (status_code, "1234567890") != 3)
643 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
645 http_status = atoi (status_code);
647 /* check the return code */
649 if (http_status >= 600 || http_status < 100)
650 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
652 /* server errors result in a critical state */
653 else if (http_status >= 500)
654 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
656 /* client errors result in a warning state */
657 else if (http_status >= 400)
658 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
660 /* check redirected page if specified */
661 else if (http_status >= 300) {
663 if (onredirect == STATE_DEPENDENT)
664 redir (header, status_line);
665 else if (onredirect == STATE_UNKNOWN)
666 printf (_("UNKNOWN"));
667 else if (onredirect == STATE_OK)
668 printf (_("OK"));
669 else if (onredirect == STATE_WARNING)
670 printf (_("WARNING"));
671 else if (onredirect == STATE_CRITICAL)
672 printf (_("CRITICAL"));
673 microsec = deltime (tv);
674 elapsed_time = (double)microsec / 1.0e6;
675 die (onredirect,
676 _(" - %s - %.3f second response time %s%s|%s %s\n"),
677 status_line, elapsed_time, timestamp,
678 (display_html ? "</A>" : ""),
679 perfd_time (microsec), perfd_size (pagesize));
680 } /* end if (http_status >= 300) */
682 } /* end else (server_expect_yn) */
684 /* check elapsed time */
685 microsec = deltime (tv);
686 elapsed_time = (double)microsec / 1.0e6;
687 asprintf (&msg,
688 _("HTTP problem: %s - %.3f second response time %s%s|%s %s\n"),
689 status_line, elapsed_time, timestamp,
690 (display_html ? "</A>" : ""),
691 perfd_time (microsec), perfd_size (pagesize));
692 if (check_critical_time == TRUE && elapsed_time > critical_time)
693 die (STATE_CRITICAL, "%s", msg);
694 if (check_warning_time == TRUE && elapsed_time > warning_time)
695 die (STATE_WARNING, "%s", msg);
697 /* Page and Header content checks go here */
698 /* these checks should be last */
700 if (strlen (string_expect)) {
701 if (strstr (page, string_expect)) {
702 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
703 status_line, elapsed_time,
704 timestamp, (display_html ? "</A>" : ""),
705 perfd_time (microsec), perfd_size (pagesize));
706 exit (STATE_OK);
707 }
708 else {
709 printf (_("CRITICAL - string not found%s|%s %s\n"),
710 (display_html ? "</A>" : ""),
711 perfd_time (microsec), perfd_size (pagesize));
712 exit (STATE_CRITICAL);
713 }
714 }
715 #ifdef HAVE_REGEX_H
716 if (strlen (regexp)) {
717 errcode = regexec (&preg, page, REGS, pmatch, 0);
718 if (errcode == 0) {
719 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
720 status_line, elapsed_time,
721 timestamp, (display_html ? "</A>" : ""),
722 perfd_time (microsec), perfd_size (pagesize));
723 exit (STATE_OK);
724 }
725 else {
726 if (errcode == REG_NOMATCH) {
727 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
728 (display_html ? "</A>" : ""),
729 perfd_time (microsec), perfd_size (pagesize));
730 exit (STATE_CRITICAL);
731 }
732 else {
733 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
734 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
735 exit (STATE_CRITICAL);
736 }
737 }
738 }
739 #endif
741 /* make sure the page is of an appropriate size */
742 page_len = strlen (page);
743 if ((min_page_len > 0) && (page_len < min_page_len)) {
744 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
745 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
746 exit (STATE_WARNING);
747 }
748 /* We only get here if all tests have been passed */
749 asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
750 status_line, page_len, elapsed_time,
751 timestamp, (display_html ? "</A>" : ""),
752 perfd_time (microsec), perfd_size (page_len));
753 die (STATE_OK, "%s", msg);
754 return STATE_UNKNOWN;
755 }
760 /* per RFC 2396 */
761 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
762 #define URI_HTTP "%[HTPShtps]://"
763 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
764 #define URI_PORT ":%[0123456789]"
765 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
766 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
767 #define HD2 URI_HTTP URI_HOST URI_PATH
768 #define HD3 URI_HTTP URI_HOST URI_PORT
769 #define HD4 URI_HTTP URI_HOST
770 #define HD5 URI_PATH
772 void
773 redir (char *pos, char *status_line)
774 {
775 int i = 0;
776 char *x;
777 char xx[2];
778 char type[6];
779 char *addr;
780 char port[6];
781 char *url;
783 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
784 if (addr == NULL)
785 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
787 url = malloc (strcspn (pos, "\r\n"));
788 if (url == NULL)
789 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
791 while (pos) {
793 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
795 pos += (size_t) strcspn (pos, "\r\n");
796 pos += (size_t) strspn (pos, "\r\n");
797 if (strlen(pos) == 0)
798 die (STATE_UNKNOWN,
799 _("UNKNOWN - Could not find redirect location - %s%s\n"),
800 status_line, (display_html ? "</A>" : ""));
801 continue;
802 }
804 pos += i;
805 pos += strspn (pos, " \t\r\n");
807 url = realloc (url, strcspn (pos, "\r\n"));
808 if (url == NULL)
809 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
811 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
812 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
813 use_ssl = server_type_check (type);
814 i = atoi (port);
815 }
817 /* URI_HTTP URI_HOST URI_PATH */
818 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
819 use_ssl = server_type_check (type);
820 i = server_port_check (use_ssl);
821 }
823 /* URI_HTTP URI_HOST URI_PORT */
824 else if(sscanf (pos, HD3, type, addr, port) == 3) {
825 strcpy (url, HTTP_URL);
826 use_ssl = server_type_check (type);
827 i = atoi (port);
828 }
830 /* URI_HTTP URI_HOST */
831 else if(sscanf (pos, HD4, type, addr) == 2) {
832 strcpy (url, HTTP_URL);
833 use_ssl = server_type_check (type);
834 i = server_port_check (use_ssl);
835 }
837 /* URI_PATH */
838 else if (sscanf (pos, HD5, url) == 1) {
839 /* relative url */
840 if ((url[0] != '/')) {
841 if ((x = strrchr(url, '/')))
842 *x = '\0';
843 asprintf (&server_url, "%s/%s", server_url, url);
844 }
845 i = server_port;
846 strcpy (type, server_type);
847 strcpy (addr, host_name);
848 }
850 else {
851 die (STATE_UNKNOWN,
852 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
853 pos, (display_html ? "</A>" : ""));
854 }
856 break;
858 } /* end while (pos) */
860 if (++redir_depth > max_depth)
861 die (STATE_WARNING,
862 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
863 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
865 if (server_port==i &&
866 !strcmp(server_address, addr) &&
867 (host_name && !strcmp(host_name, addr)) &&
868 !strcmp(server_url, url))
869 die (STATE_WARNING,
870 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
871 type, addr, i, url, (display_html ? "</A>" : ""));
873 server_port = i;
874 strcpy (server_type, type);
876 free (host_name);
877 host_name = strdup (addr);
879 free (server_address);
880 server_address = strdup (addr);
882 free (server_url);
883 server_url = strdup (url);
885 check_http ();
886 }
890 int
891 server_type_check (const char *type)
892 {
893 if (strcmp (type, "https"))
894 return FALSE;
895 else
896 return TRUE;
897 }
899 int
900 server_port_check (int ssl_flag)
901 {
902 if (ssl_flag)
903 return HTTPS_PORT;
904 else
905 return HTTP_PORT;
906 }
910 #ifdef HAVE_SSL
911 int connect_SSL (void)
912 {
913 SSL_METHOD *meth;
915 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
916 RAND_seed (randbuff, (int)strlen(randbuff));
917 if (verbose)
918 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
920 /* Initialize SSL context */
921 SSLeay_add_ssl_algorithms ();
922 meth = SSLv23_client_method ();
923 SSL_load_error_strings ();
924 if ((ctx = SSL_CTX_new (meth)) == NULL) {
925 printf (_("CRITICAL - Cannot create SSL context.\n"));
926 return STATE_CRITICAL;
927 }
929 /* Initialize alarm signal handling */
930 signal (SIGALRM, socket_timeout_alarm_handler);
932 /* Set socket timeout */
933 alarm (socket_timeout);
935 /* Save start time */
936 gettimeofday (&tv, NULL);
938 /* Make TCP connection */
939 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
940 /* Do the SSL handshake */
941 if ((ssl = SSL_new (ctx)) != NULL) {
942 SSL_set_cipher_list(ssl, "ALL");
943 SSL_set_fd (ssl, sd);
944 if (SSL_connect (ssl) != -1)
945 return OK;
946 ERR_print_errors_fp (stderr);
947 }
948 else {
949 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
950 }
951 SSL_free (ssl);
952 }
954 SSL_CTX_free (ctx);
955 close (sd);
957 return STATE_CRITICAL;
958 }
959 #endif
961 #ifdef HAVE_SSL
962 int
963 check_certificate (X509 ** certificate)
964 {
965 ASN1_STRING *tm;
966 int offset;
967 struct tm stamp;
968 int days_left;
971 /* Retrieve timestamp of certificate */
972 tm = X509_get_notAfter (*certificate);
974 /* Generate tm structure to process timestamp */
975 if (tm->type == V_ASN1_UTCTIME) {
976 if (tm->length < 10) {
977 printf (_("CRITICAL - Wrong time format in certificate.\n"));
978 return STATE_CRITICAL;
979 }
980 else {
981 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
982 if (stamp.tm_year < 50)
983 stamp.tm_year += 100;
984 offset = 0;
985 }
986 }
987 else {
988 if (tm->length < 12) {
989 printf (_("CRITICAL - Wrong time format in certificate.\n"));
990 return STATE_CRITICAL;
991 }
992 else {
993 stamp.tm_year =
994 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
995 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
996 stamp.tm_year -= 1900;
997 offset = 2;
998 }
999 }
1000 stamp.tm_mon =
1001 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1002 stamp.tm_mday =
1003 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1004 stamp.tm_hour =
1005 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1006 stamp.tm_min =
1007 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1008 stamp.tm_sec = 0;
1009 stamp.tm_isdst = -1;
1011 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1012 snprintf
1013 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1014 stamp.tm_mon + 1,
1015 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1017 if (days_left > 0 && days_left <= days_till_exp) {
1018 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1019 return STATE_WARNING;
1020 }
1021 if (days_left < 0) {
1022 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1023 return STATE_CRITICAL;
1024 }
1026 if (days_left == 0) {
1027 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1028 return STATE_WARNING;
1029 }
1031 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1033 return STATE_OK;
1034 }
1035 #endif
1036 \f
1039 char *perfd_time (long microsec)
1040 {
1041 return perfdata ("time", microsec, "us",
1042 check_warning_time, (int)(1e6*warning_time),
1043 check_critical_time, (int)(1e6*critical_time),
1044 TRUE, 0, FALSE, 0);
1045 }
1048 char *perfd_size (int page_len)
1049 {
1050 return perfdata ("size", page_len, "B",
1051 (min_page_len>0?TRUE:FALSE), min_page_len,
1052 (min_page_len>0?TRUE:FALSE), 0,
1053 TRUE, 0, FALSE, 0);
1054 }
1057 int
1058 my_recv (void)
1059 {
1060 int i;
1061 #ifdef HAVE_SSL
1062 if (use_ssl) {
1063 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1064 }
1065 else {
1066 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1067 }
1068 #else
1069 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1070 #endif
1071 return i;
1072 }
1075 int
1076 my_close (void)
1077 {
1078 #ifdef HAVE_SSL
1079 if (use_ssl == TRUE) {
1080 SSL_shutdown (ssl);
1081 SSL_free (ssl);
1082 SSL_CTX_free (ctx);
1083 return 0;
1084 }
1085 else {
1086 #endif
1087 return close (sd);
1088 #ifdef HAVE_SSL
1089 }
1090 #endif
1091 }
1097 \f
1098 void
1099 print_help (void)
1100 {
1101 print_revision (progname, revision);
1103 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1104 printf (_(COPYRIGHT), copyright, email);
1106 printf (_("\
1107 This plugin tests the HTTP service on the specified host. It can test\n\
1108 normal (http) and secure (https) servers, follow redirects, search for\n\
1109 strings and regular expressions, check connection times, and report on\n\
1110 certificate expiration times.\n"));
1112 print_usage ();
1114 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1116 printf (_(UT_HELP_VRSN));
1118 printf (_("\
1119 -H, --hostname=ADDRESS\n\
1120 Host name argument for servers using host headers (virtual host)\n\
1121 -I, --IP-address=ADDRESS\n\
1122 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1123 -p, --port=INTEGER\n\
1124 Port number (default: %d)\n"), HTTP_PORT);
1126 printf (_(UT_IPv46));
1128 #ifdef HAVE_SSL
1129 printf (_("\
1130 -S, --ssl\n\
1131 Connect via SSL\n\
1132 -C, --certificate=INTEGER\n\
1133 Minimum number of days a certificate has to be valid.\n\
1134 (when this option is used the url is not checked.)\n"));
1135 #endif
1137 printf (_("\
1138 -e, --expect=STRING\n\
1139 String to expect in first (status) line of server response (default: %s)\n\
1140 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1141 -s, --string=STRING\n\
1142 String to expect in the content\n\
1143 -u, --url=PATH\n\
1144 URL to GET or POST (default: /)\n\
1145 -P, --post=STRING\n\
1146 URL encoded http POST data\n"), HTTP_EXPECT);
1148 #ifdef HAVE_REGEX_H
1149 printf (_("\
1150 -l, --linespan\n\
1151 Allow regex to span newlines (must precede -r or -R)\n\
1152 -r, --regex, --ereg=STRING\n\
1153 Search page for regex STRING\n\
1154 -R, --eregi=STRING\n\
1155 Search page for case-insensitive regex STRING\n"));
1156 #endif
1158 printf (_("\
1159 -a, --authorization=AUTH_PAIR\n\
1160 Username:password on sites with basic authentication\n\
1161 -L, --link=URL\n\
1162 Wrap output in HTML link (obsoleted by urlize)\n\
1163 -f, --onredirect=<ok|warning|critical|follow>\n\
1164 How to handle redirected pages\n\
1165 -m, --min=INTEGER\n\
1166 Minimum page size required (bytes)\n"));
1168 printf (_(UT_WARN_CRIT));
1170 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1172 printf (_(UT_VERBOSE));
1174 printf (_("\
1175 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1176 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1177 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1178 messages from the host result in STATE_WARNING return values. If you are\n\
1179 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1180 (fully qualified domain name) as the [host_name] argument.\n"));
1182 #ifdef HAVE_SSL
1183 printf (_("\n\
1184 This plugin can also check whether an SSL enabled web server is able to\n\
1185 serve content (optionally within a specified time) or whether the X509 \n\
1186 certificate is still valid for the specified number of days.\n"));
1187 printf (_("\n\
1188 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1189 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1190 STATE_OK will be returned. When the server returns its content but exceeds\n\
1191 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1192 a STATE_CRITICAL will be returned.\n\n"));
1194 printf (_("\
1195 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1196 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1197 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1198 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1199 the certificate is expired.\n"));
1200 #endif
1202 printf (_(UT_SUPPORT));
1204 }
1209 void
1210 print_usage (void)
1211 {
1212 printf (_("\
1213 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1214 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1215 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1216 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1217 [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1218 printf (_(UT_HLP_VRS), progname, progname);
1219 }