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