Code

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