Code

check_http min size option (680467 - Dave Viner)
[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]"
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 -m, --min=INTEGER\n\
79    Minimum page size required (bytes)\n\
80  -v, --verbose\n\
81     Show details for command-line debugging (do not use with nagios server)\n\
82  -h, --help\n\
83     Print detailed help screen\n\
84  -V, --version\n\
85     Print version information\n"
87 #ifdef HAVE_SSL
88 #define SSLOPTIONS "\
89  -S, --ssl\n\
90     Connect via SSL\n\
91  -C, --certificate=INTEGER\n\
92     Minimum number of days a certificate has to be valid.\n\
93     (when this option is used the url is not checked.)\n"
94 #else
95 #define SSLOPTIONS ""
96 #endif
98 #ifdef HAVE_REGEX_H
99 #define REGOPTIONS "\
100  -l, --linespan\n\
101     Allow regex to span newlines (must precede -r or -R)\n\
102  -r, --regex, --ereg=STRING\n\
103     Search page for regex STRING\n\
104  -R, --eregi=STRING\n\
105     Search page for case-insensitive regex STRING\n"
106 #else
107 #define REGOPTIONS ""
108 #endif
110 #define DESCRIPTION "\
111 This plugin will attempt to open an HTTP connection with the host. Successul\n\
112 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
113 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
114 messages from the host result in STATE_WARNING return values.  If you are\n\
115 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
116 \(fully qualified domain name) as the [host_name] argument.\n"
118 #define SSLDESCRIPTION "\
119 This plugin can also check whether an SSL enabled web server is able to\n\
120 serve content (optionally within a specified time) or whether the X509 \n\
121 certificate is still valid for the specified number of days.\n\n\
122 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
123 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
124 STATE_OK will be returned. When the server returns its content but exceeds\n\
125 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
126 a STATE_CRITICAL will be returned.\n\n\
127 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
128 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
129 STATE_OK is returned. When the certificate is still valid, but for less than\n\
130 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
131 the certificate is expired.\n"
133 #ifdef HAVE_SSL_H
134 #include <rsa.h>
135 #include <crypto.h>
136 #include <x509.h>
137 #include <pem.h>
138 #include <ssl.h>
139 #include <err.h>
140 #include <rand.h>
141 #endif
143 #ifdef HAVE_OPENSSL_SSL_H
144 #include <openssl/rsa.h>
145 #include <openssl/crypto.h>
146 #include <openssl/x509.h>
147 #include <openssl/pem.h>
148 #include <openssl/ssl.h>
149 #include <openssl/err.h>
150 #include <openssl/rand.h>
151 #endif
153 #ifdef HAVE_SSL
154 int check_cert = FALSE;
155 int days_till_exp;
156 char *randbuff = "";
157 SSL_CTX *ctx;
158 SSL *ssl;
159 X509 *server_cert;
160 int connect_SSL (void);
161 int check_certificate (X509 **);
162 #endif
164 #ifdef HAVE_REGEX_H
165 enum {
166         REGS = 2,
167         MAX_RE_SIZE = 256
168 };
169 #include <regex.h>
170 regex_t preg;
171 regmatch_t pmatch[REGS];
172 char regexp[MAX_RE_SIZE];
173 char errbuf[MAX_INPUT_BUFFER];
174 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
175 int errcode;
176 #endif
178 struct timeval tv;
180 #define server_type_check(server_type) \
181 (strcmp (server_type, "https") ? FALSE : TRUE)
183 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
185 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
186 #define URI_HTTP "%[HTPShtps]://"
187 #define URI_HOST "%[a-zA-Z0-9.-]"
188 #define URI_PORT ":%[0-9]"
189 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
191 enum {
192         MAX_IPV4_HOSTLENGTH = 64,
193         HTTP_PORT = 80,
194         HTTPS_PORT = 443
195 };
197 #define HTTP_EXPECT "HTTP/1."
198 #define HTTP_URL "/"
199 #define CRLF "\r\n"
201 char timestamp[17] = "";
202 int specify_port = FALSE;
203 int server_port = HTTP_PORT;
204 char server_port_text[6] = "";
205 char server_type[6] = "http";
206 char *server_address = ""; 
207 char *host_name = "";
208 char *server_url = "";
209 int server_url_length;
210 int server_expect_yn = 0;
211 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
212 char string_expect[MAX_INPUT_BUFFER] = "";
213 double warning_time = 0;
214 int check_warning_time = FALSE;
215 double critical_time = 0;
216 int check_critical_time = FALSE;
217 char user_auth[MAX_INPUT_BUFFER] = "";
218 int display_html = FALSE;
219 int onredirect = STATE_OK;
220 int use_ssl = FALSE;
221 int verbose = FALSE;
222 int sd;
223 int min_page_len = 0;
224 char *http_method = "GET";
225 char *http_post_data = "";
226 char buffer[MAX_INPUT_BUFFER];
228 void print_usage (void);
229 void print_help (void);
230 int process_arguments (int, char **);
231 static char *base64 (char *bin, int len);
232 int check_http (void);
233 int my_recv (void);
234 int my_close (void);
236 int
237 main (int argc, char **argv)
239         int result = STATE_UNKNOWN;
241         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
242         asprintf (&server_url, "%s", HTTP_URL);
243         server_url_length = strlen(server_url);
245         if (process_arguments (argc, argv) == ERROR)
246                 usage ("check_http: could not parse arguments\n");
248         if (strstr (timestamp, ":")) {
249                 if (strstr (server_url, "?"))
250                         asprintf (&server_url, "%s&%s", server_url, timestamp);
251                 else
252                         asprintf (&server_url, "%s?%s", server_url, timestamp);
253         }
255         if (display_html == TRUE)
256                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
257                         host_name, server_port, server_url);
259         /* initialize alarm signal handling, set socket timeout, start timer */
260         (void) signal (SIGALRM, socket_timeout_alarm_handler);
261         (void) alarm (socket_timeout);
262         gettimeofday (&tv, NULL);
264 #ifdef HAVE_SSL
265         if (use_ssl && check_cert == TRUE) {
266                 if (connect_SSL () != OK)
267                         terminate (STATE_CRITICAL,
268                                    "HTTP CRITICAL - Could not make SSL connection\n");
269                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
270                         result = check_certificate (&server_cert);
271                         X509_free (server_cert);
272                 }
273                 else {
274                         printf ("ERROR: Cannot retrieve server certificate.\n");
275                         result = STATE_CRITICAL;
276                 }
277                 SSL_shutdown (ssl);
278                 SSL_free (ssl);
279                 SSL_CTX_free (ctx);
280                 close (sd);
281         }
282         else {
283                 result = check_http ();
284         }
285 #else
286         result = check_http ();
287 #endif
288         return result;
290 \f
293 /* process command-line arguments */
294 int
295 process_arguments (int argc, char **argv)
297         int c = 1;
299 #ifdef HAVE_GETOPT_H
300         int option_index = 0;
301         static struct option long_options[] = {
302                 STD_LONG_OPTS,
303                 {"file",required_argument,0,'F'},
304                 {"link", no_argument, 0, 'L'},
305                 {"nohtml", no_argument, 0, 'n'},
306                 {"ssl", no_argument, 0, 'S'},
307                 {"verbose", no_argument, 0, 'v'},
308                 {"post", required_argument, 0, 'P'},
309                 {"IP-address", required_argument, 0, 'I'},
310                 {"string", required_argument, 0, 's'},
311                 {"regex", required_argument, 0, 'r'},
312                 {"ereg", required_argument, 0, 'r'},
313                 {"eregi", required_argument, 0, 'R'},
314                 {"linespan", no_argument, 0, 'l'},
315                 {"onredirect", required_argument, 0, 'f'},
316                 {"certificate", required_argument, 0, 'C'},
317                 {"min", required_argument, 0, 'm'},
318                 {0, 0, 0, 0}
319         };
320 #endif
322         if (argc < 2)
323                 return ERROR;
325         for (c = 1; c < argc; c++) {
326                 if (strcmp ("-to", argv[c]) == 0)
327                         strcpy (argv[c], "-t");
328                 if (strcmp ("-hn", argv[c]) == 0)
329                         strcpy (argv[c], "-H");
330                 if (strcmp ("-wt", argv[c]) == 0)
331                         strcpy (argv[c], "-w");
332                 if (strcmp ("-ct", argv[c]) == 0)
333                         strcpy (argv[c], "-c");
334                 if (strcmp ("-nohtml", argv[c]) == 0)
335                         strcpy (argv[c], "-n");
336         }
338 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:"
340         while (1) {
341 #ifdef HAVE_GETOPT_H
342                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
343 #else
344                 c = getopt (argc, argv, OPTCHARS);
345 #endif
346                 if (c == -1 || c == EOF)
347                         break;
349                 switch (c) {
350                 case '?': /* usage */
351                         usage2 ("unknown argument", optarg);
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 'v': /* verbose */
474                         verbose = TRUE;
475                         break;
476                 case 'm': /* min_page_length */
477                         min_page_len = atoi (optarg);
478                         break;
479                 }
480         }
482         c = optind;
484         if (strcmp (server_address, "") == 0 && c < argc)
485                         asprintf (&server_address, "%s", argv[c++]);
487         if (strcmp (host_name, "") == 0 && c < argc)
488                 asprintf (&host_name, "%s", argv[c++]);
490         if (strcmp (server_address ,"") == 0) {
491                 if (strcmp (host_name, "") == 0)
492                         usage ("check_http: you must specify a server address or host name\n");
493                 else
494                         asprintf (&server_address, "%s", host_name);
495         }
497         return TRUE;
499 \f
502 /* written by lauri alanko */
503 static char *
504 base64 (char *bin, int len)
507         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
508         int i = 0, j = 0;
510         char BASE64_END = '=';
511         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
513         while (j < len - 2) {
514                 buf[i++] = base64_table[bin[j] >> 2];
515                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
516                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
517                 buf[i++] = base64_table[bin[j + 2] & 63];
518                 j += 3;
519         }
521         switch (len - j) {
522         case 1:
523                 buf[i++] = base64_table[bin[j] >> 2];
524                 buf[i++] = base64_table[(bin[j] & 3) << 4];
525                 buf[i++] = BASE64_END;
526                 buf[i++] = BASE64_END;
527                 break;
528         case 2:
529                 buf[i++] = base64_table[bin[j] >> 2];
530                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
531                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
532                 buf[i++] = BASE64_END;
533                 break;
534         case 0:
535                 break;
536         }
538         buf[i] = '\0';
539         return buf;
541 \f
544 int
545 check_http (void)
547         char *msg = NULL;
548         char *status_line = "";
549         char *header = NULL;
550         char *page = "";
551         char *auth = NULL;
552         int i = 0;
553         size_t pagesize = 0;
554         char *full_page = "";
555         char *buf = NULL;
556         char *pos = "";
557         char *x = NULL;
558         char *orig_url = NULL;
559         double elapsed_time;
560         int page_len = 0;
561 #ifdef HAVE_SSL
562         int sslerr;
563 #endif
565         /* try to connect to the host at the given port number */
566 #ifdef HAVE_SSL
567         if (use_ssl == TRUE) {
569                 if (connect_SSL () != OK) {
570                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
571                 }
573                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
574                         X509_free (server_cert);
575                 }
576                 else {
577                         printf ("ERROR: Cannot retrieve server certificate.\n");
578                         return STATE_CRITICAL;
579                 }
581         }
582         else {
583 #endif
584                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
585                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
586 #ifdef HAVE_SSL
587         }
588 #endif
590         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
592         /* optionally send the host header info (not clear if it's usable) */
593         if (strcmp (host_name, ""))
594                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
596         /* send user agent */
597         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
598                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
600         /* optionally send the authentication info */
601         if (strcmp (user_auth, "")) {
602                 auth = base64 (user_auth, strlen (user_auth));
603                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
604         }
606         /* either send http POST data */
607         if (strlen (http_post_data)) {
608                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
609                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
610                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
611         }
612         else {
613                 /* or just a newline so the server knows we're done with the request */
614                 asprintf (&buf, "%s%s", buf, CRLF);
615         }
617 #ifdef HAVE_SSL
618         if (use_ssl == TRUE) {
619                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
620                         ERR_print_errors_fp (stderr);
621                         return STATE_CRITICAL;
622                 }
623         }
624         else {
625 #endif
626                 send (sd, buf, strlen (buf), 0);
627 #ifdef HAVE_SSL
628         }
629 #endif
631         /* fetch the page */
632         while ((i = my_recv ()) > 0) {
633                 buffer[i] = '\0';
634                 asprintf (&full_page, "%s%s", full_page, buffer);
635                 pagesize += i;
636         }
638         if (i < 0) {
639 #ifdef HAVE_SSL
640                 sslerr=SSL_get_error(ssl, i);
641                 if ( sslerr == SSL_ERROR_SSL ) {
642                         terminate (STATE_WARNING, "Client Certificate Required\n");
643                 } else {
644                         terminate (STATE_CRITICAL, "Error in recv()");
645                 }
646 #else
647                 terminate (STATE_CRITICAL, "Error in recv()");
648 #endif
649         }
651         /* return a CRITICAL status if we couldn't read any data */
652         if (pagesize == (size_t) 0)
653                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
655         /* close the connection */
656         my_close ();
658         /* reset the alarm */
659         alarm (0);
661         /* leave full_page untouched so we can free it later */
662         page = full_page;
664         if (verbose)
665                 printf ("Page is %d characters\n", pagesize);
667         /* find status line and null-terminate it */
668         status_line = page;
669         page += (size_t) strcspn (page, "\r\n");
670         pos = page;
671         page += (size_t) strspn (page, "\r\n");
672         status_line[strcspn(status_line, "\r\n")] = 0;
673         strip (status_line);
674         if (verbose)
675                 printf ("STATUS: %s\n", status_line);
677         /* find header info and null terminate it */
678         header = page;
679         while (strcspn (page, "\r\n") > 0) {
680                 page += (size_t) strcspn (page, "\r\n");
681                 pos = page;
682                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
683                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
684                         page += (size_t) 2;
685                 else
686                         page += (size_t) 1;
687         }
688         page += (size_t) strspn (page, "\r\n");
689         header[pos - header] = 0;
690         if (verbose)
691                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
693         /* make sure the status line matches the response we are looking for */
694         if (!strstr (status_line, server_expect)) {
695                 if (server_port == HTTP_PORT)
696                         asprintf (&msg, "Invalid HTTP response received from host\n");
697                 else
698                         asprintf (&msg,
699                                         "Invalid HTTP response received from host on port %d\n",
700                                         server_port);
701                 terminate (STATE_CRITICAL, msg);
702         }
705         /* Exit here if server_expect was set by user and not default */
706         if ( server_expect_yn  )  {
707                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
708                           server_expect);
709                 if (verbose)
710                         printf ("%s\n",msg);
712         }
713         else {
714         
716                 /* check the return code */
717                 /* server errors result in a critical state */
718                 if (strstr (status_line, "500") ||
719                   strstr (status_line, "501") ||
720                 strstr (status_line, "502") ||
721                     strstr (status_line, "503")) {
722                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
723                 }
725                 /* client errors result in a warning state */
726                 if (strstr (status_line, "400") ||
727                   strstr (status_line, "401") ||
728                 strstr (status_line, "402") ||
729                     strstr (status_line, "403") ||
730                     strstr (status_line, "404")) {
731                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
732                 }
734                 /* check redirected page if specified */
735                 if (strstr (status_line, "300") ||
736                   strstr (status_line, "301") ||
737                 strstr (status_line, "302") ||
738                     strstr (status_line, "303") ||
739                     strstr (status_line, "304")) {
740                         if (onredirect == STATE_DEPENDENT) {
742                                 asprintf (&orig_url, "%s", server_url);
743                                 pos = header;
744                                 while (pos) {
745                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
746                                         if (server_address == NULL)
747                                                 terminate (STATE_UNKNOWN,
748                                                                                  "HTTP UNKNOWN: could not allocate server_address");
749                                         if (strcspn (pos, "\r\n") > server_url_length) {
750                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
751                                                 if (server_url == NULL)
752                                                         terminate (STATE_UNKNOWN,
753                                                                    "HTTP UNKNOWN: could not allocate server_url");
754                                                 server_url_length = strcspn (pos, "\r\n");
755                                         }
756                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
757                                                 asprintf (&host_name, "%s", server_address);
758                                                 use_ssl = server_type_check (server_type);
759                                                 server_port = atoi (server_port_text);
760                                                 check_http ();
761                                         }
762                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
763                                                 asprintf (&host_name, "%s", server_address);
764                                                 use_ssl = server_type_check (server_type);
765                                                 server_port = server_port_check (use_ssl);
766                                                 check_http ();
767                                         }
768                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
769                                                 asprintf (&host_name, "%s", server_address);
770                                                 strcpy (server_url, "/");
771                                                 use_ssl = server_type_check (server_type);
772                                                 server_port = atoi (server_port_text);
773                                                 check_http ();
774                                         }
775                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
776                                                 asprintf (&host_name, "%s", server_address);
777                                                 strcpy (server_url, "/");
778                                                 use_ssl = server_type_check (server_type);
779                                                 server_port = server_port_check (use_ssl);
780                                                 check_http ();
781                                         }
782                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
783                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
784                                                         *x = '\0';
785                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
786                                                 }
787                                                 check_http ();
788                                         }                                       
789                                         pos += (size_t) strcspn (pos, "\r\n");
790                                         pos += (size_t) strspn (pos, "\r\n");
791                                 } /* end while (pos) */
792                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
793                                         status_line, (display_html ? "</A>" : ""));
794                                 exit (STATE_UNKNOWN);
795                         } /* end if (onredirect == STATE_DEPENDENT) */
796                         
797                         else if (onredirect == STATE_UNKNOWN)
798                                 printf ("HTTP UNKNOWN");
799                         else if (onredirect == STATE_OK)
800                                 printf ("HTTP ok");
801                         else if (onredirect == STATE_WARNING)
802                                 printf ("HTTP WARNING");
803                         else if (onredirect == STATE_CRITICAL)
804                                 printf ("HTTP CRITICAL");
805                         elapsed_time = delta_time (tv);
806                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
807                                  status_line, elapsed_time, timestamp,
808                            (display_html ? "</A>" : ""), elapsed_time);
809                         terminate (onredirect, msg);
810                 } /* end if (strstr (status_line, "30[0-4]") */
813         } /* end else (server_expect_yn)  */
815                 
816         /* check elapsed time */
817         elapsed_time = delta_time (tv);
818         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
819                        status_line, elapsed_time, timestamp,
820                        (display_html ? "</A>" : ""), elapsed_time);
821         if (check_critical_time == TRUE && elapsed_time > critical_time)
822                 terminate (STATE_CRITICAL, msg);
823         if (check_warning_time == TRUE && elapsed_time > warning_time)
824                 terminate (STATE_WARNING, msg);
826         /* Page and Header content checks go here */
827         /* these checks should be last */
829         if (strlen (string_expect)) {
830                 if (strstr (page, string_expect)) {
831                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
832                                 status_line, elapsed_time,
833                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
834                         exit (STATE_OK);
835                 }
836                 else {
837                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
838                                 (display_html ? "</A>" : ""), elapsed_time);
839                         exit (STATE_CRITICAL);
840                 }
841         }
842 #ifdef HAVE_REGEX_H
843         if (strlen (regexp)) {
844                 errcode = regexec (&preg, page, REGS, pmatch, 0);
845                 if (errcode == 0) {
846                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
847                                 status_line, elapsed_time,
848                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
849                         exit (STATE_OK);
850                 }
851                 else {
852                         if (errcode == REG_NOMATCH) {
853                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
854                                         (display_html ? "</A>" : ""), elapsed_time);
855                                 exit (STATE_CRITICAL);
856                         }
857                         else {
858                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
859                                 printf ("Execute Error: %s\n", errbuf);
860                                 exit (STATE_CRITICAL);
861                         }
862                 }
863         }
864 #endif
866         /* make sure the page is of an appropriate size */
867         page_len = strlen (page);
868         if ((min_page_len > 0) && (page_len < min_page_len)) {
869                 printf ("HTTP WARNING: page size too small%s|size=%i\n",
870                         (display_html ? "</A>" : ""), page_len );
871                 exit (STATE_WARNING);
872         }
873         /* We only get here if all tests have been passed */
874         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
875                         status_line, (float)elapsed_time,
876                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
877         terminate (STATE_OK, msg);
878         return STATE_UNKNOWN;
883 #ifdef HAVE_SSL
884 int connect_SSL (void)
886         SSL_METHOD *meth;
888         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
889         RAND_seed (randbuff, strlen (randbuff));
890         if (verbose)
891                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
893         /* Initialize SSL context */
894         SSLeay_add_ssl_algorithms ();
895         meth = SSLv23_client_method ();
896         SSL_load_error_strings ();
897         if ((ctx = SSL_CTX_new (meth)) == NULL) {
898                 printf ("ERROR: Cannot create SSL context.\n");
899                 return STATE_CRITICAL;
900         }
902         /* Initialize alarm signal handling */
903         signal (SIGALRM, socket_timeout_alarm_handler);
905         /* Set socket timeout */
906         alarm (socket_timeout);
908         /* Save start time */
909         gettimeofday (&tv, NULL);
911         /* Make TCP connection */
912         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
913                 /* Do the SSL handshake */
914                 if ((ssl = SSL_new (ctx)) != NULL) {
915                         SSL_set_cipher_list(ssl, "ALL");
916                         SSL_set_fd (ssl, sd);
917                         if (SSL_connect (ssl) != -1)
918                                 return OK;
919                         ERR_print_errors_fp (stderr);
920                 }
921                 else {
922                         printf ("ERROR: Cannot initiate SSL handshake.\n");
923                 }
924                 SSL_free (ssl);
925         }
927         SSL_CTX_free (ctx);
928         close (sd);
930         return STATE_CRITICAL;
932 #endif
934 #ifdef HAVE_SSL
935 int
936 check_certificate (X509 ** certificate)
938         ASN1_STRING *tm;
939         int offset;
940         struct tm stamp;
941         int days_left;
944         /* Retrieve timestamp of certificate */
945         tm = X509_get_notAfter (*certificate);
947         /* Generate tm structure to process timestamp */
948         if (tm->type == V_ASN1_UTCTIME) {
949                 if (tm->length < 10) {
950                         printf ("ERROR: Wrong time format in certificate.\n");
951                         return STATE_CRITICAL;
952                 }
953                 else {
954                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
955                         if (stamp.tm_year < 50)
956                                 stamp.tm_year += 100;
957                         offset = 0;
958                 }
959         }
960         else {
961                 if (tm->length < 12) {
962                         printf ("ERROR: Wrong time format in certificate.\n");
963                         return STATE_CRITICAL;
964                 }
965                 else {
966                         stamp.tm_year =
967                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
968                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
969                         stamp.tm_year -= 1900;
970                         offset = 2;
971                 }
972         }
973         stamp.tm_mon =
974                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
975         stamp.tm_mday =
976                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
977         stamp.tm_hour =
978                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
979         stamp.tm_min =
980                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
981         stamp.tm_sec = 0;
982         stamp.tm_isdst = -1;
984         days_left = (mktime (&stamp) - time (NULL)) / 86400;
985         snprintf
986                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
987                  stamp.tm_mon + 1,
988                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
990         if (days_left > 0 && days_left <= days_till_exp) {
991                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
992                 return STATE_WARNING;
993         }
994         if (days_left < 0) {
995                 printf ("Certificate expired on %s.\n", timestamp);
996                 return STATE_CRITICAL;
997         }
999         if (days_left == 0) {
1000                 printf ("Certificate expires today (%s).\n", timestamp);
1001                 return STATE_WARNING;
1002         }
1004         printf ("Certificate will expire on %s.\n", timestamp);
1006         return STATE_OK;
1008 #endif
1009 \f
1012 int
1013 my_recv (void)
1015         int i;
1016 #ifdef HAVE_SSL
1017         if (use_ssl) {
1018                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1019         }
1020         else {
1021                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1022         }
1023 #else
1024         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1025 #endif
1026         return i;
1030 int
1031 my_close (void)
1033 #ifdef HAVE_SSL
1034         if (use_ssl == TRUE) {
1035                 SSL_shutdown (ssl);
1036                 SSL_free (ssl);
1037                 SSL_CTX_free (ctx);
1038                 return 0;
1039         }
1040         else {
1041 #endif
1042                 return close (sd);
1043 #ifdef HAVE_SSL
1044         }
1045 #endif
1047 \f
1050 void
1051 print_help (void)
1053         print_revision (progname, REVISION);
1054         printf
1055                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1056                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1057         print_usage ();
1058         printf ("NOTE: One or both of -H and -I must be specified\n");
1059         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1060                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1061 #ifdef HAVE_SSL
1062         printf (SSLDESCRIPTION);
1063 #endif
1067 void
1068 print_usage (void)
1070         printf ("Usage:\n" " %s %s\n"
1071 #ifdef HAVE_GETOPT_H
1072                 " %s (-h | --help) for detailed help\n"
1073                 " %s (-V | --version) for version information\n",
1074 #else
1075                 " %s -h for detailed help\n"
1076                 " %s -V for version information\n",
1077 #endif
1078         progname, OPTIONS, progname, progname);