Code

incorporate comment on my_recv from Russell Scibetti
[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 #define 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 "version.h"
35 #include "netutils.h"
36 #include "utils.h"
38 #define SUMMARY "\
39 This plugin tests the HTTP service on the specified host. It can test\n\
40 normal (http) and secure (https) servers, follow redirects, search for\n\
41 strings and regular expressions, check connection times, and report on\n\
42 certificate expiration times.\n"
44 #define OPTIONS "\
45 \(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
46             [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
47             [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
48             [-s string] [-r <regex> | -R <case-insensitive regex>]\n\
49             [-P string]"
51 #define LONGOPTIONS "\
52  -H, --hostname=ADDRESS\n\
53     Host name argument for servers using host headers (virtual host)\n\
54  -I, --IP-address=ADDRESS\n\
55    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
56  -e, --expect=STRING\n\
57    String to expect in first (status) line of server response (default: %s)\n\
58    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
59  -s, --string=STRING\n\
60    String to expect in the content\n\
61  -u, --url=PATH\n\
62    URL to GET or POST (default: /)\n\
63  -p, --port=INTEGER\n\
64    Port number (default: %d)\n\
65  -P, --post=STRING\n\
66    URL encoded http POST data\n\
67  -w, --warning=INTEGER\n\
68    Response time to result in warning status (seconds)\n\
69  -c, --critical=INTEGER\n\
70    Response time to result in critical status (seconds)\n\
71  -t, --timeout=INTEGER\n\
72    Seconds before connection times out (default: %d)\n\
73  -a, --authorization=AUTH_PAIR\n\
74    Username:password on sites with basic authentication\n\
75  -L, --link=URL\n\
76    Wrap output in HTML link (obsoleted by urlize)\n\
77  -f, --onredirect=<ok|warning|critical|follow>\n\
78    How to handle redirected pages\n%s\
79  -v, --verbose\n\
80     Show details for command-line debugging (do not use with nagios server)\n\
81  -h, --help\n\
82     Print detailed help screen\n\
83  -V, --version\n\
84     Print version information\n"
86 #ifdef HAVE_SSL
87 #define SSLOPTIONS "\
88  -S, --ssl\n\
89     Connect via SSL\n\
90  -C, --certificate=INTEGER\n\
91     Minimum number of days a certificate has to be valid.\n\
92     (when this option is used the url is not checked.)\n"
93 #else
94 #define SSLOPTIONS ""
95 #endif
97 #define DESCRIPTION "\
98 This plugin will attempt to open an HTTP connection with the host. Successul\n\
99 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
100 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
101 messages from the host result in STATE_WARNING return values.  If you are\n\
102 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
103 \(fully qualified domain name) as the [host_name] argument.\n"
105 #define SSLDESCRIPTION "\
106 This plugin can also check whether an SSL enabled web server is able to\n\
107 serve content (optionally within a specified time) or whether the X509 \n\
108 certificate is still valid for the specified number of days.\n\n\
109 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
110 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
111 STATE_OK will be returned. When the server returns its content but exceeds\n\
112 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
113 a STATE_CRITICAL will be returned.\n\n\
114 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
115 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
116 STATE_OK is returned. When the certificate is still valid, but for less than\n\
117 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
118 the certificate is expired.\n"
120 #ifdef HAVE_SSL_H
121 #include <rsa.h>
122 #include <crypto.h>
123 #include <x509.h>
124 #include <pem.h>
125 #include <ssl.h>
126 #include <err.h>
127 #include <rand.h>
128 #endif
130 #ifdef HAVE_OPENSSL_SSL_H
131 #include <openssl/rsa.h>
132 #include <openssl/crypto.h>
133 #include <openssl/x509.h>
134 #include <openssl/pem.h>
135 #include <openssl/ssl.h>
136 #include <openssl/err.h>
137 #include <openssl/rand.h>
138 #endif
140 #ifdef HAVE_SSL
141 int check_cert = FALSE;
142 int days_till_exp;
143 unsigned char *randbuff;
144 SSL_CTX *ctx;
145 SSL *ssl;
146 X509 *server_cert;
147 int connect_SSL (void);
148 int check_certificate (X509 **);
149 #endif
151 #ifdef HAVE_REGEX_H
152 #define REGS 2
153 #define MAX_RE_SIZE 256
154 #include <regex.h>
155 regex_t preg;
156 regmatch_t pmatch[REGS];
157 char regexp[MAX_RE_SIZE];
158 char errbuf[MAX_INPUT_BUFFER];
159 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
160 int errcode;
161 #endif
163 #define server_type_check(server_type) \
164 (strcmp (server_type, "https") ? FALSE : TRUE)
166 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
168 #define MAX_IPV4_HOSTLENGTH 64
169 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
170 #define URI_HTTP "%[HTPShtps]://"
171 #define URI_HOST "%[a-zA-Z0-9.-]"
172 #define URI_PORT ":%[0-9]"
173 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
175 #define HTTP_PORT 80
176 #define HTTPS_PORT 443
177 #define HTTP_EXPECT "HTTP/1."
178 #define HTTP_URL "/"
180 char timestamp[17] = "";
181 int specify_port = FALSE;
182 int server_port = HTTP_PORT;
183 char server_port_text[6] = "";
184 char server_type[6] = "http";
185 /*@null@*/ char *server_address = NULL; 
186 /*@null@*/ char *host_name = NULL;
187 /*@null@*/ char *server_url = NULL;
188 int server_url_length = 0;
189 int server_expect_yn = 0;
190 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
191 char string_expect[MAX_INPUT_BUFFER] = "";
192 int warning_time = 0;
193 int check_warning_time = FALSE;
194 int critical_time = 0;
195 int check_critical_time = FALSE;
196 char user_auth[MAX_INPUT_BUFFER] = "";
197 int display_html = FALSE;
198 int onredirect = STATE_OK;
199 int use_ssl = FALSE;
200 int verbose = FALSE;
201 int sd;
202 /*@null@*/ char *http_method = NULL;
203 /*@null@*/ char *http_post_data = NULL;
204 char buffer[MAX_INPUT_BUFFER];
206 void print_usage (void);
207 void print_help (void);
208 int process_arguments (int, char **);
209 int call_getopt (int, char **);
210 static char *base64 (char *bin, int len);
211 int check_http (void);
212 int my_recv (void);
213 int my_close (void);
215 int
216 main (int argc, char **argv)
218         int result = STATE_UNKNOWN;
220         if (process_arguments (argc, argv) == ERROR)
221                 usage ("check_http: could not parse arguments\n");
223         if (strstr (timestamp, ":")) {
224                 if (strstr (server_url, "?"))
225                         server_url = ssprintf (server_url, "%s&%s", server_url, timestamp);
226                 else
227                         server_url = ssprintf (server_url, "%s?%s", server_url, timestamp);
228         }
230         if (display_html == TRUE)
231                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
232                         host_name, server_port, server_url);
234         /* initialize alarm signal handling, set socket timeout, start timer */
235         (void) signal (SIGALRM, socket_timeout_alarm_handler);
236         (void) alarm (socket_timeout);
237         (void) time (&start_time);
239 #ifdef HAVE_SSL
240         if (use_ssl && check_cert == TRUE) {
241                 if (connect_SSL () != OK)
242                         terminate (STATE_CRITICAL,
243                                    "HTTP CRITICAL - Could not make SSL connection\n");
244                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
245                         result = check_certificate (&server_cert);
246                         X509_free (server_cert);
247                 }
248                 else {
249                         printf ("ERROR: Cannot retrieve server certificate.\n");
250                         result = STATE_CRITICAL;
251                 }
252                 SSL_shutdown (ssl);
253                 SSL_free (ssl);
254                 SSL_CTX_free (ctx);
255                 close (sd);
256         }
257         else {
258                 result = check_http ();
259         }
260 #else
261         result = check_http ();
262 #endif
263         return result;
265 \f
268 /* process command-line arguments */
269 int
270 process_arguments (int argc, char **argv)
272         int c, i = 1;
273         char optchars[MAX_INPUT_BUFFER];
275 #ifdef HAVE_GETOPT_H
276         int option_index = 0;
277         static struct option long_options[] = {
278                 STD_OPTS_LONG,
279                 {"link", no_argument, 0, 'L'},
280                 {"nohtml", no_argument, 0, 'n'},
281                 {"ssl", no_argument, 0, 'S'},
282                 {"verbose", no_argument, 0, 'v'},
283                 {"post", required_argument, 0, 'P'},
284                 {"IP-address", required_argument, 0, 'I'},
285                 {"string", required_argument, 0, 's'},
286                 {"regex", required_argument, 0, 'r'},
287                 {"ereg", required_argument, 0, 'r'},
288                 {"eregi", required_argument, 0, 'R'},
289                 {"onredirect", required_argument, 0, 'f'},
290                 {"certificate", required_argument, 0, 'C'},
291                 {0, 0, 0, 0}
292         };
293 #endif
295         if (argc < 2)
296                 return ERROR;
298         for (c = 1; c < argc; c++) {
299                 if (strcmp ("-to", argv[c]) == 0)
300                         strcpy (argv[c], "-t");
301                 if (strcmp ("-hn", argv[c]) == 0)
302                         strcpy (argv[c], "-H");
303                 if (strcmp ("-wt", argv[c]) == 0)
304                         strcpy (argv[c], "-w");
305                 if (strcmp ("-ct", argv[c]) == 0)
306                         strcpy (argv[c], "-c");
307                 if (strcmp ("-nohtml", argv[c]) == 0)
308                         strcpy (argv[c], "-n");
309         }
311         snprintf (optchars, MAX_INPUT_BUFFER, "%s%s", STD_OPTS,
312                   "P:I:a:e:p:s:R:r:u:f:C:nLS");
314         while (1) {
315 #ifdef HAVE_GETOPT_H
316                 c = getopt_long (argc, argv, optchars, long_options, &option_index);
317 #else
318                 c = getopt (argc, argv, optchars);
319 #endif
320                 if (c == -1 || c == EOF)
321                         break;
323                 switch (c) {
324                 case '?': /* usage */
325                         usage2 ("unknown argument", optarg);
326                         break;
327                 case 'h': /* help */
328                         print_help ();
329                         exit (STATE_OK);
330                         break;
331                 case 'V': /* version */
332                         print_revision (PROGNAME, REVISION);
333                         exit (STATE_OK);
334                         break;
335                 case 't': /* timeout period */
336                         if (!is_intnonneg (optarg))
337                                 usage2 ("timeout interval must be a non-negative integer", optarg);
338                         socket_timeout = atoi (optarg);
339                         break;
340                 case 'c': /* critical time threshold */
341                         if (!is_intnonneg (optarg))
342                                 usage2 ("invalid critical threshold", optarg);
343                         critical_time = atoi (optarg);
344                         check_critical_time = TRUE;
345                         break;
346                 case 'w': /* warning time threshold */
347                         if (!is_intnonneg (optarg))
348                                 usage2 ("invalid warning threshold", optarg);
349                         warning_time = atoi (optarg);
350                         check_warning_time = TRUE;
351                         break;
352                 case 'L': /* show html link */
353                         display_html = TRUE;
354                         break;
355                 case 'n': /* do not show html link */
356                         display_html = FALSE;
357                         break;
358                 case 'S': /* use SSL */
359 #ifndef HAVE_SSL
360                         usage ("check_http: invalid option - SSL is not available\n");
361 #endif
362                         use_ssl = TRUE;
363                         if (specify_port == FALSE)
364                                 server_port = HTTPS_PORT;
365                         break;
366                 case 'C': /* warning time threshold */
367 #ifdef HAVE_SSL
368                         if (!is_intnonneg (optarg))
369                                 usage2 ("invalid certificate expiration period", optarg);
370                         days_till_exp = atoi (optarg);
371                         check_cert = TRUE;
372 #else
373                         usage ("check_http: invalid option - SSL is not available\n");
374 #endif
375                         break;
376                 case 'f': /* onredirect */
377                         if (!strcmp (optarg, "follow"))
378                                 onredirect = STATE_DEPENDENT;
379                         if (!strcmp (optarg, "unknown"))
380                                 onredirect = STATE_UNKNOWN;
381                         if (!strcmp (optarg, "ok"))
382                                 onredirect = STATE_OK;
383                         if (!strcmp (optarg, "warning"))
384                                 onredirect = STATE_WARNING;
385                         if (!strcmp (optarg, "critical"))
386                                 onredirect = STATE_CRITICAL;
387                         if (verbose)
388                                 printf("option f:%d \n", onredirect);  
389                         break;
390                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
391                 case 'H': /* Host Name (virtual host) */
392                         host_name = strscpy (host_name, optarg);
393                         break;
394                 case 'I': /* Server IP-address */
395                         server_address = strscpy (server_address, optarg);
396                         break;
397                 case 'u': /* Host or server */
398                         server_url = strscpy (server_url, optarg);
399                         server_url_length = strlen (optarg);
400                         break;
401                 case 'p': /* Host or server */
402                         if (!is_intnonneg (optarg))
403                                 usage2 ("invalid port number", optarg);
404                         server_port = atoi (optarg);
405                         specify_port = TRUE;
406                         break;
407                 case 'a': /* authorization info */
408                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
409                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
410                         break;
411                 case 'P': /* HTTP POST data in URL encoded format */
412                         http_method = strscpy (http_method, "POST");
413                         http_post_data = strscpy (http_post_data, optarg);
414                         break;
415                 case 's': /* string or substring */
416                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
417                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
418                         break;
419                 case 'e': /* string or substring */
420                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
421                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
422                         server_expect_yn = 1;
423                         break;
424                 case 'R': /* regex */
425 #ifdef HAVE_REGEX_H
426                         cflags = REG_ICASE;
427 #else
428                         usage ("check_http: call for regex which was not a compiled option\n");
429 #endif
430                 case 'r': /* regex */
431 #ifdef HAVE_REGEX_H
432                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
433                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
434                         regexp[MAX_RE_SIZE - 1] = 0;
435                         errcode = regcomp (&preg, regexp, cflags);
436                         if (errcode != 0) {
437                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
438                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
439                                 return ERROR;
440                         }
441 #else
442                         usage ("check_http: call for regex which was not a compiled option\n");
443 #endif
444                         break;
445                 case 'v': /* verbose */
446                         verbose = TRUE;
447                         break;
448                 }
449         }
451         c = optind;
453         if (server_address == NULL && host_name == NULL) {
454                 server_address = strscpy (NULL, argv[c]);
455                 host_name = strscpy (NULL, argv[c++]);
456         }
458         if (server_address == NULL && host_name == NULL)
459                 usage ("check_http: you must specify a host name\n");
461         if (server_address == NULL)
462                 server_address = strscpy (NULL, host_name);
464         if (host_name == NULL)
465                 host_name = strscpy (NULL, server_address);
467         if (http_method == NULL)
468                 http_method = strscpy (http_method, "GET");
470         if (server_url == NULL) {
471                 server_url = strscpy (NULL, "/");
472                 server_url_length = strlen(HTTP_URL);
473         }
475         return TRUE;
477 \f
480 /* written by lauri alanko */
481 static char *
482 base64 (char *bin, int len)
485         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
486         int i = 0, j = 0;
488         char BASE64_END = '=';
489         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
491         while (j < len - 2) {
492                 buf[i++] = base64_table[bin[j] >> 2];
493                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
494                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
495                 buf[i++] = base64_table[bin[j + 2] & 63];
496                 j += 3;
497         }
499         switch (len - j) {
500         case 1:
501                 buf[i++] = base64_table[bin[j] >> 2];
502                 buf[i++] = base64_table[(bin[j] & 3) << 4];
503                 buf[i++] = BASE64_END;
504                 buf[i++] = BASE64_END;
505                 break;
506         case 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];
510                 buf[i++] = BASE64_END;
511                 break;
512         case 0:
513                 break;
514         }
516         buf[i] = '\0';
517         return buf;
519 \f
522 int
523 check_http (void)
525         char *msg = NULL;
526         char *status_line = NULL;
527         char *header = NULL;
528         char *page = NULL;
529         char *auth = NULL;
530         int i = 0;
531         size_t pagesize = 0;
532         char *full_page = NULL;
533         char *buf = NULL;
534         char *pos = NULL;
535         char *x = NULL;
536         char *orig_url = NULL;
538         /* try to connect to the host at the given port number */
539 #ifdef HAVE_SSL
540         if (use_ssl == TRUE) {
542                 if (connect_SSL () != OK) {
543                         msg = ssprintf (msg, "Unable to open TCP socket");
544                         terminate (STATE_CRITICAL, msg);
545                 }
547                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
548                         X509_free (server_cert);
549                 }
550                 else {
551                         printf ("ERROR: Cannot retrieve server certificate.\n");
552                         return STATE_CRITICAL;
553                 }
555                 buf = ssprintf (buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
556                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
557                         ERR_print_errors_fp (stderr);
558                         return STATE_CRITICAL;
559                 }
561                 /* optionally send the host header info (not clear if it's usable) */
562                 if (strcmp (host_name, "")) {
563                         buf = ssprintf (buf, "Host: %s\r\n", host_name);
564                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
565                                 ERR_print_errors_fp (stderr);
566                                 return STATE_CRITICAL;
567                         }
568                 }
570                 /* send user agent */
571                 buf = ssprintf (buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
572                          clean_revstring (REVISION), PACKAGE_VERSION);
573                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
574                         ERR_print_errors_fp (stderr);
575                         return STATE_CRITICAL;
576                 }
578                 /* optionally send the authentication info */
579                 if (strcmp (user_auth, "")) {
580                         auth = base64 (user_auth, strlen (user_auth));
581                         buf = ssprintf (buf, "Authorization: Basic %s\r\n", auth);
582                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
583                                 ERR_print_errors_fp (stderr);
584                                 return STATE_CRITICAL;
585                         }
586                 }
588                 /* optionally send http POST data */
589                 if (http_post_data) {
590                         buf = ssprintf (buf, "Content-Type: application/x-www-form-urlencoded\r\n");
591                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
592                                 ERR_print_errors_fp (stderr);
593                                 return STATE_CRITICAL;
594                         }
595                         buf = ssprintf (buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
596                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
597                                 ERR_print_errors_fp (stderr);
598                                 return STATE_CRITICAL;
599                         }
600                         http_post_data = strscat (http_post_data, "\r\n");
601                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
602                                 ERR_print_errors_fp (stderr);
603                                 return STATE_CRITICAL;
604                         }
605                 }
607                 /* send a newline so the server knows we're done with the request */
608                 buf = ssprintf (buf, "\r\n\r\n");
609                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
610                         ERR_print_errors_fp (stderr);
611                         return STATE_CRITICAL;
612                 }
614         }
615         else {
616 #endif
617                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) {
618                         msg = ssprintf (msg, "Unable to open TCP socket");
619                         terminate (STATE_CRITICAL, msg);
620                 }
621                 buf = ssprintf (buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
622                 send (sd, buf, strlen (buf), 0);
623                 
626                 /* optionally send the host header info */
627                 if (strcmp (host_name, "")) {
628                         buf = ssprintf (buf, "Host: %s\r\n", host_name);
629                         send (sd, buf, strlen (buf), 0);
630                 }
632                 /* send user agent */
633                 buf = ssprintf (buf,
634                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
635                          clean_revstring (REVISION), PACKAGE_VERSION);
636                 send (sd, buf, strlen (buf), 0);
638                 /* optionally send the authentication info */
639                 if (strcmp (user_auth, "")) {
640                         auth = base64 (user_auth, strlen (user_auth));
641                         buf = ssprintf (buf, "Authorization: Basic %s\r\n", auth);
642                         send (sd, buf, strlen (buf), 0);
643                 }
645                 /* optionally send http POST data */
646                 /* written by Chris Henesy <lurker@shadowtech.org> */
647                 if (http_post_data) {
648                         buf = ssprintf (buf, "Content-Type: application/x-www-form-urlencoded\r\n");
649                         send (sd, buf, strlen (buf), 0);
650                         buf = ssprintf (buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
651                         send (sd, buf, strlen (buf), 0);
652                         http_post_data = strscat (http_post_data, "\r\n");
653                         send (sd, http_post_data, strlen (http_post_data), 0);
654                 }
656                 /* send a newline so the server knows we're done with the request */
657                 buf = ssprintf (buf, "\r\n\r\n");
658                 send (sd, buf, strlen (buf), 0);
659 #ifdef HAVE_SSL
660         }
661 #endif
663         /* fetch the page */
664         pagesize = (size_t) 0;
665         while ((i = my_recv ()) > 0) {
666                 buffer[i] = "\0"; 
667                 full_page = strscat (full_page, buffer);
668                 pagesize += i;
669         }
671         if (i < 0)
672                 terminate (STATE_CRITICAL, "Error in recv()");
674         /* return a CRITICAL status if we couldn't read any data */
675         if (pagesize == (size_t) 0)
676                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
678         /* close the connection */
679         my_close ();
681         /* reset the alarm */
682         alarm (0);
684         /* leave full_page untouched so we can free it later */
685         page = full_page;
687         if (verbose)
688                 printf ("Page is %d characters\n", pagesize);
690         /* find status line and null-terminate it */
691         status_line = page;
692         page += (size_t) strcspn (page, "\r\n");
693         pos = page;
694         page += (size_t) strspn (page, "\r\n");
695         status_line[pos - status_line] = 0;
696         strip (status_line);
697         if (verbose)
698                 printf ("STATUS: %s\n", status_line);
700         /* find header info and null terminate it */
701         header = page;
702         while (strcspn (page, "\r\n") > 0) {
703                 page += (size_t) strcspn (page, "\r\n");
704                 pos = page;
705                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
706                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
707                         page += (size_t) 2;
708                 else
709                         page += (size_t) 1;
710         }
711         page += (size_t) strspn (page, "\r\n");
712         header[pos - header] = 0;
713         if (verbose)
714                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
716         /* make sure the status line matches the response we are looking for */
717         if (!strstr (status_line, server_expect)) {
718                 if (server_port == HTTP_PORT)
719                         msg = ssprintf (msg, "Invalid HTTP response received from host\n");
720                 else
721                         msg = ssprintf (msg,
722                                         "Invalid HTTP response received from host on port %d\n",
723                                         server_port);
724                 terminate (STATE_CRITICAL, msg);
725         }
728         /* Exit here if server_expect was set by user and not default */
729         if ( server_expect_yn  )  {
730                 msg = ssprintf (msg, "HTTP OK: Status line output matched \"%s\"\n",
731                           server_expect);
732                 if (verbose)
733                         printf ("%s\n",msg);
735         }
736         else {
737         
739                 /* check the return code */
740                 /* server errors result in a critical state */
741                 if (strstr (status_line, "500") ||
742                   strstr (status_line, "501") ||
743                 strstr (status_line, "502") ||
744                     strstr (status_line, "503")) {
745                         msg = ssprintf (msg, "HTTP CRITICAL: %s\n", status_line);
746                         terminate (STATE_CRITICAL, msg);
747                 }
749                 /* client errors result in a warning state */
750                 if (strstr (status_line, "400") ||
751                   strstr (status_line, "401") ||
752                 strstr (status_line, "402") ||
753                     strstr (status_line, "403") ||
754                     strstr (status_line, "404")) {
755                         msg = ssprintf (msg, "HTTP WARNING: %s\n", status_line);
756                         terminate (STATE_WARNING, msg);
757                 }
759                 /* check redirected page if specified */
760                 if (strstr (status_line, "300") ||
761                   strstr (status_line, "301") ||
762                 strstr (status_line, "302") ||
763                     strstr (status_line, "303") ||
764                     strstr (status_line, "304")) {
765                         if (onredirect == STATE_DEPENDENT) {
767                                 orig_url = strscpy(NULL, server_url);
768                                 pos = header;
769                                 while (pos) {
770                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
771                                         if (server_address == NULL)
772                                                 terminate (STATE_UNKNOWN,
773                                                                                  "HTTP UNKNOWN: could not allocate server_address");
774                                         if (strcspn (pos, "\r\n") > server_url_length) {
775                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
776                                                 if (server_url == NULL)
777                                                         terminate (STATE_UNKNOWN,
778                                                                    "HTTP UNKNOWN: could not allocate server_url");
779                                                 server_url_length = strcspn (pos, "\r\n");
780                                         }
781                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
782                                                 host_name = strscpy (host_name, server_address);
783                                                 use_ssl = server_type_check (server_type);
784                                                 server_port = atoi (server_port_text);
785                                                 check_http ();
786                                         }
787                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
788                                                 host_name = strscpy (host_name, server_address);
789                                                 use_ssl = server_type_check (server_type);
790                                                 server_port = server_port_check (use_ssl);
791                                                 check_http ();
792                                         }
793                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
794                                                 host_name = strscpy (host_name, server_address);
795                                                 strcpy (server_url, "/");
796                                                 use_ssl = server_type_check (server_type);
797                                                 server_port = atoi (server_port_text);
798                                                 check_http ();
799                                         }
800                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
801                                                 host_name = strscpy (host_name, server_address);
802                                                 strcpy (server_url, "/");
803                                                 use_ssl = server_type_check (server_type);
804                                                 server_port = server_port_check (use_ssl);
805                                                 check_http ();
806                                         }
807                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
808                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
809                                                         *x = '\0';
810                                                         server_url = ssprintf (server_url, "%s/%s", orig_url, server_url);
811                                                 }
812                                                 check_http ();
813                                         }                                       
814                                         pos += (size_t) strcspn (pos, "\r\n");
815                                         pos += (size_t) strspn (pos, "\r\n");
816                                 } /* end while (pos) */
817                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
818                                         status_line, (display_html ? "</A>" : ""));
819                                 exit (STATE_UNKNOWN);
820                         } /* end if (onredirect == STATE_DEPENDENT) */
821                         
822                         else if (onredirect == STATE_UNKNOWN)
823                                 printf ("HTTP UNKNOWN");
824                         else if (onredirect == STATE_OK)
825                                 printf ("HTTP ok");
826                         else if (onredirect == STATE_WARNING)
827                                 printf ("HTTP WARNING");
828                         else if (onredirect == STATE_CRITICAL)
829                                 printf ("HTTP CRITICAL");
830                         time (&end_time);
831                         msg = ssprintf (msg, ": %s - %d second response time %s%s|time=%d\n",
832                                  status_line, (int) (end_time - start_time), timestamp,
833                            (display_html ? "</A>" : ""), (int) (end_time - start_time));
834                         terminate (onredirect, msg);
835                 } /* end if (strstr (status_line, "30[0-4]") */
838         } /* end else (server_expect_yn)  */
840                 
841         /* check elapsed time */
842         time (&end_time);
843         msg = ssprintf (msg, "HTTP problem: %s - %d second response time %s%s|time=%d\n",
844                        status_line, (int) (end_time - start_time), timestamp,
845                        (display_html ? "</A>" : ""), (int) (end_time - start_time));
846         if (check_critical_time == TRUE && (end_time - start_time) > critical_time)
847                 terminate (STATE_CRITICAL, msg);
848         if (check_warning_time == TRUE && (end_time - start_time) > warning_time)
849                 terminate (STATE_WARNING, msg);
851         /* Page and Header content checks go here */
852         /* these checks should be last */
854         if (strlen (string_expect)) {
855                 if (strstr (page, string_expect)) {
856                         printf ("HTTP ok: %s - %d second response time %s%s|time=%d\n",
857                                 status_line, (int) (end_time - start_time),
858                                 timestamp, (display_html ? "</A>" : ""), (int) (end_time - start_time));
859                         exit (STATE_OK);
860                 }
861                 else {
862                         printf ("HTTP CRITICAL: string not found%s|time=%d\n",
863                                 (display_html ? "</A>" : ""), (int) (end_time - start_time));
864                         exit (STATE_CRITICAL);
865                 }
866         }
867 #ifdef HAVE_REGEX_H
868         if (strlen (regexp)) {
869                 errcode = regexec (&preg, page, REGS, pmatch, 0);
870                 if (errcode == 0) {
871                         printf ("HTTP ok: %s - %d second response time %s%s|time=%d\n",
872                                 status_line, (int) (end_time - start_time),
873                                 timestamp, (display_html ? "</A>" : ""), (int) (end_time - start_time));
874                         exit (STATE_OK);
875                 }
876                 else {
877                         if (errcode == REG_NOMATCH) {
878                                 printf ("HTTP CRITICAL: pattern not found%s|time=%d\n",
879                                         (display_html ? "</A>" : ""), (int) (end_time - start_time));
880                                 exit (STATE_CRITICAL);
881                         }
882                         else {
883                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
884                                 printf ("Execute Error: %s\n", errbuf);
885                                 exit (STATE_CRITICAL);
886                         }
887                 }
888         }
889 #endif
891         /* We only get here if all tests have been passed */
892         msg = ssprintf (msg, "HTTP ok: %s - %d second response time %s%s|time=%d\n",
893                         status_line, (int) (end_time - start_time),
894                         timestamp, (display_html ? "</A>" : ""), (int) (end_time - start_time));
895         terminate (STATE_OK, msg);
896         return STATE_UNKNOWN;
901 #ifdef HAVE_SSL
902 int connect_SSL (void)
904         SSL_METHOD *meth;
906         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
907         RAND_seed (randbuff, strlen (randbuff));
908         /* Initialize SSL context */
909         SSLeay_add_ssl_algorithms ();
910         meth = SSLv23_client_method ();
911         SSL_load_error_strings ();
912         if ((ctx = SSL_CTX_new (meth)) == NULL) {
913                 printf ("ERROR: Cannot create SSL context.\n");
914                 return STATE_CRITICAL;
915         }
917         /* Initialize alarm signal handling */
918         signal (SIGALRM, socket_timeout_alarm_handler);
920         /* Set socket timeout */
921         alarm (socket_timeout);
923         /* Save start time */
924         time (&start_time);
926         /* Make TCP connection */
927         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
928                 /* Do the SSL handshake */
929                 if ((ssl = SSL_new (ctx)) != NULL) {
930                         SSL_set_cipher_list(ssl, "ALL");
931                         SSL_set_fd (ssl, sd);
932                         if (SSL_connect (ssl) != -1)
933                                 return OK;
934                         ERR_print_errors_fp (stderr);
935                 }
936                 else {
937                         printf ("ERROR: Cannot initiate SSL handshake.\n");
938                 }
939                 SSL_free (ssl);
940         }
942         SSL_CTX_free (ctx);
943         close (sd);
945         return STATE_CRITICAL;
947 #endif
949 #ifdef HAVE_SSL
950 int
951 check_certificate (X509 ** certificate)
953         ASN1_STRING *tm;
954         int offset;
955         struct tm stamp;
956         int days_left;
959         /* Retrieve timestamp of certificate */
960         tm = X509_get_notAfter (*certificate);
962         /* Generate tm structure to process timestamp */
963         if (tm->type == V_ASN1_UTCTIME) {
964                 if (tm->length < 10) {
965                         printf ("ERROR: Wrong time format in certificate.\n");
966                         return STATE_CRITICAL;
967                 }
968                 else {
969                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
970                         if (stamp.tm_year < 50)
971                                 stamp.tm_year += 100;
972                         offset = 0;
973                 }
974         }
975         else {
976                 if (tm->length < 12) {
977                         printf ("ERROR: Wrong time format in certificate.\n");
978                         return STATE_CRITICAL;
979                 }
980                 else {
981                         stamp.tm_year =
982                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
983                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
984                         stamp.tm_year -= 1900;
985                         offset = 2;
986                 }
987         }
988         stamp.tm_mon =
989                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
990         stamp.tm_mday =
991                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
992         stamp.tm_hour =
993                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
994         stamp.tm_min =
995                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
996         stamp.tm_sec = 0;
997         stamp.tm_isdst = -1;
999         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1000         snprintf
1001                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1002                  stamp.tm_mon + 1,
1003                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1005         if (days_left > 0 && days_left <= days_till_exp) {
1006                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1007                 return STATE_WARNING;
1008         }
1009         if (days_left < 0) {
1010                 printf ("Certificate expired on %s.\n", timestamp);
1011                 return STATE_CRITICAL;
1012         }
1014         if (days_left == 0) {
1015                 printf ("Certificate expires today (%s).\n", timestamp);
1016                 return STATE_WARNING;
1017         }
1019         printf ("Certificate will expire on %s.\n", timestamp);
1021         return STATE_OK;
1023 #endif
1024 \f
1027 int
1028 my_recv (void)
1030         int i;
1031 #ifdef HAVE_SSL
1032         if (use_ssl) {
1033                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1034         }
1035         else {
1036                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1037         }
1038 #else
1039         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1040 #endif
1041         return i;
1045 int
1046 my_close (void)
1048 #ifdef HAVE_SSL
1049         if (use_ssl == TRUE) {
1050                 SSL_shutdown (ssl);
1051                 SSL_free (ssl);
1052                 SSL_CTX_free (ctx);
1053                 return 0;
1054         }
1055         else {
1056 #endif
1057                 return close (sd);
1058 #ifdef HAVE_SSL
1059         }
1060 #endif
1062 \f
1065 void
1066 print_help (void)
1068         print_revision (PROGNAME, REVISION);
1069         printf
1070                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1071                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1072         print_usage ();
1073         printf ("NOTE: One or both of -H and -I must be specified\n");
1074         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1075                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1076 #ifdef HAVE_SSL
1077         printf (SSLDESCRIPTION);
1078 #endif
1082 void
1083 print_usage (void)
1085         printf ("Usage:\n" " %s %s\n"
1086 #ifdef HAVE_GETOPT_H
1087                 " %s (-h | --help) for detailed help\n"
1088                 " %s (-V | --version) for version information\n",
1089 #else
1090                 " %s -h for detailed help\n"
1091                 " %s -V for version information\n",
1092 #endif
1093         PROGNAME, OPTIONS, PROGNAME, PROGNAME);