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