Code

cleanup to suppress various strict compiler warnings
[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         int option_index = 0;
300         static struct option long_options[] = {
301                 STD_LONG_OPTS,
302                 {"file",required_argument,0,'F'},
303                 {"link", no_argument, 0, 'L'},
304                 {"nohtml", no_argument, 0, 'n'},
305                 {"ssl", no_argument, 0, 'S'},
306                 {"verbose", no_argument, 0, 'v'},
307                 {"post", required_argument, 0, 'P'},
308                 {"IP-address", required_argument, 0, 'I'},
309                 {"string", required_argument, 0, 's'},
310                 {"regex", required_argument, 0, 'r'},
311                 {"ereg", required_argument, 0, 'r'},
312                 {"eregi", required_argument, 0, 'R'},
313                 {"linespan", no_argument, 0, 'l'},
314                 {"onredirect", required_argument, 0, 'f'},
315                 {"certificate", required_argument, 0, 'C'},
316                 {"min", required_argument, 0, 'm'},
317                 {0, 0, 0, 0}
318         };
320         if (argc < 2)
321                 return ERROR;
323         for (c = 1; c < argc; c++) {
324                 if (strcmp ("-to", argv[c]) == 0)
325                         strcpy (argv[c], "-t");
326                 if (strcmp ("-hn", argv[c]) == 0)
327                         strcpy (argv[c], "-H");
328                 if (strcmp ("-wt", argv[c]) == 0)
329                         strcpy (argv[c], "-w");
330                 if (strcmp ("-ct", argv[c]) == 0)
331                         strcpy (argv[c], "-c");
332                 if (strcmp ("-nohtml", argv[c]) == 0)
333                         strcpy (argv[c], "-n");
334         }
336         while (1) {
337                 c = getopt_long (argc, argv, "Vvht:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", long_options, &option_index);
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                 case 'm': /* min_page_length */
469                         min_page_len = atoi (optarg);
470                         break;
471                 }
472         }
474         c = optind;
476         if (strcmp (server_address, "") == 0 && c < argc)
477                         asprintf (&server_address, "%s", argv[c++]);
479         if (strcmp (host_name, "") == 0 && c < argc)
480                 asprintf (&host_name, "%s", argv[c++]);
482         if (strcmp (server_address ,"") == 0) {
483                 if (strcmp (host_name, "") == 0)
484                         usage ("check_http: you must specify a server address or host name\n");
485                 else
486                         asprintf (&server_address, "%s", host_name);
487         }
489         if (check_critical_time && critical_time>(double)socket_timeout)
490                 socket_timeout = (int)critical_time + 1;
492         return TRUE;
494 \f
497 /* written by lauri alanko */
498 static char *
499 base64 (char *bin, int len)
502         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
503         int i = 0, j = 0;
505         char BASE64_END = '=';
506         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
508         while (j < len - 2) {
509                 buf[i++] = base64_table[bin[j] >> 2];
510                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
511                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
512                 buf[i++] = base64_table[bin[j + 2] & 63];
513                 j += 3;
514         }
516         switch (len - j) {
517         case 1:
518                 buf[i++] = base64_table[bin[j] >> 2];
519                 buf[i++] = base64_table[(bin[j] & 3) << 4];
520                 buf[i++] = BASE64_END;
521                 buf[i++] = BASE64_END;
522                 break;
523         case 2:
524                 buf[i++] = base64_table[bin[j] >> 2];
525                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
526                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
527                 buf[i++] = BASE64_END;
528                 break;
529         case 0:
530                 break;
531         }
533         buf[i] = '\0';
534         return buf;
536 \f
539 int
540 check_http (void)
542         char *msg = NULL;
543         char *status_line = "";
544         char *header = NULL;
545         char *page = "";
546         char *auth = NULL;
547         int i = 0;
548         size_t pagesize = 0;
549         char *full_page = "";
550         char *buf = NULL;
551         char *pos = "";
552         char *x = NULL;
553         char *orig_url = NULL;
554         double elapsed_time;
555         int page_len = 0;
556 #ifdef HAVE_SSL
557         int sslerr;
558 #endif
560         /* try to connect to the host at the given port number */
561 #ifdef HAVE_SSL
562         if (use_ssl == TRUE) {
564                 if (connect_SSL () != OK) {
565                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
566                 }
568                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
569                         X509_free (server_cert);
570                 }
571                 else {
572                         printf ("ERROR: Cannot retrieve server certificate.\n");
573                         return STATE_CRITICAL;
574                 }
576         }
577         else {
578 #endif
579                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
580                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
581 #ifdef HAVE_SSL
582         }
583 #endif
585         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
587         /* optionally send the host header info (not clear if it's usable) */
588         if (strcmp (host_name, ""))
589                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
591         /* send user agent */
592         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
593                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
595         /* optionally send the authentication info */
596         if (strcmp (user_auth, "")) {
597                 auth = base64 (user_auth, strlen (user_auth));
598                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
599         }
601         /* either send http POST data */
602         if (strlen (http_post_data)) {
603                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
604                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
605                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
606         }
607         else {
608                 /* or just a newline so the server knows we're done with the request */
609                 asprintf (&buf, "%s%s", buf, CRLF);
610         }
612 #ifdef HAVE_SSL
613         if (use_ssl == TRUE) {
614                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
615                         ERR_print_errors_fp (stderr);
616                         return STATE_CRITICAL;
617                 }
618         }
619         else {
620 #endif
621                 send (sd, buf, strlen (buf), 0);
622 #ifdef HAVE_SSL
623         }
624 #endif
626         /* fetch the page */
627         while ((i = my_recv ()) > 0) {
628                 buffer[i] = '\0';
629                 asprintf (&full_page, "%s%s", full_page, buffer);
630                 pagesize += i;
631         }
633         if (i < 0) {
634 #ifdef HAVE_SSL
635                 sslerr=SSL_get_error(ssl, i);
636                 if ( sslerr == SSL_ERROR_SSL ) {
637                         terminate (STATE_WARNING, "Client Certificate Required\n");
638                 } else {
639                         terminate (STATE_CRITICAL, "Error in recv()");
640                 }
641 #else
642                 terminate (STATE_CRITICAL, "Error in recv()");
643 #endif
644         }
646         /* return a CRITICAL status if we couldn't read any data */
647         if (pagesize == (size_t) 0)
648                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
650         /* close the connection */
651         my_close ();
653         /* reset the alarm */
654         alarm (0);
656         /* leave full_page untouched so we can free it later */
657         page = full_page;
659         if (verbose)
660                 printf ("Page is %d characters\n", pagesize);
662         /* find status line and null-terminate it */
663         status_line = page;
664         page += (size_t) strcspn (page, "\r\n");
665         pos = page;
666         page += (size_t) strspn (page, "\r\n");
667         status_line[strcspn(status_line, "\r\n")] = 0;
668         strip (status_line);
669         if (verbose)
670                 printf ("STATUS: %s\n", status_line);
672         /* find header info and null terminate it */
673         header = page;
674         while (strcspn (page, "\r\n") > 0) {
675                 page += (size_t) strcspn (page, "\r\n");
676                 pos = page;
677                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
678                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
679                         page += (size_t) 2;
680                 else
681                         page += (size_t) 1;
682         }
683         page += (size_t) strspn (page, "\r\n");
684         header[pos - header] = 0;
685         if (verbose)
686                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
688         /* make sure the status line matches the response we are looking for */
689         if (!strstr (status_line, server_expect)) {
690                 if (server_port == HTTP_PORT)
691                         asprintf (&msg, "Invalid HTTP response received from host\n");
692                 else
693                         asprintf (&msg,
694                                         "Invalid HTTP response received from host on port %d\n",
695                                         server_port);
696                 terminate (STATE_CRITICAL, msg);
697         }
700         /* Exit here if server_expect was set by user and not default */
701         if ( server_expect_yn  )  {
702                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
703                           server_expect);
704                 if (verbose)
705                         printf ("%s\n",msg);
707         }
708         else {
709         
711                 /* check the return code */
712                 /* server errors result in a critical state */
713                 if (strstr (status_line, "500") ||
714                   strstr (status_line, "501") ||
715                 strstr (status_line, "502") ||
716                     strstr (status_line, "503")) {
717                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
718                 }
720                 /* client errors result in a warning state */
721                 if (strstr (status_line, "400") ||
722                   strstr (status_line, "401") ||
723                 strstr (status_line, "402") ||
724                     strstr (status_line, "403") ||
725                     strstr (status_line, "404")) {
726                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
727                 }
729                 /* check redirected page if specified */
730                 if (strstr (status_line, "300") ||
731                   strstr (status_line, "301") ||
732                 strstr (status_line, "302") ||
733                     strstr (status_line, "303") ||
734                     strstr (status_line, "304")) {
735                         if (onredirect == STATE_DEPENDENT) {
737                                 asprintf (&orig_url, "%s", server_url);
738                                 pos = header;
739                                 while (pos) {
740                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
741                                         if (server_address == NULL)
742                                                 terminate (STATE_UNKNOWN,
743                                                                                  "HTTP UNKNOWN: could not allocate server_address");
744                                         if (strcspn (pos, "\r\n") > server_url_length) {
745                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
746                                                 if (server_url == NULL)
747                                                         terminate (STATE_UNKNOWN,
748                                                                    "HTTP UNKNOWN: could not allocate server_url");
749                                                 server_url_length = strcspn (pos, "\r\n");
750                                         }
751                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
752                                                 asprintf (&host_name, "%s", server_address);
753                                                 use_ssl = server_type_check (server_type);
754                                                 server_port = atoi (server_port_text);
755                                                 check_http ();
756                                         }
757                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
758                                                 asprintf (&host_name, "%s", server_address);
759                                                 use_ssl = server_type_check (server_type);
760                                                 server_port = server_port_check (use_ssl);
761                                                 check_http ();
762                                         }
763                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
764                                                 asprintf (&host_name, "%s", server_address);
765                                                 strcpy (server_url, "/");
766                                                 use_ssl = server_type_check (server_type);
767                                                 server_port = atoi (server_port_text);
768                                                 check_http ();
769                                         }
770                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
771                                                 asprintf (&host_name, "%s", server_address);
772                                                 strcpy (server_url, "/");
773                                                 use_ssl = server_type_check (server_type);
774                                                 server_port = server_port_check (use_ssl);
775                                                 check_http ();
776                                         }
777                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
778                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
779                                                         *x = '\0';
780                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
781                                                 }
782                                                 check_http ();
783                                         }                                       
784                                         pos += (size_t) strcspn (pos, "\r\n");
785                                         pos += (size_t) strspn (pos, "\r\n");
786                                 } /* end while (pos) */
787                                 printf ("UNKNOWN - Could not find redirect location - %s%s",
788                                         status_line, (display_html ? "</A>" : ""));
789                                 exit (STATE_UNKNOWN);
790                         } /* end if (onredirect == STATE_DEPENDENT) */
791                         
792                         else if (onredirect == STATE_UNKNOWN)
793                                 printf ("UNKNOWN");
794                         else if (onredirect == STATE_OK)
795                                 printf ("OK");
796                         else if (onredirect == STATE_WARNING)
797                                 printf ("WARNING");
798                         else if (onredirect == STATE_CRITICAL)
799                                 printf ("CRITICAL");
800                         elapsed_time = delta_time (tv);
801                         asprintf (&msg, " - %s - %.3f second response time %s%s|time=%.3f\n",
802                                  status_line, elapsed_time, timestamp,
803                            (display_html ? "</A>" : ""), elapsed_time);
804                         terminate (onredirect, msg);
805                 } /* end if (strstr (status_line, "30[0-4]") */
808         } /* end else (server_expect_yn)  */
810                 
811         /* check elapsed time */
812         elapsed_time = delta_time (tv);
813         asprintf (&msg, "HTTP problem: %s - %.3f second response time %s%s|time=%.3f\n",
814                        status_line, elapsed_time, timestamp,
815                        (display_html ? "</A>" : ""), elapsed_time);
816         if (check_critical_time == TRUE && elapsed_time > critical_time)
817                 terminate (STATE_CRITICAL, msg);
818         if (check_warning_time == TRUE && elapsed_time > warning_time)
819                 terminate (STATE_WARNING, msg);
821         /* Page and Header content checks go here */
822         /* these checks should be last */
824         if (strlen (string_expect)) {
825                 if (strstr (page, string_expect)) {
826                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
827                                 status_line, elapsed_time,
828                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
829                         exit (STATE_OK);
830                 }
831                 else {
832                         printf ("CRITICAL - string not found%s|time=%.3f\n",
833                                 (display_html ? "</A>" : ""), elapsed_time);
834                         exit (STATE_CRITICAL);
835                 }
836         }
837 #ifdef HAVE_REGEX_H
838         if (strlen (regexp)) {
839                 errcode = regexec (&preg, page, REGS, pmatch, 0);
840                 if (errcode == 0) {
841                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
842                                 status_line, elapsed_time,
843                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
844                         exit (STATE_OK);
845                 }
846                 else {
847                         if (errcode == REG_NOMATCH) {
848                                 printf ("CRITICAL - pattern not found%s|time=%.3f\n",
849                                         (display_html ? "</A>" : ""), elapsed_time);
850                                 exit (STATE_CRITICAL);
851                         }
852                         else {
853                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
854                                 printf ("CRITICAL - Execute Error: %s\n", errbuf);
855                                 exit (STATE_CRITICAL);
856                         }
857                 }
858         }
859 #endif
861         /* make sure the page is of an appropriate size */
862         page_len = strlen (page);
863         if ((min_page_len > 0) && (page_len < min_page_len)) {
864                 printf ("HTTP WARNING: page size too small%s|size=%i\n",
865                         (display_html ? "</A>" : ""), page_len );
866                 exit (STATE_WARNING);
867         }
868         /* We only get here if all tests have been passed */
869         asprintf (&msg, "HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
870                         status_line, (float)elapsed_time,
871                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
872         terminate (STATE_OK, msg);
873         return STATE_UNKNOWN;
878 #ifdef HAVE_SSL
879 int connect_SSL (void)
881         SSL_METHOD *meth;
883         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
884         RAND_seed (randbuff, strlen (randbuff));
885         if (verbose)
886                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
888         /* Initialize SSL context */
889         SSLeay_add_ssl_algorithms ();
890         meth = SSLv23_client_method ();
891         SSL_load_error_strings ();
892         if ((ctx = SSL_CTX_new (meth)) == NULL) {
893                 printf ("CRITICAL -  Cannot create SSL context.\n");
894                 return STATE_CRITICAL;
895         }
897         /* Initialize alarm signal handling */
898         signal (SIGALRM, socket_timeout_alarm_handler);
900         /* Set socket timeout */
901         alarm (socket_timeout);
903         /* Save start time */
904         gettimeofday (&tv, NULL);
906         /* Make TCP connection */
907         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
908                 /* Do the SSL handshake */
909                 if ((ssl = SSL_new (ctx)) != NULL) {
910                         SSL_set_cipher_list(ssl, "ALL");
911                         SSL_set_fd (ssl, sd);
912                         if (SSL_connect (ssl) != -1)
913                                 return OK;
914                         ERR_print_errors_fp (stderr);
915                 }
916                 else {
917                         printf ("CRITICAL - Cannot initiate SSL handshake.\n");
918                 }
919                 SSL_free (ssl);
920         }
922         SSL_CTX_free (ctx);
923         close (sd);
925         return STATE_CRITICAL;
927 #endif
929 #ifdef HAVE_SSL
930 int
931 check_certificate (X509 ** certificate)
933         ASN1_STRING *tm;
934         int offset;
935         struct tm stamp;
936         int days_left;
939         /* Retrieve timestamp of certificate */
940         tm = X509_get_notAfter (*certificate);
942         /* Generate tm structure to process timestamp */
943         if (tm->type == V_ASN1_UTCTIME) {
944                 if (tm->length < 10) {
945                         printf ("CRITICAL - Wrong time format in certificate.\n");
946                         return STATE_CRITICAL;
947                 }
948                 else {
949                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
950                         if (stamp.tm_year < 50)
951                                 stamp.tm_year += 100;
952                         offset = 0;
953                 }
954         }
955         else {
956                 if (tm->length < 12) {
957                         printf ("CRITICAL - Wrong time format in certificate.\n");
958                         return STATE_CRITICAL;
959                 }
960                 else {
961                         stamp.tm_year =
962                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
963                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
964                         stamp.tm_year -= 1900;
965                         offset = 2;
966                 }
967         }
968         stamp.tm_mon =
969                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
970         stamp.tm_mday =
971                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
972         stamp.tm_hour =
973                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
974         stamp.tm_min =
975                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
976         stamp.tm_sec = 0;
977         stamp.tm_isdst = -1;
979         days_left = (mktime (&stamp) - time (NULL)) / 86400;
980         snprintf
981                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
982                  stamp.tm_mon + 1,
983                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
985         if (days_left > 0 && days_left <= days_till_exp) {
986                 printf ("WARNING - Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
987                 return STATE_WARNING;
988         }
989         if (days_left < 0) {
990                 printf ("CRITICAL - Certificate expired on %s.\n", timestamp);
991                 return STATE_CRITICAL;
992         }
994         if (days_left == 0) {
995                 printf ("WARNING - Certificate expires today (%s).\n", timestamp);
996                 return STATE_WARNING;
997         }
999         printf ("OK - Certificate will expire on %s.\n", timestamp);
1001         return STATE_OK;
1003 #endif
1004 \f
1007 int
1008 my_recv (void)
1010         int i;
1011 #ifdef HAVE_SSL
1012         if (use_ssl) {
1013                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1014         }
1015         else {
1016                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1017         }
1018 #else
1019         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1020 #endif
1021         return i;
1025 int
1026 my_close (void)
1028 #ifdef HAVE_SSL
1029         if (use_ssl == TRUE) {
1030                 SSL_shutdown (ssl);
1031                 SSL_free (ssl);
1032                 SSL_CTX_free (ctx);
1033                 return 0;
1034         }
1035         else {
1036 #endif
1037                 return close (sd);
1038 #ifdef HAVE_SSL
1039         }
1040 #endif
1042 \f
1045 void
1046 print_help (void)
1048         print_revision (progname, REVISION);
1049         printf
1050                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1051                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1052         print_usage ();
1053         printf ("NOTE: One or both of -H and -I must be specified\n");
1054         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1055                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1056 #ifdef HAVE_SSL
1057         printf (SSLDESCRIPTION);
1058 #endif
1062 void
1063 print_usage (void)
1065         printf ("\
1066 Usage:\n\
1067  %s %s\n\
1068  %s (-h | --help) for detailed help\n\
1069  %s (-V | --version) for version information\n",
1070         progname, OPTIONS, progname, progname);