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