db949329ff77ca52ec5f99b34a3e92961dfa1191
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 *header;
467 char *page;
468 char *auth;
469 int i = 0;
470 size_t pagesize = 0;
471 char *full_page;
472 char *buf;
473 char *pos;
474 long microsec;
475 double elapsed_time;
476 int page_len = 0;
477 #ifdef HAVE_SSL
478 int sslerr;
479 #endif
481 /* try to connect to the host at the given port number */
482 #ifdef HAVE_SSL
483 if (use_ssl == TRUE) {
485 if (connect_SSL () != OK) {
486 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
487 }
489 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
490 X509_free (server_cert);
491 }
492 else {
493 printf (_("ERROR: Cannot retrieve server certificate.\n"));
494 return STATE_CRITICAL;
495 }
497 }
498 else {
499 #endif
500 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
501 die (STATE_CRITICAL, _("Unable to open TCP socket\n"));
502 #ifdef HAVE_SSL
503 }
504 #endif
506 asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
508 /* optionally send the host header info */
509 if (host_name)
510 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
512 /* optionally send the authentication info */
513 if (strlen(user_auth)) {
514 auth = base64 (user_auth, strlen (user_auth));
515 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
516 }
518 /* either send http POST data */
519 if (http_post_data) {
520 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
521 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
522 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
523 }
524 else {
525 /* or just a newline so the server knows we're done with the request */
526 asprintf (&buf, "%s%s", buf, CRLF);
527 }
529 if (verbose)
530 printf ("%s\n", buf);
532 #ifdef HAVE_SSL
533 if (use_ssl == TRUE) {
534 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
535 ERR_print_errors_fp (stderr);
536 return STATE_CRITICAL;
537 }
538 }
539 else {
540 #endif
541 send (sd, buf, strlen (buf), 0);
542 #ifdef HAVE_SSL
543 }
544 #endif
546 /* fetch the page */
547 full_page = strdup("");
548 while ((i = my_recv ()) > 0) {
549 buffer[i] = '\0';
550 asprintf (&full_page, "%s%s", full_page, buffer);
551 pagesize += i;
552 }
554 if (i < 0 && errno != ECONNRESET) {
555 #ifdef HAVE_SSL
556 if (use_ssl) {
557 sslerr=SSL_get_error(ssl, i);
558 if ( sslerr == SSL_ERROR_SSL ) {
559 die (STATE_WARNING, _("Client Certificate Required\n"));
560 } else {
561 die (STATE_CRITICAL, _("Error in recv()\n"));
562 }
563 }
564 else {
565 #endif
566 die (STATE_CRITICAL, _("Error in recv()\n"));
567 #ifdef HAVE_SSL
568 }
569 #endif
570 }
572 /* return a CRITICAL status if we couldn't read any data */
573 if (pagesize == (size_t) 0)
574 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
576 /* close the connection */
577 my_close ();
579 /* reset the alarm */
580 alarm (0);
582 /* leave full_page untouched so we can free it later */
583 page = full_page;
585 if (verbose)
586 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
588 /* find status line and null-terminate it */
589 status_line = page;
590 page += (size_t) strcspn (page, "\r\n");
591 pos = page;
592 page += (size_t) strspn (page, "\r\n");
593 status_line[strcspn(status_line, "\r\n")] = 0;
594 strip (status_line);
595 if (verbose)
596 printf ("STATUS: %s\n", status_line);
598 /* find header info and null-terminate it */
599 header = page;
600 while (strcspn (page, "\r\n") > 0) {
601 page += (size_t) strcspn (page, "\r\n");
602 pos = page;
603 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
604 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
605 page += (size_t) 2;
606 else
607 page += (size_t) 1;
608 }
609 page += (size_t) strspn (page, "\r\n");
610 header[pos - header] = 0;
611 if (verbose)
612 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
614 /* make sure the status line matches the response we are looking for */
615 if (!strstr (status_line, server_expect)) {
616 if (server_port == HTTP_PORT)
617 asprintf (&msg, _("Invalid HTTP response received from host\n"));
618 else
619 asprintf (&msg,
620 _("Invalid HTTP response received from host on port %d\n"),
621 server_port);
622 die (STATE_CRITICAL, "%s", msg);
623 }
625 /* Exit here if server_expect was set by user and not default */
626 if ( server_expect_yn ) {
627 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
628 server_expect);
629 if (verbose)
630 printf ("%s\n",msg);
632 }
633 else {
636 /* check the return code */
637 /* server errors result in a critical state */
638 if (strstr (status_line, "500") || strstr (status_line, "501") ||
639 strstr (status_line, "502") || strstr (status_line, "503") ||
640 strstr (status_line, "504") || strstr (status_line, "505")) {
641 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
642 }
644 /* client errors result in a warning state */
645 if (strstr (status_line, "400") || strstr (status_line, "401") ||
646 strstr (status_line, "402") || strstr (status_line, "403") ||
647 strstr (status_line, "404") || strstr (status_line, "405") ||
648 strstr (status_line, "406") || strstr (status_line, "407") ||
649 strstr (status_line, "408") || strstr (status_line, "409") ||
650 strstr (status_line, "410") || strstr (status_line, "411") ||
651 strstr (status_line, "412") || strstr (status_line, "413") ||
652 strstr (status_line, "414") || strstr (status_line, "415") ||
653 strstr (status_line, "416") || strstr (status_line, "417")) {
654 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
655 }
657 /* check redirected page if specified */
658 if (strstr (status_line, "300") || strstr (status_line, "301") ||
659 strstr (status_line, "302") || strstr (status_line, "303") ||
660 strstr (status_line, "304") || strstr (status_line, "305") ||
661 strstr (status_line, "306")) {
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 (strstr (status_line, "30[0-4]") */
683 } /* end else (server_expect_yn) */
686 /* check elapsed time */
687 microsec = deltime (tv);
688 elapsed_time = (double)microsec / 1.0e6;
689 asprintf (&msg,
690 _("HTTP problem: %s - %.3f second response time %s%s|%s %s\n"),
691 status_line, elapsed_time, timestamp,
692 (display_html ? "</A>" : ""),
693 perfd_time (microsec), perfd_size (pagesize));
694 if (check_critical_time == TRUE && elapsed_time > critical_time)
695 die (STATE_CRITICAL, "%s", msg);
696 if (check_warning_time == TRUE && elapsed_time > warning_time)
697 die (STATE_WARNING, "%s", msg);
699 /* Page and Header content checks go here */
700 /* these checks should be last */
702 if (strlen (string_expect)) {
703 if (strstr (page, string_expect)) {
704 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
705 status_line, elapsed_time,
706 timestamp, (display_html ? "</A>" : ""),
707 perfd_time (microsec), perfd_size (pagesize));
708 exit (STATE_OK);
709 }
710 else {
711 printf (_("CRITICAL - string not found%s|%s %s\n"),
712 (display_html ? "</A>" : ""),
713 perfd_time (microsec), perfd_size (pagesize));
714 exit (STATE_CRITICAL);
715 }
716 }
717 #ifdef HAVE_REGEX_H
718 if (strlen (regexp)) {
719 errcode = regexec (&preg, page, REGS, pmatch, 0);
720 if (errcode == 0) {
721 printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
722 status_line, elapsed_time,
723 timestamp, (display_html ? "</A>" : ""),
724 perfd_time (microsec), perfd_size (pagesize));
725 exit (STATE_OK);
726 }
727 else {
728 if (errcode == REG_NOMATCH) {
729 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
730 (display_html ? "</A>" : ""),
731 perfd_time (microsec), perfd_size (pagesize));
732 exit (STATE_CRITICAL);
733 }
734 else {
735 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
736 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
737 exit (STATE_CRITICAL);
738 }
739 }
740 }
741 #endif
743 /* make sure the page is of an appropriate size */
744 page_len = strlen (page);
745 if ((min_page_len > 0) && (page_len < min_page_len)) {
746 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
747 page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
748 exit (STATE_WARNING);
749 }
750 /* We only get here if all tests have been passed */
751 asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
752 status_line, page_len, elapsed_time,
753 timestamp, (display_html ? "</A>" : ""),
754 perfd_time (microsec), perfd_size (page_len));
755 die (STATE_OK, "%s", msg);
756 return STATE_UNKNOWN;
757 }
762 /* per RFC 2396 */
763 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
764 #define URI_HTTP "%[HTPShtps]://"
765 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
766 #define URI_PORT ":%[0123456789]"
767 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
768 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
769 #define HD2 URI_HTTP URI_HOST URI_PATH
770 #define HD3 URI_HTTP URI_HOST URI_PORT
771 #define HD4 URI_HTTP URI_HOST
772 #define HD5 URI_PATH
774 void
775 redir (char *pos, char *status_line)
776 {
777 int i = 0;
778 char *x;
779 char xx[2];
780 char type[6];
781 char *addr;
782 char port[6];
783 char *url;
785 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
786 if (addr == NULL)
787 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
789 url = malloc (strcspn (pos, "\r\n"));
790 if (url == NULL)
791 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
793 while (pos) {
795 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
797 pos += (size_t) strcspn (pos, "\r\n");
798 pos += (size_t) strspn (pos, "\r\n");
799 if (strlen(pos) == 0)
800 die (STATE_UNKNOWN,
801 _("UNKNOWN - Could not find redirect location - %s%s\n"),
802 status_line, (display_html ? "</A>" : ""));
803 continue;
804 }
806 pos += i;
807 pos += strspn (pos, " \t\r\n");
809 url = realloc (url, strcspn (pos, "\r\n"));
810 if (url == NULL)
811 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
813 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
814 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
815 use_ssl = server_type_check (type);
816 i = atoi (port);
817 }
819 /* URI_HTTP URI_HOST URI_PATH */
820 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
821 use_ssl = server_type_check (type);
822 i = server_port_check (use_ssl);
823 }
825 /* URI_HTTP URI_HOST URI_PORT */
826 else if(sscanf (pos, HD3, type, addr, port) == 3) {
827 strcpy (url, HTTP_URL);
828 use_ssl = server_type_check (type);
829 i = atoi (port);
830 }
832 /* URI_HTTP URI_HOST */
833 else if(sscanf (pos, HD4, type, addr) == 2) {
834 strcpy (url, HTTP_URL);
835 use_ssl = server_type_check (type);
836 i = server_port_check (use_ssl);
837 }
839 /* URI_PATH */
840 else if (sscanf (pos, HD5, url) == 1) {
841 /* relative url */
842 if ((url[0] != '/')) {
843 if ((x = strrchr(url, '/')))
844 *x = '\0';
845 asprintf (&server_url, "%s/%s", server_url, url);
846 }
847 i = server_port;
848 strcpy (type, server_type);
849 strcpy (addr, host_name);
850 }
852 else {
853 die (STATE_UNKNOWN,
854 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
855 pos, (display_html ? "</A>" : ""));
856 }
858 break;
860 } /* end while (pos) */
862 if (++redir_depth > max_depth)
863 die (STATE_WARNING,
864 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
865 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
867 if (server_port==i &&
868 !strcmp(server_address, addr) &&
869 (host_name && !strcmp(host_name, addr)) &&
870 !strcmp(server_url, url))
871 die (STATE_WARNING,
872 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
873 type, addr, i, url, (display_html ? "</A>" : ""));
875 server_port = i;
876 strcpy (server_type, type);
878 free (host_name);
879 host_name = strdup (addr);
881 free (server_address);
882 server_address = strdup (addr);
884 free (server_url);
885 server_url = strdup (url);
887 check_http ();
888 }
892 int
893 server_type_check (const char *type)
894 {
895 if (strcmp (type, "https"))
896 return FALSE;
897 else
898 return TRUE;
899 }
901 int
902 server_port_check (int ssl_flag)
903 {
904 if (ssl_flag)
905 return HTTPS_PORT;
906 else
907 return HTTP_PORT;
908 }
912 #ifdef HAVE_SSL
913 int connect_SSL (void)
914 {
915 SSL_METHOD *meth;
917 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
918 RAND_seed (randbuff, (int)strlen(randbuff));
919 if (verbose)
920 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
922 /* Initialize SSL context */
923 SSLeay_add_ssl_algorithms ();
924 meth = SSLv23_client_method ();
925 SSL_load_error_strings ();
926 if ((ctx = SSL_CTX_new (meth)) == NULL) {
927 printf (_("CRITICAL - Cannot create SSL context.\n"));
928 return STATE_CRITICAL;
929 }
931 /* Initialize alarm signal handling */
932 signal (SIGALRM, socket_timeout_alarm_handler);
934 /* Set socket timeout */
935 alarm (socket_timeout);
937 /* Save start time */
938 gettimeofday (&tv, NULL);
940 /* Make TCP connection */
941 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
942 /* Do the SSL handshake */
943 if ((ssl = SSL_new (ctx)) != NULL) {
944 SSL_set_cipher_list(ssl, "ALL");
945 SSL_set_fd (ssl, sd);
946 if (SSL_connect (ssl) != -1)
947 return OK;
948 ERR_print_errors_fp (stderr);
949 }
950 else {
951 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
952 }
953 SSL_free (ssl);
954 }
956 SSL_CTX_free (ctx);
957 close (sd);
959 return STATE_CRITICAL;
960 }
961 #endif
963 #ifdef HAVE_SSL
964 int
965 check_certificate (X509 ** certificate)
966 {
967 ASN1_STRING *tm;
968 int offset;
969 struct tm stamp;
970 int days_left;
973 /* Retrieve timestamp of certificate */
974 tm = X509_get_notAfter (*certificate);
976 /* Generate tm structure to process timestamp */
977 if (tm->type == V_ASN1_UTCTIME) {
978 if (tm->length < 10) {
979 printf (_("CRITICAL - Wrong time format in certificate.\n"));
980 return STATE_CRITICAL;
981 }
982 else {
983 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
984 if (stamp.tm_year < 50)
985 stamp.tm_year += 100;
986 offset = 0;
987 }
988 }
989 else {
990 if (tm->length < 12) {
991 printf (_("CRITICAL - Wrong time format in certificate.\n"));
992 return STATE_CRITICAL;
993 }
994 else {
995 stamp.tm_year =
996 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
997 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
998 stamp.tm_year -= 1900;
999 offset = 2;
1000 }
1001 }
1002 stamp.tm_mon =
1003 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1004 stamp.tm_mday =
1005 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1006 stamp.tm_hour =
1007 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1008 stamp.tm_min =
1009 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1010 stamp.tm_sec = 0;
1011 stamp.tm_isdst = -1;
1013 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1014 snprintf
1015 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1016 stamp.tm_mon + 1,
1017 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1019 if (days_left > 0 && days_left <= days_till_exp) {
1020 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1021 return STATE_WARNING;
1022 }
1023 if (days_left < 0) {
1024 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1025 return STATE_CRITICAL;
1026 }
1028 if (days_left == 0) {
1029 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1030 return STATE_WARNING;
1031 }
1033 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1035 return STATE_OK;
1036 }
1037 #endif
1038 \f
1041 char *perfd_time (long microsec)
1042 {
1043 return perfdata ("time", microsec, "us",
1044 check_warning_time, (int)(1e6*warning_time),
1045 check_critical_time, (int)(1e6*critical_time),
1046 TRUE, 0, FALSE, 0);
1047 }
1050 char *perfd_size (int page_len)
1051 {
1052 return perfdata ("size", page_len, "B",
1053 (min_page_len>0?TRUE:FALSE), min_page_len,
1054 (min_page_len>0?TRUE:FALSE), 0,
1055 TRUE, 0, FALSE, 0);
1056 }
1059 int
1060 my_recv (void)
1061 {
1062 int i;
1063 #ifdef HAVE_SSL
1064 if (use_ssl) {
1065 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1066 }
1067 else {
1068 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1069 }
1070 #else
1071 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1072 #endif
1073 return i;
1074 }
1077 int
1078 my_close (void)
1079 {
1080 #ifdef HAVE_SSL
1081 if (use_ssl == TRUE) {
1082 SSL_shutdown (ssl);
1083 SSL_free (ssl);
1084 SSL_CTX_free (ctx);
1085 return 0;
1086 }
1087 else {
1088 #endif
1089 return close (sd);
1090 #ifdef HAVE_SSL
1091 }
1092 #endif
1093 }
1099 \f
1100 void
1101 print_help (void)
1102 {
1103 print_revision (progname, revision);
1105 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1106 printf (_(COPYRIGHT), copyright, email);
1108 printf (_("\
1109 This plugin tests the HTTP service on the specified host. It can test\n\
1110 normal (http) and secure (https) servers, follow redirects, search for\n\
1111 strings and regular expressions, check connection times, and report on\n\
1112 certificate expiration times.\n"));
1114 print_usage ();
1116 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1118 printf (_(UT_HELP_VRSN));
1120 printf (_("\
1121 -H, --hostname=ADDRESS\n\
1122 Host name argument for servers using host headers (virtual host)\n\
1123 -I, --IP-address=ADDRESS\n\
1124 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1125 -p, --port=INTEGER\n\
1126 Port number (default: %d)\n"), HTTP_PORT);
1128 printf (_(UT_IPv46));
1130 #ifdef HAVE_SSL
1131 printf (_("\
1132 -S, --ssl\n\
1133 Connect via SSL\n\
1134 -C, --certificate=INTEGER\n\
1135 Minimum number of days a certificate has to be valid.\n\
1136 (when this option is used the url is not checked.)\n"));
1137 #endif
1139 printf (_("\
1140 -e, --expect=STRING\n\
1141 String to expect in first (status) line of server response (default: %s)\n\
1142 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1143 -s, --string=STRING\n\
1144 String to expect in the content\n\
1145 -u, --url=PATH\n\
1146 URL to GET or POST (default: /)\n\
1147 -P, --post=STRING\n\
1148 URL encoded http POST data\n"), HTTP_EXPECT);
1150 #ifdef HAVE_REGEX_H
1151 printf (_("\
1152 -l, --linespan\n\
1153 Allow regex to span newlines (must precede -r or -R)\n\
1154 -r, --regex, --ereg=STRING\n\
1155 Search page for regex STRING\n\
1156 -R, --eregi=STRING\n\
1157 Search page for case-insensitive regex STRING\n"));
1158 #endif
1160 printf (_("\
1161 -a, --authorization=AUTH_PAIR\n\
1162 Username:password on sites with basic authentication\n\
1163 -L, --link=URL\n\
1164 Wrap output in HTML link (obsoleted by urlize)\n\
1165 -f, --onredirect=<ok|warning|critical|follow>\n\
1166 How to handle redirected pages\n\
1167 -m, --min=INTEGER\n\
1168 Minimum page size required (bytes)\n"));
1170 printf (_(UT_WARN_CRIT));
1172 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1174 printf (_(UT_VERBOSE));
1176 printf (_("\
1177 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1178 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1179 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1180 messages from the host result in STATE_WARNING return values. If you are\n\
1181 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1182 (fully qualified domain name) as the [host_name] argument.\n"));
1184 #ifdef HAVE_SSL
1185 printf (_("\n\
1186 This plugin can also check whether an SSL enabled web server is able to\n\
1187 serve content (optionally within a specified time) or whether the X509 \n\
1188 certificate is still valid for the specified number of days.\n"));
1189 printf (_("\n\
1190 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1191 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1192 STATE_OK will be returned. When the server returns its content but exceeds\n\
1193 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1194 a STATE_CRITICAL will be returned.\n\n"));
1196 printf (_("\
1197 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1198 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1199 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1200 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1201 the certificate is expired.\n"));
1202 #endif
1204 printf (_(UT_SUPPORT));
1206 }
1211 void
1212 print_usage (void)
1213 {
1214 printf (_("\
1215 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1216 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1217 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1218 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1219 [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1220 printf (_(UT_HLP_VRS), progname, progname);
1221 }