Code

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