Code

provide on more decimal point printing time
[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, i = 1;
273         char optchars[MAX_INPUT_BUFFER];
275 #ifdef HAVE_GETOPT_H
276         int option_index = 0;
277         static struct option long_options[] = {
278                 STD_LONG_OPTS,
279                 {"file",required_argument,0,'F'},
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 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nLS"
314         while (1) {
315 #ifdef HAVE_GETOPT_H
316                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
317 #else
318                 c = getopt (argc, argv, OPTCHARS);
319 #endif
320                 if (c == -1 || c == EOF)
321                         break;
323                 switch (c) {
324                 case '?': /* usage */
325                         usage2 ("unknown argument", optarg);
326                         break;
327                 case 'h': /* help */
328                         print_help ();
329                         exit (STATE_OK);
330                         break;
331                 case 'V': /* version */
332                         print_revision (PROGNAME, REVISION);
333                         exit (STATE_OK);
334                         break;
335                 case 't': /* timeout period */
336                         if (!is_intnonneg (optarg))
337                                 usage2 ("timeout interval must be a non-negative integer", optarg);
338                         socket_timeout = atoi (optarg);
339                         break;
340                 case 'c': /* critical time threshold */
341                         if (!is_intnonneg (optarg))
342                                 usage2 ("invalid critical threshold", optarg);
343                         critical_time = strtod (optarg, NULL);
344                         check_critical_time = TRUE;
345                         break;
346                 case 'w': /* warning time threshold */
347                         if (!is_intnonneg (optarg))
348                                 usage2 ("invalid warning threshold", optarg);
349                         warning_time = strtod (optarg, NULL);
350                         check_warning_time = TRUE;
351                         break;
352                 case 'L': /* show html link */
353                         display_html = TRUE;
354                         break;
355                 case 'n': /* do not show html link */
356                         display_html = FALSE;
357                         break;
358                 case 'S': /* use SSL */
359 #ifndef HAVE_SSL
360                         usage ("check_http: invalid option - SSL is not available\n");
361 #endif
362                         use_ssl = TRUE;
363                         if (specify_port == FALSE)
364                                 server_port = HTTPS_PORT;
365                         break;
366                 case 'C': /* Check SSL cert validity */
367 #ifdef HAVE_SSL
368                         if (!is_intnonneg (optarg))
369                                 usage2 ("invalid certificate expiration period", optarg);
370                         days_till_exp = atoi (optarg);
371                         check_cert = TRUE;
372 #else
373                         usage ("check_http: invalid option - SSL is not available\n");
374 #endif
375                         break;
376                 case 'f': /* onredirect */
377                         if (!strcmp (optarg, "follow"))
378                                 onredirect = STATE_DEPENDENT;
379                         if (!strcmp (optarg, "unknown"))
380                                 onredirect = STATE_UNKNOWN;
381                         if (!strcmp (optarg, "ok"))
382                                 onredirect = STATE_OK;
383                         if (!strcmp (optarg, "warning"))
384                                 onredirect = STATE_WARNING;
385                         if (!strcmp (optarg, "critical"))
386                                 onredirect = STATE_CRITICAL;
387                         if (verbose)
388                                 printf("option f:%d \n", onredirect);  
389                         break;
390                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
391                 case 'H': /* Host Name (virtual host) */
392                         host_name = strscpy (host_name, optarg);
393                         break;
394                 case 'I': /* Server IP-address */
395                         server_address = strscpy (server_address, optarg);
396                         break;
397                 case 'u': /* Host or server */
398                         server_url = strscpy (server_url, optarg);
399                         server_url_length = strlen (optarg);
400                         break;
401                 case 'p': /* Host or server */
402                         if (!is_intnonneg (optarg))
403                                 usage2 ("invalid port number", optarg);
404                         server_port = atoi (optarg);
405                         specify_port = TRUE;
406                         break;
407                 case 'a': /* authorization info */
408                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
409                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
410                         break;
411                 case 'P': /* HTTP POST data in URL encoded format */
412                         http_method = strscpy (http_method, "POST");
413                         http_post_data = strscpy (http_post_data, optarg);
414                         break;
415                 case 's': /* string or substring */
416                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
417                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
418                         break;
419                 case 'e': /* string or substring */
420                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
421                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
422                         server_expect_yn = 1;
423                         break;
424                 case 'R': /* regex */
425 #ifdef HAVE_REGEX_H
426                         cflags = REG_ICASE;
427 #else
428                         usage ("check_http: call for regex which was not a compiled option\n");
429 #endif
430                 case 'r': /* regex */
431 #ifdef HAVE_REGEX_H
432                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
433                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
434                         regexp[MAX_RE_SIZE - 1] = 0;
435                         errcode = regcomp (&preg, regexp, cflags);
436                         if (errcode != 0) {
437                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
438                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
439                                 return ERROR;
440                         }
441 #else
442                         usage ("check_http: call for regex which was not a compiled option\n");
443 #endif
444                         break;
445                 case 'v': /* verbose */
446                         verbose = TRUE;
447                         break;
448                 }
449         }
451         c = optind;
453         if (server_address == NULL && host_name == NULL) {
454                 server_address = strscpy (NULL, argv[c]);
455                 host_name = strscpy (NULL, argv[c++]);
456         }
458         if (server_address == NULL && host_name == NULL)
459                 usage ("check_http: you must specify a host name\n");
461         if (server_address == NULL)
462                 server_address = strscpy (NULL, host_name);
464         if (host_name == NULL)
465                 host_name = strscpy (NULL, server_address);
467         if (http_method == NULL)
468                 http_method = strscpy (http_method, "GET");
470         if (server_url == NULL) {
471                 server_url = strscpy (NULL, "/");
472                 server_url_length = strlen(HTTP_URL);
473         }
475         return TRUE;
477 \f
480 /* written by lauri alanko */
481 static char *
482 base64 (char *bin, int len)
485         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
486         int i = 0, j = 0;
488         char BASE64_END = '=';
489         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
491         while (j < len - 2) {
492                 buf[i++] = base64_table[bin[j] >> 2];
493                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
494                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
495                 buf[i++] = base64_table[bin[j + 2] & 63];
496                 j += 3;
497         }
499         switch (len - j) {
500         case 1:
501                 buf[i++] = base64_table[bin[j] >> 2];
502                 buf[i++] = base64_table[(bin[j] & 3) << 4];
503                 buf[i++] = BASE64_END;
504                 buf[i++] = BASE64_END;
505                 break;
506         case 2:
507                 buf[i++] = base64_table[bin[j] >> 2];
508                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
509                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
510                 buf[i++] = BASE64_END;
511                 break;
512         case 0:
513                 break;
514         }
516         buf[i] = '\0';
517         return buf;
519 \f
522 int
523 check_http (void)
525         char *msg = NULL;
526         char *status_line = NULL;
527         char *header = NULL;
528         char *page = NULL;
529         char *auth = NULL;
530         int i = 0;
531         size_t pagesize = 0;
532         char *full_page = NULL;
533         char *buf = NULL;
534         char *pos = NULL;
535         char *x = NULL;
536         char *orig_url = NULL;
537         double elapsed_time;
539         /* try to connect to the host at the given port number */
540 #ifdef HAVE_SSL
541         if (use_ssl == TRUE) {
543                 if (connect_SSL () != OK)
544                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
546                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
547                         X509_free (server_cert);
548                 }
549                 else {
550                         printf ("ERROR: Cannot retrieve server certificate.\n");
551                         return STATE_CRITICAL;
552                 }
554                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
555                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
556                         ERR_print_errors_fp (stderr);
557                         return STATE_CRITICAL;
558                 }
560                 /* optionally send the host header info (not clear if it's usable) */
561                 if (strcmp (host_name, "")) {
562                         asprintf (&buf, "Host: %s\r\n", host_name);
563                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
564                                 ERR_print_errors_fp (stderr);
565                                 return STATE_CRITICAL;
566                         }
567                 }
569                 /* send user agent */
570                 asprintf (&buf, "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
571                          clean_revstring (REVISION), PACKAGE_VERSION);
572                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
573                         ERR_print_errors_fp (stderr);
574                         return STATE_CRITICAL;
575                 }
577                 /* optionally send the authentication info */
578                 if (strcmp (user_auth, "")) {
579                         auth = base64 (user_auth, strlen (user_auth));
580                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
581                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
582                                 ERR_print_errors_fp (stderr);
583                                 return STATE_CRITICAL;
584                         }
585                 }
587                 /* optionally send http POST data */
588                 if (http_post_data) {
589                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
590                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
591                                 ERR_print_errors_fp (stderr);
592                                 return STATE_CRITICAL;
593                         }
594                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
595                         if (SSL_write (ssl, buf, strlen (buf)) == -1) {
596                                 ERR_print_errors_fp (stderr);
597                                 return STATE_CRITICAL;
598                         }
599                         http_post_data = strscat (http_post_data, "\r\n");
600                         if (SSL_write (ssl, http_post_data, strlen (http_post_data)) == -1) {
601                                 ERR_print_errors_fp (stderr);
602                                 return STATE_CRITICAL;
603                         }
604                 }
606                 /* send a newline so the server knows we're done with the request */
607                 asprintf (&buf, "\r\n\r\n");
608                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
609                         ERR_print_errors_fp (stderr);
610                         return STATE_CRITICAL;
611                 }
613         }
614         else {
615 #endif
616                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
617                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
618                 asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
619                 send (sd, buf, strlen (buf), 0);
620                 
623                 /* optionally send the host header info */
624                 if (strcmp (host_name, "")) {
625                         asprintf (&buf, "Host: %s\r\n", host_name);
626                         send (sd, buf, strlen (buf), 0);
627                 }
629                 /* send user agent */
630                 asprintf (&buf,
631                          "User-Agent: check_http/%s (nagios-plugins %s)\r\n",
632                          clean_revstring (REVISION), PACKAGE_VERSION);
633                 send (sd, buf, strlen (buf), 0);
635                 /* optionally send the authentication info */
636                 if (strcmp (user_auth, "")) {
637                         auth = base64 (user_auth, strlen (user_auth));
638                         asprintf (&buf, "Authorization: Basic %s\r\n", auth);
639                         send (sd, buf, strlen (buf), 0);
640                 }
642                 /* optionally send http POST data */
643                 /* written by Chris Henesy <lurker@shadowtech.org> */
644                 if (http_post_data) {
645                         asprintf (&buf, "Content-Type: application/x-www-form-urlencoded\r\n");
646                         send (sd, buf, strlen (buf), 0);
647                         asprintf (&buf, "Content-Length: %i\r\n\r\n", strlen (http_post_data));
648                         send (sd, buf, strlen (buf), 0);
649                         http_post_data = strscat (http_post_data, "\r\n");
650                         send (sd, http_post_data, strlen (http_post_data), 0);
651                 }
653                 /* send a newline so the server knows we're done with the request */
654                 asprintf (&buf, "\r\n\r\n");
655                 send (sd, buf, strlen (buf), 0);
656 #ifdef HAVE_SSL
657         }
658 #endif
660         /* fetch the page */
661         pagesize = (size_t) 1;
662         asprintf (&full_page, "");
663         while ((i = my_recv ()) > 0) {
664                 buffer[i] = '\0';
665                 asprintf (&full_page, "%s%s", full_page, buffer);
666                 pagesize += i;
667         }
669         if (i < 0)
670                 terminate (STATE_CRITICAL, "Error in recv()");
672         /* return a CRITICAL status if we couldn't read any data */
673         if (pagesize == (size_t) 0)
674                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
676         /* close the connection */
677         my_close ();
679         /* reset the alarm */
680         alarm (0);
682         /* leave full_page untouched so we can free it later */
683         page = full_page;
685         if (verbose)
686                 printf ("Page is %d characters\n", pagesize);
688         /* find status line and null-terminate it */
689         status_line = page;
690         page += (size_t) strcspn (page, "\r\n");
691         pos = page;
692         page += (size_t) strspn (page, "\r\n");
693         status_line[pos - status_line] = 0;
694         strip (status_line);
695         if (verbose)
696                 printf ("STATUS: %s\n", status_line);
698         /* find header info and null terminate it */
699         header = page;
700         while (strcspn (page, "\r\n") > 0) {
701                 page += (size_t) strcspn (page, "\r\n");
702                 pos = page;
703                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
704                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
705                         page += (size_t) 2;
706                 else
707                         page += (size_t) 1;
708         }
709         page += (size_t) strspn (page, "\r\n");
710         header[pos - header] = 0;
711         if (verbose)
712                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
714         /* make sure the status line matches the response we are looking for */
715         if (!strstr (status_line, server_expect)) {
716                 if (server_port == HTTP_PORT)
717                         asprintf (&msg, "Invalid HTTP response received from host\n");
718                 else
719                         asprintf (&msg,
720                                         "Invalid HTTP response received from host on port %d\n",
721                                         server_port);
722                 terminate (STATE_CRITICAL, msg);
723         }
726         /* Exit here if server_expect was set by user and not default */
727         if ( server_expect_yn  )  {
728                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
729                           server_expect);
730                 if (verbose)
731                         printf ("%s\n",msg);
733         }
734         else {
735         
737                 /* check the return code */
738                 /* server errors result in a critical state */
739                 if (strstr (status_line, "500") ||
740                   strstr (status_line, "501") ||
741                 strstr (status_line, "502") ||
742                     strstr (status_line, "503")) {
743                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
744                 }
746                 /* client errors result in a warning state */
747                 if (strstr (status_line, "400") ||
748                   strstr (status_line, "401") ||
749                 strstr (status_line, "402") ||
750                     strstr (status_line, "403") ||
751                     strstr (status_line, "404")) {
752                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
753                 }
755                 /* check redirected page if specified */
756                 if (strstr (status_line, "300") ||
757                   strstr (status_line, "301") ||
758                 strstr (status_line, "302") ||
759                     strstr (status_line, "303") ||
760                     strstr (status_line, "304")) {
761                         if (onredirect == STATE_DEPENDENT) {
763                                 orig_url = strscpy(NULL, server_url);
764                                 pos = header;
765                                 while (pos) {
766                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
767                                         if (server_address == NULL)
768                                                 terminate (STATE_UNKNOWN,
769                                                                                  "HTTP UNKNOWN: could not allocate server_address");
770                                         if (strcspn (pos, "\r\n") > server_url_length) {
771                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
772                                                 if (server_url == NULL)
773                                                         terminate (STATE_UNKNOWN,
774                                                                    "HTTP UNKNOWN: could not allocate server_url");
775                                                 server_url_length = strcspn (pos, "\r\n");
776                                         }
777                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
778                                                 host_name = strscpy (host_name, server_address);
779                                                 use_ssl = server_type_check (server_type);
780                                                 server_port = atoi (server_port_text);
781                                                 check_http ();
782                                         }
783                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
784                                                 host_name = strscpy (host_name, server_address);
785                                                 use_ssl = server_type_check (server_type);
786                                                 server_port = server_port_check (use_ssl);
787                                                 check_http ();
788                                         }
789                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
790                                                 host_name = strscpy (host_name, server_address);
791                                                 strcpy (server_url, "/");
792                                                 use_ssl = server_type_check (server_type);
793                                                 server_port = atoi (server_port_text);
794                                                 check_http ();
795                                         }
796                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
797                                                 host_name = strscpy (host_name, server_address);
798                                                 strcpy (server_url, "/");
799                                                 use_ssl = server_type_check (server_type);
800                                                 server_port = server_port_check (use_ssl);
801                                                 check_http ();
802                                         }
803                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
804                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
805                                                         *x = '\0';
806                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
807                                                 }
808                                                 check_http ();
809                                         }                                       
810                                         pos += (size_t) strcspn (pos, "\r\n");
811                                         pos += (size_t) strspn (pos, "\r\n");
812                                 } /* end while (pos) */
813                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
814                                         status_line, (display_html ? "</A>" : ""));
815                                 exit (STATE_UNKNOWN);
816                         } /* end if (onredirect == STATE_DEPENDENT) */
817                         
818                         else if (onredirect == STATE_UNKNOWN)
819                                 printf ("HTTP UNKNOWN");
820                         else if (onredirect == STATE_OK)
821                                 printf ("HTTP ok");
822                         else if (onredirect == STATE_WARNING)
823                                 printf ("HTTP WARNING");
824                         else if (onredirect == STATE_CRITICAL)
825                                 printf ("HTTP CRITICAL");
826                         elapsed_time = delta_time (tv);
827                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
828                                  status_line, elapsed_time, timestamp,
829                            (display_html ? "</A>" : ""), elapsed_time);
830                         terminate (onredirect, msg);
831                 } /* end if (strstr (status_line, "30[0-4]") */
834         } /* end else (server_expect_yn)  */
836                 
837         /* check elapsed time */
838         elapsed_time = delta_time (tv);
839         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
840                        status_line, elapsed_time, timestamp,
841                        (display_html ? "</A>" : ""), elapsed_time);
842         if (check_critical_time == TRUE && elapsed_time > critical_time)
843                 terminate (STATE_CRITICAL, msg);
844         if (check_warning_time == TRUE && elapsed_time > warning_time)
845                 terminate (STATE_WARNING, msg);
847         /* Page and Header content checks go here */
848         /* these checks should be last */
850         if (strlen (string_expect)) {
851                 if (strstr (page, string_expect)) {
852                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
853                                 status_line, elapsed_time,
854                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
855                         exit (STATE_OK);
856                 }
857                 else {
858                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
859                                 (display_html ? "</A>" : ""), elapsed_time);
860                         exit (STATE_CRITICAL);
861                 }
862         }
863 #ifdef HAVE_REGEX_H
864         if (strlen (regexp)) {
865                 errcode = regexec (&preg, page, REGS, pmatch, 0);
866                 if (errcode == 0) {
867                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
868                                 status_line, elapsed_time,
869                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
870                         exit (STATE_OK);
871                 }
872                 else {
873                         if (errcode == REG_NOMATCH) {
874                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
875                                         (display_html ? "</A>" : ""), elapsed_time);
876                                 exit (STATE_CRITICAL);
877                         }
878                         else {
879                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
880                                 printf ("Execute Error: %s\n", errbuf);
881                                 exit (STATE_CRITICAL);
882                         }
883                 }
884         }
885 #endif
887         /* We only get here if all tests have been passed */
888         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
889                         status_line, (float)elapsed_time,
890                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
891         terminate (STATE_OK, msg);
892         return STATE_UNKNOWN;
897 #ifdef HAVE_SSL
898 int connect_SSL (void)
900         SSL_METHOD *meth;
902         randbuff = strscpy (NULL, "qwertyuiopasdfghjkl");
903         RAND_seed (randbuff, strlen (randbuff));
904         /* Initialize SSL context */
905         SSLeay_add_ssl_algorithms ();
906         meth = SSLv23_client_method ();
907         SSL_load_error_strings ();
908         if ((ctx = SSL_CTX_new (meth)) == NULL) {
909                 printf ("ERROR: Cannot create SSL context.\n");
910                 return STATE_CRITICAL;
911         }
913         /* Initialize alarm signal handling */
914         signal (SIGALRM, socket_timeout_alarm_handler);
916         /* Set socket timeout */
917         alarm (socket_timeout);
919         /* Save start time */
920         gettimeofday (&tv, NULL);
922         /* Make TCP connection */
923         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
924                 /* Do the SSL handshake */
925                 if ((ssl = SSL_new (ctx)) != NULL) {
926                         SSL_set_cipher_list(ssl, "ALL");
927                         SSL_set_fd (ssl, sd);
928                         if (SSL_connect (ssl) != -1)
929                                 return OK;
930                         ERR_print_errors_fp (stderr);
931                 }
932                 else {
933                         printf ("ERROR: Cannot initiate SSL handshake.\n");
934                 }
935                 SSL_free (ssl);
936         }
938         SSL_CTX_free (ctx);
939         close (sd);
941         return STATE_CRITICAL;
943 #endif
945 #ifdef HAVE_SSL
946 int
947 check_certificate (X509 ** certificate)
949         ASN1_STRING *tm;
950         int offset;
951         struct tm stamp;
952         int days_left;
955         /* Retrieve timestamp of certificate */
956         tm = X509_get_notAfter (*certificate);
958         /* Generate tm structure to process timestamp */
959         if (tm->type == V_ASN1_UTCTIME) {
960                 if (tm->length < 10) {
961                         printf ("ERROR: Wrong time format in certificate.\n");
962                         return STATE_CRITICAL;
963                 }
964                 else {
965                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
966                         if (stamp.tm_year < 50)
967                                 stamp.tm_year += 100;
968                         offset = 0;
969                 }
970         }
971         else {
972                 if (tm->length < 12) {
973                         printf ("ERROR: Wrong time format in certificate.\n");
974                         return STATE_CRITICAL;
975                 }
976                 else {
977                         stamp.tm_year =
978                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
979                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
980                         stamp.tm_year -= 1900;
981                         offset = 2;
982                 }
983         }
984         stamp.tm_mon =
985                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
986         stamp.tm_mday =
987                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
988         stamp.tm_hour =
989                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
990         stamp.tm_min =
991                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
992         stamp.tm_sec = 0;
993         stamp.tm_isdst = -1;
995         days_left = (mktime (&stamp) - time (NULL)) / 86400;
996         snprintf
997                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
998                  stamp.tm_mon + 1,
999                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1001         if (days_left > 0 && days_left <= days_till_exp) {
1002                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1003                 return STATE_WARNING;
1004         }
1005         if (days_left < 0) {
1006                 printf ("Certificate expired on %s.\n", timestamp);
1007                 return STATE_CRITICAL;
1008         }
1010         if (days_left == 0) {
1011                 printf ("Certificate expires today (%s).\n", timestamp);
1012                 return STATE_WARNING;
1013         }
1015         printf ("Certificate will expire on %s.\n", timestamp);
1017         return STATE_OK;
1019 #endif
1020 \f
1023 int
1024 my_recv (void)
1026         int i;
1027 #ifdef HAVE_SSL
1028         if (use_ssl) {
1029                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1030         }
1031         else {
1032                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1033         }
1034 #else
1035         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1036 #endif
1037         return i;
1041 int
1042 my_close (void)
1044 #ifdef HAVE_SSL
1045         if (use_ssl == TRUE) {
1046                 SSL_shutdown (ssl);
1047                 SSL_free (ssl);
1048                 SSL_CTX_free (ctx);
1049                 return 0;
1050         }
1051         else {
1052 #endif
1053                 return close (sd);
1054 #ifdef HAVE_SSL
1055         }
1056 #endif
1058 \f
1061 void
1062 print_help (void)
1064         print_revision (PROGNAME, REVISION);
1065         printf
1066                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1067                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1068         print_usage ();
1069         printf ("NOTE: One or both of -H and -I must be specified\n");
1070         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1071                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS);
1072 #ifdef HAVE_SSL
1073         printf (SSLDESCRIPTION);
1074 #endif
1078 void
1079 print_usage (void)
1081         printf ("Usage:\n" " %s %s\n"
1082 #ifdef HAVE_GETOPT_H
1083                 " %s (-h | --help) for detailed help\n"
1084                 " %s (-V | --version) for version information\n",
1085 #else
1086                 " %s -h for detailed help\n"
1087                 " %s -V for version information\n",
1088 #endif
1089         PROGNAME, OPTIONS, PROGNAME, PROGNAME);