Code

fixes for using POSIX return codes
[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 line of server response (default: %s)\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\
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 #define DESCRIPTION "\
97 This plugin will attempt to open an HTTP connection with the host. Successul\n\
98 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
99 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
100 messages from the host result in STATE_WARNING return values.  If you are\n\
101 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
102 \(fully qualified domain name) as the [host_name] argument.\n"
104 #define SSLDESCRIPTION "\
105 This plugin can also check whether an SSL enabled web server is able to\n\
106 serve content (optionally within a specified time) or whether the X509 \n\
107 certificate is still valid for the specified number of days.\n\n\
108 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
109 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
110 STATE_OK will be returned. When the server returns its content but exceeds\n\
111 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
112 a STATE_CRITICAL will be returned.\n\n\
113 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
114 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
115 STATE_OK is returned. When the certificate is still valid, but for less than\n\
116 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
117 the certificate is expired.\n"
119 #ifdef HAVE_SSL_H
120 #include <rsa.h>
121 #include <crypto.h>
122 #include <x509.h>
123 #include <pem.h>
124 #include <ssl.h>
125 #include <err.h>
126 #include <rand.h>
127 #endif
129 #ifdef HAVE_OPENSSL_SSL_H
130 #include <openssl/rsa.h>
131 #include <openssl/crypto.h>
132 #include <openssl/x509.h>
133 #include <openssl/pem.h>
134 #include <openssl/ssl.h>
135 #include <openssl/err.h>
136 #include <openssl/rand.h>
137 #endif
139 #ifdef HAVE_SSL
140 int check_cert = FALSE;
141 int days_till_exp;
142 unsigned char *randbuff;
143 SSL_CTX *ctx;
144 SSL *ssl;
145 X509 *server_cert;
146 int connect_SSL (void);
147 int check_certificate (X509 **);
148 #endif
150 #ifdef HAVE_REGEX_H
151 #define REGS 2
152 #define MAX_RE_SIZE 256
153 #include <regex.h>
154 regex_t preg;
155 regmatch_t pmatch[REGS];
156 char regexp[MAX_RE_SIZE];
157 char errbuf[MAX_INPUT_BUFFER];
158 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
159 int errcode;
160 #endif
162 #define server_type_check(server_type) \
163 (strcmp (server_type, "https") ? FALSE : TRUE)
165 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
167 #define MAX_IPV4_HOSTLENGTH 64
168 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
169 #define URI_HTTP "%[HTPShtps]://"
170 #define URI_HOST "%[a-zA-Z0-9.-]"
171 #define URI_PORT ":%[0-9]"
172 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
174 #define HTTP_PORT 80
175 #define HTTPS_PORT 443
176 #define HTTP_EXPECT "HTTP/1."
177 #define HTTP_URL "/"
179 time_t start_time, end_time;
180 char timestamp[10] = "";
181 int specify_port = FALSE;
182 int server_port = HTTP_PORT;
183 char server_port_text[6] = "";
184 char server_type[6] = "http";
185 char *server_address = NULL;
186 char *host_name = NULL;
187 char *server_url = NULL;
188 int server_url_length = 0;
189 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
190 char string_expect[MAX_INPUT_BUFFER] = "";
191 int warning_time = 0;
192 int check_warning_time = FALSE;
193 int critical_time = 0;
194 int check_critical_time = FALSE;
195 char user_auth[MAX_INPUT_BUFFER] = "";
196 int display_html = FALSE;
197 int onredirect = STATE_OK;
198 int use_ssl = FALSE;
199 int verbose = FALSE;
200 int sd;
201 char *http_method = NULL;
202 char *http_post_data = NULL;
203 char buffer[MAX_INPUT_BUFFER];
205 void print_usage (void);
206 void print_help (void);
207 int process_arguments (int, char **);
208 int call_getopt (int, char **);
209 static char *base64 (char *bin, int len);
210 int check_http (void);
211 int my_recv (void);
212 int my_close (void);
214 int
215 main (int argc, char **argv)
217         int result = STATE_UNKNOWN;
219         if (process_arguments (argc, argv) == ERROR)
220                 usage ("check_http: could not parse arguments\n");
222         if (strstr (timestamp, ":")) {
223                 if (strstr (server_url, "?"))
224                         sprintf (server_url, "%s&%s", server_url, timestamp);
225                 else
226                         sprintf (server_url, "%s?%s", server_url, timestamp);
227         }
229         if (display_html == TRUE)
230                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
231                         host_name, server_port, server_url);
233         /* initialize alarm signal handling, set socket timeout, start timer */
234         signal (SIGALRM, socket_timeout_alarm_handler);
235         alarm (socket_timeout);
236         time (&start_time);
238 #ifdef HAVE_SSL
239         if (use_ssl && check_cert == TRUE) {
240                 if (connect_SSL () != OK)
241                         terminate (STATE_CRITICAL,
242                                    "HTTP CRITICAL - Could not make SSL connection\n");
243                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
244                         result = check_certificate (&server_cert);
245                         X509_free (server_cert);
246                 }
247                 else {
248                         printf ("ERROR: Cannot retrieve server certificate.\n");
249                         result = STATE_CRITICAL;
250                 }
251                 SSL_shutdown (ssl);
252                 SSL_free (ssl);
253                 SSL_CTX_free (ctx);
254                 close (sd);
255         }
256         else {
257                 result = check_http ();
258         }
259 #else
260         result = check_http ();
261 #endif
262         return result;
264 \f
267 /* process command-line arguments */
268 int
269 process_arguments (int argc, char **argv)
271         int c, i = 1;
272         char optchars[MAX_INPUT_BUFFER];
274 #ifdef HAVE_GETOPT_H
275         int option_index = 0;
276         static struct option long_options[] = {
277                 STD_OPTS_LONG,
278                 {"link", no_argument, 0, 'L'},
279                 {"nohtml", no_argument, 0, 'n'},
280                 {"ssl", no_argument, 0, 'S'},
281                 {"verbose", no_argument, 0, 'v'},
282                 {"post", required_argument, 0, 'P'},
283                 {"IP-address", required_argument, 0, 'I'},
284                 {"string", required_argument, 0, 's'},
285                 {"regex", required_argument, 0, 'r'},
286                 {"ereg", required_argument, 0, 'r'},
287                 {"eregi", required_argument, 0, 'R'},
288                 {"onredirect", required_argument, 0, 'f'},
289                 {"certificate", required_argument, 0, 'C'},
290                 {0, 0, 0, 0}
291         };
292 #endif
294         if (argc < 2)
295                 return ERROR;
297         for (c = 1; c < argc; c++) {
298                 if (strcmp ("-to", argv[c]) == 0)
299                         strcpy (argv[c], "-t");
300                 if (strcmp ("-hn", argv[c]) == 0)
301                         strcpy (argv[c], "-H");
302                 if (strcmp ("-wt", argv[c]) == 0)
303                         strcpy (argv[c], "-w");
304                 if (strcmp ("-ct", argv[c]) == 0)
305                         strcpy (argv[c], "-c");
306                 if (strcmp ("-nohtml", argv[c]) == 0)
307                         strcpy (argv[c], "-n");
308         }
310         snprintf (optchars, MAX_INPUT_BUFFER, "%s%s", STD_OPTS,
311                   "P:I:a:e:p:s:R:r:u:f:C:nLS");
313         while (1) {
314 #ifdef HAVE_GETOPT_H
315                 c = getopt_long (argc, argv, optchars, long_options, &option_index);
316 #else
317                 c = getopt (argc, argv, optchars);
318 #endif
319                 if (c == -1 || c == EOF)
320                         break;
322                 switch (c) {
323                 case '?': /* usage */
324                         usage2 ("unknown argument", optarg);
325                         break;
326                 case 'h': /* help */
327                         print_help ();
328                         exit (STATE_OK);
329                         break;
330                 case 'V': /* version */
331                         print_revision (PROGNAME, REVISION);
332                         exit (STATE_OK);
333                         break;
334                 case 't': /* timeout period */
335                         if (!is_intnonneg (optarg))
336                                 usage2 ("timeout interval must be a non-negative integer", optarg);
337                         socket_timeout = atoi (optarg);
338                         break;
339                 case 'c': /* critical time threshold */
340                         if (!is_intnonneg (optarg))
341                                 usage2 ("invalid critical threshold", optarg);
342                         critical_time = atoi (optarg);
343                         check_critical_time = TRUE;
344                         break;
345                 case 'w': /* warning time threshold */
346                         if (!is_intnonneg (optarg))
347                                 usage2 ("invalid warning threshold", optarg);
348                         warning_time = atoi (optarg);
349                         check_warning_time = TRUE;
350                         break;
351                 case 'L': /* show html link */
352                         display_html = TRUE;
353                         break;
354                 case 'n': /* do not show html link */
355                         display_html = FALSE;
356                         break;
357                 case 'S': /* use SSL */
358 #ifndef HAVE_SSL
359                         usage ("check_http: invalid option - SSL is not available\n");
360 #endif
361                         use_ssl = TRUE;
362                         if (specify_port == FALSE)
363                                 server_port = HTTPS_PORT;
364                         break;
365                 case 'C': /* warning time threshold */
366 #ifdef HAVE_SSL
367                         if (!is_intnonneg (optarg))
368                                 usage2 ("invalid certificate expiration period", optarg);
369                         days_till_exp = atoi (optarg);
370                         check_cert = TRUE;
371 #else
372                         usage ("check_http: invalid option - SSL is not available\n");
373 #endif
374                         break;
375                 case 'f': /* onredirect */
376                         if (!strcmp (optarg, "follow"))
377                                 onredirect = STATE_DEPENDENT;
378                         if (!strcmp (optarg, "unknown"))
379                                 onredirect = STATE_UNKNOWN;
380                         if (!strcmp (optarg, "ok"))
381                                 onredirect = STATE_OK;
382                         if (!strcmp (optarg, "warning"))
383                                 onredirect = STATE_WARNING;
384                         if (!strcmp (optarg, "critical"))
385                                 onredirect = STATE_CRITICAL;
386                         break;
387                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
388                 case 'H': /* Host Name (virtual host) */
389                         host_name = strscpy (host_name, optarg);
390                         break;
391                 case 'I': /* Server IP-address */
392                         server_address = strscpy (server_address, optarg);
393                         break;
394                 case 'u': /* Host or server */
395                         server_url = strscpy (server_url, optarg);
396                         server_url_length = strlen (optarg);
397                         break;
398                 case 'p': /* Host or server */
399                         if (!is_intnonneg (optarg))
400                                 usage2 ("invalid port number", optarg);
401                         server_port = atoi (optarg);
402                         specify_port = TRUE;
403                         break;
404                 case 'a': /* authorization info */
405                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
406                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
407                         break;
408                 case 'P': /* HTTP POST data in URL encoded format */
409                         http_method = strscpy (http_method, "POST");
410                         http_post_data = strscpy (http_post_data, optarg);
411                         break;
412                 case 's': /* string or substring */
413                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
414                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
415                         break;
416                 case 'e': /* string or substring */
417                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
418                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
419                         break;
420                 case 'R': /* regex */
421 #ifdef HAVE_REGEX_H
422                         cflags = REG_ICASE;
423 #else
424                         usage ("check_http: call for regex which was not a compiled option\n");
425 #endif
426                 case 'r': /* regex */
427 #ifdef HAVE_REGEX_H
428                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
429                         strncpy (regexp, optarg, MAX_INPUT_BUFFER - 1);
430                         regexp[MAX_INPUT_BUFFER - 1] = 0;
431                         errcode = regcomp (&preg, regexp, cflags);
432                         if (errcode != 0) {
433                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
434                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
435                                 return ERROR;
436                         }
437 #else
438                         usage ("check_http: call for regex which was not a compiled option\n");
439 #endif
440                         break;
441                 case 'v': /* verbose */
442                         verbose = TRUE;
443                         break;
444                 }
445         }
447         c = optind;
449         if (server_address == NULL && host_name == NULL) {
450                 server_address = strscpy (NULL, argv[c]);
451                 host_name = strscpy (NULL, argv[c++]);
452         }
454         if (server_address == NULL && host_name == NULL)
455                 usage ("check_http: you must specify a host name\n");
457         if (server_address == NULL)
458                 server_address = strscpy (NULL, host_name);
460         if (host_name == NULL)
461                 host_name = strscpy (NULL, server_address);
463         if (http_method == NULL)
464                 http_method = strscpy (http_method, "GET");
466         if (server_url == NULL) {
467                 server_url = strscpy (NULL, "/");
468                 server_url_length = strlen(HTTP_URL);
469         }
471         return TRUE;
473 \f
476 /* written by lauri alanko */
477 static char *
478 base64 (char *bin, int len)
481         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
482         int i = 0, j = 0;
484         char BASE64_END = '=';
485         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
486                 "abcdefghijklmnopqrstuvwxyz"
487                 "0123456789+/";
489         while (j < len - 2) {
490                 buf[i++] = base64_table[bin[j] >> 2];
491                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
492                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
493                 buf[i++] = base64_table[bin[j + 2] & 63];
494                 j += 3;
495         }
497         switch (len - j) {
498         case 1:
499                 buf[i++] = base64_table[bin[j] >> 2];
500                 buf[i++] = base64_table[(bin[j] & 3) << 4];
501                 buf[i++] = BASE64_END;
502                 buf[i++] = BASE64_END;
503                 break;
504         case 2:
505                 buf[i++] = base64_table[bin[j] >> 2];
506                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
507                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
508                 buf[i++] = BASE64_END;
509                 break;
510         case 0:
511                 break;
512         }
514         buf[i] = '\0';
515         return buf;
517 \f
520 int
521 check_http (void)
523         char *msg = NULL;
524         char *status_line = NULL;
525         char *header = NULL;
526         char *page = NULL;
527         char *auth = NULL;
528         int i = 0;
529         size_t pagesize = 0;
530         char *full_page = NULL;
531         char *pos = NULL;
533         /* try to connect to the host at the given port number */
534 #ifdef HAVE_SSL
535         if (use_ssl == TRUE) {
537                 if (connect_SSL () != OK) {
538                         msg = ssprintf (msg, "Unable to open TCP socket");
539                         terminate (STATE_CRITICAL, msg);
540                 }
542                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
543                         X509_free (server_cert);
544                 }
545                 else {
546                         printf ("ERROR: Cannot retrieve server certificate.\n");
547                         return STATE_CRITICAL;
548                 }
550                 sprintf (buffer, "%s %s HTTP/1.0\r\n", http_method, server_url);
551                 if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
552                         ERR_print_errors_fp (stderr);
553                         return STATE_CRITICAL;
554                 }
556                 /* optionally send the host header info (not clear if it's usable) */
557                 if (strcmp (host_name, "")) {
558                         sprintf (buffer, "Host: %s\r\n", host_name);
559                         if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
560                                 ERR_print_errors_fp (stderr);
561                                 return STATE_CRITICAL;
562                         }
563                 }
565                 /* send user agent */
566                 sprintf (buffer, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
567                          clean_revstring (REVISION), PACKAGE_VERSION);
568                 if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
569                         ERR_print_errors_fp (stderr);
570                         return STATE_CRITICAL;
571                 }
573                 /* optionally send the authentication info */
574                 if (strcmp (user_auth, "")) {
575                         auth = base64 (user_auth, strlen (user_auth));
576                         sprintf (buffer, "Authorization: Basic %s\r\n", auth);
577                         if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
578                                 ERR_print_errors_fp (stderr);
579                                 return STATE_CRITICAL;
580                         }
581                 }
583                 /* optionally send http POST data */
584                 if (http_post_data) {
585                         sprintf (buffer, "Content-Type: application/x-www-form-urlencoded\r\n");
586                         if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
587                                 ERR_print_errors_fp (stderr);
588                                 return STATE_CRITICAL;
589                         }
590                         sprintf (buffer, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
591                         if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
592                                 ERR_print_errors_fp (stderr);
593                                 return STATE_CRITICAL;
594                         }
595                         http_post_data = strscat (http_post_data, "\r\n");
596                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
597                                 ERR_print_errors_fp (stderr);
598                                 return STATE_CRITICAL;
599                         }
600                 }
602                 /* send a newline so the server knows we're done with the request */
603                 sprintf (buffer, "\r\n\r\n");
604                 if (SSL_write (ssl, buffer, strlen (buffer)) == -1) {
605                         ERR_print_errors_fp (stderr);
606                         return STATE_CRITICAL;
607                 }
609         }
610         else {
611 #endif
612                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) {
613                         msg = ssprintf (msg, "Unable to open TCP socket");
614                         terminate (STATE_CRITICAL, msg);
615                 }
616                 sprintf (buffer, "%s %s HTTP/1.0\r\n", http_method, server_url);
617                 send (sd, buffer, strlen (buffer), 0);
619                 /* optionally send the host header info */
620                 if (strcmp (host_name, "")) {
621                         sprintf (buffer, "Host: %s\r\n", host_name);
622                         send (sd, buffer, strlen (buffer), 0);
623                 }
625                 /* send user agent */
626                 sprintf (buffer,
627                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
628                          clean_revstring (REVISION), PACKAGE_VERSION);
629                 send (sd, buffer, strlen (buffer), 0);
631                 /* optionally send the authentication info */
632                 if (strcmp (user_auth, "")) {
633                         auth = base64 (user_auth, strlen (user_auth));
634                         sprintf (buffer, "Authorization: Basic %s\r\n", auth);
635                         send (sd, buffer, strlen (buffer), 0);
636                 }
638                 /* optionally send http POST data */
639                 /* written by Chris Henesy <lurker@shadowtech.org> */
640                 if (http_post_data) {
641                         sprintf (buffer, "Content-Type: application/x-www-form-urlencoded\r\n");
642                         send (sd, buffer, strlen (buffer), 0);
643                         sprintf (buffer, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
644                         send (sd, buffer, strlen (buffer), 0);
645                         http_post_data = strscat (http_post_data, "\r\n");
646                         send (sd, http_post_data, strlen (http_post_data), 0);
647                 }
649                 /* send a newline so the server knows we're done with the request */
650                 sprintf (buffer, "\r\n\r\n");
651                 send (sd, buffer, strlen (buffer), 0);
652 #ifdef HAVE_SSL
653         }
654 #endif
656         /* fetch the page */
657         pagesize = (size_t) 0;
658         while ((i = my_recv ()) > 0) {
659                 full_page = strscat (full_page, buffer);
660                 pagesize += i;
661         }
663         if (i < 0)
664                 terminate (STATE_CRITICAL, "Error in recv()");
666         /* return a CRITICAL status if we couldn't read any data */
667         if (pagesize == (size_t) 0)
668                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
670         /* close the connection */
671         my_close ();
673         /* reset the alarm */
674         alarm (0);
676         /* leave full_page untouched so we can free it later */
677         page = full_page;
679         if (verbose)
680                 printf ("Page is %d characters\n", pagesize);
682         /* find status line and null-terminate it */
683         status_line = page;
684         page += (size_t) strcspn (page, "\r\n");
685         pos = page;
686         page += (size_t) strspn (page, "\r\n");
687         status_line[pos - status_line] = 0;
688         strip (status_line);
689         if (verbose)
690                 printf ("STATUS: %s\n", status_line);
692         /* find header info and null terminate it */
693         header = page;
694         while (strcspn (page, "\r\n") > 0) {
695                 page += (size_t) strcspn (page, "\r\n");
696                 pos = page;
697                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
698                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
699                         page += (size_t) 2;
700                 else
701                         page += (size_t) 1;
702         }
703         page += (size_t) strspn (page, "\r\n");
704         header[pos - header] = 0;
705         if (verbose)
706                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
708         /* make sure the status line matches the response we are looking for */
709         if (!strstr (status_line, server_expect)) {
710                 if (server_port == HTTP_PORT)
711                         msg = ssprintf (msg, "Invalid HTTP response received from host\n");
712                 else
713                         msg = ssprintf (msg,
714                                         "Invalid HTTP response received from host on port %d\n",
715                                         server_port);
716                 terminate (STATE_CRITICAL, msg);
717         }
719         /* check the return code */
720         /* server errors result in a critical state */
721         if (strstr (status_line, "500") ||
722             strstr (status_line, "501") ||
723             strstr (status_line, "502") ||
724             strstr (status_line, "503")) {
725                 msg = ssprintf (msg, "HTTP CRITICAL: %s\n", status_line);
726                 terminate (STATE_CRITICAL, msg);
727         }
729         /* client errors result in a warning state */
730         if (strstr (status_line, "400") ||
731             strstr (status_line, "401") ||
732             strstr (status_line, "402") ||
733             strstr (status_line, "403") ||
734             strstr (status_line, "404")) {
735                 msg = ssprintf (msg, "HTTP WARNING: %s\n", status_line);
736                 terminate (STATE_WARNING, msg);
737         }
739         /* check redirected page if specified */
740         if (strstr (status_line, "300") ||
741             strstr (status_line, "301") ||
742             strstr (status_line, "302") ||
743             strstr (status_line, "303") ||
744             strstr (status_line, "304")) {
745                 if (onredirect == STATE_DEPENDENT) {
746                         
747                         pos = header;
748                         while (pos) {
749                                 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
750                                 if (server_address == NULL)
751                                         terminate (STATE_UNKNOWN,
752                                                                                  "HTTP UNKNOWN: could not allocate server_address");
753                                 if (strspn (pos, "\r\n") > server_url_length) {
754                                         server_url = realloc (server_url, strspn (pos, "\r\n"));
755                                         if (server_url == NULL)
756                                                 terminate (STATE_UNKNOWN,
757                                                                                          "HTTP UNKNOWN: could not allocate server_url");
758                                         server_url_length = strspn (pos, "\r\n");
759                                 }
760                                 if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
761                                         host_name = strscpy (host_name, server_address);
762                                         use_ssl = server_type_check (server_type);
763                                         server_port = atoi (server_port_text);
764                                         check_http ();
765                                 }
766                                 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3) {
767                                         host_name = strscpy (host_name, server_address);
768                                         use_ssl = server_type_check (server_type);
769                                         server_port = server_port_check (use_ssl);
770                                         check_http ();
771                                 }
772                                 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
773                                         host_name = strscpy (host_name, server_address);
774                                         strcpy (server_url, "/");
775                                         use_ssl = server_type_check (server_type);
776                                         server_port = atoi (server_port_text);
777                                         check_http ();
778                                 }
779                                 else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
780                                         host_name = strscpy (host_name, server_address);
781                                         strcpy (server_url, "/");
782                                         use_ssl = server_type_check (server_type);
783                                         server_port = server_port_check (use_ssl);
784                                         check_http ();
785                                 }
786                                 else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
787                                         check_http ();
788                                 }
789                                 pos += (size_t) strcspn (pos, "\r\n");
790                                 pos += (size_t) strspn (pos, "\r\n");
791                         } /* end while (pos) */
792                         printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
793                                 status_line, (display_html ? "</A>" : ""));
794                         exit (STATE_UNKNOWN);
795                 } /* end if (onredirect == STATE_DEPENDENT) */
796                 else if (onredirect == STATE_UNKNOWN)
797                         printf ("HTTP UNKNOWN");
798                 else if (onredirect == STATE_OK)
799                         printf ("HTTP ok");
800                 else if (onredirect == STATE_WARNING)
801                         printf ("HTTP WARNING");
802                 else if (onredirect == STATE_CRITICAL)
803                         printf ("HTTP CRITICAL");
804                 time (&end_time);
805                 msg = ssprintf (msg, ": %s - %d second response time %s%s\n",
806                                 status_line, (int) (end_time - start_time),
807                                 timestamp, (display_html ? "</A>" : ""));
808                 terminate (onredirect, msg);
809         } /* end if (strstr (status_line, "30[0-4]") */
811         /* check elapsed time */
812         time (&end_time);
813         msg = ssprintf (msg, "HTTP problem: %s - %d second response time %s%s\n",
814                         status_line, (int) (end_time - start_time),
815                         timestamp, (display_html ? "</A>" : ""));
816         if (check_critical_time == TRUE && (end_time - start_time) > critical_time)
817                 terminate (STATE_CRITICAL, msg);
818         if (check_warning_time == TRUE && (end_time - start_time) > warning_time)
819                 terminate (STATE_WARNING, msg);
821         /* Page and Header content checks go here */
822         /* these checks should be last */
824         if (strlen (string_expect)) {
825                 if (strstr (page, string_expect)) {
826                         printf ("HTTP ok: %s - %d second response time %s%s\n",
827                                 status_line, (int) (end_time - start_time),
828                                 timestamp, (display_html ? "</A>" : ""));
829                         exit (STATE_OK);
830                 }
831                 else {
832                         printf ("HTTP CRITICAL: string not found%s\n",
833                                 (display_html ? "</A>" : ""));
834                         exit (STATE_CRITICAL);
835                 }
836         }
837 #ifdef HAVE_REGEX_H
838         if (strlen (regexp)) {
839                 errcode = regexec (&preg, page, REGS, pmatch, 0);
840                 if (errcode == 0) {
841                         printf ("HTTP ok: %s - %d second response time %s%s\n",
842                                 status_line, (int) (end_time - start_time),
843                                 timestamp, (display_html ? "</A>" : ""));
844                         exit (STATE_OK);
845                 }
846                 else {
847                         if (errcode == REG_NOMATCH) {
848                                 printf ("HTTP CRITICAL: pattern not found%s\n",
849                                         (display_html ? "</A>" : ""));
850                                 exit (STATE_CRITICAL);
851                         }
852                         else {
853                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
854                                 printf ("Execute Error: %s\n", errbuf);
855                                 exit (STATE_CRITICAL);
856                         }
857                 }
858         }
859 #endif
861         /* We only get here if all tests have been passed */
862         msg = ssprintf (msg, "HTTP ok: %s - %d second response time %s%s\n",
863                         status_line, (int) (end_time - start_time),
864                         timestamp, (display_html ? "</A>" : ""));
865         terminate (STATE_OK, msg);
866         return STATE_UNKNOWN;
868 \f
871 #ifdef HAVE_SSL
872 int
873 connect_SSL (void)
875         SSL_METHOD *meth;
877         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
878         RAND_seed (randbuff, strlen (randbuff));
879         /* Initialize SSL context */
880         SSLeay_add_ssl_algorithms ();
881         meth = SSLv23_client_method ();
882         SSL_load_error_strings ();
883         if ((ctx = SSL_CTX_new (meth)) == NULL) {
884                 printf ("ERROR: Cannot create SSL context.\n");
885                 return STATE_CRITICAL;
886         }
888         /* Initialize alarm signal handling */
889         signal (SIGALRM, socket_timeout_alarm_handler);
891         /* Set socket timeout */
892         alarm (socket_timeout);
894         /* Save start time */
895         time (&start_time);
897         /* Make TCP connection */
898         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
899                 /* Do the SSL handshake */
900                 if ((ssl = SSL_new (ctx)) != NULL) {
901                         SSL_set_cipher_list(ssl, "ALL");
902                         SSL_set_fd (ssl, sd);
903                         if (SSL_connect (ssl) != -1)
904                                 return OK;
905                         ERR_print_errors_fp (stderr);
906                 }
907                 else {
908                         printf ("ERROR: Cannot initiate SSL handshake.\n");
909                 }
910                 SSL_free (ssl);
911         }
913         SSL_CTX_free (ctx);
914         close (sd);
916         return STATE_CRITICAL;
918 #endif
920 #ifdef HAVE_SSL
921 int
922 check_certificate (X509 ** certificate)
924         ASN1_STRING *tm;
925         int offset;
926         struct tm stamp;
927         int days_left;
928         /* int result = STATE_OK; */
929         /* char timestamp[14]; */
932         /* Retrieve timestamp of certificate */
933         tm = X509_get_notAfter (*certificate);
935         /* Generate tm structure to process timestamp */
936         if (tm->type == V_ASN1_UTCTIME) {
937                 if (tm->length < 10) {
938                         printf ("ERROR: Wrong time format in certificate.\n");
939                         return STATE_CRITICAL;
940                 }
941                 else {
942                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
943                         if (stamp.tm_year < 50)
944                                 stamp.tm_year += 100;
945                         offset = 0;
946                 }
947         }
948         else {
949                 if (tm->length < 12) {
950                         printf ("ERROR: Wrong time format in certificate.\n");
951                         return STATE_CRITICAL;
952                 }
953                 else {
954                         stamp.tm_year =
955                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
956                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
957                         stamp.tm_year -= 1900;
958                         offset = 2;
959                 }
960         }
961         stamp.tm_mon =
962                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
963         stamp.tm_mday =
964                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
965         stamp.tm_hour =
966                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
967         stamp.tm_min =
968                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
969         stamp.tm_sec = 0;
970         stamp.tm_isdst = -1;
972         days_left = (mktime (&stamp) - time (NULL)) / 86400;
973         sprintf
974                 (timestamp, "%02d/%02d/%04d %02d:%02d",
975                  stamp.tm_mon + 1,
976                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
978         if (days_left > 0 && days_left <= days_till_exp) {
979                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
980                 return STATE_WARNING;
981         }
982         if (days_left < 0) {
983                 printf ("Certificate expired on %s.\n", timestamp);
984                 return STATE_CRITICAL;
985         }
987         if (days_left == 0) {
988                 printf ("Certificate expires today (%s).\n", timestamp);
989                 return STATE_WARNING;
990         }
992         printf ("Certificate will expire on %s.\n", timestamp);
994         return STATE_OK;
996 #endif
997 \f
1000 int
1001 my_recv (void)
1003         int i;
1004 #ifdef HAVE_SSL
1005         if (use_ssl) {
1006                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1007         }
1008         else {
1009                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1010         }
1011 #else
1012         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1013 #endif
1014         return i;
1018 int
1019 my_close (void)
1021 #ifdef HAVE_SSL
1022         if (use_ssl == TRUE) {
1023                 SSL_shutdown (ssl);
1024                 SSL_free (ssl);
1025                 SSL_CTX_free (ctx);
1026                 return 0;
1027         }
1028         else {
1029 #endif
1030                 return close (sd);
1031 #ifdef HAVE_SSL
1032         }
1033 #endif
1035 \f
1038 void
1039 print_help (void)
1041         print_revision (PROGNAME, REVISION);
1042         printf
1043                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1044                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1045         print_usage ();
1046         printf ("NOTE: One or both of -H and -I must be specified\n");
1047         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1048                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1049 #ifdef HAVE_SSL
1050         printf (SSLDESCRIPTION);
1051 #endif
1055 void
1056 print_usage (void)
1058         printf ("Usage:\n" " %s %s\n"
1059 #ifdef HAVE_GETOPT_H
1060                 " %s (-h | --help) for detailed help\n"
1061                 " %s (-V | --version) for version information\n",
1062 #else
1063                 " %s -h for detailed help\n"
1064                 " %s -V for version information\n",
1065 #endif
1066         PROGNAME, OPTIONS, PROGNAME, PROGNAME);