Code

fix segfault due to bad asprintf invocation
[nagiosplug.git] / plugins / check_http.c
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]"
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  -v, --verbose\n\
79     Show details for command-line debugging (do not use with nagios server)\n\
80  -h, --help\n\
81     Print detailed help screen\n\
82  -V, --version\n\
83     Print version information\n"
85 #ifdef HAVE_SSL
86 #define SSLOPTIONS "\
87  -S, --ssl\n\
88     Connect via SSL\n\
89  -C, --certificate=INTEGER\n\
90     Minimum number of days a certificate has to be valid.\n\
91     (when this option is used the url is not checked.)\n"
92 #else
93 #define SSLOPTIONS ""
94 #endif
96 #ifdef HAVE_REGEX_H
97 #define REGOPTIONS "\
98  -l, --linespan\n\
99     Allow regex to span newlines (must precede -r or -R)\n\
100  -r, --regex, --ereg=STRING\n\
101     Search page for regex STRING\n\
102  -R, --eregi=STRING\n\
103     Search page for case-insensitive regex STRING\n"
104 #else
105 #define REGOPTIONS ""
106 #endif
108 #define DESCRIPTION "\
109 This plugin will attempt to open an HTTP connection with the host. Successul\n\
110 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
111 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
112 messages from the host result in STATE_WARNING return values.  If you are\n\
113 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
114 \(fully qualified domain name) as the [host_name] argument.\n"
116 #define SSLDESCRIPTION "\
117 This plugin can also check whether an SSL enabled web server is able to\n\
118 serve content (optionally within a specified time) or whether the X509 \n\
119 certificate is still valid for the specified number of days.\n\n\
120 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
121 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
122 STATE_OK will be returned. When the server returns its content but exceeds\n\
123 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
124 a STATE_CRITICAL will be returned.\n\n\
125 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
126 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
127 STATE_OK is returned. When the certificate is still valid, but for less than\n\
128 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
129 the certificate is expired.\n"
131 #ifdef HAVE_SSL_H
132 #include <rsa.h>
133 #include <crypto.h>
134 #include <x509.h>
135 #include <pem.h>
136 #include <ssl.h>
137 #include <err.h>
138 #include <rand.h>
139 #endif
141 #ifdef HAVE_OPENSSL_SSL_H
142 #include <openssl/rsa.h>
143 #include <openssl/crypto.h>
144 #include <openssl/x509.h>
145 #include <openssl/pem.h>
146 #include <openssl/ssl.h>
147 #include <openssl/err.h>
148 #include <openssl/rand.h>
149 #endif
151 #ifdef HAVE_SSL
152 int check_cert = FALSE;
153 int days_till_exp;
154 unsigned char *randbuff;
155 SSL_CTX *ctx;
156 SSL *ssl;
157 X509 *server_cert;
158 int connect_SSL (void);
159 int check_certificate (X509 **);
160 #endif
162 #ifdef HAVE_REGEX_H
163 #define REGS 2
164 #define MAX_RE_SIZE 256
165 #include <regex.h>
166 regex_t preg;
167 regmatch_t pmatch[REGS];
168 char regexp[MAX_RE_SIZE];
169 char errbuf[MAX_INPUT_BUFFER];
170 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
171 int errcode;
172 #endif
174 struct timeval tv;
176 #define server_type_check(server_type) \
177 (strcmp (server_type, "https") ? FALSE : TRUE)
179 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
181 #define MAX_IPV4_HOSTLENGTH 64
182 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
183 #define URI_HTTP "%[HTPShtps]://"
184 #define URI_HOST "%[a-zA-Z0-9.-]"
185 #define URI_PORT ":%[0-9]"
186 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
188 #define HTTP_PORT 80
189 #define HTTPS_PORT 443
190 #define HTTP_EXPECT "HTTP/1."
191 #define HTTP_URL "/"
192 #define CRLF "\r\n"
194 char timestamp[17] = "";
195 int specify_port = FALSE;
196 int server_port = HTTP_PORT;
197 char server_port_text[6] = "";
198 char server_type[6] = "http";
199 char *server_address = ""; 
200 char *host_name = "";
201 char *server_url = HTTP_URL;
202 int server_url_length = 1;
203 int server_expect_yn = 0;
204 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
205 char string_expect[MAX_INPUT_BUFFER] = "";
206 double warning_time = 0;
207 int check_warning_time = FALSE;
208 double critical_time = 0;
209 int check_critical_time = FALSE;
210 char user_auth[MAX_INPUT_BUFFER] = "";
211 int display_html = FALSE;
212 int onredirect = STATE_OK;
213 int use_ssl = FALSE;
214 int verbose = FALSE;
215 int sd;
216 char *http_method = "GET";
217 char *http_post_data = "";
218 char buffer[MAX_INPUT_BUFFER];
220 void print_usage (void);
221 void print_help (void);
222 int process_arguments (int, char **);
223 static char *base64 (char *bin, int len);
224 int check_http (void);
225 int my_recv (void);
226 int my_close (void);
228 int
229 main (int argc, char **argv)
231         int result = STATE_UNKNOWN;
233         if (process_arguments (argc, argv) == ERROR)
234                 usage ("check_http: could not parse arguments\n");
236         if (strstr (timestamp, ":")) {
237                 if (strstr (server_url, "?"))
238                         asprintf (&server_url, "%s&%s", server_url, timestamp);
239                 else
240                         asprintf (&server_url, "%s?%s", server_url, timestamp);
241         }
243         if (display_html == TRUE)
244                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
245                         host_name, server_port, server_url);
247         /* initialize alarm signal handling, set socket timeout, start timer */
248         (void) signal (SIGALRM, socket_timeout_alarm_handler);
249         (void) alarm (socket_timeout);
250         gettimeofday (&tv, NULL);
252 #ifdef HAVE_SSL
253         if (use_ssl && check_cert == TRUE) {
254                 if (connect_SSL () != OK)
255                         terminate (STATE_CRITICAL,
256                                    "HTTP CRITICAL - Could not make SSL connection\n");
257                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
258                         result = check_certificate (&server_cert);
259                         X509_free (server_cert);
260                 }
261                 else {
262                         printf ("ERROR: Cannot retrieve server certificate.\n");
263                         result = STATE_CRITICAL;
264                 }
265                 SSL_shutdown (ssl);
266                 SSL_free (ssl);
267                 SSL_CTX_free (ctx);
268                 close (sd);
269         }
270         else {
271                 result = check_http ();
272         }
273 #else
274         result = check_http ();
275 #endif
276         return result;
278 \f
281 /* process command-line arguments */
282 int
283 process_arguments (int argc, char **argv)
285         int c = 1;
287 #ifdef HAVE_GETOPT_H
288         int option_index = 0;
289         static struct option long_options[] = {
290                 STD_LONG_OPTS,
291                 {"file",required_argument,0,'F'},
292                 {"link", no_argument, 0, 'L'},
293                 {"nohtml", no_argument, 0, 'n'},
294                 {"ssl", no_argument, 0, 'S'},
295                 {"verbose", no_argument, 0, 'v'},
296                 {"post", required_argument, 0, 'P'},
297                 {"IP-address", required_argument, 0, 'I'},
298                 {"string", required_argument, 0, 's'},
299                 {"regex", required_argument, 0, 'r'},
300                 {"ereg", required_argument, 0, 'r'},
301                 {"eregi", required_argument, 0, 'R'},
302                 {"linespan", no_argument, 0, 'l'},
303                 {"onredirect", required_argument, 0, 'f'},
304                 {"certificate", required_argument, 0, 'C'},
305                 {0, 0, 0, 0}
306         };
307 #endif
309         if (argc < 2)
310                 return ERROR;
312         for (c = 1; c < argc; c++) {
313                 if (strcmp ("-to", argv[c]) == 0)
314                         strcpy (argv[c], "-t");
315                 if (strcmp ("-hn", argv[c]) == 0)
316                         strcpy (argv[c], "-H");
317                 if (strcmp ("-wt", argv[c]) == 0)
318                         strcpy (argv[c], "-w");
319                 if (strcmp ("-ct", argv[c]) == 0)
320                         strcpy (argv[c], "-c");
321                 if (strcmp ("-nohtml", argv[c]) == 0)
322                         strcpy (argv[c], "-n");
323         }
325 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
327         while (1) {
328 #ifdef HAVE_GETOPT_H
329                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
330 #else
331                 c = getopt (argc, argv, OPTCHARS);
332 #endif
333                 if (c == -1 || c == EOF)
334                         break;
336                 switch (c) {
337                 case '?': /* usage */
338                         usage2 ("unknown argument", optarg);
339                         break;
340                 case 'h': /* help */
341                         print_help ();
342                         exit (STATE_OK);
343                         break;
344                 case 'V': /* version */
345                         print_revision (progname, REVISION);
346                         exit (STATE_OK);
347                         break;
348                 case 't': /* timeout period */
349                         if (!is_intnonneg (optarg))
350                                 usage2 ("timeout interval must be a non-negative integer", optarg);
351                         socket_timeout = atoi (optarg);
352                         break;
353                 case 'c': /* critical time threshold */
354                         if (!is_intnonneg (optarg))
355                                 usage2 ("invalid critical threshold", optarg);
356                         critical_time = strtod (optarg, NULL);
357                         check_critical_time = TRUE;
358                         break;
359                 case 'w': /* warning time threshold */
360                         if (!is_intnonneg (optarg))
361                                 usage2 ("invalid warning threshold", optarg);
362                         warning_time = strtod (optarg, NULL);
363                         check_warning_time = TRUE;
364                         break;
365                 case 'L': /* show html link */
366                         display_html = TRUE;
367                         break;
368                 case 'n': /* do not show html link */
369                         display_html = FALSE;
370                         break;
371                 case 'S': /* use SSL */
372 #ifndef HAVE_SSL
373                         usage ("check_http: invalid option - SSL is not available\n");
374 #endif
375                         use_ssl = TRUE;
376                         if (specify_port == FALSE)
377                                 server_port = HTTPS_PORT;
378                         break;
379                 case 'C': /* Check SSL cert validity */
380 #ifdef HAVE_SSL
381                         if (!is_intnonneg (optarg))
382                                 usage2 ("invalid certificate expiration period", optarg);
383                         days_till_exp = atoi (optarg);
384                         check_cert = TRUE;
385 #else
386                         usage ("check_http: invalid option - SSL is not available\n");
387 #endif
388                         break;
389                 case 'f': /* onredirect */
390                         if (!strcmp (optarg, "follow"))
391                                 onredirect = STATE_DEPENDENT;
392                         if (!strcmp (optarg, "unknown"))
393                                 onredirect = STATE_UNKNOWN;
394                         if (!strcmp (optarg, "ok"))
395                                 onredirect = STATE_OK;
396                         if (!strcmp (optarg, "warning"))
397                                 onredirect = STATE_WARNING;
398                         if (!strcmp (optarg, "critical"))
399                                 onredirect = STATE_CRITICAL;
400                         if (verbose)
401                                 printf("option f:%d \n", onredirect);  
402                         break;
403                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
404                 case 'H': /* Host Name (virtual host) */
405                         asprintf (&host_name, "%s", optarg);
406                         break;
407                 case 'I': /* Server IP-address */
408                         asprintf (&server_address, "%s", optarg);
409                         break;
410                 case 'u': /* Host or server */
411                         asprintf (&server_url, "%s", optarg);
412                         server_url_length = strlen (server_url);
413                         break;
414                 case 'p': /* Host or server */
415                         if (!is_intnonneg (optarg))
416                                 usage2 ("invalid port number", optarg);
417                         server_port = atoi (optarg);
418                         specify_port = TRUE;
419                         break;
420                 case 'a': /* authorization info */
421                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
422                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
423                         break;
424                 case 'P': /* HTTP POST data in URL encoded format */
425                         asprintf (&http_method, "%s", "POST");
426                         asprintf (&http_post_data, "%s", optarg);
427                         break;
428                 case 's': /* string or substring */
429                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
430                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
431                         break;
432                 case 'e': /* string or substring */
433                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
434                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
435                         server_expect_yn = 1;
436                         break;
437 #ifndef HAVE_REGEX_H
438                 case 'l': /* linespan */
439                 case 'r': /* linespan */
440                 case 'R': /* linespan */
441                         usage ("check_http: call for regex which was not a compiled option\n");
442                         break;
443 #else
444                 case 'l': /* linespan */
445                         cflags &= ~REG_NEWLINE;
446                         break;
447                 case 'R': /* regex */
448                         cflags |= REG_ICASE;
449                 case 'r': /* regex */
450                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
451                         regexp[MAX_RE_SIZE - 1] = 0;
452                         errcode = regcomp (&preg, regexp, cflags);
453                         if (errcode != 0) {
454                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
455                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
456                                 return ERROR;
457                         }
458                         break;
459 #endif
460                 case 'v': /* verbose */
461                         verbose = TRUE;
462                         break;
463                 }
464         }
466         c = optind;
468         if (strcmp (server_address, "") == 0 && c < argc)
469                         asprintf (&server_address, "%s", argv[c++]);
471         if (strcmp (host_name, "") == 0 && c < argc)
472                 asprintf (&host_name, "%s", argv[c++]);
474         if (strcmp (server_address ,"") == 0) {
475                 if (strcmp (host_name, "") == 0)
476                         usage ("check_http: you must specify a server address or host name\n");
477                 else
478                         asprintf (&server_address, "%s", host_name);
479         }
481         return TRUE;
483 \f
486 /* written by lauri alanko */
487 static char *
488 base64 (char *bin, int len)
491         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
492         int i = 0, j = 0;
494         char BASE64_END = '=';
495         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
497         while (j < len - 2) {
498                 buf[i++] = base64_table[bin[j] >> 2];
499                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
500                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
501                 buf[i++] = base64_table[bin[j + 2] & 63];
502                 j += 3;
503         }
505         switch (len - j) {
506         case 1:
507                 buf[i++] = base64_table[bin[j] >> 2];
508                 buf[i++] = base64_table[(bin[j] & 3) << 4];
509                 buf[i++] = BASE64_END;
510                 buf[i++] = BASE64_END;
511                 break;
512         case 2:
513                 buf[i++] = base64_table[bin[j] >> 2];
514                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
515                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
516                 buf[i++] = BASE64_END;
517                 break;
518         case 0:
519                 break;
520         }
522         buf[i] = '\0';
523         return buf;
525 \f
528 int
529 check_http (void)
531         char *msg = NULL;
532         char *status_line = "";
533         char *header = NULL;
534         char *page = "";
535         char *auth = NULL;
536         int i = 0;
537         size_t pagesize = 0;
538         char *full_page = "";
539         char *buf = NULL;
540         char *pos = "";
541         char *x = NULL;
542         char *orig_url = NULL;
543         double elapsed_time;
544 #ifdef HAVE_SSL
545         int sslerr;
546 #endif
548         /* try to connect to the host at the given port number */
549 #ifdef HAVE_SSL
550         if (use_ssl == TRUE) {
552                 if (connect_SSL () != OK)
553                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
555                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
556                         X509_free (server_cert);
557                 }
558                 else {
559                         printf ("ERROR: Cannot retrieve server certificate.\n");
560                         return STATE_CRITICAL;
561                 }
563                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
564                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
565                         ERR_print_errors_fp (stderr);
566                         return STATE_CRITICAL;
567                 }
569                 /* optionally send the host header info (not clear if it's usable) */
570                 if (strcmp (host_name, "")) {
571                         asprintf (&buf, "Host: %s\r\n", host_name);
572                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
573                                 ERR_print_errors_fp (stderr);
574                                 return STATE_CRITICAL;
575                         }
576                 }
578                 /* send user agent */
579                 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
580                          clean_revstring (REVISION), PACKAGE_VERSION);
581                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
582                         ERR_print_errors_fp (stderr);
583                         return STATE_CRITICAL;
584                 }
586                 /* optionally send the authentication info */
587                 if (strcmp (user_auth, "")) {
588                         auth = base64 (user_auth, strlen (user_auth));
589                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
590                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
591                                 ERR_print_errors_fp (stderr);
592                                 return STATE_CRITICAL;
593                         }
594                 }
596                 /* either send http POST data */
597                 if (strlen (http_post_data)) {
598                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
599                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
600                                 ERR_print_errors_fp (stderr);
601                                 return STATE_CRITICAL;
602                         }
603                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
604                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
605                                 ERR_print_errors_fp (stderr);
606                                 return STATE_CRITICAL;
607                         }
608                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
609                                 ERR_print_errors_fp (stderr);
610                                 return STATE_CRITICAL;
611                         }
612                         asprintf (&buf, CRLF);
613                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
614                                 ERR_print_errors_fp (stderr);
615                                 return STATE_CRITICAL;
616                         }
617                 }
618                 else {
619                         /* or just a newline so the server knows we're done with the request */
620                         asprintf (&buf, "\r\n");
621                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
622                                 ERR_print_errors_fp (stderr);
623                                 return STATE_CRITICAL;
624                         }
625                 }
627         }
628         else {
629 #endif
630                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
631                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
632                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
633                 send (sd, buf, strlen (buf), 0);
635                 /* optionally send the host header info */
636                 if (strcmp (host_name, "")) {
637                         asprintf (&buf, "Host: %s\r\n", host_name);
638                         send (sd, buf, strlen (buf), 0);
639                 }
641                 /* send user agent */
642                 asprintf (&buf,
643                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
644                          clean_revstring (REVISION), PACKAGE_VERSION);
645                 send (sd, buf, strlen (buf), 0);
647                 /* optionally send the authentication info */
648                 if (strcmp (user_auth, "")) {
649                         auth = base64 (user_auth, strlen (user_auth));
650                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
651                         send (sd, buf, strlen (buf), 0);
652                 }
654                 /* either send http POST data */
655                 /* written by Chris Henesy <lurker@shadowtech.org> */
656                 if (strlen (http_post_data)) {
657                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
658                         send (sd, buf, strlen (buf), 0);
659                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
660                         send (sd, buf, strlen (buf), 0);
661                         send (sd, http_post_data, strlen (http_post_data), 0);
662                         send (sd, CRLF, strlen (CRLF), 0);
663                 }
664                 else {
665                         /* or just a newline so the server knows we're done with the request */
666                         asprintf (&buf, "\r\n");
667                         send (sd, buf, strlen (buf), 0);
668                 }
669 #ifdef HAVE_SSL
670         }
671 #endif
673         /* fetch the page */
674         while ((i = my_recv ()) > 0) {
675                 buffer[i] = '\0';
676                 asprintf (&full_page, "%s%s", full_page, buffer);
677                 pagesize += i;
678         }
680         if (i < 0) {
681 #ifdef HAVE_SSL
682                 sslerr=SSL_get_error(ssl, i);
683                 if ( sslerr == SSL_ERROR_SSL ) {
684                         terminate (STATE_WARNING, "Client Certificate Required\n");
685                 } else {
686                         terminate (STATE_CRITICAL, "Error in recv()");
687                 }
688 #else
689                 terminate (STATE_CRITICAL, "Error in recv()");
690 #endif
691         }
693         /* return a CRITICAL status if we couldn't read any data */
694         if (pagesize == (size_t) 0)
695                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
697         /* close the connection */
698         my_close ();
700         /* reset the alarm */
701         alarm (0);
703         /* leave full_page untouched so we can free it later */
704         page = full_page;
706         if (verbose)
707                 printf ("Page is %d characters\n", pagesize);
709         /* find status line and null-terminate it */
710         status_line = page;
711         page += (size_t) strcspn (page, "\r\n");
712         pos = page;
713         page += (size_t) strspn (page, "\r\n");
714         status_line[strcspn(status_line, "\r\n")] = 0;
715         strip (status_line);
716         if (verbose)
717                 printf ("STATUS: %s\n", status_line);
719         /* find header info and null terminate it */
720         header = page;
721         while (strcspn (page, "\r\n") > 0) {
722                 page += (size_t) strcspn (page, "\r\n");
723                 pos = page;
724                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
725                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
726                         page += (size_t) 2;
727                 else
728                         page += (size_t) 1;
729         }
730         page += (size_t) strspn (page, "\r\n");
731         header[pos - header] = 0;
732         if (verbose)
733                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
735         /* make sure the status line matches the response we are looking for */
736         if (!strstr (status_line, server_expect)) {
737                 if (server_port == HTTP_PORT)
738                         asprintf (&msg, "Invalid HTTP response received from host\n");
739                 else
740                         asprintf (&msg,
741                                         "Invalid HTTP response received from host on port %d\n",
742                                         server_port);
743                 terminate (STATE_CRITICAL, msg);
744         }
747         /* Exit here if server_expect was set by user and not default */
748         if ( server_expect_yn  )  {
749                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
750                           server_expect);
751                 if (verbose)
752                         printf ("%s\n",msg);
754         }
755         else {
756         
758                 /* check the return code */
759                 /* server errors result in a critical state */
760                 if (strstr (status_line, "500") ||
761                   strstr (status_line, "501") ||
762                 strstr (status_line, "502") ||
763                     strstr (status_line, "503")) {
764                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
765                 }
767                 /* client errors result in a warning state */
768                 if (strstr (status_line, "400") ||
769                   strstr (status_line, "401") ||
770                 strstr (status_line, "402") ||
771                     strstr (status_line, "403") ||
772                     strstr (status_line, "404")) {
773                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
774                 }
776                 /* check redirected page if specified */
777                 if (strstr (status_line, "300") ||
778                   strstr (status_line, "301") ||
779                 strstr (status_line, "302") ||
780                     strstr (status_line, "303") ||
781                     strstr (status_line, "304")) {
782                         if (onredirect == STATE_DEPENDENT) {
784                                 asprintf (&orig_url, "%s", server_url);
785                                 pos = header;
786                                 while (pos) {
787                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
788                                         if (server_address == NULL)
789                                                 terminate (STATE_UNKNOWN,
790                                                                                  "HTTP UNKNOWN: could not allocate server_address");
791                                         if (strcspn (pos, "\r\n") > server_url_length) {
792                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
793                                                 if (server_url == NULL)
794                                                         terminate (STATE_UNKNOWN,
795                                                                    "HTTP UNKNOWN: could not allocate server_url");
796                                                 server_url_length = strcspn (pos, "\r\n");
797                                         }
798                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
799                                                 asprintf (&host_name, "%s", server_address);
800                                                 use_ssl = server_type_check (server_type);
801                                                 server_port = atoi (server_port_text);
802                                                 check_http ();
803                                         }
804                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
805                                                 asprintf (&host_name, "%s", server_address);
806                                                 use_ssl = server_type_check (server_type);
807                                                 server_port = server_port_check (use_ssl);
808                                                 check_http ();
809                                         }
810                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
811                                                 asprintf (&host_name, "%s", server_address);
812                                                 strcpy (server_url, "/");
813                                                 use_ssl = server_type_check (server_type);
814                                                 server_port = atoi (server_port_text);
815                                                 check_http ();
816                                         }
817                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
818                                                 asprintf (&host_name, "%s", server_address);
819                                                 strcpy (server_url, "/");
820                                                 use_ssl = server_type_check (server_type);
821                                                 server_port = server_port_check (use_ssl);
822                                                 check_http ();
823                                         }
824                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
825                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
826                                                         *x = '\0';
827                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
828                                                 }
829                                                 check_http ();
830                                         }                                       
831                                         pos += (size_t) strcspn (pos, "\r\n");
832                                         pos += (size_t) strspn (pos, "\r\n");
833                                 } /* end while (pos) */
834                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
835                                         status_line, (display_html ? "</A>" : ""));
836                                 exit (STATE_UNKNOWN);
837                         } /* end if (onredirect == STATE_DEPENDENT) */
838                         
839                         else if (onredirect == STATE_UNKNOWN)
840                                 printf ("HTTP UNKNOWN");
841                         else if (onredirect == STATE_OK)
842                                 printf ("HTTP ok");
843                         else if (onredirect == STATE_WARNING)
844                                 printf ("HTTP WARNING");
845                         else if (onredirect == STATE_CRITICAL)
846                                 printf ("HTTP CRITICAL");
847                         elapsed_time = delta_time (tv);
848                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
849                                  status_line, elapsed_time, timestamp,
850                            (display_html ? "</A>" : ""), elapsed_time);
851                         terminate (onredirect, msg);
852                 } /* end if (strstr (status_line, "30[0-4]") */
855         } /* end else (server_expect_yn)  */
857                 
858         /* check elapsed time */
859         elapsed_time = delta_time (tv);
860         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
861                        status_line, elapsed_time, timestamp,
862                        (display_html ? "</A>" : ""), elapsed_time);
863         if (check_critical_time == TRUE && elapsed_time > critical_time)
864                 terminate (STATE_CRITICAL, msg);
865         if (check_warning_time == TRUE && elapsed_time > warning_time)
866                 terminate (STATE_WARNING, msg);
868         /* Page and Header content checks go here */
869         /* these checks should be last */
871         if (strlen (string_expect)) {
872                 if (strstr (page, string_expect)) {
873                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
874                                 status_line, elapsed_time,
875                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
876                         exit (STATE_OK);
877                 }
878                 else {
879                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
880                                 (display_html ? "</A>" : ""), elapsed_time);
881                         exit (STATE_CRITICAL);
882                 }
883         }
884 #ifdef HAVE_REGEX_H
885         if (strlen (regexp)) {
886                 errcode = regexec (&preg, page, REGS, pmatch, 0);
887                 if (errcode == 0) {
888                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
889                                 status_line, elapsed_time,
890                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
891                         exit (STATE_OK);
892                 }
893                 else {
894                         if (errcode == REG_NOMATCH) {
895                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
896                                         (display_html ? "</A>" : ""), elapsed_time);
897                                 exit (STATE_CRITICAL);
898                         }
899                         else {
900                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
901                                 printf ("Execute Error: %s\n", errbuf);
902                                 exit (STATE_CRITICAL);
903                         }
904                 }
905         }
906 #endif
908         /* We only get here if all tests have been passed */
909         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
910                         status_line, (float)elapsed_time,
911                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
912         terminate (STATE_OK, msg);
913         return STATE_UNKNOWN;
918 #ifdef HAVE_SSL
919 int connect_SSL (void)
921         SSL_METHOD *meth;
923         asprintf (&randbuff, "%s", "qwertyuiopasdfghjkl");
924         RAND_seed (randbuff, strlen (randbuff));
925         /* Initialize SSL context */
926         SSLeay_add_ssl_algorithms ();
927         meth = SSLv23_client_method ();
928         SSL_load_error_strings ();
929         if ((ctx = SSL_CTX_new (meth)) == NULL) {
930                 printf ("ERROR: Cannot create SSL context.\n");
931                 return STATE_CRITICAL;
932         }
934         /* Initialize alarm signal handling */
935         signal (SIGALRM, socket_timeout_alarm_handler);
937         /* Set socket timeout */
938         alarm (socket_timeout);
940         /* Save start time */
941         gettimeofday (&tv, NULL);
943         /* Make TCP connection */
944         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
945                 /* Do the SSL handshake */
946                 if ((ssl = SSL_new (ctx)) != NULL) {
947                         SSL_set_cipher_list(ssl, "ALL");
948                         SSL_set_fd (ssl, sd);
949                         if (SSL_connect (ssl) != -1)
950                                 return OK;
951                         ERR_print_errors_fp (stderr);
952                 }
953                 else {
954                         printf ("ERROR: Cannot initiate SSL handshake.\n");
955                 }
956                 SSL_free (ssl);
957         }
959         SSL_CTX_free (ctx);
960         close (sd);
962         return STATE_CRITICAL;
964 #endif
966 #ifdef HAVE_SSL
967 int
968 check_certificate (X509 ** certificate)
970         ASN1_STRING *tm;
971         int offset;
972         struct tm stamp;
973         int days_left;
976         /* Retrieve timestamp of certificate */
977         tm = X509_get_notAfter (*certificate);
979         /* Generate tm structure to process timestamp */
980         if (tm->type == V_ASN1_UTCTIME) {
981                 if (tm->length < 10) {
982                         printf ("ERROR: Wrong time format in certificate.\n");
983                         return STATE_CRITICAL;
984                 }
985                 else {
986                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
987                         if (stamp.tm_year < 50)
988                                 stamp.tm_year += 100;
989                         offset = 0;
990                 }
991         }
992         else {
993                 if (tm->length < 12) {
994                         printf ("ERROR: Wrong time format in certificate.\n");
995                         return STATE_CRITICAL;
996                 }
997                 else {
998                         stamp.tm_year =
999                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1000                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1001                         stamp.tm_year -= 1900;
1002                         offset = 2;
1003                 }
1004         }
1005         stamp.tm_mon =
1006                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1007         stamp.tm_mday =
1008                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1009         stamp.tm_hour =
1010                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1011         stamp.tm_min =
1012                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1013         stamp.tm_sec = 0;
1014         stamp.tm_isdst = -1;
1016         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1017         snprintf
1018                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1019                  stamp.tm_mon + 1,
1020                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1022         if (days_left > 0 && days_left <= days_till_exp) {
1023                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1024                 return STATE_WARNING;
1025         }
1026         if (days_left < 0) {
1027                 printf ("Certificate expired on %s.\n", timestamp);
1028                 return STATE_CRITICAL;
1029         }
1031         if (days_left == 0) {
1032                 printf ("Certificate expires today (%s).\n", timestamp);
1033                 return STATE_WARNING;
1034         }
1036         printf ("Certificate will expire on %s.\n", timestamp);
1038         return STATE_OK;
1040 #endif
1041 \f
1044 int
1045 my_recv (void)
1047         int i;
1048 #ifdef HAVE_SSL
1049         if (use_ssl) {
1050                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1051         }
1052         else {
1053                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1054         }
1055 #else
1056         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1057 #endif
1058         return i;
1062 int
1063 my_close (void)
1065 #ifdef HAVE_SSL
1066         if (use_ssl == TRUE) {
1067                 SSL_shutdown (ssl);
1068                 SSL_free (ssl);
1069                 SSL_CTX_free (ctx);
1070                 return 0;
1071         }
1072         else {
1073 #endif
1074                 return close (sd);
1075 #ifdef HAVE_SSL
1076         }
1077 #endif
1079 \f
1082 void
1083 print_help (void)
1085         print_revision (progname, REVISION);
1086         printf
1087                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1088                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1089         print_usage ();
1090         printf ("NOTE: One or both of -H and -I must be specified\n");
1091         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1092                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1093 #ifdef HAVE_SSL
1094         printf (SSLDESCRIPTION);
1095 #endif
1099 void
1100 print_usage (void)
1102         printf ("Usage:\n" " %s %s\n"
1103 #ifdef HAVE_GETOPT_H
1104                 " %s (-h | --help) for detailed help\n"
1105                 " %s (-V | --version) for version information\n",
1106 #else
1107                 " %s -h for detailed help\n"
1108                 " %s -V for version information\n",
1109 #endif
1110         progname, OPTIONS, progname, progname);