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