Code

8ffbf989bef7df3b08e012c40f69c9e42bd2cebc
[nagiosplug.git] / plugins / check_http.c
1 /****************************************************************************
2  *
3  * Program: HTTP plugin for Nagios
4  * License: GPL
5  *
6  * License Information:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * $Id$
23  *
24  *****************************************************************************/
26 const char *progname = "check_http";
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Ethan Galstad/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
32 #include "config.h"
33 #include "common.h"
34 #include "netutils.h"
35 #include "utils.h"
37 #define SUMMARY "\
38 This plugin tests the HTTP service on the specified host. It can test\n\
39 normal (http) and secure (https) servers, follow redirects, search for\n\
40 strings and regular expressions, check connection times, and report on\n\
41 certificate expiration times.\n"
43 #define OPTIONS "\
44 (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
45             [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
46             [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
47             [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
48             [-P string]"
50 #define LONGOPTIONS "\
51  -H, --hostname=ADDRESS\n\
52     Host name argument for servers using host headers (virtual host)\n\
53  -I, --IP-address=ADDRESS\n\
54    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
55  -e, --expect=STRING\n\
56    String to expect in first (status) line of server response (default: %s)\n\
57    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
58  -s, --string=STRING\n\
59    String to expect in the content\n\
60  -u, --url=PATH\n\
61    URL to GET or POST (default: /)\n\
62  -p, --port=INTEGER\n\
63    Port number (default: %d)\n\
64  -P, --post=STRING\n\
65    URL encoded http POST data\n\
66  -w, --warning=INTEGER\n\
67    Response time to result in warning status (seconds)\n\
68  -c, --critical=INTEGER\n\
69    Response time to result in critical status (seconds)\n\
70  -t, --timeout=INTEGER\n\
71    Seconds before connection times out (default: %d)\n\
72  -a, --authorization=AUTH_PAIR\n\
73    Username:password on sites with basic authentication\n\
74  -L, --link=URL\n\
75    Wrap output in HTML link (obsoleted by urlize)\n\
76  -f, --onredirect=<ok|warning|critical|follow>\n\
77    How to handle redirected pages\n%s%s\
78  -v, --verbose\n\
79     Show details for command-line debugging (do not use with nagios server)\n\
80  -h, --help\n\
81     Print detailed help screen\n\
82  -V, --version\n\
83     Print version information\n"
85 #ifdef HAVE_SSL
86 #define SSLOPTIONS "\
87  -S, --ssl\n\
88     Connect via SSL\n\
89  -C, --certificate=INTEGER\n\
90     Minimum number of days a certificate has to be valid.\n\
91     (when this option is used the url is not checked.)\n"
92 #else
93 #define SSLOPTIONS ""
94 #endif
96 #ifdef HAVE_REGEX_H
97 #define REGOPTIONS "\
98  -l, --linespan\n\
99     Allow regex to span newlines (must precede -r or -R)\n\
100  -r, --regex, --ereg=STRING\n\
101     Search page for regex STRING\n\
102  -R, --eregi=STRING\n\
103     Search page for case-insensitive regex STRING\n"
104 #else
105 #define REGOPTIONS ""
106 #endif
108 #define DESCRIPTION "\
109 This plugin will attempt to open an HTTP connection with the host. Successul\n\
110 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
111 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
112 messages from the host result in STATE_WARNING return values.  If you are\n\
113 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
114 \(fully qualified domain name) as the [host_name] argument.\n"
116 #define SSLDESCRIPTION "\
117 This plugin can also check whether an SSL enabled web server is able to\n\
118 serve content (optionally within a specified time) or whether the X509 \n\
119 certificate is still valid for the specified number of days.\n\n\
120 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
121 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
122 STATE_OK will be returned. When the server returns its content but exceeds\n\
123 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
124 a STATE_CRITICAL will be returned.\n\n\
125 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
126 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
127 STATE_OK is returned. When the certificate is still valid, but for less than\n\
128 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
129 the certificate is expired.\n"
131 #ifdef HAVE_SSL_H
132 #include <rsa.h>
133 #include <crypto.h>
134 #include <x509.h>
135 #include <pem.h>
136 #include <ssl.h>
137 #include <err.h>
138 #include <rand.h>
139 #endif
141 #ifdef HAVE_OPENSSL_SSL_H
142 #include <openssl/rsa.h>
143 #include <openssl/crypto.h>
144 #include <openssl/x509.h>
145 #include <openssl/pem.h>
146 #include <openssl/ssl.h>
147 #include <openssl/err.h>
148 #include <openssl/rand.h>
149 #endif
151 #ifdef HAVE_SSL
152 int check_cert = FALSE;
153 int days_till_exp;
154 char *randbuff = "";
155 SSL_CTX *ctx;
156 SSL *ssl;
157 X509 *server_cert;
158 int connect_SSL (void);
159 int check_certificate (X509 **);
160 #endif
162 #ifdef HAVE_REGEX_H
163 enum {
164         REGS = 2,
165         MAX_RE_SIZE = 256
166 };
167 #include <regex.h>
168 regex_t preg;
169 regmatch_t pmatch[REGS];
170 char regexp[MAX_RE_SIZE];
171 char errbuf[MAX_INPUT_BUFFER];
172 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
173 int errcode;
174 #endif
176 struct timeval tv;
178 #define server_type_check(server_type) \
179 (strcmp (server_type, "https") ? FALSE : TRUE)
181 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
183 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
184 #define URI_HTTP "%[HTPShtps]://"
185 #define URI_HOST "%[a-zA-Z0-9.-]"
186 #define URI_PORT ":%[0-9]"
187 #define URI_PATH "%[/a-zA-Z0-9._-=@,]"
189 enum {
190         MAX_IPV4_HOSTLENGTH = 64,
191         HTTP_PORT = 80,
192         HTTPS_PORT = 443
193 };
195 #define HTTP_EXPECT "HTTP/1."
196 #define HTTP_URL "/"
197 #define CRLF "\r\n"
199 char timestamp[17] = "";
200 int specify_port = FALSE;
201 int server_port = HTTP_PORT;
202 char server_port_text[6] = "";
203 char server_type[6] = "http";
204 char *server_address = ""; 
205 char *host_name = "";
206 char *server_url = HTTP_URL;
207 int server_url_length = 1;
208 int server_expect_yn = 0;
209 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
210 char string_expect[MAX_INPUT_BUFFER] = "";
211 double warning_time = 0;
212 int check_warning_time = FALSE;
213 double critical_time = 0;
214 int check_critical_time = FALSE;
215 char user_auth[MAX_INPUT_BUFFER] = "";
216 int display_html = FALSE;
217 int onredirect = STATE_OK;
218 int use_ssl = FALSE;
219 int verbose = FALSE;
220 int sd;
221 char *http_method = "GET";
222 char *http_post_data = "";
223 char buffer[MAX_INPUT_BUFFER];
225 void print_usage (void);
226 void print_help (void);
227 int process_arguments (int, char **);
228 static char *base64 (char *bin, int len);
229 int check_http (void);
230 int my_recv (void);
231 int my_close (void);
233 int
234 main (int argc, char **argv)
236         int result = STATE_UNKNOWN;
238         if (process_arguments (argc, argv) == ERROR)
239                 usage ("check_http: could not parse arguments\n");
241         if (strstr (timestamp, ":")) {
242                 if (strstr (server_url, "?"))
243                         asprintf (&server_url, "%s&%s", server_url, timestamp);
244                 else
245                         asprintf (&server_url, "%s?%s", server_url, timestamp);
246         }
248         if (display_html == TRUE)
249                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
250                         host_name, server_port, server_url);
252         /* initialize alarm signal handling, set socket timeout, start timer */
253         (void) signal (SIGALRM, socket_timeout_alarm_handler);
254         (void) alarm (socket_timeout);
255         gettimeofday (&tv, NULL);
257 #ifdef HAVE_SSL
258         if (use_ssl && check_cert == TRUE) {
259                 if (connect_SSL () != OK)
260                         terminate (STATE_CRITICAL,
261                                    "HTTP CRITICAL - Could not make SSL connection\n");
262                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
263                         result = check_certificate (&server_cert);
264                         X509_free (server_cert);
265                 }
266                 else {
267                         printf ("ERROR: Cannot retrieve server certificate.\n");
268                         result = STATE_CRITICAL;
269                 }
270                 SSL_shutdown (ssl);
271                 SSL_free (ssl);
272                 SSL_CTX_free (ctx);
273                 close (sd);
274         }
275         else {
276                 result = check_http ();
277         }
278 #else
279         result = check_http ();
280 #endif
281         return result;
283 \f
286 /* process command-line arguments */
287 int
288 process_arguments (int argc, char **argv)
290         int c = 1;
292 #ifdef HAVE_GETOPT_H
293         int option_index = 0;
294         static struct option long_options[] = {
295                 STD_LONG_OPTS,
296                 {"file",required_argument,0,'F'},
297                 {"link", no_argument, 0, 'L'},
298                 {"nohtml", no_argument, 0, 'n'},
299                 {"ssl", no_argument, 0, 'S'},
300                 {"verbose", no_argument, 0, 'v'},
301                 {"post", required_argument, 0, 'P'},
302                 {"IP-address", required_argument, 0, 'I'},
303                 {"string", required_argument, 0, 's'},
304                 {"regex", required_argument, 0, 'r'},
305                 {"ereg", required_argument, 0, 'r'},
306                 {"eregi", required_argument, 0, 'R'},
307                 {"linespan", no_argument, 0, 'l'},
308                 {"onredirect", required_argument, 0, 'f'},
309                 {"certificate", required_argument, 0, 'C'},
310                 {0, 0, 0, 0}
311         };
312 #endif
314         if (argc < 2)
315                 return ERROR;
317         for (c = 1; c < argc; c++) {
318                 if (strcmp ("-to", argv[c]) == 0)
319                         strcpy (argv[c], "-t");
320                 if (strcmp ("-hn", argv[c]) == 0)
321                         strcpy (argv[c], "-H");
322                 if (strcmp ("-wt", argv[c]) == 0)
323                         strcpy (argv[c], "-w");
324                 if (strcmp ("-ct", argv[c]) == 0)
325                         strcpy (argv[c], "-c");
326                 if (strcmp ("-nohtml", argv[c]) == 0)
327                         strcpy (argv[c], "-n");
328         }
330 #define OPTCHARS "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLS"
332         while (1) {
333 #ifdef HAVE_GETOPT_H
334                 c = getopt_long (argc, argv, OPTCHARS, long_options, &option_index);
335 #else
336                 c = getopt (argc, argv, OPTCHARS);
337 #endif
338                 if (c == -1 || c == EOF)
339                         break;
341                 switch (c) {
342                 case '?': /* usage */
343                         usage2 ("unknown argument", optarg);
344                         break;
345                 case 'h': /* help */
346                         print_help ();
347                         exit (STATE_OK);
348                         break;
349                 case 'V': /* version */
350                         print_revision (progname, REVISION);
351                         exit (STATE_OK);
352                         break;
353                 case 't': /* timeout period */
354                         if (!is_intnonneg (optarg))
355                                 usage2 ("timeout interval must be a non-negative integer", optarg);
356                         socket_timeout = atoi (optarg);
357                         break;
358                 case 'c': /* critical time threshold */
359                         if (!is_intnonneg (optarg))
360                                 usage2 ("invalid critical threshold", optarg);
361                         critical_time = strtod (optarg, NULL);
362                         check_critical_time = TRUE;
363                         break;
364                 case 'w': /* warning time threshold */
365                         if (!is_intnonneg (optarg))
366                                 usage2 ("invalid warning threshold", optarg);
367                         warning_time = strtod (optarg, NULL);
368                         check_warning_time = TRUE;
369                         break;
370                 case 'L': /* show html link */
371                         display_html = TRUE;
372                         break;
373                 case 'n': /* do not show html link */
374                         display_html = FALSE;
375                         break;
376                 case 'S': /* use SSL */
377 #ifndef HAVE_SSL
378                         usage ("check_http: invalid option - SSL is not available\n");
379 #endif
380                         use_ssl = TRUE;
381                         if (specify_port == FALSE)
382                                 server_port = HTTPS_PORT;
383                         break;
384                 case 'C': /* Check SSL cert validity */
385 #ifdef HAVE_SSL
386                         if (!is_intnonneg (optarg))
387                                 usage2 ("invalid certificate expiration period", optarg);
388                         days_till_exp = atoi (optarg);
389                         check_cert = TRUE;
390 #else
391                         usage ("check_http: invalid option - SSL is not available\n");
392 #endif
393                         break;
394                 case 'f': /* onredirect */
395                         if (!strcmp (optarg, "follow"))
396                                 onredirect = STATE_DEPENDENT;
397                         if (!strcmp (optarg, "unknown"))
398                                 onredirect = STATE_UNKNOWN;
399                         if (!strcmp (optarg, "ok"))
400                                 onredirect = STATE_OK;
401                         if (!strcmp (optarg, "warning"))
402                                 onredirect = STATE_WARNING;
403                         if (!strcmp (optarg, "critical"))
404                                 onredirect = STATE_CRITICAL;
405                         if (verbose)
406                                 printf("option f:%d \n", onredirect);  
407                         break;
408                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
409                 case 'H': /* Host Name (virtual host) */
410                         asprintf (&host_name, "%s", optarg);
411                         break;
412                 case 'I': /* Server IP-address */
413                         asprintf (&server_address, "%s", optarg);
414                         break;
415                 case 'u': /* Host or server */
416                         asprintf (&server_url, "%s", optarg);
417                         server_url_length = strlen (server_url);
418                         break;
419                 case 'p': /* Host or server */
420                         if (!is_intnonneg (optarg))
421                                 usage2 ("invalid port number", optarg);
422                         server_port = atoi (optarg);
423                         specify_port = TRUE;
424                         break;
425                 case 'a': /* authorization info */
426                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
427                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
428                         break;
429                 case 'P': /* HTTP POST data in URL encoded format */
430                         asprintf (&http_method, "%s", "POST");
431                         asprintf (&http_post_data, "%s", optarg);
432                         break;
433                 case 's': /* string or substring */
434                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
435                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
436                         break;
437                 case 'e': /* string or substring */
438                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
439                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
440                         server_expect_yn = 1;
441                         break;
442 #ifndef HAVE_REGEX_H
443                 case 'l': /* linespan */
444                 case 'r': /* linespan */
445                 case 'R': /* linespan */
446                         usage ("check_http: call for regex which was not a compiled option\n");
447                         break;
448 #else
449                 case 'l': /* linespan */
450                         cflags &= ~REG_NEWLINE;
451                         break;
452                 case 'R': /* regex */
453                         cflags |= REG_ICASE;
454                 case 'r': /* regex */
455                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
456                         regexp[MAX_RE_SIZE - 1] = 0;
457                         errcode = regcomp (&preg, regexp, cflags);
458                         if (errcode != 0) {
459                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
460                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
461                                 return ERROR;
462                         }
463                         break;
464 #endif
465                 case 'v': /* verbose */
466                         verbose = TRUE;
467                         break;
468                 }
469         }
471         c = optind;
473         if (strcmp (server_address, "") == 0 && c < argc)
474                         asprintf (&server_address, "%s", argv[c++]);
476         if (strcmp (host_name, "") == 0 && c < argc)
477                 asprintf (&host_name, "%s", argv[c++]);
479         if (strcmp (server_address ,"") == 0) {
480                 if (strcmp (host_name, "") == 0)
481                         usage ("check_http: you must specify a server address or host name\n");
482                 else
483                         asprintf (&server_address, "%s", host_name);
484         }
486         return TRUE;
488 \f
491 /* written by lauri alanko */
492 static char *
493 base64 (char *bin, int len)
496         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
497         int i = 0, j = 0;
499         char BASE64_END = '=';
500         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
502         while (j < len - 2) {
503                 buf[i++] = base64_table[bin[j] >> 2];
504                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
505                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
506                 buf[i++] = base64_table[bin[j + 2] & 63];
507                 j += 3;
508         }
510         switch (len - j) {
511         case 1:
512                 buf[i++] = base64_table[bin[j] >> 2];
513                 buf[i++] = base64_table[(bin[j] & 3) << 4];
514                 buf[i++] = BASE64_END;
515                 buf[i++] = BASE64_END;
516                 break;
517         case 2:
518                 buf[i++] = base64_table[bin[j] >> 2];
519                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
520                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
521                 buf[i++] = BASE64_END;
522                 break;
523         case 0:
524                 break;
525         }
527         buf[i] = '\0';
528         return buf;
530 \f
533 int
534 check_http (void)
536         char *msg = NULL;
537         char *status_line = "";
538         char *header = NULL;
539         char *page = "";
540         char *auth = NULL;
541         int i = 0;
542         size_t pagesize = 0;
543         char *full_page = "";
544         char *buf = NULL;
545         char *pos = "";
546         char *x = NULL;
547         char *orig_url = NULL;
548         double elapsed_time;
549 #ifdef HAVE_SSL
550         int sslerr;
551 #endif
553         /* try to connect to the host at the given port number */
554 #ifdef HAVE_SSL
555         if (use_ssl == TRUE) {
557                 if (connect_SSL () != OK) {
558                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
559                 }
561                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
562                         X509_free (server_cert);
563                 }
564                 else {
565                         printf ("ERROR: Cannot retrieve server certificate.\n");
566                         return STATE_CRITICAL;
567                 }
569         }
570         else {
571 #endif
572                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
573                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
574 #ifdef HAVE_SSL
575         }
576 #endif
578         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
580         /* optionally send the host header info (not clear if it's usable) */
581         if (strcmp (host_name, ""))
582                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
584         /* send user agent */
585         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
586                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
588         /* optionally send the authentication info */
589         if (strcmp (user_auth, "")) {
590                 auth = base64 (user_auth, strlen (user_auth));
591                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
592         }
594         /* either send http POST data */
595         if (strlen (http_post_data)) {
596                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
597                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
598                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
599         }
600         else {
601                 /* or just a newline so the server knows we're done with the request */
602                 asprintf (&buf, "%s%s", buf, CRLF);
603         }
605 #ifdef HAVE_SSL
606         if (use_ssl == TRUE) {
607                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
608                         ERR_print_errors_fp (stderr);
609                         return STATE_CRITICAL;
610                 }
611         }
612         else {
613 #endif
614                 send (sd, buf, strlen (buf), 0);
615 #ifdef HAVE_SSL
616         }
617 #endif
619         /* fetch the page */
620         while ((i = my_recv ()) > 0) {
621                 buffer[i] = '\0';
622                 asprintf (&full_page, "%s%s", full_page, buffer);
623                 pagesize += i;
624         }
626         if (i < 0) {
627 #ifdef HAVE_SSL
628                 sslerr=SSL_get_error(ssl, i);
629                 if ( sslerr == SSL_ERROR_SSL ) {
630                         terminate (STATE_WARNING, "Client Certificate Required\n");
631                 } else {
632                         terminate (STATE_CRITICAL, "Error in recv()");
633                 }
634 #else
635                 terminate (STATE_CRITICAL, "Error in recv()");
636 #endif
637         }
639         /* return a CRITICAL status if we couldn't read any data */
640         if (pagesize == (size_t) 0)
641                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
643         /* close the connection */
644         my_close ();
646         /* reset the alarm */
647         alarm (0);
649         /* leave full_page untouched so we can free it later */
650         page = full_page;
652         if (verbose)
653                 printf ("Page is %d characters\n", pagesize);
655         /* find status line and null-terminate it */
656         status_line = page;
657         page += (size_t) strcspn (page, "\r\n");
658         pos = page;
659         page += (size_t) strspn (page, "\r\n");
660         status_line[strcspn(status_line, "\r\n")] = 0;
661         strip (status_line);
662         if (verbose)
663                 printf ("STATUS: %s\n", status_line);
665         /* find header info and null terminate it */
666         header = page;
667         while (strcspn (page, "\r\n") > 0) {
668                 page += (size_t) strcspn (page, "\r\n");
669                 pos = page;
670                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
671                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
672                         page += (size_t) 2;
673                 else
674                         page += (size_t) 1;
675         }
676         page += (size_t) strspn (page, "\r\n");
677         header[pos - header] = 0;
678         if (verbose)
679                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
681         /* make sure the status line matches the response we are looking for */
682         if (!strstr (status_line, server_expect)) {
683                 if (server_port == HTTP_PORT)
684                         asprintf (&msg, "Invalid HTTP response received from host\n");
685                 else
686                         asprintf (&msg,
687                                         "Invalid HTTP response received from host on port %d\n",
688                                         server_port);
689                 terminate (STATE_CRITICAL, msg);
690         }
693         /* Exit here if server_expect was set by user and not default */
694         if ( server_expect_yn  )  {
695                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
696                           server_expect);
697                 if (verbose)
698                         printf ("%s\n",msg);
700         }
701         else {
702         
704                 /* check the return code */
705                 /* server errors result in a critical state */
706                 if (strstr (status_line, "500") ||
707                   strstr (status_line, "501") ||
708                 strstr (status_line, "502") ||
709                     strstr (status_line, "503")) {
710                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
711                 }
713                 /* client errors result in a warning state */
714                 if (strstr (status_line, "400") ||
715                   strstr (status_line, "401") ||
716                 strstr (status_line, "402") ||
717                     strstr (status_line, "403") ||
718                     strstr (status_line, "404")) {
719                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
720                 }
722                 /* check redirected page if specified */
723                 if (strstr (status_line, "300") ||
724                   strstr (status_line, "301") ||
725                 strstr (status_line, "302") ||
726                     strstr (status_line, "303") ||
727                     strstr (status_line, "304")) {
728                         if (onredirect == STATE_DEPENDENT) {
730                                 asprintf (&orig_url, "%s", server_url);
731                                 pos = header;
732                                 while (pos) {
733                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
734                                         if (server_address == NULL)
735                                                 terminate (STATE_UNKNOWN,
736                                                                                  "HTTP UNKNOWN: could not allocate server_address");
737                                         if (strcspn (pos, "\r\n") > server_url_length) {
738                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
739                                                 if (server_url == NULL)
740                                                         terminate (STATE_UNKNOWN,
741                                                                    "HTTP UNKNOWN: could not allocate server_url");
742                                                 server_url_length = strcspn (pos, "\r\n");
743                                         }
744                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
745                                                 asprintf (&host_name, "%s", server_address);
746                                                 use_ssl = server_type_check (server_type);
747                                                 server_port = atoi (server_port_text);
748                                                 check_http ();
749                                         }
750                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
751                                                 asprintf (&host_name, "%s", server_address);
752                                                 use_ssl = server_type_check (server_type);
753                                                 server_port = server_port_check (use_ssl);
754                                                 check_http ();
755                                         }
756                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
757                                                 asprintf (&host_name, "%s", server_address);
758                                                 strcpy (server_url, "/");
759                                                 use_ssl = server_type_check (server_type);
760                                                 server_port = atoi (server_port_text);
761                                                 check_http ();
762                                         }
763                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
764                                                 asprintf (&host_name, "%s", server_address);
765                                                 strcpy (server_url, "/");
766                                                 use_ssl = server_type_check (server_type);
767                                                 server_port = server_port_check (use_ssl);
768                                                 check_http ();
769                                         }
770                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
771                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
772                                                         *x = '\0';
773                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
774                                                 }
775                                                 check_http ();
776                                         }                                       
777                                         pos += (size_t) strcspn (pos, "\r\n");
778                                         pos += (size_t) strspn (pos, "\r\n");
779                                 } /* end while (pos) */
780                                 printf ("HTTP UNKNOWN: Could not find redirect location - %s%s",
781                                         status_line, (display_html ? "</A>" : ""));
782                                 exit (STATE_UNKNOWN);
783                         } /* end if (onredirect == STATE_DEPENDENT) */
784                         
785                         else if (onredirect == STATE_UNKNOWN)
786                                 printf ("HTTP UNKNOWN");
787                         else if (onredirect == STATE_OK)
788                                 printf ("HTTP ok");
789                         else if (onredirect == STATE_WARNING)
790                                 printf ("HTTP WARNING");
791                         else if (onredirect == STATE_CRITICAL)
792                                 printf ("HTTP CRITICAL");
793                         elapsed_time = delta_time (tv);
794                         asprintf (&msg, ": %s - %7.3f second response time %s%s|time=%7.3f\n",
795                                  status_line, elapsed_time, timestamp,
796                            (display_html ? "</A>" : ""), elapsed_time);
797                         terminate (onredirect, msg);
798                 } /* end if (strstr (status_line, "30[0-4]") */
801         } /* end else (server_expect_yn)  */
803                 
804         /* check elapsed time */
805         elapsed_time = delta_time (tv);
806         asprintf (&msg, "HTTP problem: %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         if (check_critical_time == TRUE && elapsed_time > critical_time)
810                 terminate (STATE_CRITICAL, msg);
811         if (check_warning_time == TRUE && elapsed_time > warning_time)
812                 terminate (STATE_WARNING, msg);
814         /* Page and Header content checks go here */
815         /* these checks should be last */
817         if (strlen (string_expect)) {
818                 if (strstr (page, string_expect)) {
819                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
820                                 status_line, elapsed_time,
821                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
822                         exit (STATE_OK);
823                 }
824                 else {
825                         printf ("HTTP CRITICAL: string not found%s|time=%7.3f\n",
826                                 (display_html ? "</A>" : ""), elapsed_time);
827                         exit (STATE_CRITICAL);
828                 }
829         }
830 #ifdef HAVE_REGEX_H
831         if (strlen (regexp)) {
832                 errcode = regexec (&preg, page, REGS, pmatch, 0);
833                 if (errcode == 0) {
834                         printf ("HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
835                                 status_line, elapsed_time,
836                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
837                         exit (STATE_OK);
838                 }
839                 else {
840                         if (errcode == REG_NOMATCH) {
841                                 printf ("HTTP CRITICAL: pattern not found%s|time=%7.3f\n",
842                                         (display_html ? "</A>" : ""), elapsed_time);
843                                 exit (STATE_CRITICAL);
844                         }
845                         else {
846                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
847                                 printf ("Execute Error: %s\n", errbuf);
848                                 exit (STATE_CRITICAL);
849                         }
850                 }
851         }
852 #endif
854         /* We only get here if all tests have been passed */
855         asprintf (&msg, "HTTP ok: %s - %7.3f second response time %s%s|time=%7.3f\n",
856                         status_line, (float)elapsed_time,
857                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
858         terminate (STATE_OK, msg);
859         return STATE_UNKNOWN;
864 #ifdef HAVE_SSL
865 int connect_SSL (void)
867         SSL_METHOD *meth;
869         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
870         RAND_seed (randbuff, strlen (randbuff));
871         if (verbose)
872                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
874         /* Initialize SSL context */
875         SSLeay_add_ssl_algorithms ();
876         meth = SSLv23_client_method ();
877         SSL_load_error_strings ();
878         if ((ctx = SSL_CTX_new (meth)) == NULL) {
879                 printf ("ERROR: Cannot create SSL context.\n");
880                 return STATE_CRITICAL;
881         }
883         /* Initialize alarm signal handling */
884         signal (SIGALRM, socket_timeout_alarm_handler);
886         /* Set socket timeout */
887         alarm (socket_timeout);
889         /* Save start time */
890         gettimeofday (&tv, NULL);
892         /* Make TCP connection */
893         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
894                 /* Do the SSL handshake */
895                 if ((ssl = SSL_new (ctx)) != NULL) {
896                         SSL_set_cipher_list(ssl, "ALL");
897                         SSL_set_fd (ssl, sd);
898                         if (SSL_connect (ssl) != -1)
899                                 return OK;
900                         ERR_print_errors_fp (stderr);
901                 }
902                 else {
903                         printf ("ERROR: Cannot initiate SSL handshake.\n");
904                 }
905                 SSL_free (ssl);
906         }
908         SSL_CTX_free (ctx);
909         close (sd);
911         return STATE_CRITICAL;
913 #endif
915 #ifdef HAVE_SSL
916 int
917 check_certificate (X509 ** certificate)
919         ASN1_STRING *tm;
920         int offset;
921         struct tm stamp;
922         int days_left;
925         /* Retrieve timestamp of certificate */
926         tm = X509_get_notAfter (*certificate);
928         /* Generate tm structure to process timestamp */
929         if (tm->type == V_ASN1_UTCTIME) {
930                 if (tm->length < 10) {
931                         printf ("ERROR: Wrong time format in certificate.\n");
932                         return STATE_CRITICAL;
933                 }
934                 else {
935                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
936                         if (stamp.tm_year < 50)
937                                 stamp.tm_year += 100;
938                         offset = 0;
939                 }
940         }
941         else {
942                 if (tm->length < 12) {
943                         printf ("ERROR: Wrong time format in certificate.\n");
944                         return STATE_CRITICAL;
945                 }
946                 else {
947                         stamp.tm_year =
948                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
949                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
950                         stamp.tm_year -= 1900;
951                         offset = 2;
952                 }
953         }
954         stamp.tm_mon =
955                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
956         stamp.tm_mday =
957                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
958         stamp.tm_hour =
959                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
960         stamp.tm_min =
961                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
962         stamp.tm_sec = 0;
963         stamp.tm_isdst = -1;
965         days_left = (mktime (&stamp) - time (NULL)) / 86400;
966         snprintf
967                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
968                  stamp.tm_mon + 1,
969                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
971         if (days_left > 0 && days_left <= days_till_exp) {
972                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
973                 return STATE_WARNING;
974         }
975         if (days_left < 0) {
976                 printf ("Certificate expired on %s.\n", timestamp);
977                 return STATE_CRITICAL;
978         }
980         if (days_left == 0) {
981                 printf ("Certificate expires today (%s).\n", timestamp);
982                 return STATE_WARNING;
983         }
985         printf ("Certificate will expire on %s.\n", timestamp);
987         return STATE_OK;
989 #endif
990 \f
993 int
994 my_recv (void)
996         int i;
997 #ifdef HAVE_SSL
998         if (use_ssl) {
999                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1000         }
1001         else {
1002                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1003         }
1004 #else
1005         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1006 #endif
1007         return i;
1011 int
1012 my_close (void)
1014 #ifdef HAVE_SSL
1015         if (use_ssl == TRUE) {
1016                 SSL_shutdown (ssl);
1017                 SSL_free (ssl);
1018                 SSL_CTX_free (ctx);
1019                 return 0;
1020         }
1021         else {
1022 #endif
1023                 return close (sd);
1024 #ifdef HAVE_SSL
1025         }
1026 #endif
1028 \f
1031 void
1032 print_help (void)
1034         print_revision (progname, REVISION);
1035         printf
1036                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1037                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1038         print_usage ();
1039         printf ("NOTE: One or both of -H and -I must be specified\n");
1040         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1041                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1042 #ifdef HAVE_SSL
1043         printf (SSLDESCRIPTION);
1044 #endif
1048 void
1049 print_usage (void)
1051         printf ("Usage:\n" " %s %s\n"
1052 #ifdef HAVE_GETOPT_H
1053                 " %s (-h | --help) for detailed help\n"
1054                 " %s (-V | --version) for version information\n",
1055 #else
1056                 " %s -h for detailed help\n"
1057                 " %s -V for version information\n",
1058 #endif
1059         progname, OPTIONS, progname, progname);