Code

do not pass host if none is provided
[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 "/"
193 char timestamp[17] = "";
194 int specify_port = FALSE;
195 int server_port = HTTP_PORT;
196 char server_port_text[6] = "";
197 char server_type[6] = "http";
198 /*@null@*/ char *server_address = NULL; 
199 char *host_name = "";
200 /*@null@*/ char *server_url = NULL;
201 int server_url_length = 0;
202 int server_expect_yn = 0;
203 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
204 char string_expect[MAX_INPUT_BUFFER] = "";
205 double warning_time = 0;
206 int check_warning_time = FALSE;
207 double critical_time = 0;
208 int check_critical_time = FALSE;
209 char user_auth[MAX_INPUT_BUFFER] = "";
210 int display_html = FALSE;
211 int onredirect = STATE_OK;
212 int use_ssl = FALSE;
213 int verbose = FALSE;
214 int sd;
215 /*@null@*/ char *http_method = NULL;
216 /*@null@*/ char *http_post_data = NULL;
217 char buffer[MAX_INPUT_BUFFER];
219 void print_usage (void);
220 void print_help (void);
221 int process_arguments (int, char **);
222 static char *base64 (char *bin, int len);
223 int check_http (void);
224 int my_recv (void);
225 int my_close (void);
227 int
228 main (int argc, char **argv)
230         int result = STATE_UNKNOWN;
232         if (process_arguments (argc, argv) == ERROR)
233                 usage ("check_http: could not parse arguments\n");
235         if (strstr (timestamp, ":")) {
236                 if (strstr (server_url, "?"))
237                         asprintf (&server_url, "%s&%s", server_url, timestamp);
238                 else
239                         asprintf (&server_url, "%s?%s", server_url, timestamp);
240         }
242         if (display_html == TRUE)
243                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
244                         host_name, server_port, server_url);
246         /* initialize alarm signal handling, set socket timeout, start timer */
247         (void) signal (SIGALRM, socket_timeout_alarm_handler);
248         (void) alarm (socket_timeout);
249         gettimeofday (&tv, NULL);
251 #ifdef HAVE_SSL
252         if (use_ssl && check_cert == TRUE) {
253                 if (connect_SSL () != OK)
254                         terminate (STATE_CRITICAL,
255                                    "HTTP CRITICAL - Could not make SSL connection\n");
256                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
257                         result = check_certificate (&server_cert);
258                         X509_free (server_cert);
259                 }
260                 else {
261                         printf ("ERROR: Cannot retrieve server certificate.\n");
262                         result = STATE_CRITICAL;
263                 }
264                 SSL_shutdown (ssl);
265                 SSL_free (ssl);
266                 SSL_CTX_free (ctx);
267                 close (sd);
268         }
269         else {
270                 result = check_http ();
271         }
272 #else
273         result = check_http ();
274 #endif
275         return result;
277 \f
280 /* process command-line arguments */
281 int
282 process_arguments (int argc, char **argv)
284         int c = 1;
286 #ifdef HAVE_GETOPT_H
287         int option_index = 0;
288         static struct option long_options[] = {
289                 STD_LONG_OPTS,
290                 {"file",required_argument,0,'F'},
291                 {"link", no_argument, 0, 'L'},
292                 {"nohtml", no_argument, 0, 'n'},
293                 {"ssl", no_argument, 0, 'S'},
294                 {"verbose", no_argument, 0, 'v'},
295                 {"post", required_argument, 0, 'P'},
296                 {"IP-address", required_argument, 0, 'I'},
297                 {"string", required_argument, 0, 's'},
298                 {"regex", required_argument, 0, 'r'},
299                 {"ereg", required_argument, 0, 'r'},
300                 {"eregi", required_argument, 0, 'R'},
301                 {"linespan", no_argument, 0, 'l'},
302                 {"onredirect", required_argument, 0, 'f'},
303                 {"certificate", required_argument, 0, 'C'},
304                 {0, 0, 0, 0}
305         };
306 #endif
308         if (argc < 2)
309                 return ERROR;
311         for (c = 1; c < argc; c++) {
312                 if (strcmp ("-to", argv[c]) == 0)
313                         strcpy (argv[c], "-t");
314                 if (strcmp ("-hn", argv[c]) == 0)
315                         strcpy (argv[c], "-H");
316                 if (strcmp ("-wt", argv[c]) == 0)
317                         strcpy (argv[c], "-w");
318                 if (strcmp ("-ct", argv[c]) == 0)
319                         strcpy (argv[c], "-c");
320                 if (strcmp ("-nohtml", argv[c]) == 0)
321                         strcpy (argv[c], "-n");
322         }
324 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
326         while (1) {
327 #ifdef HAVE_GETOPT_H
328                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
329 #else
330                 c = getopt (argc, argv, OPTCHARS);
331 #endif
332                 if (c == -1 || c == EOF)
333                         break;
335                 switch (c) {
336                 case '?': /* usage */
337                         usage2 ("unknown argument", optarg);
338                         break;
339                 case 'h': /* help */
340                         print_help ();
341                         exit (STATE_OK);
342                         break;
343                 case 'V': /* version */
344                         print_revision (progname, REVISION);
345                         exit (STATE_OK);
346                         break;
347                 case 't': /* timeout period */
348                         if (!is_intnonneg (optarg))
349                                 usage2 ("timeout interval must be a non-negative integer", optarg);
350                         socket_timeout = atoi (optarg);
351                         break;
352                 case 'c': /* critical time threshold */
353                         if (!is_intnonneg (optarg))
354                                 usage2 ("invalid critical threshold", optarg);
355                         critical_time = strtod (optarg, NULL);
356                         check_critical_time = TRUE;
357                         break;
358                 case 'w': /* warning time threshold */
359                         if (!is_intnonneg (optarg))
360                                 usage2 ("invalid warning threshold", optarg);
361                         warning_time = strtod (optarg, NULL);
362                         check_warning_time = TRUE;
363                         break;
364                 case 'L': /* show html link */
365                         display_html = TRUE;
366                         break;
367                 case 'n': /* do not show html link */
368                         display_html = FALSE;
369                         break;
370                 case 'S': /* use SSL */
371 #ifndef HAVE_SSL
372                         usage ("check_http: invalid option - SSL is not available\n");
373 #endif
374                         use_ssl = TRUE;
375                         if (specify_port == FALSE)
376                                 server_port = HTTPS_PORT;
377                         break;
378                 case 'C': /* Check SSL cert validity */
379 #ifdef HAVE_SSL
380                         if (!is_intnonneg (optarg))
381                                 usage2 ("invalid certificate expiration period", optarg);
382                         days_till_exp = atoi (optarg);
383                         check_cert = TRUE;
384 #else
385                         usage ("check_http: invalid option - SSL is not available\n");
386 #endif
387                         break;
388                 case 'f': /* onredirect */
389                         if (!strcmp (optarg, "follow"))
390                                 onredirect = STATE_DEPENDENT;
391                         if (!strcmp (optarg, "unknown"))
392                                 onredirect = STATE_UNKNOWN;
393                         if (!strcmp (optarg, "ok"))
394                                 onredirect = STATE_OK;
395                         if (!strcmp (optarg, "warning"))
396                                 onredirect = STATE_WARNING;
397                         if (!strcmp (optarg, "critical"))
398                                 onredirect = STATE_CRITICAL;
399                         if (verbose)
400                                 printf("option f:%d \n", onredirect);  
401                         break;
402                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
403                 case 'H': /* Host Name (virtual host) */
404                         asprintf (&host_name, "%s", optarg);
405                         break;
406                 case 'I': /* Server IP-address */
407                         server_address = strscpy (server_address, optarg);
408                         break;
409                 case 'u': /* Host or server */
410                         server_url = strscpy (server_url, optarg);
411                         server_url_length = strlen (optarg);
412                         break;
413                 case 'p': /* Host or server */
414                         if (!is_intnonneg (optarg))
415                                 usage2 ("invalid port number", optarg);
416                         server_port = atoi (optarg);
417                         specify_port = TRUE;
418                         break;
419                 case 'a': /* authorization info */
420                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
421                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
422                         break;
423                 case 'P': /* HTTP POST data in URL encoded format */
424                         http_method = strscpy (http_method, "POST");
425                         http_post_data = strscpy (http_post_data, optarg);
426                         break;
427                 case 's': /* string or substring */
428                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
429                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
430                         break;
431                 case 'e': /* string or substring */
432                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
433                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
434                         server_expect_yn = 1;
435                         break;
436 #ifndef HAVE_REGEX_H
437                 case 'l': /* linespan */
438                 case 'r': /* linespan */
439                 case 'R': /* linespan */
440                         usage ("check_http: call for regex which was not a compiled option\n");
441                         break;
442 #else
443                 case 'l': /* linespan */
444                         cflags &= ~REG_NEWLINE;
445                         break;
446                 case 'R': /* regex */
447                         cflags |= REG_ICASE;
448                 case 'r': /* regex */
449                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
450                         regexp[MAX_RE_SIZE - 1] = 0;
451                         errcode = regcomp (&preg, regexp, cflags);
452                         if (errcode != 0) {
453                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
454                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
455                                 return ERROR;
456                         }
457                         break;
458 #endif
459                 case 'v': /* verbose */
460                         verbose = TRUE;
461                         break;
462                 }
463         }
465         c = optind;
467         if (server_address == NULL) {
468                 if (c < argc) {
469                         server_address = strscpy (NULL, argv[c++]);
470                 }
471                 else if (strcmp (host_name ,"") == 0) {
472                         usage ("check_http: you must specify a server address\n");
473                 }
474         }
476         if (strcmp (host_name ,"") == 0 && c < argc)
477                 asprintf (&host_name, "%s", argv[c++]);
479         if (server_address == NULL)
480                 server_address = strscpy (NULL, host_name);
482         if (http_method == NULL)
483                 http_method = strscpy (http_method, "GET");
485         if (server_url == NULL) {
486                 server_url = strscpy (NULL, "/");
487                 server_url_length = strlen(HTTP_URL);
488         }
490         return TRUE;
492 \f
495 /* written by lauri alanko */
496 static char *
497 base64 (char *bin, int len)
500         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
501         int i = 0, j = 0;
503         char BASE64_END = '=';
504         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
506         while (j < len - 2) {
507                 buf[i++] = base64_table[bin[j] >> 2];
508                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
509                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
510                 buf[i++] = base64_table[bin[j + 2] & 63];
511                 j += 3;
512         }
514         switch (len - j) {
515         case 1:
516                 buf[i++] = base64_table[bin[j] >> 2];
517                 buf[i++] = base64_table[(bin[j] & 3) << 4];
518                 buf[i++] = BASE64_END;
519                 buf[i++] = BASE64_END;
520                 break;
521         case 2:
522                 buf[i++] = base64_table[bin[j] >> 2];
523                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
524                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
525                 buf[i++] = BASE64_END;
526                 break;
527         case 0:
528                 break;
529         }
531         buf[i] = '\0';
532         return buf;
534 \f
537 int
538 check_http (void)
540         char *msg = NULL;
541         char *status_line = "";
542         char *header = NULL;
543         char *page = "";
544         char *auth = NULL;
545         int i = 0;
546         size_t pagesize = 0;
547         char *full_page = "";
548         char *buf = NULL;
549         char *pos = "";
550         char *x = NULL;
551         char *orig_url = NULL;
552         double elapsed_time;
554         /* try to connect to the host at the given port number */
555 #ifdef HAVE_SSL
556         if (use_ssl == TRUE) {
558                 if (connect_SSL () != OK)
559                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
561                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
562                         X509_free (server_cert);
563                 }
564                 else {
565                         printf ("ERROR: Cannot retrieve server certificate.\n");
566                         return STATE_CRITICAL;
567                 }
569                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
570                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
571                         ERR_print_errors_fp (stderr);
572                         return STATE_CRITICAL;
573                 }
575                 /* optionally send the host header info (not clear if it's usable) */
576                 if (strcmp (host_name, "")) {
577                         asprintf (&buf, "Host: %s\r\n", host_name);
578                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
579                                 ERR_print_errors_fp (stderr);
580                                 return STATE_CRITICAL;
581                         }
582                 }
584                 /* send user agent */
585                 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
586                          clean_revstring (REVISION), PACKAGE_VERSION);
587                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
588                         ERR_print_errors_fp (stderr);
589                         return STATE_CRITICAL;
590                 }
592                 /* optionally send the authentication info */
593                 if (strcmp (user_auth, "")) {
594                         auth = base64 (user_auth, strlen (user_auth));
595                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
596                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
597                                 ERR_print_errors_fp (stderr);
598                                 return STATE_CRITICAL;
599                         }
600                 }
602                 /* either send http POST data */
603                 if (http_post_data) {
604                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
605                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
606                                 ERR_print_errors_fp (stderr);
607                                 return STATE_CRITICAL;
608                         }
609                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
610                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
611                                 ERR_print_errors_fp (stderr);
612                                 return STATE_CRITICAL;
613                         }
614                         http_post_data = strscat (http_post_data, "\r\n");
615                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
616                                 ERR_print_errors_fp (stderr);
617                                 return STATE_CRITICAL;
618                         }
619                 }
620                 else {
621                         /* or just a newline so the server knows we're done with the request */
622                         asprintf (&buf, "\r\n");
623                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
624                                 ERR_print_errors_fp (stderr);
625                                 return STATE_CRITICAL;
626                         }
627                 }
629         }
630         else {
631 #endif
632                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
633                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
634                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
635                 send (sd, buf, strlen (buf), 0);
637                 /* optionally send the host header info */
638                 if (strcmp (host_name, "")) {
639                         asprintf (&buf, "Host: %s\r\n", host_name);
640                         send (sd, buf, strlen (buf), 0);
641                 }
643                 /* send user agent */
644                 asprintf (&buf,
645                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
646                          clean_revstring (REVISION), PACKAGE_VERSION);
647                 send (sd, buf, strlen (buf), 0);
649                 /* optionally send the authentication info */
650                 if (strcmp (user_auth, "")) {
651                         auth = base64 (user_auth, strlen (user_auth));
652                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
653                         send (sd, buf, strlen (buf), 0);
654                 }
656                 /* either send http POST data */
657                 /* written by Chris Henesy <lurker@shadowtech.org> */
658                 if (http_post_data) {
659                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
660                         send (sd, buf, strlen (buf), 0);
661                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
662                         send (sd, buf, strlen (buf), 0);
663                         http_post_data = strscat (http_post_data, "\r\n");
664                         send (sd, http_post_data, strlen (http_post_data), 0);
665                 }
666                 else {
667                         /* or just a newline so the server knows we're done with the request */
668                         asprintf (&buf, "\r\n");
669                         send (sd, buf, strlen (buf), 0);
670                 }
671 #ifdef HAVE_SSL
672         }
673 #endif
675         /* fetch the page */
676         while ((i = my_recv ()) > 0) {
677                 buffer[i] = '\0';
678                 asprintf (&full_page, "%s%s", full_page, buffer);
679                 pagesize += i;
680         }
682         if (i < 0)
683                 terminate (STATE_CRITICAL, "Error in recv()");
685         /* return a CRITICAL status if we couldn't read any data */
686         if (pagesize == (size_t) 0)
687                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
689         /* close the connection */
690         my_close ();
692         /* reset the alarm */
693         alarm (0);
695         /* leave full_page untouched so we can free it later */
696         page = full_page;
698         if (verbose)
699                 printf ("Page is %d characters\n", pagesize);
701         /* find status line and null-terminate it */
702         status_line = page;
703         page += (size_t) strcspn (page, "\r\n");
704         pos = page;
705         page += (size_t) strspn (page, "\r\n");
706         status_line[strcspn(status_line, "\r\n")] = 0;
707         strip (status_line);
708         if (verbose)
709                 printf ("STATUS: %s\n", status_line);
711         /* find header info and null terminate it */
712         header = page;
713         while (strcspn (page, "\r\n") > 0) {
714                 page += (size_t) strcspn (page, "\r\n");
715                 pos = page;
716                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
717                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
718                         page += (size_t) 2;
719                 else
720                         page += (size_t) 1;
721         }
722         page += (size_t) strspn (page, "\r\n");
723         header[pos - header] = 0;
724         if (verbose)
725                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
727         /* make sure the status line matches the response we are looking for */
728         if (!strstr (status_line, server_expect)) {
729                 if (server_port == HTTP_PORT)
730                         asprintf (&msg, "Invalid HTTP response received from host\n");
731                 else
732                         asprintf (&msg,
733                                         "Invalid HTTP response received from host on port %d\n",
734                                         server_port);
735                 terminate (STATE_CRITICAL, msg);
736         }
739         /* Exit here if server_expect was set by user and not default */
740         if ( server_expect_yn  )  {
741                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
742                           server_expect);
743                 if (verbose)
744                         printf ("%s\n",msg);
746         }
747         else {
748         
750                 /* check the return code */
751                 /* server errors result in a critical state */
752                 if (strstr (status_line, "500") ||
753                   strstr (status_line, "501") ||
754                 strstr (status_line, "502") ||
755                     strstr (status_line, "503")) {
756                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
757                 }
759                 /* client errors result in a warning state */
760                 if (strstr (status_line, "400") ||
761                   strstr (status_line, "401") ||
762                 strstr (status_line, "402") ||
763                     strstr (status_line, "403") ||
764                     strstr (status_line, "404")) {
765                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
766                 }
768                 /* check redirected page if specified */
769                 if (strstr (status_line, "300") ||
770                   strstr (status_line, "301") ||
771                 strstr (status_line, "302") ||
772                     strstr (status_line, "303") ||
773                     strstr (status_line, "304")) {
774                         if (onredirect == STATE_DEPENDENT) {
776                                 orig_url = strscpy(NULL, server_url);
777                                 pos = header;
778                                 while (pos) {
779                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
780                                         if (server_address == NULL)
781                                                 terminate (STATE_UNKNOWN,
782                                                                                  "HTTP UNKNOWN: could not allocate server_address");
783                                         if (strcspn (pos, "\r\n") > server_url_length) {
784                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
785                                                 if (server_url == NULL)
786                                                         terminate (STATE_UNKNOWN,
787                                                                    "HTTP UNKNOWN: could not allocate server_url");
788                                                 server_url_length = strcspn (pos, "\r\n");
789                                         }
790                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
791                                                 host_name = strscpy (host_name, server_address);
792                                                 use_ssl = server_type_check (server_type);
793                                                 server_port = atoi (server_port_text);
794                                                 check_http ();
795                                         }
796                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
797                                                 host_name = strscpy (host_name, server_address);
798                                                 use_ssl = server_type_check (server_type);
799                                                 server_port = server_port_check (use_ssl);
800                                                 check_http ();
801                                         }
802                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
803                                                 host_name = strscpy (host_name, server_address);
804                                                 strcpy (server_url, "/");
805                                                 use_ssl = server_type_check (server_type);
806                                                 server_port = atoi (server_port_text);
807                                                 check_http ();
808                                         }
809                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
810                                                 host_name = strscpy (host_name, server_address);
811                                                 strcpy (server_url, "/");
812                                                 use_ssl = server_type_check (server_type);
813                                                 server_port = server_port_check (use_ssl);
814                                                 check_http ();
815                                         }
816                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
817                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
818                                                         *x = '\0';
819                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
820                                                 }
821                                                 check_http ();
822                                         }                                       
823                                         pos += (size_t) strcspn (pos, "\r\n");
824                                         pos += (size_t) strspn (pos, "\r\n");
825                                 } /* end while (pos) */
826                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
827                                         status_line, (display_html ? "</A>" : ""));
828                                 exit (STATE_UNKNOWN);
829                         } /* end if (onredirect == STATE_DEPENDENT) */
830                         
831                         else if (onredirect == STATE_UNKNOWN)
832                                 printf ("HTTP UNKNOWN");
833                         else if (onredirect == STATE_OK)
834                                 printf ("HTTP ok");
835                         else if (onredirect == STATE_WARNING)
836                                 printf ("HTTP WARNING");
837                         else if (onredirect == STATE_CRITICAL)
838                                 printf ("HTTP CRITICAL");
839                         elapsed_time = delta_time (tv);
840                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
841                                  status_line, elapsed_time, timestamp,
842                            (display_html ? "</A>" : ""), elapsed_time);
843                         terminate (onredirect, msg);
844                 } /* end if (strstr (status_line, "30[0-4]") */
847         } /* end else (server_expect_yn)  */
849                 
850         /* check elapsed time */
851         elapsed_time = delta_time (tv);
852         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
853                        status_line, elapsed_time, timestamp,
854                        (display_html ? "</A>" : ""), elapsed_time);
855         if (check_critical_time == TRUE && elapsed_time > critical_time)
856                 terminate (STATE_CRITICAL, msg);
857         if (check_warning_time == TRUE && elapsed_time > warning_time)
858                 terminate (STATE_WARNING, msg);
860         /* Page and Header content checks go here */
861         /* these checks should be last */
863         if (strlen (string_expect)) {
864                 if (strstr (page, string_expect)) {
865                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
866                                 status_line, elapsed_time,
867                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
868                         exit (STATE_OK);
869                 }
870                 else {
871                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
872                                 (display_html ? "</A>" : ""), elapsed_time);
873                         exit (STATE_CRITICAL);
874                 }
875         }
876 #ifdef HAVE_REGEX_H
877         if (strlen (regexp)) {
878                 errcode = regexec (&preg, page, REGS, pmatch, 0);
879                 if (errcode == 0) {
880                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
881                                 status_line, elapsed_time,
882                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
883                         exit (STATE_OK);
884                 }
885                 else {
886                         if (errcode == REG_NOMATCH) {
887                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
888                                         (display_html ? "</A>" : ""), elapsed_time);
889                                 exit (STATE_CRITICAL);
890                         }
891                         else {
892                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
893                                 printf ("Execute Error: %s\n", errbuf);
894                                 exit (STATE_CRITICAL);
895                         }
896                 }
897         }
898 #endif
900         /* We only get here if all tests have been passed */
901         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
902                         status_line, (float)elapsed_time,
903                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
904         terminate (STATE_OK, msg);
905         return STATE_UNKNOWN;
910 #ifdef HAVE_SSL
911 int connect_SSL (void)
913         SSL_METHOD *meth;
915         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
916         RAND_seed (randbuff, strlen (randbuff));
917         /* Initialize SSL context */
918         SSLeay_add_ssl_algorithms ();
919         meth = SSLv23_client_method ();
920         SSL_load_error_strings ();
921         if ((ctx = SSL_CTX_new (meth)) == NULL) {
922                 printf ("ERROR: Cannot create SSL context.\n");
923                 return STATE_CRITICAL;
924         }
926         /* Initialize alarm signal handling */
927         signal (SIGALRM, socket_timeout_alarm_handler);
929         /* Set socket timeout */
930         alarm (socket_timeout);
932         /* Save start time */
933         gettimeofday (&tv, NULL);
935         /* Make TCP connection */
936         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
937                 /* Do the SSL handshake */
938                 if ((ssl = SSL_new (ctx)) != NULL) {
939                         SSL_set_cipher_list(ssl, "ALL");
940                         SSL_set_fd (ssl, sd);
941                         if (SSL_connect (ssl) != -1)
942                                 return OK;
943                         ERR_print_errors_fp (stderr);
944                 }
945                 else {
946                         printf ("ERROR: Cannot initiate SSL handshake.\n");
947                 }
948                 SSL_free (ssl);
949         }
951         SSL_CTX_free (ctx);
952         close (sd);
954         return STATE_CRITICAL;
956 #endif
958 #ifdef HAVE_SSL
959 int
960 check_certificate (X509 ** certificate)
962         ASN1_STRING *tm;
963         int offset;
964         struct tm stamp;
965         int days_left;
968         /* Retrieve timestamp of certificate */
969         tm = X509_get_notAfter (*certificate);
971         /* Generate tm structure to process timestamp */
972         if (tm->type == V_ASN1_UTCTIME) {
973                 if (tm->length < 10) {
974                         printf ("ERROR: Wrong time format in certificate.\n");
975                         return STATE_CRITICAL;
976                 }
977                 else {
978                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
979                         if (stamp.tm_year < 50)
980                                 stamp.tm_year += 100;
981                         offset = 0;
982                 }
983         }
984         else {
985                 if (tm->length < 12) {
986                         printf ("ERROR: Wrong time format in certificate.\n");
987                         return STATE_CRITICAL;
988                 }
989                 else {
990                         stamp.tm_year =
991                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
992                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
993                         stamp.tm_year -= 1900;
994                         offset = 2;
995                 }
996         }
997         stamp.tm_mon =
998                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
999         stamp.tm_mday =
1000                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1001         stamp.tm_hour =
1002                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1003         stamp.tm_min =
1004                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1005         stamp.tm_sec = 0;
1006         stamp.tm_isdst = -1;
1008         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1009         snprintf
1010                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1011                  stamp.tm_mon + 1,
1012                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1014         if (days_left > 0 && days_left <= days_till_exp) {
1015                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1016                 return STATE_WARNING;
1017         }
1018         if (days_left < 0) {
1019                 printf ("Certificate expired on %s.\n", timestamp);
1020                 return STATE_CRITICAL;
1021         }
1023         if (days_left == 0) {
1024                 printf ("Certificate expires today (%s).\n", timestamp);
1025                 return STATE_WARNING;
1026         }
1028         printf ("Certificate will expire on %s.\n", timestamp);
1030         return STATE_OK;
1032 #endif
1033 \f
1036 int
1037 my_recv (void)
1039         int i;
1040 #ifdef HAVE_SSL
1041         if (use_ssl) {
1042                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1043         }
1044         else {
1045                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1046         }
1047 #else
1048         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1049 #endif
1050         return i;
1054 int
1055 my_close (void)
1057 #ifdef HAVE_SSL
1058         if (use_ssl == TRUE) {
1059                 SSL_shutdown (ssl);
1060                 SSL_free (ssl);
1061                 SSL_CTX_free (ctx);
1062                 return 0;
1063         }
1064         else {
1065 #endif
1066                 return close (sd);
1067 #ifdef HAVE_SSL
1068         }
1069 #endif
1071 \f
1074 void
1075 print_help (void)
1077         print_revision (progname, REVISION);
1078         printf
1079                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1080                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1081         print_usage ();
1082         printf ("NOTE: One or both of -H and -I must be specified\n");
1083         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1084                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1085 #ifdef HAVE_SSL
1086         printf (SSLDESCRIPTION);
1087 #endif
1091 void
1092 print_usage (void)
1094         printf ("Usage:\n" " %s %s\n"
1095 #ifdef HAVE_GETOPT_H
1096                 " %s (-h | --help) for detailed help\n"
1097                 " %s (-V | --version) for version information\n",
1098 #else
1099                 " %s -h for detailed help\n"
1100                 " %s -V for version information\n",
1101 #endif
1102         progname, OPTIONS, progname, progname);