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