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 if (verbose)
479 printf ("%s://%s:%d%s [%s]\n", server_type, server_address, server_port, server_url, host_name);
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 if (verbose)
519 printf ("%s://%s:%d%s\n", server_type, server_address, server_port, server_url);
521 /* either send http POST data */
522 if (http_post_data) {
523 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
524 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
525 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
526 }
527 else {
528 /* or just a newline so the server knows we're done with the request */
529 asprintf (&buf, "%s%s", buf, CRLF);
530 }
532 if (verbose)
533 printf ("%s\n", buf);
535 #ifdef HAVE_SSL
536 if (use_ssl == TRUE) {
537 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
538 ERR_print_errors_fp (stderr);
539 return STATE_CRITICAL;
540 }
541 }
542 else {
543 #endif
544 send (sd, buf, strlen (buf), 0);
545 #ifdef HAVE_SSL
546 }
547 #endif
549 /* fetch the page */
550 full_page = strdup("");
551 while ((i = my_recv ()) > 0) {
552 buffer[i] = '\0';
553 asprintf (&full_page, "%s%s", full_page, buffer);
554 pagesize += i;
555 }
557 if (i < 0 && errno != ECONNRESET) {
558 #ifdef HAVE_SSL
559 if (use_ssl) {
560 sslerr=SSL_get_error(ssl, i);
561 if ( sslerr == SSL_ERROR_SSL ) {
562 die (STATE_WARNING, _("Client Certificate Required\n"));
563 } else {
564 die (STATE_CRITICAL, _("Error in recv()\n"));
565 }
566 }
567 else {
568 #endif
569 die (STATE_CRITICAL, _("Error in recv()\n"));
570 #ifdef HAVE_SSL
571 }
572 #endif
573 }
575 /* return a CRITICAL status if we couldn't read any data */
576 if (pagesize == (size_t) 0)
577 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
579 /* close the connection */
580 my_close ();
582 /* reset the alarm */
583 alarm (0);
585 /* leave full_page untouched so we can free it later */
586 page = full_page;
588 if (verbose)
589 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
591 /* find status line and null-terminate it */
592 status_line = page;
593 page += (size_t) strcspn (page, "\r\n");
594 pos = page;
595 page += (size_t) strspn (page, "\r\n");
596 status_line[strcspn(status_line, "\r\n")] = 0;
597 strip (status_line);
598 if (verbose)
599 printf ("STATUS: %s\n", status_line);
601 /* find header info and null-terminate it */
602 header = page;
603 while (strcspn (page, "\r\n") > 0) {
604 page += (size_t) strcspn (page, "\r\n");
605 pos = page;
606 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
607 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
608 page += (size_t) 2;
609 else
610 page += (size_t) 1;
611 }
612 page += (size_t) strspn (page, "\r\n");
613 header[pos - header] = 0;
614 if (verbose)
615 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
617 /* make sure the status line matches the response we are looking for */
618 if (!strstr (status_line, server_expect)) {
619 if (server_port == HTTP_PORT)
620 asprintf (&msg, _("Invalid HTTP response received from host\n"));
621 else
622 asprintf (&msg,
623 _("Invalid HTTP response received from host on port %d\n"),
624 server_port);
625 die (STATE_CRITICAL, "%s", msg);
626 }
628 /* Exit here if server_expect was set by user and not default */
629 if ( server_expect_yn ) {
630 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
631 server_expect);
632 if (verbose)
633 printf ("%s\n",msg);
635 }
636 else {
639 /* check the return code */
640 /* server errors result in a critical state */
641 if (strstr (status_line, "500") || strstr (status_line, "501") ||
642 strstr (status_line, "502") || strstr (status_line, "503") ||
643 strstr (status_line, "504") || strstr (status_line, "505")) {
644 die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
645 }
647 /* client errors result in a warning state */
648 if (strstr (status_line, "400") || strstr (status_line, "401") ||
649 strstr (status_line, "402") || strstr (status_line, "403") ||
650 strstr (status_line, "404") || strstr (status_line, "405") ||
651 strstr (status_line, "406") || strstr (status_line, "407") ||
652 strstr (status_line, "408") || strstr (status_line, "409") ||
653 strstr (status_line, "410") || strstr (status_line, "411") ||
654 strstr (status_line, "412") || strstr (status_line, "413") ||
655 strstr (status_line, "414") || strstr (status_line, "415") ||
656 strstr (status_line, "416") || strstr (status_line, "417")) {
657 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
658 }
660 /* check redirected page if specified */
661 if (strstr (status_line, "300") || strstr (status_line, "301") ||
662 strstr (status_line, "302") || strstr (status_line, "303") ||
663 strstr (status_line, "304") || strstr (status_line, "305") ||
664 strstr (status_line, "306")) {
666 if (onredirect == STATE_DEPENDENT)
667 redir (header, status_line);
668 else if (onredirect == STATE_UNKNOWN)
669 printf (_("UNKNOWN"));
670 else if (onredirect == STATE_OK)
671 printf (_("OK"));
672 else if (onredirect == STATE_WARNING)
673 printf (_("WARNING"));
674 else if (onredirect == STATE_CRITICAL)
675 printf (_("CRITICAL"));
676 microsec = deltime (tv);
677 elapsed_time = (double)microsec / 1.0e6;
678 asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
679 status_line, elapsed_time, timestamp,
680 (display_html ? "</A>" : ""), microsec, pagesize);
681 die (onredirect, "%s", msg);
682 } /* end if (strstr (status_line, "30[0-4]") */
685 } /* end else (server_expect_yn) */
688 /* check elapsed time */
689 microsec = deltime (tv);
690 elapsed_time = (double)microsec / 1.0e6;
691 asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
692 status_line, elapsed_time, timestamp,
693 (display_html ? "</A>" : ""), microsec, 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|time=%ldus size=%dB\n"),
705 status_line, elapsed_time,
706 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
707 exit (STATE_OK);
708 }
709 else {
710 printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
711 (display_html ? "</A>" : ""), microsec, pagesize);
712 exit (STATE_CRITICAL);
713 }
714 }
715 #ifdef HAVE_REGEX_H
716 if (strlen (regexp)) {
717 errcode = regexec (&preg, page, REGS, pmatch, 0);
718 if (errcode == 0) {
719 printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
720 status_line, elapsed_time,
721 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
722 exit (STATE_OK);
723 }
724 else {
725 if (errcode == REG_NOMATCH) {
726 printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
727 (display_html ? "</A>" : ""), microsec, pagesize);
728 exit (STATE_CRITICAL);
729 }
730 else {
731 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
732 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
733 exit (STATE_CRITICAL);
734 }
735 }
736 }
737 #endif
739 /* make sure the page is of an appropriate size */
740 page_len = strlen (page);
741 if ((min_page_len > 0) && (page_len < min_page_len)) {
742 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
743 (display_html ? "</A>" : ""), page_len );
744 exit (STATE_WARNING);
745 }
746 /* We only get here if all tests have been passed */
747 asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
748 status_line, elapsed_time,
749 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
750 die (STATE_OK, "%s", msg);
751 return STATE_UNKNOWN;
752 }
757 /* per RFC 2396 */
758 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
759 #define URI_HTTP "%[HTPShtps]://"
760 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
761 #define URI_PORT ":%[0123456789]"
762 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
763 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
764 #define HD2 URI_HTTP URI_HOST URI_PATH
765 #define HD3 URI_HTTP URI_HOST URI_PORT
766 #define HD4 URI_HTTP URI_HOST
767 #define HD5 URI_PATH
769 int
770 redir (char *pos, char *status_line)
771 {
772 int i = 0;
773 char *x;
774 char xx[2];
775 char type[6];
776 char *addr;
777 char port[6];
778 char *url;
780 addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
781 if (addr == NULL)
782 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
784 url = malloc (strcspn (pos, "\r\n"));
785 if (url == NULL)
786 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
788 while (pos) {
790 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) > 0) {
792 pos += i;
793 pos += strspn (pos, " \t\r\n");
795 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
796 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
797 use_ssl = server_type_check (type);
798 i = atoi (port);
799 }
801 /* URI_HTTP URI_HOST URI_PATH */
802 else if (sscanf (pos, HD2, type, addr, url) == 3 ) {
803 use_ssl = server_type_check (type);
804 i = server_port_check (use_ssl);
805 }
807 /* URI_HTTP URI_HOST URI_PORT */
808 else if(sscanf (pos, HD3, type, addr, port) == 3) {
809 strcpy (url, HTTP_URL);
810 use_ssl = server_type_check (type);
811 i = atoi (port);
812 }
814 /* URI_HTTP URI_HOST */
815 else if(sscanf (pos, HD4, type, addr) == 2) {
816 strcpy (url, HTTP_URL);
817 use_ssl = server_type_check (type);
818 i = server_port_check (use_ssl);
819 }
821 /* URI_PATH */
822 else if (sscanf (pos, HD5, url) == 1) {
823 /* relative url */
824 if ((url[0] != '/')) {
825 if ((x = strrchr(url, '/')))
826 *x = '\0';
827 asprintf (&server_url, "%s/%s", server_url, url);
828 }
829 i = server_port;
830 strcpy (type, server_type);
831 strcpy (addr, host_name);
832 }
834 else {
835 die (STATE_UNKNOWN,
836 _("UNKNOWN - Could not parse redirect location - %s%s\n"),
837 pos, (display_html ? "</A>" : ""));
838 }
840 break;
842 } else {
844 pos += (size_t) strcspn (pos, "\r\n");
845 pos += (size_t) strspn (pos, "\r\n");
846 if (strlen(pos) == 0)
847 die (STATE_UNKNOWN,
848 _("UNKNOWN - Could not find redirect location - %s%s\n"),
849 status_line, (display_html ? "</A>" : ""));
851 }
853 } /* end while (pos) */
855 if (++redir_depth > max_depth)
856 die (STATE_WARNING,
857 _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
858 max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
860 if (server_port==i &&
861 !strcmp(server_address, addr) &&
862 (host_name && !strcmp(host_name, addr)) &&
863 !strcmp(server_url, url))
864 die (STATE_WARNING,
865 _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
866 type, addr, i, url, (display_html ? "</A>" : ""));
868 server_port = i;
869 strcpy (server_type, type);
871 free (host_name);
872 host_name = strdup (addr);
874 free (server_address);
875 server_address = strdup (addr);
877 free (server_url);
878 server_url = strdup (url);
880 return check_http ();
881 }
885 int
886 server_type_check (const char *type)
887 {
888 if (strcmp (type, "https"))
889 return FALSE;
890 else
891 return TRUE;
892 }
894 int
895 server_port_check (int ssl_flag)
896 {
897 if (ssl_flag)
898 return HTTPS_PORT;
899 else
900 return HTTP_PORT;
901 }
905 #ifdef HAVE_SSL
906 int connect_SSL (void)
907 {
908 SSL_METHOD *meth;
910 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
911 RAND_seed (randbuff, (int)strlen(randbuff));
912 if (verbose)
913 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
915 /* Initialize SSL context */
916 SSLeay_add_ssl_algorithms ();
917 meth = SSLv23_client_method ();
918 SSL_load_error_strings ();
919 if ((ctx = SSL_CTX_new (meth)) == NULL) {
920 printf (_("CRITICAL - Cannot create SSL context.\n"));
921 return STATE_CRITICAL;
922 }
924 /* Initialize alarm signal handling */
925 signal (SIGALRM, socket_timeout_alarm_handler);
927 /* Set socket timeout */
928 alarm (socket_timeout);
930 /* Save start time */
931 gettimeofday (&tv, NULL);
933 /* Make TCP connection */
934 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
935 /* Do the SSL handshake */
936 if ((ssl = SSL_new (ctx)) != NULL) {
937 SSL_set_cipher_list(ssl, "ALL");
938 SSL_set_fd (ssl, sd);
939 if (SSL_connect (ssl) != -1)
940 return OK;
941 ERR_print_errors_fp (stderr);
942 }
943 else {
944 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
945 }
946 SSL_free (ssl);
947 }
949 SSL_CTX_free (ctx);
950 close (sd);
952 return STATE_CRITICAL;
953 }
954 #endif
956 #ifdef HAVE_SSL
957 int
958 check_certificate (X509 ** certificate)
959 {
960 ASN1_STRING *tm;
961 int offset;
962 struct tm stamp;
963 int days_left;
966 /* Retrieve timestamp of certificate */
967 tm = X509_get_notAfter (*certificate);
969 /* Generate tm structure to process timestamp */
970 if (tm->type == V_ASN1_UTCTIME) {
971 if (tm->length < 10) {
972 printf (_("CRITICAL - Wrong time format in certificate.\n"));
973 return STATE_CRITICAL;
974 }
975 else {
976 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
977 if (stamp.tm_year < 50)
978 stamp.tm_year += 100;
979 offset = 0;
980 }
981 }
982 else {
983 if (tm->length < 12) {
984 printf (_("CRITICAL - Wrong time format in certificate.\n"));
985 return STATE_CRITICAL;
986 }
987 else {
988 stamp.tm_year =
989 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
990 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
991 stamp.tm_year -= 1900;
992 offset = 2;
993 }
994 }
995 stamp.tm_mon =
996 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
997 stamp.tm_mday =
998 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
999 stamp.tm_hour =
1000 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1001 stamp.tm_min =
1002 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1003 stamp.tm_sec = 0;
1004 stamp.tm_isdst = -1;
1006 days_left = (mktime (&stamp) - time (NULL)) / 86400;
1007 snprintf
1008 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1009 stamp.tm_mon + 1,
1010 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1012 if (days_left > 0 && days_left <= days_till_exp) {
1013 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1014 return STATE_WARNING;
1015 }
1016 if (days_left < 0) {
1017 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1018 return STATE_CRITICAL;
1019 }
1021 if (days_left == 0) {
1022 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1023 return STATE_WARNING;
1024 }
1026 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1028 return STATE_OK;
1029 }
1030 #endif
1031 \f
1034 int
1035 my_recv (void)
1036 {
1037 int i;
1038 #ifdef HAVE_SSL
1039 if (use_ssl) {
1040 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1041 }
1042 else {
1043 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1044 }
1045 #else
1046 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1047 #endif
1048 return i;
1049 }
1052 int
1053 my_close (void)
1054 {
1055 #ifdef HAVE_SSL
1056 if (use_ssl == TRUE) {
1057 SSL_shutdown (ssl);
1058 SSL_free (ssl);
1059 SSL_CTX_free (ctx);
1060 return 0;
1061 }
1062 else {
1063 #endif
1064 return close (sd);
1065 #ifdef HAVE_SSL
1066 }
1067 #endif
1068 }
1074 \f
1075 void
1076 print_help (void)
1077 {
1078 print_revision (progname, revision);
1080 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1081 printf (_(COPYRIGHT), copyright, email);
1083 printf (_("\
1084 This plugin tests the HTTP service on the specified host. It can test\n\
1085 normal (http) and secure (https) servers, follow redirects, search for\n\
1086 strings and regular expressions, check connection times, and report on\n\
1087 certificate expiration times.\n"));
1089 print_usage ();
1091 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1093 printf (_(UT_HELP_VRSN));
1095 printf (_("\
1096 -H, --hostname=ADDRESS\n\
1097 Host name argument for servers using host headers (virtual host)\n\
1098 -I, --IP-address=ADDRESS\n\
1099 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1100 -p, --port=INTEGER\n\
1101 Port number (default: %d)\n"), HTTP_PORT);
1103 printf (_(UT_IPv46));
1105 #ifdef HAVE_SSL
1106 printf (_("\
1107 -S, --ssl\n\
1108 Connect via SSL\n\
1109 -C, --certificate=INTEGER\n\
1110 Minimum number of days a certificate has to be valid.\n\
1111 (when this option is used the url is not checked.)\n"));
1112 #endif
1114 printf (_("\
1115 -e, --expect=STRING\n\
1116 String to expect in first (status) line of server response (default: %s)\n\
1117 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1118 -s, --string=STRING\n\
1119 String to expect in the content\n\
1120 -u, --url=PATH\n\
1121 URL to GET or POST (default: /)\n\
1122 -P, --post=STRING\n\
1123 URL encoded http POST data\n"), HTTP_EXPECT);
1125 #ifdef HAVE_REGEX_H
1126 printf (_("\
1127 -l, --linespan\n\
1128 Allow regex to span newlines (must precede -r or -R)\n\
1129 -r, --regex, --ereg=STRING\n\
1130 Search page for regex STRING\n\
1131 -R, --eregi=STRING\n\
1132 Search page for case-insensitive regex STRING\n"));
1133 #endif
1135 printf (_("\
1136 -a, --authorization=AUTH_PAIR\n\
1137 Username:password on sites with basic authentication\n\
1138 -L, --link=URL\n\
1139 Wrap output in HTML link (obsoleted by urlize)\n\
1140 -f, --onredirect=<ok|warning|critical|follow>\n\
1141 How to handle redirected pages\n\
1142 -m, --min=INTEGER\n\
1143 Minimum page size required (bytes)\n"));
1145 printf (_(UT_WARN_CRIT));
1147 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1149 printf (_(UT_VERBOSE));
1151 printf (_("\
1152 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1153 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1154 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1155 messages from the host result in STATE_WARNING return values. If you are\n\
1156 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1157 (fully qualified domain name) as the [host_name] argument.\n"));
1159 #ifdef HAVE_SSL
1160 printf (_("\n\
1161 This plugin can also check whether an SSL enabled web server is able to\n\
1162 serve content (optionally within a specified time) or whether the X509 \n\
1163 certificate is still valid for the specified number of days.\n"));
1164 printf (_("\n\
1165 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1166 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1167 STATE_OK will be returned. When the server returns its content but exceeds\n\
1168 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1169 a STATE_CRITICAL will be returned.\n\n"));
1171 printf (_("\
1172 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1173 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1174 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1175 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1176 the certificate is expired.\n"));
1177 #endif
1179 printf (_(UT_SUPPORT));
1181 }
1186 void
1187 print_usage (void)
1188 {
1189 printf (_("\
1190 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1191 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1192 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1193 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1194 [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1195 printf (_(UT_HLP_VRS), progname, progname);
1196 }