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 = strdup (optarg);
308 break;
309 case 'I': /* Server IP-address */
310 server_address = strdup (optarg);
311 break;
312 case 'u': /* URL path */
313 server_url = strdup (optarg);
314 server_url_length = strlen (server_url);
315 break;
316 case 'p': /* Server port */
317 if (!is_intnonneg (optarg))
318 usage2 (_("invalid port number"), optarg);
319 else {
320 server_port = atoi (optarg);
321 specify_port = TRUE;
322 }
323 break;
324 case 'a': /* authorization info */
325 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
326 user_auth[MAX_INPUT_BUFFER - 1] = 0;
327 break;
328 case 'P': /* HTTP POST data in URL encoded format */
329 if (http_method || http_post_data) break;
330 http_method = strdup("POST");
331 http_post_data = strdup (optarg);
332 break;
333 case 's': /* string or substring */
334 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
335 string_expect[MAX_INPUT_BUFFER - 1] = 0;
336 break;
337 case 'e': /* string or substring */
338 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
339 server_expect[MAX_INPUT_BUFFER - 1] = 0;
340 server_expect_yn = 1;
341 break;
342 #ifndef HAVE_REGEX_H
343 case 'l': /* linespan */
344 case 'r': /* linespan */
345 case 'R': /* linespan */
346 usage (_("check_http: call for regex which was not a compiled option\n"));
347 break;
348 #else
349 case 'l': /* linespan */
350 cflags &= ~REG_NEWLINE;
351 break;
352 case 'R': /* regex */
353 cflags |= REG_ICASE;
354 case 'r': /* regex */
355 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
356 regexp[MAX_RE_SIZE - 1] = 0;
357 errcode = regcomp (&preg, regexp, cflags);
358 if (errcode != 0) {
359 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
360 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
361 return ERROR;
362 }
363 break;
364 #endif
365 case '4':
366 address_family = AF_INET;
367 break;
368 case '6':
369 #ifdef USE_IPV6
370 address_family = AF_INET6;
371 #else
372 usage (_("IPv6 support not available\n"));
373 #endif
374 break;
375 case 'v': /* verbose */
376 verbose = TRUE;
377 break;
378 case 'm': /* min_page_length */
379 min_page_len = atoi (optarg);
380 break;
381 }
382 }
384 c = optind;
386 if (server_address == NULL && c < argc)
387 server_address = strdup (argv[c++]);
389 if (host_name == NULL && c < argc)
390 host_name = strdup (argv[c++]);
392 if (server_address == NULL) {
393 if (host_name == NULL)
394 usage (_("check_http: you must specify a server address or host name\n"));
395 else
396 server_address = strdup (host_name);
397 }
399 if (check_critical_time && critical_time>(double)socket_timeout)
400 socket_timeout = (int)critical_time + 1;
402 if (http_method == NULL)
403 http_method = strdup ("GET");
405 return TRUE;
406 }
407 \f
410 /* written by lauri alanko */
411 static char *
412 base64 (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 URI_HTTP URI_HOST URI_PORT URI_PATH
460 #define HD2 URI_HTTP URI_HOST URI_PATH
461 #define HD3 URI_HTTP URI_HOST URI_PORT
462 #define HD4 URI_HTTP URI_HOST
463 #define HD5 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 xx[2];
480 char *orig_url;
481 long microsec;
482 double elapsed_time;
483 int page_len = 0;
484 #ifdef HAVE_SSL
485 int sslerr;
486 #endif
488 /* try to connect to the host at the given port number */
489 #ifdef HAVE_SSL
490 if (use_ssl == TRUE) {
492 if (connect_SSL () != OK) {
493 die (STATE_CRITICAL, _("Unable to open TCP socket"));
494 }
496 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
497 X509_free (server_cert);
498 }
499 else {
500 printf (_("ERROR: Cannot retrieve server certificate.\n"));
501 return STATE_CRITICAL;
502 }
504 }
505 else {
506 #endif
507 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
508 die (STATE_CRITICAL, _("Unable to open TCP socket"));
509 #ifdef HAVE_SSL
510 }
511 #endif
513 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
515 /* optionally send the host header info (not clear if it's usable) */
516 if (host_name)
517 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
519 /* send user agent */
520 asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
521 buf, clean_revstring (revision), VERSION);
523 /* optionally send the authentication info */
524 if (strlen(user_auth)) {
525 auth = base64 (user_auth, strlen (user_auth));
526 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
527 }
529 /* either send http POST data */
530 if (http_post_data) {
531 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
532 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
533 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
534 }
535 else {
536 /* or just a newline so the server knows we're done with the request */
537 asprintf (&buf, "%s%s", buf, CRLF);
538 }
540 #ifdef HAVE_SSL
541 if (use_ssl == TRUE) {
542 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
543 ERR_print_errors_fp (stderr);
544 return STATE_CRITICAL;
545 }
546 }
547 else {
548 #endif
549 send (sd, buf, strlen (buf), 0);
550 #ifdef HAVE_SSL
551 }
552 #endif
554 /* fetch the page */
555 full_page = strdup("");
556 while ((i = my_recv ()) > 0) {
557 buffer[i] = '\0';
558 asprintf (&full_page, "%s%s", full_page, buffer);
559 pagesize += i;
560 }
562 if (i < 0 && errno != ECONNRESET) {
563 #ifdef HAVE_SSL
564 if (use_ssl) {
565 sslerr=SSL_get_error(ssl, i);
566 if ( sslerr == SSL_ERROR_SSL ) {
567 die (STATE_WARNING, _("Client Certificate Required\n"));
568 } else {
569 die (STATE_CRITICAL, _("Error in recv()"));
570 }
571 }
572 else {
573 #endif
574 die (STATE_CRITICAL, _("Error in recv()"));
575 #ifdef HAVE_SSL
576 }
577 #endif
578 }
580 /* return a CRITICAL status if we couldn't read any data */
581 if (pagesize == (size_t) 0)
582 die (STATE_CRITICAL, _("No data received %s"), timestamp);
584 /* close the connection */
585 my_close ();
587 /* reset the alarm */
588 alarm (0);
590 /* leave full_page untouched so we can free it later */
591 page = full_page;
593 if (verbose)
594 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
596 /* find status line and null-terminate it */
597 status_line = page;
598 page += (size_t) strcspn (page, "\r\n");
599 pos = page;
600 page += (size_t) strspn (page, "\r\n");
601 status_line[strcspn(status_line, "\r\n")] = 0;
602 strip (status_line);
603 if (verbose)
604 printf ("STATUS: %s\n", status_line);
606 /* find header info and null-terminate it */
607 header = page;
608 while (strcspn (page, "\r\n") > 0) {
609 page += (size_t) strcspn (page, "\r\n");
610 pos = page;
611 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
612 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
613 page += (size_t) 2;
614 else
615 page += (size_t) 1;
616 }
617 page += (size_t) strspn (page, "\r\n");
618 header[pos - header] = 0;
619 if (verbose)
620 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
622 /* make sure the status line matches the response we are looking for */
623 if (!strstr (status_line, server_expect)) {
624 if (server_port == HTTP_PORT)
625 asprintf (&msg, _("Invalid HTTP response received from host\n"));
626 else
627 asprintf (&msg,
628 _("Invalid HTTP response received from host on port %d\n"),
629 server_port);
630 die (STATE_CRITICAL, "%s", msg);
631 }
634 /* Exit here if server_expect was set by user and not default */
635 if ( server_expect_yn ) {
636 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
637 server_expect);
638 if (verbose)
639 printf ("%s\n",msg);
641 }
642 else {
645 /* check the return code */
646 /* server errors result in a critical state */
647 if (strstr (status_line, "500") || strstr (status_line, "501") ||
648 strstr (status_line, "502") || strstr (status_line, "503") ||
649 strstr (status_line, "504") || strstr (status_line, "505")) {
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") || strstr (status_line, "401") ||
655 strstr (status_line, "402") || strstr (status_line, "403") ||
656 strstr (status_line, "404") || strstr (status_line, "405") ||
657 strstr (status_line, "406") || strstr (status_line, "407") ||
658 strstr (status_line, "408") || strstr (status_line, "409") ||
659 strstr (status_line, "410") || strstr (status_line, "411") ||
660 strstr (status_line, "412") || strstr (status_line, "413") ||
661 strstr (status_line, "414") || strstr (status_line, "415") ||
662 strstr (status_line, "416") || strstr (status_line, "417")) {
663 die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
664 }
666 /* check redirected page if specified */
667 if (strstr (status_line, "300") || strstr (status_line, "301") ||
668 strstr (status_line, "302") || strstr (status_line, "303") ||
669 strstr (status_line, "304") || strstr (status_line, "305") ||
670 strstr (status_line, "306")) {
671 if (onredirect == STATE_DEPENDENT) {
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"));
677 asprintf (&orig_url, "%s", server_url);
678 if (strcspn (pos, "\r\n") > (size_t)server_url_length) {
679 server_url = realloc (server_url, strcspn (pos, "\r\n"));
680 if (server_url == NULL)
681 die (STATE_UNKNOWN, _("ERROR: could not allocate server_url"));
682 server_url_length = strcspn (pos, "\r\n");
683 }
685 pos = header;
686 while (pos) {
687 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) > 0) {
688 pos += i;
689 pos += strspn (pos, " \t\r\n");
690 } else {
691 pos += (size_t) strcspn (pos, "\r\n");
692 pos += (size_t) strspn (pos, "\r\n");
693 continue;
694 }
695 /* HDR_LOCATION, URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
696 if (sscanf (pos, HD1, server_type, server_address, server_port_text, server_url) == 4) {
697 if (host_name != NULL) free(host_name);
698 host_name = strdup(server_address);
699 use_ssl = server_type_check (server_type);
700 server_port = atoi (server_port_text);
701 check_http ();
702 }
703 /* HDR_LOCATION URI_HTTP URI_HOST URI_PATH */
704 else if (sscanf (pos, HD2, server_type, server_address, server_url) == 3 ) {
705 if (host_name != NULL) free(host_name);
706 host_name = strdup(server_address);
707 use_ssl = server_type_check (server_type);
708 server_port = server_port_check (use_ssl);
709 check_http ();
710 }
711 /* HDR_LOCATION URI_HTTP URI_HOST URI_PORT */
712 else if(sscanf (pos, HD3, server_type, server_address, server_port_text) == 3) {
713 if (host_name != NULL) free(host_name);
714 host_name = strdup(server_address);
715 strcpy (server_url, "/");
716 use_ssl = server_type_check (server_type);
717 server_port = atoi (server_port_text);
718 check_http ();
719 }
720 /* HDR_LOCATION URI_HTTP URI_HOST */
721 else if(sscanf (pos, HD4, server_type, server_address) == 2) {
722 if (host_name != NULL) free(host_name);
723 host_name = strdup(server_address);
724 strcpy (server_url, "/");
725 use_ssl = server_type_check (server_type);
726 server_port = server_port_check (use_ssl);
727 check_http ();
728 }
729 /* HDR_LOCATION URI_PATH */
730 else if (sscanf (pos, HD5, server_url) == 1) {
731 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
732 *x = '\0';
733 asprintf (&server_url, "%s/%s", orig_url, server_url);
734 }
735 check_http ();
736 }
737 } /* end while (pos) */
738 printf (_("UNKNOWN - Could not find redirect location - %s%s"),
739 status_line, (display_html ? "</A>" : ""));
740 exit (STATE_UNKNOWN);
741 } /* end if (onredirect == STATE_DEPENDENT) */
743 else if (onredirect == STATE_UNKNOWN)
744 printf (_("UNKNOWN"));
745 else if (onredirect == STATE_OK)
746 printf (_("OK"));
747 else if (onredirect == STATE_WARNING)
748 printf (_("WARNING"));
749 else if (onredirect == STATE_CRITICAL)
750 printf (_("CRITICAL"));
751 microsec = deltime (tv);
752 elapsed_time = (double)microsec / 1.0e6;
753 asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
754 status_line, elapsed_time, timestamp,
755 (display_html ? "</A>" : ""), microsec, pagesize);
756 die (onredirect, "%s", msg);
757 } /* end if (strstr (status_line, "30[0-4]") */
760 } /* end else (server_expect_yn) */
763 /* check elapsed time */
764 microsec = deltime (tv);
765 elapsed_time = (double)microsec / 1.0e6;
766 asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
767 status_line, elapsed_time, timestamp,
768 (display_html ? "</A>" : ""), microsec, pagesize);
769 if (check_critical_time == TRUE && elapsed_time > critical_time)
770 die (STATE_CRITICAL, "%s", msg);
771 if (check_warning_time == TRUE && elapsed_time > warning_time)
772 die (STATE_WARNING, "%s", msg);
774 /* Page and Header content checks go here */
775 /* these checks should be last */
777 if (strlen (string_expect)) {
778 if (strstr (page, string_expect)) {
779 printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
780 status_line, elapsed_time,
781 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
782 exit (STATE_OK);
783 }
784 else {
785 printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
786 (display_html ? "</A>" : ""), microsec, pagesize);
787 exit (STATE_CRITICAL);
788 }
789 }
790 #ifdef HAVE_REGEX_H
791 if (strlen (regexp)) {
792 errcode = regexec (&preg, page, REGS, pmatch, 0);
793 if (errcode == 0) {
794 printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
795 status_line, elapsed_time,
796 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
797 exit (STATE_OK);
798 }
799 else {
800 if (errcode == REG_NOMATCH) {
801 printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
802 (display_html ? "</A>" : ""), microsec, pagesize);
803 exit (STATE_CRITICAL);
804 }
805 else {
806 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
807 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
808 exit (STATE_CRITICAL);
809 }
810 }
811 }
812 #endif
814 /* make sure the page is of an appropriate size */
815 page_len = strlen (page);
816 if ((min_page_len > 0) && (page_len < min_page_len)) {
817 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
818 (display_html ? "</A>" : ""), page_len );
819 exit (STATE_WARNING);
820 }
821 /* We only get here if all tests have been passed */
822 asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
823 status_line, elapsed_time,
824 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
825 die (STATE_OK, "%s", msg);
826 return STATE_UNKNOWN;
827 }
831 #ifdef HAVE_SSL
832 int connect_SSL (void)
833 {
834 SSL_METHOD *meth;
836 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
837 RAND_seed (randbuff, (int)strlen(randbuff));
838 if (verbose)
839 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
841 /* Initialize SSL context */
842 SSLeay_add_ssl_algorithms ();
843 meth = SSLv23_client_method ();
844 SSL_load_error_strings ();
845 if ((ctx = SSL_CTX_new (meth)) == NULL) {
846 printf (_("CRITICAL - Cannot create SSL context.\n"));
847 return STATE_CRITICAL;
848 }
850 /* Initialize alarm signal handling */
851 signal (SIGALRM, socket_timeout_alarm_handler);
853 /* Set socket timeout */
854 alarm (socket_timeout);
856 /* Save start time */
857 gettimeofday (&tv, NULL);
859 /* Make TCP connection */
860 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
861 /* Do the SSL handshake */
862 if ((ssl = SSL_new (ctx)) != NULL) {
863 SSL_set_cipher_list(ssl, "ALL");
864 SSL_set_fd (ssl, sd);
865 if (SSL_connect (ssl) != -1)
866 return OK;
867 ERR_print_errors_fp (stderr);
868 }
869 else {
870 printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
871 }
872 SSL_free (ssl);
873 }
875 SSL_CTX_free (ctx);
876 close (sd);
878 return STATE_CRITICAL;
879 }
880 #endif
882 #ifdef HAVE_SSL
883 int
884 check_certificate (X509 ** certificate)
885 {
886 ASN1_STRING *tm;
887 int offset;
888 struct tm stamp;
889 int days_left;
892 /* Retrieve timestamp of certificate */
893 tm = X509_get_notAfter (*certificate);
895 /* Generate tm structure to process timestamp */
896 if (tm->type == V_ASN1_UTCTIME) {
897 if (tm->length < 10) {
898 printf (_("CRITICAL - Wrong time format in certificate.\n"));
899 return STATE_CRITICAL;
900 }
901 else {
902 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
903 if (stamp.tm_year < 50)
904 stamp.tm_year += 100;
905 offset = 0;
906 }
907 }
908 else {
909 if (tm->length < 12) {
910 printf (_("CRITICAL - Wrong time format in certificate.\n"));
911 return STATE_CRITICAL;
912 }
913 else {
914 stamp.tm_year =
915 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
916 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
917 stamp.tm_year -= 1900;
918 offset = 2;
919 }
920 }
921 stamp.tm_mon =
922 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
923 stamp.tm_mday =
924 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
925 stamp.tm_hour =
926 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
927 stamp.tm_min =
928 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
929 stamp.tm_sec = 0;
930 stamp.tm_isdst = -1;
932 days_left = (mktime (&stamp) - time (NULL)) / 86400;
933 snprintf
934 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
935 stamp.tm_mon + 1,
936 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
938 if (days_left > 0 && days_left <= days_till_exp) {
939 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
940 return STATE_WARNING;
941 }
942 if (days_left < 0) {
943 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
944 return STATE_CRITICAL;
945 }
947 if (days_left == 0) {
948 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
949 return STATE_WARNING;
950 }
952 printf (_("OK - Certificate will expire on %s.\n"), timestamp);
954 return STATE_OK;
955 }
956 #endif
957 \f
960 int
961 my_recv (void)
962 {
963 int i;
964 #ifdef HAVE_SSL
965 if (use_ssl) {
966 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
967 }
968 else {
969 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
970 }
971 #else
972 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
973 #endif
974 return i;
975 }
978 int
979 my_close (void)
980 {
981 #ifdef HAVE_SSL
982 if (use_ssl == TRUE) {
983 SSL_shutdown (ssl);
984 SSL_free (ssl);
985 SSL_CTX_free (ctx);
986 return 0;
987 }
988 else {
989 #endif
990 return close (sd);
991 #ifdef HAVE_SSL
992 }
993 #endif
994 }
1000 \f
1001 void
1002 print_help (void)
1003 {
1004 print_revision (progname, revision);
1006 printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1007 printf (_(COPYRIGHT), copyright, email);
1009 printf (_("\
1010 This plugin tests the HTTP service on the specified host. It can test\n\
1011 normal (http) and secure (https) servers, follow redirects, search for\n\
1012 strings and regular expressions, check connection times, and report on\n\
1013 certificate expiration times.\n"));
1015 print_usage ();
1017 printf (_("NOTE: One or both of -H and -I must be specified\n"));
1019 printf (_(UT_HELP_VRSN));
1021 printf (_("\
1022 -H, --hostname=ADDRESS\n\
1023 Host name argument for servers using host headers (virtual host)\n\
1024 -I, --IP-address=ADDRESS\n\
1025 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1026 -p, --port=INTEGER\n\
1027 Port number (default: %d)\n"), HTTP_PORT);
1029 printf (_(UT_IPv46));
1031 #ifdef HAVE_SSL
1032 printf (_("\
1033 -S, --ssl\n\
1034 Connect via SSL\n\
1035 -C, --certificate=INTEGER\n\
1036 Minimum number of days a certificate has to be valid.\n\
1037 (when this option is used the url is not checked.)\n"));
1038 #endif
1040 printf (_("\
1041 -e, --expect=STRING\n\
1042 String to expect in first (status) line of server response (default: %s)\n\
1043 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1044 -s, --string=STRING\n\
1045 String to expect in the content\n\
1046 -u, --url=PATH\n\
1047 URL to GET or POST (default: /)\n\
1048 -P, --post=STRING\n\
1049 URL encoded http POST data\n"), HTTP_EXPECT);
1051 #ifdef HAVE_REGEX_H
1052 printf (_("\
1053 -l, --linespan\n\
1054 Allow regex to span newlines (must precede -r or -R)\n\
1055 -r, --regex, --ereg=STRING\n\
1056 Search page for regex STRING\n\
1057 -R, --eregi=STRING\n\
1058 Search page for case-insensitive regex STRING\n"));
1059 #endif
1061 printf (_("\
1062 -a, --authorization=AUTH_PAIR\n\
1063 Username:password on sites with basic authentication\n\
1064 -L, --link=URL\n\
1065 Wrap output in HTML link (obsoleted by urlize)\n\
1066 -f, --onredirect=<ok|warning|critical|follow>\n\
1067 How to handle redirected pages\n\
1068 -m, --min=INTEGER\n\
1069 Minimum page size required (bytes)\n"));
1071 printf (_(UT_WARN_CRIT));
1073 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1075 printf (_(UT_VERBOSE));
1077 printf (_("\
1078 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1079 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1080 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
1081 messages from the host result in STATE_WARNING return values. If you are\n\
1082 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1083 (fully qualified domain name) as the [host_name] argument.\n"));
1085 #ifdef HAVE_SSL
1086 printf (_("\n\
1087 This plugin can also check whether an SSL enabled web server is able to\n\
1088 serve content (optionally within a specified time) or whether the X509 \n\
1089 certificate is still valid for the specified number of days.\n"));
1090 printf (_("\n\
1091 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1092 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1093 STATE_OK will be returned. When the server returns its content but exceeds\n\
1094 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1095 a STATE_CRITICAL will be returned.\n\n"));
1097 printf (_("\
1098 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1099 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1100 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1101 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1102 the certificate is expired.\n"));
1103 #endif
1105 printf (_(UT_SUPPORT));
1107 }
1112 void
1113 print_usage (void)
1114 {
1115 printf (_("\
1116 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1117 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1118 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1119 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1120 [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1121 printf (_(UT_HLP_VRS), progname, progname);
1122 }