1 /****************************************************************************
2 *
3 * Program: HTTP plugin for Nagios
4 * License: GPL
5 *
6 * License Information:
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Id$
23 *
24 *****************************************************************************/
26 const char *progname = "check_http";
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Ethan Galstad/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
32 #include "config.h"
33 #include "common.h"
34 #include "netutils.h"
35 #include "utils.h"
37 #define SUMMARY "\
38 This plugin tests the HTTP service on the specified host. It can test\n\
39 normal (http) and secure (https) servers, follow redirects, search for\n\
40 strings and regular expressions, check connection times, and report on\n\
41 certificate expiration times.\n"
43 #define OPTIONS "\
44 (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
45 [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
46 [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
47 [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
48 [-P string] [-m min_pg_size]"
50 #define LONGOPTIONS "\
51 -H, --hostname=ADDRESS\n\
52 Host name argument for servers using host headers (virtual host)\n\
53 -I, --IP-address=ADDRESS\n\
54 IP address or name (use numeric address if possible to bypass DNS lookup).\n\
55 -e, --expect=STRING\n\
56 String to expect in first (status) line of server response (default: %s)\n\
57 If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
58 -s, --string=STRING\n\
59 String to expect in the content\n\
60 -u, --url=PATH\n\
61 URL to GET or POST (default: /)\n\
62 -p, --port=INTEGER\n\
63 Port number (default: %d)\n\
64 -P, --post=STRING\n\
65 URL encoded http POST data\n\
66 -w, --warning=INTEGER\n\
67 Response time to result in warning status (seconds)\n\
68 -c, --critical=INTEGER\n\
69 Response time to result in critical status (seconds)\n\
70 -t, --timeout=INTEGER\n\
71 Seconds before connection times out (default: %d)\n\
72 -a, --authorization=AUTH_PAIR\n\
73 Username:password on sites with basic authentication\n\
74 -L, --link=URL\n\
75 Wrap output in HTML link (obsoleted by urlize)\n\
76 -f, --onredirect=<ok|warning|critical|follow>\n\
77 How to handle redirected pages\n%s%s\
78 -m, --min=INTEGER\n\
79 Minimum page size required (bytes)\n\
80 -v, --verbose\n\
81 Show details for command-line debugging (do not use with nagios server)\n\
82 -h, --help\n\
83 Print detailed help screen\n\
84 -V, --version\n\
85 Print version information\n"
87 #ifdef HAVE_SSL
88 #define SSLOPTIONS "\
89 -S, --ssl\n\
90 Connect via SSL\n\
91 -C, --certificate=INTEGER\n\
92 Minimum number of days a certificate has to be valid.\n\
93 (when this option is used the url is not checked.)\n"
94 #else
95 #define SSLOPTIONS ""
96 #endif
98 #ifdef HAVE_REGEX_H
99 #define REGOPTIONS "\
100 -l, --linespan\n\
101 Allow regex to span newlines (must precede -r or -R)\n\
102 -r, --regex, --ereg=STRING\n\
103 Search page for regex STRING\n\
104 -R, --eregi=STRING\n\
105 Search page for case-insensitive regex STRING\n"
106 #else
107 #define REGOPTIONS ""
108 #endif
110 #define DESCRIPTION "\
111 This plugin will attempt to open an HTTP connection with the host. Successul\n\
112 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
113 errors return STATE_UNKNOWN. Successful connects, but incorrect reponse\n\
114 messages from the host result in STATE_WARNING return values. If you are\n\
115 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
116 \(fully qualified domain name) as the [host_name] argument.\n"
118 #define SSLDESCRIPTION "\
119 This plugin can also check whether an SSL enabled web server is able to\n\
120 serve content (optionally within a specified time) or whether the X509 \n\
121 certificate is still valid for the specified number of days.\n\n\
122 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
123 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
124 STATE_OK will be returned. When the server returns its content but exceeds\n\
125 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
126 a STATE_CRITICAL will be returned.\n\n\
127 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
128 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
129 STATE_OK is returned. When the certificate is still valid, but for less than\n\
130 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
131 the certificate is expired.\n"
133 #ifdef HAVE_SSL_H
134 #include <rsa.h>
135 #include <crypto.h>
136 #include <x509.h>
137 #include <pem.h>
138 #include <ssl.h>
139 #include <err.h>
140 #include <rand.h>
141 #endif
143 #ifdef HAVE_OPENSSL_SSL_H
144 #include <openssl/rsa.h>
145 #include <openssl/crypto.h>
146 #include <openssl/x509.h>
147 #include <openssl/pem.h>
148 #include <openssl/ssl.h>
149 #include <openssl/err.h>
150 #include <openssl/rand.h>
151 #endif
153 #ifdef HAVE_SSL
154 int check_cert = FALSE;
155 int days_till_exp;
156 char *randbuff = "";
157 SSL_CTX *ctx;
158 SSL *ssl;
159 X509 *server_cert;
160 int connect_SSL (void);
161 int check_certificate (X509 **);
162 #endif
164 #ifdef HAVE_REGEX_H
165 enum {
166 REGS = 2,
167 MAX_RE_SIZE = 256
168 };
169 #include <regex.h>
170 regex_t preg;
171 regmatch_t pmatch[REGS];
172 char regexp[MAX_RE_SIZE];
173 char errbuf[MAX_INPUT_BUFFER];
174 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
175 int errcode;
176 #endif
178 struct timeval tv;
180 #define server_type_check(server_type) \
181 (strcmp (server_type, "https") ? FALSE : TRUE)
183 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
185 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
186 #define URI_HTTP "%[HTPShtps]://"
187 #define URI_HOST "%[a-zA-Z0-9.-]"
188 #define URI_PORT ":%[0-9]"
189 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
191 enum {
192 MAX_IPV4_HOSTLENGTH = 64,
193 HTTP_PORT = 80,
194 HTTPS_PORT = 443
195 };
197 #define HTTP_EXPECT "HTTP/1."
198 #define HTTP_URL "/"
199 #define CRLF "\r\n"
201 char timestamp[17] = "";
202 int specify_port = FALSE;
203 int server_port = HTTP_PORT;
204 char server_port_text[6] = "";
205 char server_type[6] = "http";
206 char *server_address = "";
207 char *host_name = "";
208 char *server_url = "";
209 int server_url_length;
210 int server_expect_yn = 0;
211 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
212 char string_expect[MAX_INPUT_BUFFER] = "";
213 double warning_time = 0;
214 int check_warning_time = FALSE;
215 double critical_time = 0;
216 int check_critical_time = FALSE;
217 char user_auth[MAX_INPUT_BUFFER] = "";
218 int display_html = FALSE;
219 int onredirect = STATE_OK;
220 int use_ssl = FALSE;
221 int verbose = FALSE;
222 int sd;
223 int min_page_len = 0;
224 char *http_method = "GET";
225 char *http_post_data = "";
226 char buffer[MAX_INPUT_BUFFER];
228 void print_usage (void);
229 void print_help (void);
230 int process_arguments (int, char **);
231 static char *base64 (char *bin, int len);
232 int check_http (void);
233 int my_recv (void);
234 int my_close (void);
236 int
237 main (int argc, char **argv)
238 {
239 int result = STATE_UNKNOWN;
241 /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
242 asprintf (&server_url, "%s", HTTP_URL);
243 server_url_length = strlen(server_url);
245 if (process_arguments (argc, argv) == ERROR)
246 usage ("check_http: could not parse arguments\n");
248 if (strstr (timestamp, ":")) {
249 if (strstr (server_url, "?"))
250 asprintf (&server_url, "%s&%s", server_url, timestamp);
251 else
252 asprintf (&server_url, "%s?%s", server_url, timestamp);
253 }
255 if (display_html == TRUE)
256 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
257 host_name, server_port, server_url);
259 /* initialize alarm signal handling, set socket timeout, start timer */
260 (void) signal (SIGALRM, socket_timeout_alarm_handler);
261 (void) alarm (socket_timeout);
262 gettimeofday (&tv, NULL);
264 #ifdef HAVE_SSL
265 if (use_ssl && check_cert == TRUE) {
266 if (connect_SSL () != OK)
267 terminate (STATE_CRITICAL,
268 "HTTP CRITICAL - Could not make SSL connection\n");
269 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
270 result = check_certificate (&server_cert);
271 X509_free (server_cert);
272 }
273 else {
274 printf ("ERROR: Cannot retrieve server certificate.\n");
275 result = STATE_CRITICAL;
276 }
277 SSL_shutdown (ssl);
278 SSL_free (ssl);
279 SSL_CTX_free (ctx);
280 close (sd);
281 }
282 else {
283 result = check_http ();
284 }
285 #else
286 result = check_http ();
287 #endif
288 return result;
289 }
290 \f
293 /* process command-line arguments */
294 int
295 process_arguments (int argc, char **argv)
296 {
297 int c = 1;
299 int option_index = 0;
300 static struct option long_options[] = {
301 STD_LONG_OPTS,
302 {"file",required_argument,0,'F'},
303 {"link", no_argument, 0, 'L'},
304 {"nohtml", no_argument, 0, 'n'},
305 {"ssl", no_argument, 0, 'S'},
306 {"verbose", no_argument, 0, 'v'},
307 {"post", required_argument, 0, 'P'},
308 {"IP-address", required_argument, 0, 'I'},
309 {"string", required_argument, 0, 's'},
310 {"regex", required_argument, 0, 'r'},
311 {"ereg", required_argument, 0, 'r'},
312 {"eregi", required_argument, 0, 'R'},
313 {"linespan", no_argument, 0, 'l'},
314 {"onredirect", required_argument, 0, 'f'},
315 {"certificate", required_argument, 0, 'C'},
316 {"min", required_argument, 0, 'm'},
317 {0, 0, 0, 0}
318 };
320 if (argc < 2)
321 return ERROR;
323 for (c = 1; c < argc; c++) {
324 if (strcmp ("-to", argv[c]) == 0)
325 strcpy (argv[c], "-t");
326 if (strcmp ("-hn", argv[c]) == 0)
327 strcpy (argv[c], "-H");
328 if (strcmp ("-wt", argv[c]) == 0)
329 strcpy (argv[c], "-w");
330 if (strcmp ("-ct", argv[c]) == 0)
331 strcpy (argv[c], "-c");
332 if (strcmp ("-nohtml", argv[c]) == 0)
333 strcpy (argv[c], "-n");
334 }
336 while (1) {
337 c = getopt_long (argc, argv, "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", long_options, &option_index);
338 if (c == -1 || c == EOF)
339 break;
341 switch (c) {
342 case '?': /* usage */
343 usage2 ("unknown argument", optarg);
344 break;
345 case 'h': /* help */
346 print_help ();
347 exit (STATE_OK);
348 break;
349 case 'V': /* version */
350 print_revision (progname, REVISION);
351 exit (STATE_OK);
352 break;
353 case 't': /* timeout period */
354 if (!is_intnonneg (optarg))
355 usage2 ("timeout interval must be a non-negative integer", optarg);
356 socket_timeout = atoi (optarg);
357 break;
358 case 'c': /* critical time threshold */
359 if (!is_intnonneg (optarg))
360 usage2 ("invalid critical threshold", optarg);
361 critical_time = strtod (optarg, NULL);
362 check_critical_time = TRUE;
363 break;
364 case 'w': /* warning time threshold */
365 if (!is_intnonneg (optarg))
366 usage2 ("invalid warning threshold", optarg);
367 warning_time = strtod (optarg, NULL);
368 check_warning_time = TRUE;
369 break;
370 case 'L': /* show html link */
371 display_html = TRUE;
372 break;
373 case 'n': /* do not show html link */
374 display_html = FALSE;
375 break;
376 case 'S': /* use SSL */
377 #ifndef HAVE_SSL
378 usage ("check_http: invalid option - SSL is not available\n");
379 #endif
380 use_ssl = TRUE;
381 if (specify_port == FALSE)
382 server_port = HTTPS_PORT;
383 break;
384 case 'C': /* Check SSL cert validity */
385 #ifdef HAVE_SSL
386 if (!is_intnonneg (optarg))
387 usage2 ("invalid certificate expiration period", optarg);
388 days_till_exp = atoi (optarg);
389 check_cert = TRUE;
390 #else
391 usage ("check_http: invalid option - SSL is not available\n");
392 #endif
393 break;
394 case 'f': /* onredirect */
395 if (!strcmp (optarg, "follow"))
396 onredirect = STATE_DEPENDENT;
397 if (!strcmp (optarg, "unknown"))
398 onredirect = STATE_UNKNOWN;
399 if (!strcmp (optarg, "ok"))
400 onredirect = STATE_OK;
401 if (!strcmp (optarg, "warning"))
402 onredirect = STATE_WARNING;
403 if (!strcmp (optarg, "critical"))
404 onredirect = STATE_CRITICAL;
405 if (verbose)
406 printf("option f:%d \n", onredirect);
407 break;
408 /* Note: H, I, and u must be malloc'd or will fail on redirects */
409 case 'H': /* Host Name (virtual host) */
410 asprintf (&host_name, "%s", optarg);
411 break;
412 case 'I': /* Server IP-address */
413 asprintf (&server_address, "%s", optarg);
414 break;
415 case 'u': /* Host or server */
416 asprintf (&server_url, "%s", optarg);
417 server_url_length = strlen (server_url);
418 break;
419 case 'p': /* Host or server */
420 if (!is_intnonneg (optarg))
421 usage2 ("invalid port number", optarg);
422 server_port = atoi (optarg);
423 specify_port = TRUE;
424 break;
425 case 'a': /* authorization info */
426 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
427 user_auth[MAX_INPUT_BUFFER - 1] = 0;
428 break;
429 case 'P': /* HTTP POST data in URL encoded format */
430 asprintf (&http_method, "%s", "POST");
431 asprintf (&http_post_data, "%s", optarg);
432 break;
433 case 's': /* string or substring */
434 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
435 string_expect[MAX_INPUT_BUFFER - 1] = 0;
436 break;
437 case 'e': /* string or substring */
438 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
439 server_expect[MAX_INPUT_BUFFER - 1] = 0;
440 server_expect_yn = 1;
441 break;
442 #ifndef HAVE_REGEX_H
443 case 'l': /* linespan */
444 case 'r': /* linespan */
445 case 'R': /* linespan */
446 usage ("check_http: call for regex which was not a compiled option\n");
447 break;
448 #else
449 case 'l': /* linespan */
450 cflags &= ~REG_NEWLINE;
451 break;
452 case 'R': /* regex */
453 cflags |= REG_ICASE;
454 case 'r': /* regex */
455 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
456 regexp[MAX_RE_SIZE - 1] = 0;
457 errcode = regcomp (&preg, regexp, cflags);
458 if (errcode != 0) {
459 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
460 printf ("Could Not Compile Regular Expression: %s", errbuf);
461 return ERROR;
462 }
463 break;
464 #endif
465 case 'v': /* verbose */
466 verbose = TRUE;
467 break;
468 case 'm': /* min_page_length */
469 min_page_len = atoi (optarg);
470 break;
471 }
472 }
474 c = optind;
476 if (strcmp (server_address, "") == 0 && c < argc)
477 asprintf (&server_address, "%s", argv[c++]);
479 if (strcmp (host_name, "") == 0 && c < argc)
480 asprintf (&host_name, "%s", argv[c++]);
482 if (strcmp (server_address ,"") == 0) {
483 if (strcmp (host_name, "") == 0)
484 usage ("check_http: you must specify a server address or host name\n");
485 else
486 asprintf (&server_address, "%s", host_name);
487 }
489 if (check_critical_time && critical_time>(double)socket_timeout)
490 socket_timeout = (int)critical_time + 1;
492 return TRUE;
493 }
494 \f
497 /* written by lauri alanko */
498 static char *
499 base64 (char *bin, int len)
500 {
502 char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
503 int i = 0, j = 0;
505 char BASE64_END = '=';
506 char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
508 while (j < len - 2) {
509 buf[i++] = base64_table[bin[j] >> 2];
510 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
511 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
512 buf[i++] = base64_table[bin[j + 2] & 63];
513 j += 3;
514 }
516 switch (len - j) {
517 case 1:
518 buf[i++] = base64_table[bin[j] >> 2];
519 buf[i++] = base64_table[(bin[j] & 3) << 4];
520 buf[i++] = BASE64_END;
521 buf[i++] = BASE64_END;
522 break;
523 case 2:
524 buf[i++] = base64_table[bin[j] >> 2];
525 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
526 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
527 buf[i++] = BASE64_END;
528 break;
529 case 0:
530 break;
531 }
533 buf[i] = '\0';
534 return buf;
535 }
536 \f
539 int
540 check_http (void)
541 {
542 char *msg = NULL;
543 char *status_line = "";
544 char *header = NULL;
545 char *page = "";
546 char *auth = NULL;
547 int i = 0;
548 size_t pagesize = 0;
549 char *full_page = "";
550 char *buf = NULL;
551 char *pos = "";
552 char *x = NULL;
553 char *orig_url = NULL;
554 double elapsed_time;
555 int page_len = 0;
556 #ifdef HAVE_SSL
557 int sslerr;
558 #endif
560 /* try to connect to the host at the given port number */
561 #ifdef HAVE_SSL
562 if (use_ssl == TRUE) {
564 if (connect_SSL () != OK) {
565 terminate (STATE_CRITICAL, "Unable to open TCP socket");
566 }
568 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
569 X509_free (server_cert);
570 }
571 else {
572 printf ("ERROR: Cannot retrieve server certificate.\n");
573 return STATE_CRITICAL;
574 }
576 }
577 else {
578 #endif
579 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
580 terminate (STATE_CRITICAL, "Unable to open TCP socket");
581 #ifdef HAVE_SSL
582 }
583 #endif
585 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
587 /* optionally send the host header info (not clear if it's usable) */
588 if (strcmp (host_name, ""))
589 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
591 /* send user agent */
592 asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
593 buf, clean_revstring (REVISION), PACKAGE_VERSION);
595 /* optionally send the authentication info */
596 if (strcmp (user_auth, "")) {
597 auth = base64 (user_auth, strlen (user_auth));
598 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
599 }
601 /* either send http POST data */
602 if (strlen (http_post_data)) {
603 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
604 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
605 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
606 }
607 else {
608 /* or just a newline so the server knows we're done with the request */
609 asprintf (&buf, "%s%s", buf, CRLF);
610 }
612 #ifdef HAVE_SSL
613 if (use_ssl == TRUE) {
614 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
615 ERR_print_errors_fp (stderr);
616 return STATE_CRITICAL;
617 }
618 }
619 else {
620 #endif
621 send (sd, buf, strlen (buf), 0);
622 #ifdef HAVE_SSL
623 }
624 #endif
626 /* fetch the page */
627 while ((i = my_recv ()) > 0) {
628 buffer[i] = '\0';
629 asprintf (&full_page, "%s%s", full_page, buffer);
630 pagesize += i;
631 }
633 if (i < 0) {
634 #ifdef HAVE_SSL
635 sslerr=SSL_get_error(ssl, i);
636 if ( sslerr == SSL_ERROR_SSL ) {
637 terminate (STATE_WARNING, "Client Certificate Required\n");
638 } else {
639 terminate (STATE_CRITICAL, "Error in recv()");
640 }
641 #else
642 terminate (STATE_CRITICAL, "Error in recv()");
643 #endif
644 }
646 /* return a CRITICAL status if we couldn't read any data */
647 if (pagesize == (size_t) 0)
648 terminate (STATE_CRITICAL, "No data received %s", timestamp);
650 /* close the connection */
651 my_close ();
653 /* reset the alarm */
654 alarm (0);
656 /* leave full_page untouched so we can free it later */
657 page = full_page;
659 if (verbose)
660 printf ("Page is %d characters\n", pagesize);
662 /* find status line and null-terminate it */
663 status_line = page;
664 page += (size_t) strcspn (page, "\r\n");
665 pos = page;
666 page += (size_t) strspn (page, "\r\n");
667 status_line[strcspn(status_line, "\r\n")] = 0;
668 strip (status_line);
669 if (verbose)
670 printf ("STATUS: %s\n", status_line);
672 /* find header info and null terminate it */
673 header = page;
674 while (strcspn (page, "\r\n") > 0) {
675 page += (size_t) strcspn (page, "\r\n");
676 pos = page;
677 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
678 (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
679 page += (size_t) 2;
680 else
681 page += (size_t) 1;
682 }
683 page += (size_t) strspn (page, "\r\n");
684 header[pos - header] = 0;
685 if (verbose)
686 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
688 /* make sure the status line matches the response we are looking for */
689 if (!strstr (status_line, server_expect)) {
690 if (server_port == HTTP_PORT)
691 asprintf (&msg, "Invalid HTTP response received from host\n");
692 else
693 asprintf (&msg,
694 "Invalid HTTP response received from host on port %d\n",
695 server_port);
696 terminate (STATE_CRITICAL, msg);
697 }
700 /* Exit here if server_expect was set by user and not default */
701 if ( server_expect_yn ) {
702 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
703 server_expect);
704 if (verbose)
705 printf ("%s\n",msg);
707 }
708 else {
711 /* check the return code */
712 /* server errors result in a critical state */
713 if (strstr (status_line, "500") ||
714 strstr (status_line, "501") ||
715 strstr (status_line, "502") ||
716 strstr (status_line, "503")) {
717 terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
718 }
720 /* client errors result in a warning state */
721 if (strstr (status_line, "400") ||
722 strstr (status_line, "401") ||
723 strstr (status_line, "402") ||
724 strstr (status_line, "403") ||
725 strstr (status_line, "404")) {
726 terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
727 }
729 /* check redirected page if specified */
730 if (strstr (status_line, "300") ||
731 strstr (status_line, "301") ||
732 strstr (status_line, "302") ||
733 strstr (status_line, "303") ||
734 strstr (status_line, "304")) {
735 if (onredirect == STATE_DEPENDENT) {
737 asprintf (&orig_url, "%s", server_url);
738 pos = header;
739 while (pos) {
740 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
741 if (server_address == NULL)
742 terminate (STATE_UNKNOWN,
743 "HTTP UNKNOWN: could not allocate server_address");
744 if (strcspn (pos, "\r\n") > server_url_length) {
745 server_url = realloc (server_url, strcspn (pos, "\r\n"));
746 if (server_url == NULL)
747 terminate (STATE_UNKNOWN,
748 "HTTP UNKNOWN: could not allocate server_url");
749 server_url_length = strcspn (pos, "\r\n");
750 }
751 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
752 asprintf (&host_name, "%s", server_address);
753 use_ssl = server_type_check (server_type);
754 server_port = atoi (server_port_text);
755 check_http ();
756 }
757 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) {
758 asprintf (&host_name, "%s", server_address);
759 use_ssl = server_type_check (server_type);
760 server_port = server_port_check (use_ssl);
761 check_http ();
762 }
763 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
764 asprintf (&host_name, "%s", server_address);
765 strcpy (server_url, "/");
766 use_ssl = server_type_check (server_type);
767 server_port = atoi (server_port_text);
768 check_http ();
769 }
770 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
771 asprintf (&host_name, "%s", server_address);
772 strcpy (server_url, "/");
773 use_ssl = server_type_check (server_type);
774 server_port = server_port_check (use_ssl);
775 check_http ();
776 }
777 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
778 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
779 *x = '\0';
780 asprintf (&server_url, "%s/%s", orig_url, server_url);
781 }
782 check_http ();
783 }
784 pos += (size_t) strcspn (pos, "\r\n");
785 pos += (size_t) strspn (pos, "\r\n");
786 } /* end while (pos) */
787 printf ("UNKNOWN - Could not find redirect location - %s%s",
788 status_line, (display_html ? "</A>" : ""));
789 exit (STATE_UNKNOWN);
790 } /* end if (onredirect == STATE_DEPENDENT) */
792 else if (onredirect == STATE_UNKNOWN)
793 printf ("UNKNOWN");
794 else if (onredirect == STATE_OK)
795 printf ("OK");
796 else if (onredirect == STATE_WARNING)
797 printf ("WARNING");
798 else if (onredirect == STATE_CRITICAL)
799 printf ("CRITICAL");
800 elapsed_time = delta_time (tv);
801 asprintf (&msg, " - %s - %.3f second response time %s%s|time=%.3f\n",
802 status_line, elapsed_time, timestamp,
803 (display_html ? "</A>" : ""), elapsed_time);
804 terminate (onredirect, msg);
805 } /* end if (strstr (status_line, "30[0-4]") */
808 } /* end else (server_expect_yn) */
811 /* check elapsed time */
812 elapsed_time = delta_time (tv);
813 asprintf (&msg, "HTTP problem: %s - %.3f second response time %s%s|time=%.3f\n",
814 status_line, elapsed_time, timestamp,
815 (display_html ? "</A>" : ""), elapsed_time);
816 if (check_critical_time == TRUE && elapsed_time > critical_time)
817 terminate (STATE_CRITICAL, msg);
818 if (check_warning_time == TRUE && elapsed_time > warning_time)
819 terminate (STATE_WARNING, msg);
821 /* Page and Header content checks go here */
822 /* these checks should be last */
824 if (strlen (string_expect)) {
825 if (strstr (page, string_expect)) {
826 printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
827 status_line, elapsed_time,
828 timestamp, (display_html ? "</A>" : ""), elapsed_time);
829 exit (STATE_OK);
830 }
831 else {
832 printf ("CRITICAL - string not found%s|time=%.3f\n",
833 (display_html ? "</A>" : ""), elapsed_time);
834 exit (STATE_CRITICAL);
835 }
836 }
837 #ifdef HAVE_REGEX_H
838 if (strlen (regexp)) {
839 errcode = regexec (&preg, page, REGS, pmatch, 0);
840 if (errcode == 0) {
841 printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
842 status_line, elapsed_time,
843 timestamp, (display_html ? "</A>" : ""), elapsed_time);
844 exit (STATE_OK);
845 }
846 else {
847 if (errcode == REG_NOMATCH) {
848 printf ("CRITICAL - pattern not found%s|time=%.3f\n",
849 (display_html ? "</A>" : ""), elapsed_time);
850 exit (STATE_CRITICAL);
851 }
852 else {
853 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
854 printf ("CRITICAL - Execute Error: %s\n", errbuf);
855 exit (STATE_CRITICAL);
856 }
857 }
858 }
859 #endif
861 /* make sure the page is of an appropriate size */
862 page_len = strlen (page);
863 if ((min_page_len > 0) && (page_len < min_page_len)) {
864 printf ("HTTP WARNING: page size too small%s|size=%i\n",
865 (display_html ? "</A>" : ""), page_len );
866 exit (STATE_WARNING);
867 }
868 /* We only get here if all tests have been passed */
869 asprintf (&msg, "HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
870 status_line, (float)elapsed_time,
871 timestamp, (display_html ? "</A>" : ""), elapsed_time);
872 terminate (STATE_OK, msg);
873 return STATE_UNKNOWN;
874 }
878 #ifdef HAVE_SSL
879 int connect_SSL (void)
880 {
881 SSL_METHOD *meth;
883 asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
884 RAND_seed (randbuff, strlen (randbuff));
885 if (verbose)
886 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
888 /* Initialize SSL context */
889 SSLeay_add_ssl_algorithms ();
890 meth = SSLv23_client_method ();
891 SSL_load_error_strings ();
892 if ((ctx = SSL_CTX_new (meth)) == NULL) {
893 printf ("CRITICAL - Cannot create SSL context.\n");
894 return STATE_CRITICAL;
895 }
897 /* Initialize alarm signal handling */
898 signal (SIGALRM, socket_timeout_alarm_handler);
900 /* Set socket timeout */
901 alarm (socket_timeout);
903 /* Save start time */
904 gettimeofday (&tv, NULL);
906 /* Make TCP connection */
907 if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
908 /* Do the SSL handshake */
909 if ((ssl = SSL_new (ctx)) != NULL) {
910 SSL_set_cipher_list(ssl, "ALL");
911 SSL_set_fd (ssl, sd);
912 if (SSL_connect (ssl) != -1)
913 return OK;
914 ERR_print_errors_fp (stderr);
915 }
916 else {
917 printf ("CRITICAL - Cannot initiate SSL handshake.\n");
918 }
919 SSL_free (ssl);
920 }
922 SSL_CTX_free (ctx);
923 close (sd);
925 return STATE_CRITICAL;
926 }
927 #endif
929 #ifdef HAVE_SSL
930 int
931 check_certificate (X509 ** certificate)
932 {
933 ASN1_STRING *tm;
934 int offset;
935 struct tm stamp;
936 int days_left;
939 /* Retrieve timestamp of certificate */
940 tm = X509_get_notAfter (*certificate);
942 /* Generate tm structure to process timestamp */
943 if (tm->type == V_ASN1_UTCTIME) {
944 if (tm->length < 10) {
945 printf ("CRITICAL - Wrong time format in certificate.\n");
946 return STATE_CRITICAL;
947 }
948 else {
949 stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
950 if (stamp.tm_year < 50)
951 stamp.tm_year += 100;
952 offset = 0;
953 }
954 }
955 else {
956 if (tm->length < 12) {
957 printf ("CRITICAL - Wrong time format in certificate.\n");
958 return STATE_CRITICAL;
959 }
960 else {
961 stamp.tm_year =
962 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
963 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
964 stamp.tm_year -= 1900;
965 offset = 2;
966 }
967 }
968 stamp.tm_mon =
969 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
970 stamp.tm_mday =
971 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
972 stamp.tm_hour =
973 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
974 stamp.tm_min =
975 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
976 stamp.tm_sec = 0;
977 stamp.tm_isdst = -1;
979 days_left = (mktime (&stamp) - time (NULL)) / 86400;
980 snprintf
981 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
982 stamp.tm_mon + 1,
983 stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
985 if (days_left > 0 && days_left <= days_till_exp) {
986 printf ("WARNING - Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
987 return STATE_WARNING;
988 }
989 if (days_left < 0) {
990 printf ("CRITICAL - Certificate expired on %s.\n", timestamp);
991 return STATE_CRITICAL;
992 }
994 if (days_left == 0) {
995 printf ("WARNING - Certificate expires today (%s).\n", timestamp);
996 return STATE_WARNING;
997 }
999 printf ("OK - Certificate will expire on %s.\n", timestamp);
1001 return STATE_OK;
1002 }
1003 #endif
1004 \f
1007 int
1008 my_recv (void)
1009 {
1010 int i;
1011 #ifdef HAVE_SSL
1012 if (use_ssl) {
1013 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1014 }
1015 else {
1016 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1017 }
1018 #else
1019 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1020 #endif
1021 return i;
1022 }
1025 int
1026 my_close (void)
1027 {
1028 #ifdef HAVE_SSL
1029 if (use_ssl == TRUE) {
1030 SSL_shutdown (ssl);
1031 SSL_free (ssl);
1032 SSL_CTX_free (ctx);
1033 return 0;
1034 }
1035 else {
1036 #endif
1037 return close (sd);
1038 #ifdef HAVE_SSL
1039 }
1040 #endif
1041 }
1042 \f
1045 void
1046 print_help (void)
1047 {
1048 print_revision (progname, REVISION);
1049 printf
1050 ("Copyright (c) %s %s <%s>\n\n%s\n",
1051 COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1052 print_usage ();
1053 printf ("NOTE: One or both of -H and -I must be specified\n");
1054 printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1055 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1056 #ifdef HAVE_SSL
1057 printf (SSLDESCRIPTION);
1058 #endif
1059 }
1062 void
1063 print_usage (void)
1064 {
1065 printf ("\
1066 Usage:\n\
1067 %s %s\n\
1068 %s (-h | --help) for detailed help\n\
1069 %s (-V | --version) for version information\n",
1070 progname, OPTIONS, progname, progname);
1071 }