Code

Remove unnecessary getopt references and link in lib directory
[nagiosplug.git] / plugins / check_http.c
1 /****************************************************************************
2  *
3  * Program: HTTP plugin for Nagios
4  * License: GPL
5  *
6  * License Information:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * $Id$
23  *
24  *****************************************************************************/
26 const char *progname = "check_http";
27 #define REVISION "$Revision$"
28 #define COPYRIGHT "1999-2001"
29 #define AUTHORS "Ethan Galstad/Karl DeBisschop"
30 #define EMAIL "kdebisschop@users.sourceforge.net"
32 #include "config.h"
33 #include "common.h"
34 #include "netutils.h"
35 #include "utils.h"
37 #define SUMMARY "\
38 This plugin tests the HTTP service on the specified host. It can test\n\
39 normal (http) and secure (https) servers, follow redirects, search for\n\
40 strings and regular expressions, check connection times, and report on\n\
41 certificate expiration times.\n"
43 #define OPTIONS "\
44 \(-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
45             [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
46             [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
47             [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
48             [-P string]"
50 #define LONGOPTIONS "\
51  -H, --hostname=ADDRESS\n\
52     Host name argument for servers using host headers (virtual host)\n\
53  -I, --IP-address=ADDRESS\n\
54    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
55  -e, --expect=STRING\n\
56    String to expect in first (status) line of server response (default: %s)\n\
57    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
58  -s, --string=STRING\n\
59    String to expect in the content\n\
60  -u, --url=PATH\n\
61    URL to GET or POST (default: /)\n\
62  -p, --port=INTEGER\n\
63    Port number (default: %d)\n\
64  -P, --post=STRING\n\
65    URL encoded http POST data\n\
66  -w, --warning=INTEGER\n\
67    Response time to result in warning status (seconds)\n\
68  -c, --critical=INTEGER\n\
69    Response time to result in critical status (seconds)\n\
70  -t, --timeout=INTEGER\n\
71    Seconds before connection times out (default: %d)\n\
72  -a, --authorization=AUTH_PAIR\n\
73    Username:password on sites with basic authentication\n\
74  -L, --link=URL\n\
75    Wrap output in HTML link (obsoleted by urlize)\n\
76  -f, --onredirect=<ok|warning|critical|follow>\n\
77    How to handle redirected pages\n%s%s\
78  -v, --verbose\n\
79     Show details for command-line debugging (do not use with nagios server)\n\
80  -h, --help\n\
81     Print detailed help screen\n\
82  -V, --version\n\
83     Print version information\n"
85 #ifdef HAVE_SSL
86 #define SSLOPTIONS "\
87  -S, --ssl\n\
88     Connect via SSL\n\
89  -C, --certificate=INTEGER\n\
90     Minimum number of days a certificate has to be valid.\n\
91     (when this option is used the url is not checked.)\n"
92 #else
93 #define SSLOPTIONS ""
94 #endif
96 #ifdef HAVE_REGEX_H
97 #define REGOPTIONS "\
98  -l, --linespan\n\
99     Allow regex to span newlines (must precede -r or -R)\n\
100  -r, --regex, --ereg=STRING\n\
101     Search page for regex STRING\n\
102  -R, --eregi=STRING\n\
103     Search page for case-insensitive regex STRING\n"
104 #else
105 #define REGOPTIONS ""
106 #endif
108 #define DESCRIPTION "\
109 This plugin will attempt to open an HTTP connection with the host. Successul\n\
110 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
111 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
112 messages from the host result in STATE_WARNING return values.  If you are\n\
113 checking a virtual server that uses \"host headers\" you must supply the FQDN\n\
114 \(fully qualified domain name) as the [host_name] argument.\n"
116 #define SSLDESCRIPTION "\
117 This plugin can also check whether an SSL enabled web server is able to\n\
118 serve content (optionally within a specified time) or whether the X509 \n\
119 certificate is still valid for the specified number of days.\n\n\
120 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
121 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
122 STATE_OK will be returned. When the server returns its content but exceeds\n\
123 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
124 a STATE_CRITICAL will be returned.\n\n\
125 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
126 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
127 STATE_OK is returned. When the certificate is still valid, but for less than\n\
128 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
129 the certificate is expired.\n"
131 #ifdef HAVE_SSL_H
132 #include <rsa.h>
133 #include <crypto.h>
134 #include <x509.h>
135 #include <pem.h>
136 #include <ssl.h>
137 #include <err.h>
138 #include <rand.h>
139 #endif
141 #ifdef HAVE_OPENSSL_SSL_H
142 #include <openssl/rsa.h>
143 #include <openssl/crypto.h>
144 #include <openssl/x509.h>
145 #include <openssl/pem.h>
146 #include <openssl/ssl.h>
147 #include <openssl/err.h>
148 #include <openssl/rand.h>
149 #endif
151 #ifdef HAVE_SSL
152 int check_cert = FALSE;
153 int days_till_exp;
154 unsigned char *randbuff;
155 SSL_CTX *ctx;
156 SSL *ssl;
157 X509 *server_cert;
158 int connect_SSL (void);
159 int check_certificate (X509 **);
160 #endif
162 #ifdef HAVE_REGEX_H
163 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", "qwertyuiopasdfghjkl");
870         RAND_seed (randbuff, strlen (randbuff));
871         /* Initialize SSL context */
872         SSLeay_add_ssl_algorithms ();
873         meth = SSLv23_client_method ();
874         SSL_load_error_strings ();
875         if ((ctx = SSL_CTX_new (meth)) == NULL) {
876                 printf ("ERROR: Cannot create SSL context.\n");
877                 return STATE_CRITICAL;
878         }
880         /* Initialize alarm signal handling */
881         signal (SIGALRM, socket_timeout_alarm_handler);
883         /* Set socket timeout */
884         alarm (socket_timeout);
886         /* Save start time */
887         gettimeofday (&tv, NULL);
889         /* Make TCP connection */
890         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
891                 /* Do the SSL handshake */
892                 if ((ssl = SSL_new (ctx)) != NULL) {
893                         SSL_set_cipher_list(ssl, "ALL");
894                         SSL_set_fd (ssl, sd);
895                         if (SSL_connect (ssl) != -1)
896                                 return OK;
897                         ERR_print_errors_fp (stderr);
898                 }
899                 else {
900                         printf ("ERROR: Cannot initiate SSL handshake.\n");
901                 }
902                 SSL_free (ssl);
903         }
905         SSL_CTX_free (ctx);
906         close (sd);
908         return STATE_CRITICAL;
910 #endif
912 #ifdef HAVE_SSL
913 int
914 check_certificate (X509 ** certificate)
916         ASN1_STRING *tm;
917         int offset;
918         struct tm stamp;
919         int days_left;
922         /* Retrieve timestamp of certificate */
923         tm = X509_get_notAfter (*certificate);
925         /* Generate tm structure to process timestamp */
926         if (tm->type == V_ASN1_UTCTIME) {
927                 if (tm->length < 10) {
928                         printf ("ERROR: Wrong time format in certificate.\n");
929                         return STATE_CRITICAL;
930                 }
931                 else {
932                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
933                         if (stamp.tm_year < 50)
934                                 stamp.tm_year += 100;
935                         offset = 0;
936                 }
937         }
938         else {
939                 if (tm->length < 12) {
940                         printf ("ERROR: Wrong time format in certificate.\n");
941                         return STATE_CRITICAL;
942                 }
943                 else {
944                         stamp.tm_year =
945                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
946                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
947                         stamp.tm_year -= 1900;
948                         offset = 2;
949                 }
950         }
951         stamp.tm_mon =
952                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
953         stamp.tm_mday =
954                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
955         stamp.tm_hour =
956                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
957         stamp.tm_min =
958                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
959         stamp.tm_sec = 0;
960         stamp.tm_isdst = -1;
962         days_left = (mktime (&stamp) - time (NULL)) / 86400;
963         snprintf
964                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
965                  stamp.tm_mon + 1,
966                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
968         if (days_left > 0 && days_left <= days_till_exp) {
969                 printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
970                 return STATE_WARNING;
971         }
972         if (days_left < 0) {
973                 printf ("Certificate expired on %s.\n", timestamp);
974                 return STATE_CRITICAL;
975         }
977         if (days_left == 0) {
978                 printf ("Certificate expires today (%s).\n", timestamp);
979                 return STATE_WARNING;
980         }
982         printf ("Certificate will expire on %s.\n", timestamp);
984         return STATE_OK;
986 #endif
987 \f
990 int
991 my_recv (void)
993         int i;
994 #ifdef HAVE_SSL
995         if (use_ssl) {
996                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
997         }
998         else {
999                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1000         }
1001 #else
1002         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1003 #endif
1004         return i;
1008 int
1009 my_close (void)
1011 #ifdef HAVE_SSL
1012         if (use_ssl == TRUE) {
1013                 SSL_shutdown (ssl);
1014                 SSL_free (ssl);
1015                 SSL_CTX_free (ctx);
1016                 return 0;
1017         }
1018         else {
1019 #endif
1020                 return close (sd);
1021 #ifdef HAVE_SSL
1022         }
1023 #endif
1025 \f
1028 void
1029 print_help (void)
1031         print_revision (progname, REVISION);
1032         printf
1033                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1034                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1035         print_usage ();
1036         printf ("NOTE: One or both of -H and -I must be specified\n");
1037         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1038                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1039 #ifdef HAVE_SSL
1040         printf (SSLDESCRIPTION);
1041 #endif
1045 void
1046 print_usage (void)
1048         printf ("Usage:\n" " %s %s\n"
1049 #ifdef HAVE_GETOPT_H
1050                 " %s (-h | --help) for detailed help\n"
1051                 " %s (-V | --version) for version information\n",
1052 #else
1053                 " %s -h for detailed help\n"
1054                 " %s -V for version information\n",
1055 #endif
1056         progname, OPTIONS, progname, progname);