Code

remove unused variables
[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 "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] [-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\
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 struct timeval tv;
164 #define server_type_check(server_type) \
165 (strcmp (server_type, "https") ? FALSE : TRUE)
167 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
169 #define MAX_IPV4_HOSTLENGTH 64
170 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
171 #define URI_HTTP "%[HTPShtps]://"
172 #define URI_HOST "%[a-zA-Z0-9.-]"
173 #define URI_PORT ":%[0-9]"
174 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
176 #define HTTP_PORT 80
177 #define HTTPS_PORT 443
178 #define HTTP_EXPECT "HTTP/1."
179 #define HTTP_URL "/"
181 char timestamp[17] = "";
182 int specify_port = FALSE;
183 int server_port = HTTP_PORT;
184 char server_port_text[6] = "";
185 char server_type[6] = "http";
186 /*@null@*/ char *server_address = NULL; 
187 /*@null@*/ char *host_name = NULL;
188 /*@null@*/ char *server_url = NULL;
189 int server_url_length = 0;
190 int server_expect_yn = 0;
191 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
192 char string_expect[MAX_INPUT_BUFFER] = "";
193 double warning_time = 0;
194 int check_warning_time = FALSE;
195 double critical_time = 0;
196 int check_critical_time = FALSE;
197 char user_auth[MAX_INPUT_BUFFER] = "";
198 int display_html = FALSE;
199 int onredirect = STATE_OK;
200 int use_ssl = FALSE;
201 int verbose = FALSE;
202 int sd;
203 /*@null@*/ char *http_method = NULL;
204 /*@null@*/ char *http_post_data = NULL;
205 char buffer[MAX_INPUT_BUFFER];
207 void print_usage (void);
208 void print_help (void);
209 int process_arguments (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                         asprintf (&server_url, "%s&%s", server_url, timestamp);
226                 else
227                         asprintf (&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         gettimeofday (&tv, NULL);
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 = 1;
274 #ifdef HAVE_GETOPT_H
275         int option_index = 0;
276         static struct option long_options[] = {
277                 STD_LONG_OPTS,
278                 {"file",required_argument,0,'F'},
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 #define OPTCHARS "Vvht:c:w:H: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 = strtod (optarg, NULL);
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 = strtod (optarg, NULL);
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': /* Check SSL cert validity */
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                         if (verbose)
387                                 printf("option f:%d \n", onredirect);  
388                         break;
389                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
390                 case 'H': /* Host Name (virtual host) */
391                         host_name = strscpy (host_name, optarg);
392                         break;
393                 case 'I': /* Server IP-address */
394                         server_address = strscpy (server_address, optarg);
395                         break;
396                 case 'u': /* Host or server */
397                         server_url = strscpy (server_url, optarg);
398                         server_url_length = strlen (optarg);
399                         break;
400                 case 'p': /* Host or server */
401                         if (!is_intnonneg (optarg))
402                                 usage2 ("invalid port number", optarg);
403                         server_port = atoi (optarg);
404                         specify_port = TRUE;
405                         break;
406                 case 'a': /* authorization info */
407                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
408                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
409                         break;
410                 case 'P': /* HTTP POST data in URL encoded format */
411                         http_method = strscpy (http_method, "POST");
412                         http_post_data = strscpy (http_post_data, optarg);
413                         break;
414                 case 's': /* string or substring */
415                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
416                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
417                         break;
418                 case 'e': /* string or substring */
419                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
420                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
421                         server_expect_yn = 1;
422                         break;
423                 case 'R': /* regex */
424 #ifdef HAVE_REGEX_H
425                         cflags = REG_ICASE;
426 #else
427                         usage ("check_http: call for regex which was not a compiled option\n");
428 #endif
429                 case 'r': /* regex */
430 #ifdef HAVE_REGEX_H
431                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
432                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
433                         regexp[MAX_RE_SIZE - 1] = 0;
434                         errcode = regcomp (&preg, regexp, cflags);
435                         if (errcode != 0) {
436                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
437                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
438                                 return ERROR;
439                         }
440 #else
441                         usage ("check_http: call for regex which was not a compiled option\n");
442 #endif
443                         break;
444                 case 'v': /* verbose */
445                         verbose = TRUE;
446                         break;
447                 }
448         }
450         c = optind;
452         if (server_address == NULL && host_name == NULL) {
453                 server_address = strscpy (NULL, argv[c]);
454                 host_name = strscpy (NULL, argv[c++]);
455         }
457         if (server_address == NULL && host_name == NULL)
458                 usage ("check_http: you must specify a host name\n");
460         if (server_address == NULL)
461                 server_address = strscpy (NULL, host_name);
463         if (host_name == NULL)
464                 host_name = strscpy (NULL, server_address);
466         if (http_method == NULL)
467                 http_method = strscpy (http_method, "GET");
469         if (server_url == NULL) {
470                 server_url = strscpy (NULL, "/");
471                 server_url_length = strlen(HTTP_URL);
472         }
474         return TRUE;
476 \f
479 /* written by lauri alanko */
480 static char *
481 base64 (char *bin, int len)
484         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
485         int i = 0, j = 0;
487         char BASE64_END = '=';
488         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
490         while (j < len - 2) {
491                 buf[i++] = base64_table[bin[j] >> 2];
492                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
493                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
494                 buf[i++] = base64_table[bin[j + 2] & 63];
495                 j += 3;
496         }
498         switch (len - j) {
499         case 1:
500                 buf[i++] = base64_table[bin[j] >> 2];
501                 buf[i++] = base64_table[(bin[j] & 3) << 4];
502                 buf[i++] = BASE64_END;
503                 buf[i++] = BASE64_END;
504                 break;
505         case 2:
506                 buf[i++] = base64_table[bin[j] >> 2];
507                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
508                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
509                 buf[i++] = BASE64_END;
510                 break;
511         case 0:
512                 break;
513         }
515         buf[i] = '\0';
516         return buf;
518 \f
521 int
522 check_http (void)
524         char *msg = NULL;
525         char *status_line = NULL;
526         char *header = NULL;
527         char *page = NULL;
528         char *auth = NULL;
529         int i = 0;
530         size_t pagesize = 0;
531         char *full_page = NULL;
532         char *buf = NULL;
533         char *pos = NULL;
534         char *x = NULL;
535         char *orig_url = NULL;
536         double elapsed_time;
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                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
545                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
546                         X509_free (server_cert);
547                 }
548                 else {
549                         printf ("ERROR: Cannot retrieve server certificate.\n");
550                         return STATE_CRITICAL;
551                 }
553                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
554                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
555                         ERR_print_errors_fp (stderr);
556                         return STATE_CRITICAL;
557                 }
559                 /* optionally send the host header info (not clear if it's usable) */
560                 if (strcmp (host_name, "")) {
561                         asprintf (&buf, "Host: %s\r\n", host_name);
562                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
563                                 ERR_print_errors_fp (stderr);
564                                 return STATE_CRITICAL;
565                         }
566                 }
568                 /* send user agent */
569                 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
570                          clean_revstring (REVISION), PACKAGE_VERSION);
571                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
572                         ERR_print_errors_fp (stderr);
573                         return STATE_CRITICAL;
574                 }
576                 /* optionally send the authentication info */
577                 if (strcmp (user_auth, "")) {
578                         auth = base64 (user_auth, strlen (user_auth));
579                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
580                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
581                                 ERR_print_errors_fp (stderr);
582                                 return STATE_CRITICAL;
583                         }
584                 }
586                 /* optionally send http POST data */
587                 if (http_post_data) {
588                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
589                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
590                                 ERR_print_errors_fp (stderr);
591                                 return STATE_CRITICAL;
592                         }
593                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
594                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
595                                 ERR_print_errors_fp (stderr);
596                                 return STATE_CRITICAL;
597                         }
598                         http_post_data = strscat (http_post_data, "\r\n");
599                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
600                                 ERR_print_errors_fp (stderr);
601                                 return STATE_CRITICAL;
602                         }
603                 }
605                 /* send a newline so the server knows we're done with the request */
606                 asprintf (&buf, "\r\n\r\n");
607                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
608                         ERR_print_errors_fp (stderr);
609                         return STATE_CRITICAL;
610                 }
612         }
613         else {
614 #endif
615                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
616                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
617                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
618                 send (sd, buf, strlen (buf), 0);
619                 
622                 /* optionally send the host header info */
623                 if (strcmp (host_name, "")) {
624                         asprintf (&buf, "Host: %s\r\n", host_name);
625                         send (sd, buf, strlen (buf), 0);
626                 }
628                 /* send user agent */
629                 asprintf (&buf,
630                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
631                          clean_revstring (REVISION), PACKAGE_VERSION);
632                 send (sd, buf, strlen (buf), 0);
634                 /* optionally send the authentication info */
635                 if (strcmp (user_auth, "")) {
636                         auth = base64 (user_auth, strlen (user_auth));
637                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
638                         send (sd, buf, strlen (buf), 0);
639                 }
641                 /* optionally send http POST data */
642                 /* written by Chris Henesy <lurker@shadowtech.org> */
643                 if (http_post_data) {
644                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
645                         send (sd, buf, strlen (buf), 0);
646                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
647                         send (sd, buf, strlen (buf), 0);
648                         http_post_data = strscat (http_post_data, "\r\n");
649                         send (sd, http_post_data, strlen (http_post_data), 0);
650                 }
652                 /* send a newline so the server knows we're done with the request */
653                 asprintf (&buf, "\r\n\r\n");
654                 send (sd, buf, strlen (buf), 0);
655 #ifdef HAVE_SSL
656         }
657 #endif
659         /* fetch the page */
660         pagesize = (size_t) 1;
661         asprintf (&full_page, "");
662         while ((i = my_recv ()) > 0) {
663                 buffer[i] = '\0';
664                 asprintf (&full_page, "%s%s", full_page, buffer);
665                 pagesize += i;
666         }
668         if (i < 0)
669                 terminate (STATE_CRITICAL, "Error in recv()");
671         /* return a CRITICAL status if we couldn't read any data */
672         if (pagesize == (size_t) 0)
673                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
675         /* close the connection */
676         my_close ();
678         /* reset the alarm */
679         alarm (0);
681         /* leave full_page untouched so we can free it later */
682         page = full_page;
684         if (verbose)
685                 printf ("Page is %d characters\n", pagesize);
687         /* find status line and null-terminate it */
688         status_line = page;
689         page += (size_t) strcspn (page, "\r\n");
690         pos = page;
691         page += (size_t) strspn (page, "\r\n");
692         status_line[pos - status_line] = 0;
693         strip (status_line);
694         if (verbose)
695                 printf ("STATUS: %s\n", status_line);
697         /* find header info and null terminate it */
698         header = page;
699         while (strcspn (page, "\r\n") > 0) {
700                 page += (size_t) strcspn (page, "\r\n");
701                 pos = page;
702                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
703                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
704                         page += (size_t) 2;
705                 else
706                         page += (size_t) 1;
707         }
708         page += (size_t) strspn (page, "\r\n");
709         header[pos - header] = 0;
710         if (verbose)
711                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
713         /* make sure the status line matches the response we are looking for */
714         if (!strstr (status_line, server_expect)) {
715                 if (server_port == HTTP_PORT)
716                         asprintf (&msg, "Invalid HTTP response received from host\n");
717                 else
718                         asprintf (&msg,
719                                         "Invalid HTTP response received from host on port %d\n",
720                                         server_port);
721                 terminate (STATE_CRITICAL, msg);
722         }
725         /* Exit here if server_expect was set by user and not default */
726         if ( server_expect_yn  )  {
727                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
728                           server_expect);
729                 if (verbose)
730                         printf ("%s\n",msg);
732         }
733         else {
734         
736                 /* check the return code */
737                 /* server errors result in a critical state */
738                 if (strstr (status_line, "500") ||
739                   strstr (status_line, "501") ||
740                 strstr (status_line, "502") ||
741                     strstr (status_line, "503")) {
742                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
743                 }
745                 /* client errors result in a warning state */
746                 if (strstr (status_line, "400") ||
747                   strstr (status_line, "401") ||
748                 strstr (status_line, "402") ||
749                     strstr (status_line, "403") ||
750                     strstr (status_line, "404")) {
751                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
752                 }
754                 /* check redirected page if specified */
755                 if (strstr (status_line, "300") ||
756                   strstr (status_line, "301") ||
757                 strstr (status_line, "302") ||
758                     strstr (status_line, "303") ||
759                     strstr (status_line, "304")) {
760                         if (onredirect == STATE_DEPENDENT) {
762                                 orig_url = strscpy(NULL, server_url);
763                                 pos = header;
764                                 while (pos) {
765                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
766                                         if (server_address == NULL)
767                                                 terminate (STATE_UNKNOWN,
768                                                                                  "HTTP UNKNOWN: could not allocate server_address");
769                                         if (strcspn (pos, "\r\n") > server_url_length) {
770                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
771                                                 if (server_url == NULL)
772                                                         terminate (STATE_UNKNOWN,
773                                                                    "HTTP UNKNOWN: could not allocate server_url");
774                                                 server_url_length = strcspn (pos, "\r\n");
775                                         }
776                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
777                                                 host_name = strscpy (host_name, server_address);
778                                                 use_ssl = server_type_check (server_type);
779                                                 server_port = atoi (server_port_text);
780                                                 check_http ();
781                                         }
782                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
783                                                 host_name = strscpy (host_name, server_address);
784                                                 use_ssl = server_type_check (server_type);
785                                                 server_port = server_port_check (use_ssl);
786                                                 check_http ();
787                                         }
788                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
789                                                 host_name = strscpy (host_name, server_address);
790                                                 strcpy (server_url, "/");
791                                                 use_ssl = server_type_check (server_type);
792                                                 server_port = atoi (server_port_text);
793                                                 check_http ();
794                                         }
795                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
796                                                 host_name = strscpy (host_name, server_address);
797                                                 strcpy (server_url, "/");
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_PATH, server_url) == 1) {
803                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
804                                                         *x = '\0';
805                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
806                                                 }
807                                                 check_http ();
808                                         }                                       
809                                         pos += (size_t) strcspn (pos, "\r\n");
810                                         pos += (size_t) strspn (pos, "\r\n");
811                                 } /* end while (pos) */
812                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
813                                         status_line, (display_html ? "</A>" : ""));
814                                 exit (STATE_UNKNOWN);
815                         } /* end if (onredirect == STATE_DEPENDENT) */
816                         
817                         else if (onredirect == STATE_UNKNOWN)
818                                 printf ("HTTP UNKNOWN");
819                         else if (onredirect == STATE_OK)
820                                 printf ("HTTP ok");
821                         else if (onredirect == STATE_WARNING)
822                                 printf ("HTTP WARNING");
823                         else if (onredirect == STATE_CRITICAL)
824                                 printf ("HTTP CRITICAL");
825                         elapsed_time = delta_time (tv);
826                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
827                                  status_line, elapsed_time, timestamp,
828                            (display_html ? "</A>" : ""), elapsed_time);
829                         terminate (onredirect, msg);
830                 } /* end if (strstr (status_line, "30[0-4]") */
833         } /* end else (server_expect_yn)  */
835                 
836         /* check elapsed time */
837         elapsed_time = delta_time (tv);
838         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
839                        status_line, elapsed_time, timestamp,
840                        (display_html ? "</A>" : ""), elapsed_time);
841         if (check_critical_time == TRUE && elapsed_time > critical_time)
842                 terminate (STATE_CRITICAL, msg);
843         if (check_warning_time == TRUE && elapsed_time > warning_time)
844                 terminate (STATE_WARNING, msg);
846         /* Page and Header content checks go here */
847         /* these checks should be last */
849         if (strlen (string_expect)) {
850                 if (strstr (page, string_expect)) {
851                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
852                                 status_line, elapsed_time,
853                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
854                         exit (STATE_OK);
855                 }
856                 else {
857                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
858                                 (display_html ? "</A>" : ""), elapsed_time);
859                         exit (STATE_CRITICAL);
860                 }
861         }
862 #ifdef HAVE_REGEX_H
863         if (strlen (regexp)) {
864                 errcode = regexec (&preg, page, REGS, pmatch, 0);
865                 if (errcode == 0) {
866                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
867                                 status_line, elapsed_time,
868                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
869                         exit (STATE_OK);
870                 }
871                 else {
872                         if (errcode == REG_NOMATCH) {
873                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
874                                         (display_html ? "</A>" : ""), elapsed_time);
875                                 exit (STATE_CRITICAL);
876                         }
877                         else {
878                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
879                                 printf ("Execute Error: %s\n", errbuf);
880                                 exit (STATE_CRITICAL);
881                         }
882                 }
883         }
884 #endif
886         /* We only get here if all tests have been passed */
887         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
888                         status_line, (float)elapsed_time,
889                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
890         terminate (STATE_OK, msg);
891         return STATE_UNKNOWN;
896 #ifdef HAVE_SSL
897 int connect_SSL (void)
899         SSL_METHOD *meth;
901         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
902         RAND_seed (randbuff, strlen (randbuff));
903         /* Initialize SSL context */
904         SSLeay_add_ssl_algorithms ();
905         meth = SSLv23_client_method ();
906         SSL_load_error_strings ();
907         if ((ctx = SSL_CTX_new (meth)) == NULL) {
908                 printf ("ERROR: Cannot create SSL context.\n");
909                 return STATE_CRITICAL;
910         }
912         /* Initialize alarm signal handling */
913         signal (SIGALRM, socket_timeout_alarm_handler);
915         /* Set socket timeout */
916         alarm (socket_timeout);
918         /* Save start time */
919         gettimeofday (&tv, NULL);
921         /* Make TCP connection */
922         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
923                 /* Do the SSL handshake */
924                 if ((ssl = SSL_new (ctx)) != NULL) {
925                         SSL_set_cipher_list(ssl, "ALL");
926                         SSL_set_fd (ssl, sd);
927                         if (SSL_connect (ssl) != -1)
928                                 return OK;
929                         ERR_print_errors_fp (stderr);
930                 }
931                 else {
932                         printf ("ERROR: Cannot initiate SSL handshake.\n");
933                 }
934                 SSL_free (ssl);
935         }
937         SSL_CTX_free (ctx);
938         close (sd);
940         return STATE_CRITICAL;
942 #endif
944 #ifdef HAVE_SSL
945 int
946 check_certificate (X509 ** certificate)
948         ASN1_STRING *tm;
949         int offset;
950         struct tm stamp;
951         int days_left;
954         /* Retrieve timestamp of certificate */
955         tm = X509_get_notAfter (*certificate);
957         /* Generate tm structure to process timestamp */
958         if (tm->type == V_ASN1_UTCTIME) {
959                 if (tm->length < 10) {
960                         printf ("ERROR: Wrong time format in certificate.\n");
961                         return STATE_CRITICAL;
962                 }
963                 else {
964                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
965                         if (stamp.tm_year < 50)
966                                 stamp.tm_year += 100;
967                         offset = 0;
968                 }
969         }
970         else {
971                 if (tm->length < 12) {
972                         printf ("ERROR: Wrong time format in certificate.\n");
973                         return STATE_CRITICAL;
974                 }
975                 else {
976                         stamp.tm_year =
977                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
978                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
979                         stamp.tm_year -= 1900;
980                         offset = 2;
981                 }
982         }
983         stamp.tm_mon =
984                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
985         stamp.tm_mday =
986                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
987         stamp.tm_hour =
988                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
989         stamp.tm_min =
990                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
991         stamp.tm_sec = 0;
992         stamp.tm_isdst = -1;
994         days_left = (mktime (&stamp) - time (NULL)) / 86400;
995         snprintf
996                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
997                  stamp.tm_mon + 1,
998                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1000         if (days_left > 0 && days_left <= days_till_exp) {
1001                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1002                 return STATE_WARNING;
1003         }
1004         if (days_left < 0) {
1005                 printf ("Certificate expired on %s.\n", timestamp);
1006                 return STATE_CRITICAL;
1007         }
1009         if (days_left == 0) {
1010                 printf ("Certificate expires today (%s).\n", timestamp);
1011                 return STATE_WARNING;
1012         }
1014         printf ("Certificate will expire on %s.\n", timestamp);
1016         return STATE_OK;
1018 #endif
1019 \f
1022 int
1023 my_recv (void)
1025         int i;
1026 #ifdef HAVE_SSL
1027         if (use_ssl) {
1028                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1029         }
1030         else {
1031                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1032         }
1033 #else
1034         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1035 #endif
1036         return i;
1040 int
1041 my_close (void)
1043 #ifdef HAVE_SSL
1044         if (use_ssl == TRUE) {
1045                 SSL_shutdown (ssl);
1046                 SSL_free (ssl);
1047                 SSL_CTX_free (ctx);
1048                 return 0;
1049         }
1050         else {
1051 #endif
1052                 return close (sd);
1053 #ifdef HAVE_SSL
1054         }
1055 #endif
1057 \f
1060 void
1061 print_help (void)
1063         print_revision (PROGNAME, REVISION);
1064         printf
1065                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1066                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1067         print_usage ();
1068         printf ("NOTE: One or both of -H and -I must be specified\n");
1069         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1070                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1071 #ifdef HAVE_SSL
1072         printf (SSLDESCRIPTION);
1073 #endif
1077 void
1078 print_usage (void)
1080         printf ("Usage:\n" " %s %s\n"
1081 #ifdef HAVE_GETOPT_H
1082                 " %s (-h | --help) for detailed help\n"
1083                 " %s (-V | --version) for version information\n",
1084 #else
1085                 " %s -h for detailed help\n"
1086                 " %s -V for version information\n",
1087 #endif
1088         PROGNAME, OPTIONS, PROGNAME, PROGNAME);