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