Code

millisecond timimg where supported
[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 int call_getopt (int, char **);
211 static char *base64 (char *bin, int len);
212 int check_http (void);
213 int my_recv (void);
214 int my_close (void);
216 int
217 main (int argc, char **argv)
219         int result = STATE_UNKNOWN;
221         if (process_arguments (argc, argv) == ERROR)
222                 usage ("check_http: could not parse arguments\n");
224         if (strstr (timestamp, ":")) {
225                 if (strstr (server_url, "?"))
226                         asprintf (&server_url, "%s&%s", server_url, timestamp);
227                 else
228                         asprintf (&server_url, "%s?%s", server_url, timestamp);
229         }
231         if (display_html == TRUE)
232                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
233                         host_name, server_port, server_url);
235         /* initialize alarm signal handling, set socket timeout, start timer */
236         (void) signal (SIGALRM, socket_timeout_alarm_handler);
237         (void) alarm (socket_timeout);
238         gettimeofday (&tv, NULL);
240 #ifdef HAVE_SSL
241         if (use_ssl && check_cert == TRUE) {
242                 if (connect_SSL () != OK)
243                         terminate (STATE_CRITICAL,
244                                    "HTTP CRITICAL - Could not make SSL connection\n");
245                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
246                         result = check_certificate (&server_cert);
247                         X509_free (server_cert);
248                 }
249                 else {
250                         printf ("ERROR: Cannot retrieve server certificate.\n");
251                         result = STATE_CRITICAL;
252                 }
253                 SSL_shutdown (ssl);
254                 SSL_free (ssl);
255                 SSL_CTX_free (ctx);
256                 close (sd);
257         }
258         else {
259                 result = check_http ();
260         }
261 #else
262         result = check_http ();
263 #endif
264         return result;
266 \f
269 /* process command-line arguments */
270 int
271 process_arguments (int argc, char **argv)
273         int c, i = 1;
274         char optchars[MAX_INPUT_BUFFER];
276 #ifdef HAVE_GETOPT_H
277         int option_index = 0;
278         static struct option long_options[] = {
279                 STD_OPTS_LONG,
280                 {"link", no_argument, 0, 'L'},
281                 {"nohtml", no_argument, 0, 'n'},
282                 {"ssl", no_argument, 0, 'S'},
283                 {"verbose", no_argument, 0, 'v'},
284                 {"post", required_argument, 0, 'P'},
285                 {"IP-address", required_argument, 0, 'I'},
286                 {"string", required_argument, 0, 's'},
287                 {"regex", required_argument, 0, 'r'},
288                 {"ereg", required_argument, 0, 'r'},
289                 {"eregi", required_argument, 0, 'R'},
290                 {"onredirect", required_argument, 0, 'f'},
291                 {"certificate", required_argument, 0, 'C'},
292                 {0, 0, 0, 0}
293         };
294 #endif
296         if (argc < 2)
297                 return ERROR;
299         for (c = 1; c < argc; c++) {
300                 if (strcmp ("-to", argv[c]) == 0)
301                         strcpy (argv[c], "-t");
302                 if (strcmp ("-hn", argv[c]) == 0)
303                         strcpy (argv[c], "-H");
304                 if (strcmp ("-wt", argv[c]) == 0)
305                         strcpy (argv[c], "-w");
306                 if (strcmp ("-ct", argv[c]) == 0)
307                         strcpy (argv[c], "-c");
308                 if (strcmp ("-nohtml", argv[c]) == 0)
309                         strcpy (argv[c], "-n");
310         }
312         snprintf (optchars, MAX_INPUT_BUFFER, "%s%s", STD_OPTS,
313                   "P:I:a:e:p:s:R:r:u:f:C:nLS");
315         while (1) {
316 #ifdef HAVE_GETOPT_H
317                 c = getopt_long (argc, argv, optchars, long_options, &option_index);
318 #else
319                 c = getopt (argc, argv, optchars);
320 #endif
321                 if (c == -1 || c == EOF)
322                         break;
324                 switch (c) {
325                 case '?': /* usage */
326                         usage2 ("unknown argument", optarg);
327                         break;
328                 case 'h': /* help */
329                         print_help ();
330                         exit (STATE_OK);
331                         break;
332                 case 'V': /* version */
333                         print_revision (PROGNAME, REVISION);
334                         exit (STATE_OK);
335                         break;
336                 case 't': /* timeout period */
337                         if (!is_intnonneg (optarg))
338                                 usage2 ("timeout interval must be a non-negative integer", optarg);
339                         socket_timeout = atoi (optarg);
340                         break;
341                 case 'c': /* critical time threshold */
342                         if (!is_intnonneg (optarg))
343                                 usage2 ("invalid critical threshold", optarg);
344                         critical_time = strtod (optarg, NULL);
345                         check_critical_time = TRUE;
346                         break;
347                 case 'w': /* warning time threshold */
348                         if (!is_intnonneg (optarg))
349                                 usage2 ("invalid warning threshold", optarg);
350                         warning_time = strtod (optarg, NULL);
351                         check_warning_time = TRUE;
352                         break;
353                 case 'L': /* show html link */
354                         display_html = TRUE;
355                         break;
356                 case 'n': /* do not show html link */
357                         display_html = FALSE;
358                         break;
359                 case 'S': /* use SSL */
360 #ifndef HAVE_SSL
361                         usage ("check_http: invalid option - SSL is not available\n");
362 #endif
363                         use_ssl = TRUE;
364                         if (specify_port == FALSE)
365                                 server_port = HTTPS_PORT;
366                         break;
367                 case 'C': /* Check SSL cert validity */
368 #ifdef HAVE_SSL
369                         if (!is_intnonneg (optarg))
370                                 usage2 ("invalid certificate expiration period", optarg);
371                         days_till_exp = atoi (optarg);
372                         check_cert = TRUE;
373 #else
374                         usage ("check_http: invalid option - SSL is not available\n");
375 #endif
376                         break;
377                 case 'f': /* onredirect */
378                         if (!strcmp (optarg, "follow"))
379                                 onredirect = STATE_DEPENDENT;
380                         if (!strcmp (optarg, "unknown"))
381                                 onredirect = STATE_UNKNOWN;
382                         if (!strcmp (optarg, "ok"))
383                                 onredirect = STATE_OK;
384                         if (!strcmp (optarg, "warning"))
385                                 onredirect = STATE_WARNING;
386                         if (!strcmp (optarg, "critical"))
387                                 onredirect = STATE_CRITICAL;
388                         if (verbose)
389                                 printf("option f:%d \n", onredirect);  
390                         break;
391                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
392                 case 'H': /* Host Name (virtual host) */
393                         host_name = strscpy (host_name, optarg);
394                         break;
395                 case 'I': /* Server IP-address */
396                         server_address = strscpy (server_address, optarg);
397                         break;
398                 case 'u': /* Host or server */
399                         server_url = strscpy (server_url, optarg);
400                         server_url_length = strlen (optarg);
401                         break;
402                 case 'p': /* Host or server */
403                         if (!is_intnonneg (optarg))
404                                 usage2 ("invalid port number", optarg);
405                         server_port = atoi (optarg);
406                         specify_port = TRUE;
407                         break;
408                 case 'a': /* authorization info */
409                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
410                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
411                         break;
412                 case 'P': /* HTTP POST data in URL encoded format */
413                         http_method = strscpy (http_method, "POST");
414                         http_post_data = strscpy (http_post_data, optarg);
415                         break;
416                 case 's': /* string or substring */
417                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
418                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
419                         break;
420                 case 'e': /* string or substring */
421                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
422                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
423                         server_expect_yn = 1;
424                         break;
425                 case 'R': /* regex */
426 #ifdef HAVE_REGEX_H
427                         cflags = REG_ICASE;
428 #else
429                         usage ("check_http: call for regex which was not a compiled option\n");
430 #endif
431                 case 'r': /* regex */
432 #ifdef HAVE_REGEX_H
433                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
434                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
435                         regexp[MAX_RE_SIZE - 1] = 0;
436                         errcode = regcomp (&preg, regexp, cflags);
437                         if (errcode != 0) {
438                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
439                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
440                                 return ERROR;
441                         }
442 #else
443                         usage ("check_http: call for regex which was not a compiled option\n");
444 #endif
445                         break;
446                 case 'v': /* verbose */
447                         verbose = TRUE;
448                         break;
449                 }
450         }
452         c = optind;
454         if (server_address == NULL && host_name == NULL) {
455                 server_address = strscpy (NULL, argv[c]);
456                 host_name = strscpy (NULL, argv[c++]);
457         }
459         if (server_address == NULL && host_name == NULL)
460                 usage ("check_http: you must specify a host name\n");
462         if (server_address == NULL)
463                 server_address = strscpy (NULL, host_name);
465         if (host_name == NULL)
466                 host_name = strscpy (NULL, server_address);
468         if (http_method == NULL)
469                 http_method = strscpy (http_method, "GET");
471         if (server_url == NULL) {
472                 server_url = strscpy (NULL, "/");
473                 server_url_length = strlen(HTTP_URL);
474         }
476         return TRUE;
478 \f
481 /* written by lauri alanko */
482 static char *
483 base64 (char *bin, int len)
486         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
487         int i = 0, j = 0;
489         char BASE64_END = '=';
490         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
492         while (j < len - 2) {
493                 buf[i++] = base64_table[bin[j] >> 2];
494                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
495                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
496                 buf[i++] = base64_table[bin[j + 2] & 63];
497                 j += 3;
498         }
500         switch (len - j) {
501         case 1:
502                 buf[i++] = base64_table[bin[j] >> 2];
503                 buf[i++] = base64_table[(bin[j] & 3) << 4];
504                 buf[i++] = BASE64_END;
505                 buf[i++] = BASE64_END;
506                 break;
507         case 2:
508                 buf[i++] = base64_table[bin[j] >> 2];
509                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
510                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
511                 buf[i++] = BASE64_END;
512                 break;
513         case 0:
514                 break;
515         }
517         buf[i] = '\0';
518         return buf;
520 \f
523 int
524 check_http (void)
526         char *msg = NULL;
527         char *status_line = NULL;
528         char *header = NULL;
529         char *page = NULL;
530         char *auth = NULL;
531         int i = 0;
532         size_t pagesize = 0;
533         char *full_page = NULL;
534         char *buf = NULL;
535         char *pos = NULL;
536         char *x = NULL;
537         char *orig_url = NULL;
538         double elapsed_time;
540         /* try to connect to the host at the given port number */
541 #ifdef HAVE_SSL
542         if (use_ssl == TRUE) {
544                 if (connect_SSL () != OK)
545                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
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                 asprintf (&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                         asprintf (&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                 asprintf (&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                         asprintf (&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                         asprintf (&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                         asprintf (&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                 asprintf (&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                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
619                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
620                 send (sd, buf, strlen (buf), 0);
621                 
624                 /* optionally send the host header info */
625                 if (strcmp (host_name, "")) {
626                         asprintf (&buf, "Host: %s\r\n", host_name);
627                         send (sd, buf, strlen (buf), 0);
628                 }
630                 /* send user agent */
631                 asprintf (&buf,
632                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
633                          clean_revstring (REVISION), PACKAGE_VERSION);
634                 send (sd, buf, strlen (buf), 0);
636                 /* optionally send the authentication info */
637                 if (strcmp (user_auth, "")) {
638                         auth = base64 (user_auth, strlen (user_auth));
639                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
640                         send (sd, buf, strlen (buf), 0);
641                 }
643                 /* optionally send http POST data */
644                 /* written by Chris Henesy <lurker@shadowtech.org> */
645                 if (http_post_data) {
646                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
647                         send (sd, buf, strlen (buf), 0);
648                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
649                         send (sd, buf, strlen (buf), 0);
650                         http_post_data = strscat (http_post_data, "\r\n");
651                         send (sd, http_post_data, strlen (http_post_data), 0);
652                 }
654                 /* send a newline so the server knows we're done with the request */
655                 asprintf (&buf, "\r\n\r\n");
656                 send (sd, buf, strlen (buf), 0);
657 #ifdef HAVE_SSL
658         }
659 #endif
661         /* fetch the page */
662         pagesize = (size_t) 1;
663         asprintf (&full_page, "");
664         while ((i = my_recv ()) > 0) {
665                 buffer[i] = '\0';
666                 asprintf (&full_page, "%s%s", full_page, buffer);
667                 pagesize += i;
668         }
670         if (i < 0)
671                 terminate (STATE_CRITICAL, "Error in recv()");
673         /* return a CRITICAL status if we couldn't read any data */
674         if (pagesize == (size_t) 0)
675                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
677         /* close the connection */
678         my_close ();
680         /* reset the alarm */
681         alarm (0);
683         /* leave full_page untouched so we can free it later */
684         page = full_page;
686         if (verbose)
687                 printf ("Page is %d characters\n", pagesize);
689         /* find status line and null-terminate it */
690         status_line = page;
691         page += (size_t) strcspn (page, "\r\n");
692         pos = page;
693         page += (size_t) strspn (page, "\r\n");
694         status_line[pos - status_line] = 0;
695         strip (status_line);
696         if (verbose)
697                 printf ("STATUS: %s\n", status_line);
699         /* find header info and null terminate it */
700         header = page;
701         while (strcspn (page, "\r\n") > 0) {
702                 page += (size_t) strcspn (page, "\r\n");
703                 pos = page;
704                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
705                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
706                         page += (size_t) 2;
707                 else
708                         page += (size_t) 1;
709         }
710         page += (size_t) strspn (page, "\r\n");
711         header[pos - header] = 0;
712         if (verbose)
713                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
715         /* make sure the status line matches the response we are looking for */
716         if (!strstr (status_line, server_expect)) {
717                 if (server_port == HTTP_PORT)
718                         asprintf (&msg, "Invalid HTTP response received from host\n");
719                 else
720                         asprintf (&msg,
721                                         "Invalid HTTP response received from host on port %d\n",
722                                         server_port);
723                 terminate (STATE_CRITICAL, msg);
724         }
727         /* Exit here if server_expect was set by user and not default */
728         if ( server_expect_yn  )  {
729                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
730                           server_expect);
731                 if (verbose)
732                         printf ("%s\n",msg);
734         }
735         else {
736         
738                 /* check the return code */
739                 /* server errors result in a critical state */
740                 if (strstr (status_line, "500") ||
741                   strstr (status_line, "501") ||
742                 strstr (status_line, "502") ||
743                     strstr (status_line, "503")) {
744                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
745                 }
747                 /* client errors result in a warning state */
748                 if (strstr (status_line, "400") ||
749                   strstr (status_line, "401") ||
750                 strstr (status_line, "402") ||
751                     strstr (status_line, "403") ||
752                     strstr (status_line, "404")) {
753                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
754                 }
756                 /* check redirected page if specified */
757                 if (strstr (status_line, "300") ||
758                   strstr (status_line, "301") ||
759                 strstr (status_line, "302") ||
760                     strstr (status_line, "303") ||
761                     strstr (status_line, "304")) {
762                         if (onredirect == STATE_DEPENDENT) {
764                                 orig_url = strscpy(NULL, server_url);
765                                 pos = header;
766                                 while (pos) {
767                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
768                                         if (server_address == NULL)
769                                                 terminate (STATE_UNKNOWN,
770                                                                                  "HTTP UNKNOWN: could not allocate server_address");
771                                         if (strcspn (pos, "\r\n") > server_url_length) {
772                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
773                                                 if (server_url == NULL)
774                                                         terminate (STATE_UNKNOWN,
775                                                                    "HTTP UNKNOWN: could not allocate server_url");
776                                                 server_url_length = strcspn (pos, "\r\n");
777                                         }
778                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
779                                                 host_name = strscpy (host_name, server_address);
780                                                 use_ssl = server_type_check (server_type);
781                                                 server_port = atoi (server_port_text);
782                                                 check_http ();
783                                         }
784                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
785                                                 host_name = strscpy (host_name, server_address);
786                                                 use_ssl = server_type_check (server_type);
787                                                 server_port = server_port_check (use_ssl);
788                                                 check_http ();
789                                         }
790                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
791                                                 host_name = strscpy (host_name, server_address);
792                                                 strcpy (server_url, "/");
793                                                 use_ssl = server_type_check (server_type);
794                                                 server_port = atoi (server_port_text);
795                                                 check_http ();
796                                         }
797                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
798                                                 host_name = strscpy (host_name, server_address);
799                                                 strcpy (server_url, "/");
800                                                 use_ssl = server_type_check (server_type);
801                                                 server_port = server_port_check (use_ssl);
802                                                 check_http ();
803                                         }
804                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
805                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
806                                                         *x = '\0';
807                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
808                                                 }
809                                                 check_http ();
810                                         }                                       
811                                         pos += (size_t) strcspn (pos, "\r\n");
812                                         pos += (size_t) strspn (pos, "\r\n");
813                                 } /* end while (pos) */
814                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
815                                         status_line, (display_html ? "</A>" : ""));
816                                 exit (STATE_UNKNOWN);
817                         } /* end if (onredirect == STATE_DEPENDENT) */
818                         
819                         else if (onredirect == STATE_UNKNOWN)
820                                 printf ("HTTP UNKNOWN");
821                         else if (onredirect == STATE_OK)
822                                 printf ("HTTP ok");
823                         else if (onredirect == STATE_WARNING)
824                                 printf ("HTTP WARNING");
825                         else if (onredirect == STATE_CRITICAL)
826                                 printf ("HTTP CRITICAL");
827                         elapsed_time = delta_time (tv);
828                         asprintf (&msg, ": %s - %6.2f second response time %s%s|time=%6.2f\n",
829                                  status_line, elapsed_time, timestamp,
830                            (display_html ? "</A>" : ""), elapsed_time);
831                         terminate (onredirect, msg);
832                 } /* end if (strstr (status_line, "30[0-4]") */
835         } /* end else (server_expect_yn)  */
837                 
838         /* check elapsed time */
839         elapsed_time = delta_time (tv);
840         asprintf (&msg, "HTTP problem: %s - %6.2f second response time %s%s|time=%6.2f\n",
841                        status_line, elapsed_time, timestamp,
842                        (display_html ? "</A>" : ""), elapsed_time);
843         if (check_critical_time == TRUE && elapsed_time > critical_time)
844                 terminate (STATE_CRITICAL, msg);
845         if (check_warning_time == TRUE && elapsed_time > warning_time)
846                 terminate (STATE_WARNING, msg);
848         /* Page and Header content checks go here */
849         /* these checks should be last */
851         if (strlen (string_expect)) {
852                 if (strstr (page, string_expect)) {
853                         printf ("HTTP ok: %s - %6.2f second response time %s%s|time=%6.2f\n",
854                                 status_line, elapsed_time,
855                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
856                         exit (STATE_OK);
857                 }
858                 else {
859                         printf ("HTTP CRITICAL: string not found%s|time=%6.2f\n",
860                                 (display_html ? "</A>" : ""), elapsed_time);
861                         exit (STATE_CRITICAL);
862                 }
863         }
864 #ifdef HAVE_REGEX_H
865         if (strlen (regexp)) {
866                 errcode = regexec (&preg, page, REGS, pmatch, 0);
867                 if (errcode == 0) {
868                         printf ("HTTP ok: %s - %6.2f second response time %s%s|time=%6.2f\n",
869                                 status_line, elapsed_time,
870                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
871                         exit (STATE_OK);
872                 }
873                 else {
874                         if (errcode == REG_NOMATCH) {
875                                 printf ("HTTP CRITICAL: pattern not found%s|time=%6.2f\n",
876                                         (display_html ? "</A>" : ""), elapsed_time);
877                                 exit (STATE_CRITICAL);
878                         }
879                         else {
880                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
881                                 printf ("Execute Error: %s\n", errbuf);
882                                 exit (STATE_CRITICAL);
883                         }
884                 }
885         }
886 #endif
888         /* We only get here if all tests have been passed */
889         asprintf (&msg, "HTTP ok: %s - %6.2f second response time %s%s|time=%6.2f\n",
890                         status_line, (float)elapsed_time,
891                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
892         terminate (STATE_OK, msg);
893         return STATE_UNKNOWN;
898 #ifdef HAVE_SSL
899 int connect_SSL (void)
901         SSL_METHOD *meth;
903         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
904         RAND_seed (randbuff, strlen (randbuff));
905         /* Initialize SSL context */
906         SSLeay_add_ssl_algorithms ();
907         meth = SSLv23_client_method ();
908         SSL_load_error_strings ();
909         if ((ctx = SSL_CTX_new (meth)) == NULL) {
910                 printf ("ERROR: Cannot create SSL context.\n");
911                 return STATE_CRITICAL;
912         }
914         /* Initialize alarm signal handling */
915         signal (SIGALRM, socket_timeout_alarm_handler);
917         /* Set socket timeout */
918         alarm (socket_timeout);
920         /* Save start time */
921         gettimeofday (&tv, NULL);
923         /* Make TCP connection */
924         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
925                 /* Do the SSL handshake */
926                 if ((ssl = SSL_new (ctx)) != NULL) {
927                         SSL_set_cipher_list(ssl, "ALL");
928                         SSL_set_fd (ssl, sd);
929                         if (SSL_connect (ssl) != -1)
930                                 return OK;
931                         ERR_print_errors_fp (stderr);
932                 }
933                 else {
934                         printf ("ERROR: Cannot initiate SSL handshake.\n");
935                 }
936                 SSL_free (ssl);
937         }
939         SSL_CTX_free (ctx);
940         close (sd);
942         return STATE_CRITICAL;
944 #endif
946 #ifdef HAVE_SSL
947 int
948 check_certificate (X509 ** certificate)
950         ASN1_STRING *tm;
951         int offset;
952         struct tm stamp;
953         int days_left;
956         /* Retrieve timestamp of certificate */
957         tm = X509_get_notAfter (*certificate);
959         /* Generate tm structure to process timestamp */
960         if (tm->type == V_ASN1_UTCTIME) {
961                 if (tm->length < 10) {
962                         printf ("ERROR: Wrong time format in certificate.\n");
963                         return STATE_CRITICAL;
964                 }
965                 else {
966                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
967                         if (stamp.tm_year < 50)
968                                 stamp.tm_year += 100;
969                         offset = 0;
970                 }
971         }
972         else {
973                 if (tm->length < 12) {
974                         printf ("ERROR: Wrong time format in certificate.\n");
975                         return STATE_CRITICAL;
976                 }
977                 else {
978                         stamp.tm_year =
979                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
980                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
981                         stamp.tm_year -= 1900;
982                         offset = 2;
983                 }
984         }
985         stamp.tm_mon =
986                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
987         stamp.tm_mday =
988                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
989         stamp.tm_hour =
990                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
991         stamp.tm_min =
992                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
993         stamp.tm_sec = 0;
994         stamp.tm_isdst = -1;
996         days_left = (mktime (&stamp) - time (NULL)) / 86400;
997         snprintf
998                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
999                  stamp.tm_mon + 1,
1000                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1002         if (days_left > 0 && days_left <= days_till_exp) {
1003                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1004                 return STATE_WARNING;
1005         }
1006         if (days_left < 0) {
1007                 printf ("Certificate expired on %s.\n", timestamp);
1008                 return STATE_CRITICAL;
1009         }
1011         if (days_left == 0) {
1012                 printf ("Certificate expires today (%s).\n", timestamp);
1013                 return STATE_WARNING;
1014         }
1016         printf ("Certificate will expire on %s.\n", timestamp);
1018         return STATE_OK;
1020 #endif
1021 \f
1024 int
1025 my_recv (void)
1027         int i;
1028 #ifdef HAVE_SSL
1029         if (use_ssl) {
1030                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1031         }
1032         else {
1033                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1034         }
1035 #else
1036         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1037 #endif
1038         return i;
1042 int
1043 my_close (void)
1045 #ifdef HAVE_SSL
1046         if (use_ssl == TRUE) {
1047                 SSL_shutdown (ssl);
1048                 SSL_free (ssl);
1049                 SSL_CTX_free (ctx);
1050                 return 0;
1051         }
1052         else {
1053 #endif
1054                 return close (sd);
1055 #ifdef HAVE_SSL
1056         }
1057 #endif
1059 \f
1062 void
1063 print_help (void)
1065         print_revision (PROGNAME, REVISION);
1066         printf
1067                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1068                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1069         print_usage ();
1070         printf ("NOTE: One or both of -H and -I must be specified\n");
1071         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1072                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1073 #ifdef HAVE_SSL
1074         printf (SSLDESCRIPTION);
1075 #endif
1079 void
1080 print_usage (void)
1082         printf ("Usage:\n" " %s %s\n"
1083 #ifdef HAVE_GETOPT_H
1084                 " %s (-h | --help) for detailed help\n"
1085                 " %s (-V | --version) for version information\n",
1086 #else
1087                 " %s -h for detailed help\n"
1088                 " %s -V for version information\n",
1089 #endif
1090         PROGNAME, OPTIONS, PROGNAME, PROGNAME);