Code

patch attempt to reference undefined optarg (credit Paul Farrall)
[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] [-m min_pg_size] [-4|-6]"
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  -4, --use-ipv4\n\
56    Use IPv4 protocol\n\
57  -6, --use-ipv6\n\
58    Use IPv6 protocol\n\
59  -e, --expect=STRING\n\
60    String to expect in first (status) line of server response (default: %s)\n\
61    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
62  -s, --string=STRING\n\
63    String to expect in the content\n\
64  -u, --url=PATH\n\
65    URL to GET or POST (default: /)\n\
66  -p, --port=INTEGER\n\
67    Port number (default: %d)\n\
68  -P, --post=STRING\n\
69    URL encoded http POST data\n\
70  -w, --warning=INTEGER\n\
71    Response time to result in warning status (seconds)\n\
72  -c, --critical=INTEGER\n\
73    Response time to result in critical status (seconds)\n\
74  -t, --timeout=INTEGER\n\
75    Seconds before connection times out (default: %d)\n\
76  -a, --authorization=AUTH_PAIR\n\
77    Username:password on sites with basic authentication\n\
78  -L, --link=URL\n\
79    Wrap output in HTML link (obsoleted by urlize)\n\
80  -f, --onredirect=<ok|warning|critical|follow>\n\
81    How to handle redirected pages\n%s%s\
82 -m, --min=INTEGER\n\
83    Minimum page size required (bytes)\n\
84  -v, --verbose\n\
85     Show details for command-line debugging (do not use with nagios server)\n\
86  -h, --help\n\
87     Print detailed help screen\n\
88  -V, --version\n\
89     Print version information\n"
91 #ifdef HAVE_SSL
92 #define SSLOPTIONS "\
93  -S, --ssl\n\
94     Connect via SSL\n\
95  -C, --certificate=INTEGER\n\
96     Minimum number of days a certificate has to be valid.\n\
97     (when this option is used the url is not checked.)\n"
98 #else
99 #define SSLOPTIONS ""
100 #endif
102 #ifdef HAVE_REGEX_H
103 #define REGOPTIONS "\
104  -l, --linespan\n\
105     Allow regex to span newlines (must precede -r or -R)\n\
106  -r, --regex, --ereg=STRING\n\
107     Search page for regex STRING\n\
108  -R, --eregi=STRING\n\
109     Search page for case-insensitive regex STRING\n"
110 #else
111 #define REGOPTIONS ""
112 #endif
114 #define DESCRIPTION "\
115 This plugin will attempt to open an HTTP connection with the host. Successul\n\
116 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
117 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
118 messages from the host result in STATE_WARNING return values.  If you are\n\
119 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
120 \(fully qualified domain name) as the [host_name] argument.\n"
122 #define SSLDESCRIPTION "\
123 This plugin can also check whether an SSL enabled web server is able to\n\
124 serve content (optionally within a specified time) or whether the X509 \n\
125 certificate is still valid for the specified number of days.\n\n\
126 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
127 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
128 STATE_OK will be returned. When the server returns its content but exceeds\n\
129 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
130 a STATE_CRITICAL will be returned.\n\n\
131 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
132 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
133 STATE_OK is returned. When the certificate is still valid, but for less than\n\
134 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
135 the certificate is expired.\n"
137 #ifdef HAVE_SSL_H
138 #include <rsa.h>
139 #include <crypto.h>
140 #include <x509.h>
141 #include <pem.h>
142 #include <ssl.h>
143 #include <err.h>
144 #include <rand.h>
145 #endif
147 #ifdef HAVE_OPENSSL_SSL_H
148 #include <openssl/rsa.h>
149 #include <openssl/crypto.h>
150 #include <openssl/x509.h>
151 #include <openssl/pem.h>
152 #include <openssl/ssl.h>
153 #include <openssl/err.h>
154 #include <openssl/rand.h>
155 #endif
157 #ifdef HAVE_SSL
158 int check_cert = FALSE;
159 int days_till_exp;
160 char *randbuff = "";
161 SSL_CTX *ctx;
162 SSL *ssl;
163 X509 *server_cert;
164 int connect_SSL (void);
165 int check_certificate (X509 **);
166 #endif
168 #ifdef HAVE_REGEX_H
169 enum {
170         REGS = 2,
171         MAX_RE_SIZE = 256
172 };
173 #include <regex.h>
174 regex_t preg;
175 regmatch_t pmatch[REGS];
176 char regexp[MAX_RE_SIZE];
177 char errbuf[MAX_INPUT_BUFFER];
178 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
179 int errcode;
180 #endif
182 struct timeval tv;
184 #define server_type_check(server_type) \
185 (strcmp (server_type, "https") ? FALSE : TRUE)
187 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
189 /* per RFC 2396 */
190  
191 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
192 #define URI_HTTP "%[HTPShtps]://"
193 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
194 #define URI_PORT ":%[0123456789]"
195 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
197 enum {
198         MAX_IPV4_HOSTLENGTH = 255,
199         HTTP_PORT = 80,
200         HTTPS_PORT = 443
201 };
203 #define HTTP_EXPECT "HTTP/1."
204 #define HTTP_URL "/"
205 #define CRLF "\r\n"
207 char timestamp[17] = "";
208 int specify_port = FALSE;
209 int server_port = HTTP_PORT;
210 char server_port_text[6] = "";
211 char server_type[6] = "http";
212 char *server_address = ""; 
213 char *host_name = "";
214 char *server_url = "";
215 int server_url_length;
216 int server_expect_yn = 0;
217 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
218 char string_expect[MAX_INPUT_BUFFER] = "";
219 double warning_time = 0;
220 int check_warning_time = FALSE;
221 double critical_time = 0;
222 int check_critical_time = FALSE;
223 char user_auth[MAX_INPUT_BUFFER] = "";
224 int display_html = FALSE;
225 int onredirect = STATE_OK;
226 int use_ssl = FALSE;
227 int verbose = FALSE;
228 int sd;
229 int min_page_len = 0;
230 char *http_method = "GET";
231 char *http_post_data = "";
232 char buffer[MAX_INPUT_BUFFER];
234 void print_usage (void);
235 void print_help (void);
236 int process_arguments (int, char **);
237 static char *base64 (char *bin, int len);
238 int check_http (void);
239 int my_recv (void);
240 int my_close (void);
242 int
243 main (int argc, char **argv)
245         int result = STATE_UNKNOWN;
247         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
248         asprintf (&server_url, "%s", HTTP_URL);
249         server_url_length = strlen(server_url);
251         if (process_arguments (argc, argv) == ERROR)
252                 usage ("check_http: could not parse arguments\n");
254         if (strstr (timestamp, ":")) {
255                 if (strstr (server_url, "?"))
256                         asprintf (&server_url, "%s&%s", server_url, timestamp);
257                 else
258                         asprintf (&server_url, "%s?%s", server_url, timestamp);
259         }
261         if (display_html == TRUE)
262                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
263                         host_name, server_port, server_url);
265         /* initialize alarm signal handling, set socket timeout, start timer */
266         (void) signal (SIGALRM, socket_timeout_alarm_handler);
267         (void) alarm (socket_timeout);
268         gettimeofday (&tv, NULL);
270 #ifdef HAVE_SSL
271         if (use_ssl && check_cert == TRUE) {
272                 if (connect_SSL () != OK)
273                         terminate (STATE_CRITICAL,
274                                    "HTTP CRITICAL - Could not make SSL connection\n");
275                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
276                         result = check_certificate (&server_cert);
277                         X509_free (server_cert);
278                 }
279                 else {
280                         printf ("ERROR: Cannot retrieve server certificate.\n");
281                         result = STATE_CRITICAL;
282                 }
283                 SSL_shutdown (ssl);
284                 SSL_free (ssl);
285                 SSL_CTX_free (ctx);
286                 close (sd);
287         }
288         else {
289                 result = check_http ();
290         }
291 #else
292         result = check_http ();
293 #endif
294         return result;
296 \f
299 /* process command-line arguments */
300 int
301 process_arguments (int argc, char **argv)
303         int c = 1;
305         int option_index = 0;
306         static struct option long_options[] = {
307                 STD_LONG_OPTS,
308                 {"file",required_argument,0,'F'},
309                 {"link", no_argument, 0, 'L'},
310                 {"nohtml", no_argument, 0, 'n'},
311                 {"ssl", no_argument, 0, 'S'},
312                 {"verbose", no_argument, 0, 'v'},
313                 {"post", required_argument, 0, 'P'},
314                 {"IP-address", required_argument, 0, 'I'},
315                 {"string", required_argument, 0, 's'},
316                 {"regex", required_argument, 0, 'r'},
317                 {"ereg", required_argument, 0, 'r'},
318                 {"eregi", required_argument, 0, 'R'},
319                 {"linespan", no_argument, 0, 'l'},
320                 {"onredirect", required_argument, 0, 'f'},
321                 {"certificate", required_argument, 0, 'C'},
322                 {"min", required_argument, 0, 'm'},
323                 {"use-ipv4", no_argument, 0, '4'},
324                 {"use-ipv6", no_argument, 0, '6'},
325                 {0, 0, 0, 0}
326         };
328         if (argc < 2)
329                 return ERROR;
331         for (c = 1; c < argc; c++) {
332                 if (strcmp ("-to", argv[c]) == 0)
333                         strcpy (argv[c], "-t");
334                 if (strcmp ("-hn", argv[c]) == 0)
335                         strcpy (argv[c], "-H");
336                 if (strcmp ("-wt", argv[c]) == 0)
337                         strcpy (argv[c], "-w");
338                 if (strcmp ("-ct", argv[c]) == 0)
339                         strcpy (argv[c], "-c");
340                 if (strcmp ("-nohtml", argv[c]) == 0)
341                         strcpy (argv[c], "-n");
342         }
344         while (1) {
345                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", long_options, &option_index);
346                 if (c == -1 || c == EOF)
347                         break;
349                 switch (c) {
350                 case '?': /* usage */
351                         usage3 ("unknown argument", optopt);
352                         break;
353                 case 'h': /* help */
354                         print_help ();
355                         exit (STATE_OK);
356                         break;
357                 case 'V': /* version */
358                         print_revision (progname, REVISION);
359                         exit (STATE_OK);
360                         break;
361                 case 't': /* timeout period */
362                         if (!is_intnonneg (optarg))
363                                 usage2 ("timeout interval must be a non-negative integer", optarg);
364                         socket_timeout = atoi (optarg);
365                         break;
366                 case 'c': /* critical time threshold */
367                         if (!is_intnonneg (optarg))
368                                 usage2 ("invalid critical threshold", optarg);
369                         critical_time = strtod (optarg, NULL);
370                         check_critical_time = TRUE;
371                         break;
372                 case 'w': /* warning time threshold */
373                         if (!is_intnonneg (optarg))
374                                 usage2 ("invalid warning threshold", optarg);
375                         warning_time = strtod (optarg, NULL);
376                         check_warning_time = TRUE;
377                         break;
378                 case 'L': /* show html link */
379                         display_html = TRUE;
380                         break;
381                 case 'n': /* do not show html link */
382                         display_html = FALSE;
383                         break;
384                 case 'S': /* use SSL */
385 #ifndef HAVE_SSL
386                         usage ("check_http: invalid option - SSL is not available\n");
387 #endif
388                         use_ssl = TRUE;
389                         if (specify_port == FALSE)
390                                 server_port = HTTPS_PORT;
391                         break;
392                 case 'C': /* Check SSL cert validity */
393 #ifdef HAVE_SSL
394                         if (!is_intnonneg (optarg))
395                                 usage2 ("invalid certificate expiration period", optarg);
396                         days_till_exp = atoi (optarg);
397                         check_cert = TRUE;
398 #else
399                         usage ("check_http: invalid option - SSL is not available\n");
400 #endif
401                         break;
402                 case 'f': /* onredirect */
403                         if (!strcmp (optarg, "follow"))
404                                 onredirect = STATE_DEPENDENT;
405                         if (!strcmp (optarg, "unknown"))
406                                 onredirect = STATE_UNKNOWN;
407                         if (!strcmp (optarg, "ok"))
408                                 onredirect = STATE_OK;
409                         if (!strcmp (optarg, "warning"))
410                                 onredirect = STATE_WARNING;
411                         if (!strcmp (optarg, "critical"))
412                                 onredirect = STATE_CRITICAL;
413                         if (verbose)
414                                 printf("option f:%d \n", onredirect);  
415                         break;
416                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
417                 case 'H': /* Host Name (virtual host) */
418                         asprintf (&host_name, "%s", optarg);
419                         break;
420                 case 'I': /* Server IP-address */
421                         asprintf (&server_address, "%s", optarg);
422                         break;
423                 case 'u': /* Host or server */
424                         asprintf (&server_url, "%s", optarg);
425                         server_url_length = strlen (server_url);
426                         break;
427                 case 'p': /* Host or server */
428                         if (!is_intnonneg (optarg))
429                                 usage2 ("invalid port number", optarg);
430                         server_port = atoi (optarg);
431                         specify_port = TRUE;
432                         break;
433                 case 'a': /* authorization info */
434                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
435                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
436                         break;
437                 case 'P': /* HTTP POST data in URL encoded format */
438                         asprintf (&http_method, "%s", "POST");
439                         asprintf (&http_post_data, "%s", optarg);
440                         break;
441                 case 's': /* string or substring */
442                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
443                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
444                         break;
445                 case 'e': /* string or substring */
446                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
447                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
448                         server_expect_yn = 1;
449                         break;
450 #ifndef HAVE_REGEX_H
451                 case 'l': /* linespan */
452                 case 'r': /* linespan */
453                 case 'R': /* linespan */
454                         usage ("check_http: call for regex which was not a compiled option\n");
455                         break;
456 #else
457                 case 'l': /* linespan */
458                         cflags &= ~REG_NEWLINE;
459                         break;
460                 case 'R': /* regex */
461                         cflags |= REG_ICASE;
462                 case 'r': /* regex */
463                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
464                         regexp[MAX_RE_SIZE - 1] = 0;
465                         errcode = regcomp (&preg, regexp, cflags);
466                         if (errcode != 0) {
467                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
468                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
469                                 return ERROR;
470                         }
471                         break;
472 #endif
473                 case '4':
474                         address_family = AF_INET;
475                         break;
476                 case '6':
477 #ifdef USE_IPV6
478                         address_family = AF_INET6;
479 #else
480                         usage ("IPv6 support not available\n");
481 #endif
482                         break;
483                 case 'v': /* verbose */
484                         verbose = TRUE;
485                         break;
486                 case 'm': /* min_page_length */
487                         min_page_len = atoi (optarg);
488                         break;
489                 }
490         }
492         c = optind;
494         if (strcmp (server_address, "") == 0 && c < argc)
495                         asprintf (&server_address, "%s", argv[c++]);
497         if (strcmp (host_name, "") == 0 && c < argc)
498                 asprintf (&host_name, "%s", argv[c++]);
500         if (strcmp (server_address ,"") == 0) {
501                 if (strcmp (host_name, "") == 0)
502                         usage ("check_http: you must specify a server address or host name\n");
503                 else
504                         asprintf (&server_address, "%s", host_name);
505         }
507         if (check_critical_time && critical_time>(double)socket_timeout)
508                 socket_timeout = (int)critical_time + 1;
510         return TRUE;
512 \f
515 /* written by lauri alanko */
516 static char *
517 base64 (char *bin, int len)
520         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
521         int i = 0, j = 0;
523         char BASE64_END = '=';
524         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
526         while (j < len - 2) {
527                 buf[i++] = base64_table[bin[j] >> 2];
528                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
529                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
530                 buf[i++] = base64_table[bin[j + 2] & 63];
531                 j += 3;
532         }
534         switch (len - j) {
535         case 1:
536                 buf[i++] = base64_table[bin[j] >> 2];
537                 buf[i++] = base64_table[(bin[j] & 3) << 4];
538                 buf[i++] = BASE64_END;
539                 buf[i++] = BASE64_END;
540                 break;
541         case 2:
542                 buf[i++] = base64_table[bin[j] >> 2];
543                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
544                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
545                 buf[i++] = BASE64_END;
546                 break;
547         case 0:
548                 break;
549         }
551         buf[i] = '\0';
552         return buf;
554 \f
557 int
558 check_http (void)
560         char *msg = NULL;
561         char *status_line = "";
562         char *header = NULL;
563         char *page = "";
564         char *auth = NULL;
565         int i = 0;
566         size_t pagesize = 0;
567         char *full_page = "";
568         char *buf = NULL;
569         char *pos = "";
570         char *x = NULL;
571         char *orig_url = NULL;
572         double elapsed_time;
573         int page_len = 0;
574 #ifdef HAVE_SSL
575         int sslerr;
576 #endif
578         /* try to connect to the host at the given port number */
579 #ifdef HAVE_SSL
580         if (use_ssl == TRUE) {
582                 if (connect_SSL () != OK) {
583                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
584                 }
586                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
587                         X509_free (server_cert);
588                 }
589                 else {
590                         printf ("ERROR: Cannot retrieve server certificate.\n");
591                         return STATE_CRITICAL;
592                 }
594         }
595         else {
596 #endif
597                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
598                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
599 #ifdef HAVE_SSL
600         }
601 #endif
603         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
605         /* optionally send the host header info (not clear if it's usable) */
606         if (strcmp (host_name, ""))
607                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
609         /* send user agent */
610         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
611                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
613         /* optionally send the authentication info */
614         if (strcmp (user_auth, "")) {
615                 auth = base64 (user_auth, strlen (user_auth));
616                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
617         }
619         /* either send http POST data */
620         if (strlen (http_post_data)) {
621                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
622                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
623                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
624         }
625         else {
626                 /* or just a newline so the server knows we're done with the request */
627                 asprintf (&buf, "%s%s", buf, CRLF);
628         }
630 #ifdef HAVE_SSL
631         if (use_ssl == TRUE) {
632                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
633                         ERR_print_errors_fp (stderr);
634                         return STATE_CRITICAL;
635                 }
636         }
637         else {
638 #endif
639                 send (sd, buf, strlen (buf), 0);
640 #ifdef HAVE_SSL
641         }
642 #endif
644         /* fetch the page */
645         while ((i = my_recv ()) > 0) {
646                 buffer[i] = '\0';
647                 asprintf (&full_page, "%s%s", full_page, buffer);
648                 pagesize += i;
649         }
651         if (i < 0 && errno != ECONNRESET) {
652 #ifdef HAVE_SSL
653                 if (use_ssl) {
654                         sslerr=SSL_get_error(ssl, i);
655                         if ( sslerr == SSL_ERROR_SSL ) {
656                                 terminate (STATE_WARNING, "Client Certificate Required\n");
657                         } else {
658                                 terminate (STATE_CRITICAL, "Error in recv()");
659                         }
660                 }
661                 else {
662 #endif
663                         terminate (STATE_CRITICAL, "Error in recv()");
664 #ifdef HAVE_SSL
665                 }
666 #endif
667         }
669         /* return a CRITICAL status if we couldn't read any data */
670         if (pagesize == (size_t) 0)
671                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
673         /* close the connection */
674         my_close ();
676         /* reset the alarm */
677         alarm (0);
679         /* leave full_page untouched so we can free it later */
680         page = full_page;
682         if (verbose)
683                 printf ("Page is %d characters\n", pagesize);
685         /* find status line and null-terminate it */
686         status_line = page;
687         page += (size_t) strcspn (page, "\r\n");
688         pos = page;
689         page += (size_t) strspn (page, "\r\n");
690         status_line[strcspn(status_line, "\r\n")] = 0;
691         strip (status_line);
692         if (verbose)
693                 printf ("STATUS: %s\n", status_line);
695         /* find header info and null terminate it */
696         header = page;
697         while (strcspn (page, "\r\n") > 0) {
698                 page += (size_t) strcspn (page, "\r\n");
699                 pos = page;
700                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
701                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
702                         page += (size_t) 2;
703                 else
704                         page += (size_t) 1;
705         }
706         page += (size_t) strspn (page, "\r\n");
707         header[pos - header] = 0;
708         if (verbose)
709                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
711         /* make sure the status line matches the response we are looking for */
712         if (!strstr (status_line, server_expect)) {
713                 if (server_port == HTTP_PORT)
714                         asprintf (&msg, "Invalid HTTP response received from host\n");
715                 else
716                         asprintf (&msg,
717                                         "Invalid HTTP response received from host on port %d\n",
718                                         server_port);
719                 terminate (STATE_CRITICAL, msg);
720         }
723         /* Exit here if server_expect was set by user and not default */
724         if ( server_expect_yn  )  {
725                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
726                           server_expect);
727                 if (verbose)
728                         printf ("%s\n",msg);
730         }
731         else {
732         
734                 /* check the return code */
735                 /* server errors result in a critical state */
736                 if (strstr (status_line, "500") ||
737                   strstr (status_line, "501") ||
738                 strstr (status_line, "502") ||
739                     strstr (status_line, "503")) {
740                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
741                 }
743                 /* client errors result in a warning state */
744                 if (strstr (status_line, "400") ||
745                   strstr (status_line, "401") ||
746                 strstr (status_line, "402") ||
747                     strstr (status_line, "403") ||
748                     strstr (status_line, "404")) {
749                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
750                 }
752                 /* check redirected page if specified */
753                 if (strstr (status_line, "300") ||
754                   strstr (status_line, "301") ||
755                 strstr (status_line, "302") ||
756                     strstr (status_line, "303") ||
757                     strstr (status_line, "304")) {
758                         if (onredirect == STATE_DEPENDENT) {
760                                 asprintf (&orig_url, "%s", server_url);
761                                 pos = header;
762                                 while (pos) {
763                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
764                                         if (server_address == NULL)
765                                                 terminate (STATE_UNKNOWN,
766                                                                                  "HTTP UNKNOWN: could not allocate server_address");
767                                         if (strcspn (pos, "\r\n") > server_url_length) {
768                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
769                                                 if (server_url == NULL)
770                                                         terminate (STATE_UNKNOWN,
771                                                                    "HTTP UNKNOWN: could not allocate server_url");
772                                                 server_url_length = strcspn (pos, "\r\n");
773                                         }
774                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
775                                                 asprintf (&host_name, "%s", server_address);
776                                                 use_ssl = server_type_check (server_type);
777                                                 server_port = atoi (server_port_text);
778                                                 check_http ();
779                                         }
780                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
781                                                 asprintf (&host_name, "%s", server_address);
782                                                 use_ssl = server_type_check (server_type);
783                                                 server_port = server_port_check (use_ssl);
784                                                 check_http ();
785                                         }
786                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
787                                                 asprintf (&host_name, "%s", server_address);
788                                                 strcpy (server_url, "/");
789                                                 use_ssl = server_type_check (server_type);
790                                                 server_port = atoi (server_port_text);
791                                                 check_http ();
792                                         }
793                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
794                                                 asprintf (&host_name, "%s", server_address);
795                                                 strcpy (server_url, "/");
796                                                 use_ssl = server_type_check (server_type);
797                                                 server_port = server_port_check (use_ssl);
798                                                 check_http ();
799                                         }
800                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
801                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
802                                                         *x = '\0';
803                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
804                                                 }
805                                                 check_http ();
806                                         }                                       
807                                         pos += (size_t) strcspn (pos, "\r\n");
808                                         pos += (size_t) strspn (pos, "\r\n");
809                                 } /* end while (pos) */
810                                 printf ("UNKNOWN - Could not find redirect location - %s%s",
811                                         status_line, (display_html ? "</A>" : ""));
812                                 exit (STATE_UNKNOWN);
813                         } /* end if (onredirect == STATE_DEPENDENT) */
814                         
815                         else if (onredirect == STATE_UNKNOWN)
816                                 printf ("UNKNOWN");
817                         else if (onredirect == STATE_OK)
818                                 printf ("OK");
819                         else if (onredirect == STATE_WARNING)
820                                 printf ("WARNING");
821                         else if (onredirect == STATE_CRITICAL)
822                                 printf ("CRITICAL");
823                         elapsed_time = delta_time (tv);
824                         asprintf (&msg, " - %s - %.3f second response time %s%s|time=%.3f\n",
825                                  status_line, elapsed_time, timestamp,
826                            (display_html ? "</A>" : ""), elapsed_time);
827                         terminate (onredirect, msg);
828                 } /* end if (strstr (status_line, "30[0-4]") */
831         } /* end else (server_expect_yn)  */
833                 
834         /* check elapsed time */
835         elapsed_time = delta_time (tv);
836         asprintf (&msg, "HTTP problem: %s - %.3f second response time %s%s|time=%.3f\n",
837                        status_line, elapsed_time, timestamp,
838                        (display_html ? "</A>" : ""), elapsed_time);
839         if (check_critical_time == TRUE && elapsed_time > critical_time)
840                 terminate (STATE_CRITICAL, msg);
841         if (check_warning_time == TRUE && elapsed_time > warning_time)
842                 terminate (STATE_WARNING, msg);
844         /* Page and Header content checks go here */
845         /* these checks should be last */
847         if (strlen (string_expect)) {
848                 if (strstr (page, string_expect)) {
849                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
850                                 status_line, elapsed_time,
851                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
852                         exit (STATE_OK);
853                 }
854                 else {
855                         printf ("CRITICAL - string not found%s|time=%.3f\n",
856                                 (display_html ? "</A>" : ""), elapsed_time);
857                         exit (STATE_CRITICAL);
858                 }
859         }
860 #ifdef HAVE_REGEX_H
861         if (strlen (regexp)) {
862                 errcode = regexec (&preg, page, REGS, pmatch, 0);
863                 if (errcode == 0) {
864                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
865                                 status_line, elapsed_time,
866                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
867                         exit (STATE_OK);
868                 }
869                 else {
870                         if (errcode == REG_NOMATCH) {
871                                 printf ("CRITICAL - pattern not found%s|time=%.3f\n",
872                                         (display_html ? "</A>" : ""), elapsed_time);
873                                 exit (STATE_CRITICAL);
874                         }
875                         else {
876                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
877                                 printf ("CRITICAL - Execute Error: %s\n", errbuf);
878                                 exit (STATE_CRITICAL);
879                         }
880                 }
881         }
882 #endif
884         /* make sure the page is of an appropriate size */
885         page_len = strlen (page);
886         if ((min_page_len > 0) && (page_len < min_page_len)) {
887                 printf ("HTTP WARNING: page size too small%s|size=%i\n",
888                         (display_html ? "</A>" : ""), page_len );
889                 exit (STATE_WARNING);
890         }
891         /* We only get here if all tests have been passed */
892         asprintf (&msg, "HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
893                         status_line, (float)elapsed_time,
894                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
895         terminate (STATE_OK, msg);
896         return STATE_UNKNOWN;
901 #ifdef HAVE_SSL
902 int connect_SSL (void)
904         SSL_METHOD *meth;
906         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
907         RAND_seed (randbuff, strlen (randbuff));
908         if (verbose)
909                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
911         /* Initialize SSL context */
912         SSLeay_add_ssl_algorithms ();
913         meth = SSLv23_client_method ();
914         SSL_load_error_strings ();
915         if ((ctx = SSL_CTX_new (meth)) == NULL) {
916                 printf ("CRITICAL -  Cannot create SSL context.\n");
917                 return STATE_CRITICAL;
918         }
920         /* Initialize alarm signal handling */
921         signal (SIGALRM, socket_timeout_alarm_handler);
923         /* Set socket timeout */
924         alarm (socket_timeout);
926         /* Save start time */
927         gettimeofday (&tv, NULL);
929         /* Make TCP connection */
930         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
931                 /* Do the SSL handshake */
932                 if ((ssl = SSL_new (ctx)) != NULL) {
933                         SSL_set_cipher_list(ssl, "ALL");
934                         SSL_set_fd (ssl, sd);
935                         if (SSL_connect (ssl) != -1)
936                                 return OK;
937                         ERR_print_errors_fp (stderr);
938                 }
939                 else {
940                         printf ("CRITICAL - Cannot initiate SSL handshake.\n");
941                 }
942                 SSL_free (ssl);
943         }
945         SSL_CTX_free (ctx);
946         close (sd);
948         return STATE_CRITICAL;
950 #endif
952 #ifdef HAVE_SSL
953 int
954 check_certificate (X509 ** certificate)
956         ASN1_STRING *tm;
957         int offset;
958         struct tm stamp;
959         int days_left;
962         /* Retrieve timestamp of certificate */
963         tm = X509_get_notAfter (*certificate);
965         /* Generate tm structure to process timestamp */
966         if (tm->type == V_ASN1_UTCTIME) {
967                 if (tm->length < 10) {
968                         printf ("CRITICAL - Wrong time format in certificate.\n");
969                         return STATE_CRITICAL;
970                 }
971                 else {
972                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
973                         if (stamp.tm_year < 50)
974                                 stamp.tm_year += 100;
975                         offset = 0;
976                 }
977         }
978         else {
979                 if (tm->length < 12) {
980                         printf ("CRITICAL - Wrong time format in certificate.\n");
981                         return STATE_CRITICAL;
982                 }
983                 else {
984                         stamp.tm_year =
985                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
986                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
987                         stamp.tm_year -= 1900;
988                         offset = 2;
989                 }
990         }
991         stamp.tm_mon =
992                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
993         stamp.tm_mday =
994                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
995         stamp.tm_hour =
996                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
997         stamp.tm_min =
998                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
999         stamp.tm_sec = 0;
1000         stamp.tm_isdst = -1;
1002         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1003         snprintf
1004                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
1005                  stamp.tm_mon + 1,
1006                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1008         if (days_left > 0 && days_left <= days_till_exp) {
1009                 printf ("WARNING - Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
1010                 return STATE_WARNING;
1011         }
1012         if (days_left < 0) {
1013                 printf ("CRITICAL - Certificate expired on %s.\n", timestamp);
1014                 return STATE_CRITICAL;
1015         }
1017         if (days_left == 0) {
1018                 printf ("WARNING - Certificate expires today (%s).\n", timestamp);
1019                 return STATE_WARNING;
1020         }
1022         printf ("OK - Certificate will expire on %s.\n", timestamp);
1024         return STATE_OK;
1026 #endif
1027 \f
1030 int
1031 my_recv (void)
1033         int i;
1034 #ifdef HAVE_SSL
1035         if (use_ssl) {
1036                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1037         }
1038         else {
1039                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1040         }
1041 #else
1042         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1043 #endif
1044         return i;
1048 int
1049 my_close (void)
1051 #ifdef HAVE_SSL
1052         if (use_ssl == TRUE) {
1053                 SSL_shutdown (ssl);
1054                 SSL_free (ssl);
1055                 SSL_CTX_free (ctx);
1056                 return 0;
1057         }
1058         else {
1059 #endif
1060                 return close (sd);
1061 #ifdef HAVE_SSL
1062         }
1063 #endif
1065 \f
1068 void
1069 print_help (void)
1071         print_revision (progname, REVISION);
1072         printf
1073                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1074                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1075         print_usage ();
1076         printf ("NOTE: One or both of -H and -I must be specified\n");
1077         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1078                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1079 #ifdef HAVE_SSL
1080         printf (SSLDESCRIPTION);
1081 #endif
1085 void
1086 print_usage (void)
1088         printf ("\
1089 Usage:\n\
1090  %s %s\n\
1091  %s (-h | --help) for detailed help\n\
1092  %s (-V | --version) for version information\n",
1093         progname, OPTIONS, progname, progname);