Code

add remaining URI_PATH chars per RFC 2396
[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 /* per RFC 2396 */
186  
187 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
188 #define URI_HTTP "%[HTPShtps]://"
189 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
190 #define URI_PORT ":%[0123456789]"
191 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
193 enum {
194         MAX_IPV4_HOSTLENGTH = 255,
195         HTTP_PORT = 80,
196         HTTPS_PORT = 443
197 };
199 #define HTTP_EXPECT "HTTP/1."
200 #define HTTP_URL "/"
201 #define CRLF "\r\n"
203 char timestamp[17] = "";
204 int specify_port = FALSE;
205 int server_port = HTTP_PORT;
206 char server_port_text[6] = "";
207 char server_type[6] = "http";
208 char *server_address = ""; 
209 char *host_name = "";
210 char *server_url = "";
211 int server_url_length;
212 int server_expect_yn = 0;
213 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
214 char string_expect[MAX_INPUT_BUFFER] = "";
215 double warning_time = 0;
216 int check_warning_time = FALSE;
217 double critical_time = 0;
218 int check_critical_time = FALSE;
219 char user_auth[MAX_INPUT_BUFFER] = "";
220 int display_html = FALSE;
221 int onredirect = STATE_OK;
222 int use_ssl = FALSE;
223 int verbose = FALSE;
224 int sd;
225 int min_page_len = 0;
226 char *http_method = "GET";
227 char *http_post_data = "";
228 char buffer[MAX_INPUT_BUFFER];
230 void print_usage (void);
231 void print_help (void);
232 int process_arguments (int, char **);
233 static char *base64 (char *bin, int len);
234 int check_http (void);
235 int my_recv (void);
236 int my_close (void);
238 int
239 main (int argc, char **argv)
241         int result = STATE_UNKNOWN;
243         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
244         asprintf (&server_url, "%s", HTTP_URL);
245         server_url_length = strlen(server_url);
247         if (process_arguments (argc, argv) == ERROR)
248                 usage ("check_http: could not parse arguments\n");
250         if (strstr (timestamp, ":")) {
251                 if (strstr (server_url, "?"))
252                         asprintf (&server_url, "%s&%s", server_url, timestamp);
253                 else
254                         asprintf (&server_url, "%s?%s", server_url, timestamp);
255         }
257         if (display_html == TRUE)
258                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
259                         host_name, server_port, server_url);
261         /* initialize alarm signal handling, set socket timeout, start timer */
262         (void) signal (SIGALRM, socket_timeout_alarm_handler);
263         (void) alarm (socket_timeout);
264         gettimeofday (&tv, NULL);
266 #ifdef HAVE_SSL
267         if (use_ssl && check_cert == TRUE) {
268                 if (connect_SSL () != OK)
269                         terminate (STATE_CRITICAL,
270                                    "HTTP CRITICAL - Could not make SSL connection\n");
271                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
272                         result = check_certificate (&server_cert);
273                         X509_free (server_cert);
274                 }
275                 else {
276                         printf ("ERROR: Cannot retrieve server certificate.\n");
277                         result = STATE_CRITICAL;
278                 }
279                 SSL_shutdown (ssl);
280                 SSL_free (ssl);
281                 SSL_CTX_free (ctx);
282                 close (sd);
283         }
284         else {
285                 result = check_http ();
286         }
287 #else
288         result = check_http ();
289 #endif
290         return result;
292 \f
295 /* process command-line arguments */
296 int
297 process_arguments (int argc, char **argv)
299         int c = 1;
301         int option_index = 0;
302         static struct option long_options[] = {
303                 STD_LONG_OPTS,
304                 {"file",required_argument,0,'F'},
305                 {"link", no_argument, 0, 'L'},
306                 {"nohtml", no_argument, 0, 'n'},
307                 {"ssl", no_argument, 0, 'S'},
308                 {"verbose", no_argument, 0, 'v'},
309                 {"post", required_argument, 0, 'P'},
310                 {"IP-address", required_argument, 0, 'I'},
311                 {"string", required_argument, 0, 's'},
312                 {"regex", required_argument, 0, 'r'},
313                 {"ereg", required_argument, 0, 'r'},
314                 {"eregi", required_argument, 0, 'R'},
315                 {"linespan", no_argument, 0, 'l'},
316                 {"onredirect", required_argument, 0, 'f'},
317                 {"certificate", required_argument, 0, 'C'},
318                 {"min", required_argument, 0, 'm'},
319                 {0, 0, 0, 0}
320         };
322         if (argc < 2)
323                 return ERROR;
325         for (c = 1; c < argc; c++) {
326                 if (strcmp ("-to", argv[c]) == 0)
327                         strcpy (argv[c], "-t");
328                 if (strcmp ("-hn", argv[c]) == 0)
329                         strcpy (argv[c], "-H");
330                 if (strcmp ("-wt", argv[c]) == 0)
331                         strcpy (argv[c], "-w");
332                 if (strcmp ("-ct", argv[c]) == 0)
333                         strcpy (argv[c], "-c");
334                 if (strcmp ("-nohtml", argv[c]) == 0)
335                         strcpy (argv[c], "-n");
336         }
338         while (1) {
339                 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);
340                 if (c == -1 || c == EOF)
341                         break;
343                 switch (c) {
344                 case '?': /* usage */
345                         usage2 ("unknown argument", optarg);
346                         break;
347                 case 'h': /* help */
348                         print_help ();
349                         exit (STATE_OK);
350                         break;
351                 case 'V': /* version */
352                         print_revision (progname, REVISION);
353                         exit (STATE_OK);
354                         break;
355                 case 't': /* timeout period */
356                         if (!is_intnonneg (optarg))
357                                 usage2 ("timeout interval must be a non-negative integer", optarg);
358                         socket_timeout = atoi (optarg);
359                         break;
360                 case 'c': /* critical time threshold */
361                         if (!is_intnonneg (optarg))
362                                 usage2 ("invalid critical threshold", optarg);
363                         critical_time = strtod (optarg, NULL);
364                         check_critical_time = TRUE;
365                         break;
366                 case 'w': /* warning time threshold */
367                         if (!is_intnonneg (optarg))
368                                 usage2 ("invalid warning threshold", optarg);
369                         warning_time = strtod (optarg, NULL);
370                         check_warning_time = TRUE;
371                         break;
372                 case 'L': /* show html link */
373                         display_html = TRUE;
374                         break;
375                 case 'n': /* do not show html link */
376                         display_html = FALSE;
377                         break;
378                 case 'S': /* use SSL */
379 #ifndef HAVE_SSL
380                         usage ("check_http: invalid option - SSL is not available\n");
381 #endif
382                         use_ssl = TRUE;
383                         if (specify_port == FALSE)
384                                 server_port = HTTPS_PORT;
385                         break;
386                 case 'C': /* Check SSL cert validity */
387 #ifdef HAVE_SSL
388                         if (!is_intnonneg (optarg))
389                                 usage2 ("invalid certificate expiration period", optarg);
390                         days_till_exp = atoi (optarg);
391                         check_cert = TRUE;
392 #else
393                         usage ("check_http: invalid option - SSL is not available\n");
394 #endif
395                         break;
396                 case 'f': /* onredirect */
397                         if (!strcmp (optarg, "follow"))
398                                 onredirect = STATE_DEPENDENT;
399                         if (!strcmp (optarg, "unknown"))
400                                 onredirect = STATE_UNKNOWN;
401                         if (!strcmp (optarg, "ok"))
402                                 onredirect = STATE_OK;
403                         if (!strcmp (optarg, "warning"))
404                                 onredirect = STATE_WARNING;
405                         if (!strcmp (optarg, "critical"))
406                                 onredirect = STATE_CRITICAL;
407                         if (verbose)
408                                 printf("option f:%d \n", onredirect);  
409                         break;
410                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
411                 case 'H': /* Host Name (virtual host) */
412                         asprintf (&host_name, "%s", optarg);
413                         break;
414                 case 'I': /* Server IP-address */
415                         asprintf (&server_address, "%s", optarg);
416                         break;
417                 case 'u': /* Host or server */
418                         asprintf (&server_url, "%s", optarg);
419                         server_url_length = strlen (server_url);
420                         break;
421                 case 'p': /* Host or server */
422                         if (!is_intnonneg (optarg))
423                                 usage2 ("invalid port number", optarg);
424                         server_port = atoi (optarg);
425                         specify_port = TRUE;
426                         break;
427                 case 'a': /* authorization info */
428                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
429                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
430                         break;
431                 case 'P': /* HTTP POST data in URL encoded format */
432                         asprintf (&http_method, "%s", "POST");
433                         asprintf (&http_post_data, "%s", optarg);
434                         break;
435                 case 's': /* string or substring */
436                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
437                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
438                         break;
439                 case 'e': /* string or substring */
440                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
441                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
442                         server_expect_yn = 1;
443                         break;
444 #ifndef HAVE_REGEX_H
445                 case 'l': /* linespan */
446                 case 'r': /* linespan */
447                 case 'R': /* linespan */
448                         usage ("check_http: call for regex which was not a compiled option\n");
449                         break;
450 #else
451                 case 'l': /* linespan */
452                         cflags &= ~REG_NEWLINE;
453                         break;
454                 case 'R': /* regex */
455                         cflags |= REG_ICASE;
456                 case 'r': /* regex */
457                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
458                         regexp[MAX_RE_SIZE - 1] = 0;
459                         errcode = regcomp (&preg, regexp, cflags);
460                         if (errcode != 0) {
461                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
462                                 printf ("Could Not Compile Regular Expression: %s", errbuf);
463                                 return ERROR;
464                         }
465                         break;
466 #endif
467                 case 'v': /* verbose */
468                         verbose = TRUE;
469                         break;
470                 case 'm': /* min_page_length */
471                         min_page_len = atoi (optarg);
472                         break;
473                 }
474         }
476         c = optind;
478         if (strcmp (server_address, "") == 0 && c < argc)
479                         asprintf (&server_address, "%s", argv[c++]);
481         if (strcmp (host_name, "") == 0 && c < argc)
482                 asprintf (&host_name, "%s", argv[c++]);
484         if (strcmp (server_address ,"") == 0) {
485                 if (strcmp (host_name, "") == 0)
486                         usage ("check_http: you must specify a server address or host name\n");
487                 else
488                         asprintf (&server_address, "%s", host_name);
489         }
491         if (check_critical_time && critical_time>(double)socket_timeout)
492                 socket_timeout = (int)critical_time + 1;
494         return TRUE;
496 \f
499 /* written by lauri alanko */
500 static char *
501 base64 (char *bin, int len)
504         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
505         int i = 0, j = 0;
507         char BASE64_END = '=';
508         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
510         while (j < len - 2) {
511                 buf[i++] = base64_table[bin[j] >> 2];
512                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
513                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
514                 buf[i++] = base64_table[bin[j + 2] & 63];
515                 j += 3;
516         }
518         switch (len - j) {
519         case 1:
520                 buf[i++] = base64_table[bin[j] >> 2];
521                 buf[i++] = base64_table[(bin[j] & 3) << 4];
522                 buf[i++] = BASE64_END;
523                 buf[i++] = BASE64_END;
524                 break;
525         case 2:
526                 buf[i++] = base64_table[bin[j] >> 2];
527                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
528                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
529                 buf[i++] = BASE64_END;
530                 break;
531         case 0:
532                 break;
533         }
535         buf[i] = '\0';
536         return buf;
538 \f
541 int
542 check_http (void)
544         char *msg = NULL;
545         char *status_line = "";
546         char *header = NULL;
547         char *page = "";
548         char *auth = NULL;
549         int i = 0;
550         size_t pagesize = 0;
551         char *full_page = "";
552         char *buf = NULL;
553         char *pos = "";
554         char *x = NULL;
555         char *orig_url = NULL;
556         double elapsed_time;
557         int page_len = 0;
558 #ifdef HAVE_SSL
559         int sslerr;
560 #endif
562         /* try to connect to the host at the given port number */
563 #ifdef HAVE_SSL
564         if (use_ssl == TRUE) {
566                 if (connect_SSL () != OK) {
567                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
568                 }
570                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
571                         X509_free (server_cert);
572                 }
573                 else {
574                         printf ("ERROR: Cannot retrieve server certificate.\n");
575                         return STATE_CRITICAL;
576                 }
578         }
579         else {
580 #endif
581                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
582                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
583 #ifdef HAVE_SSL
584         }
585 #endif
587         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
589         /* optionally send the host header info (not clear if it's usable) */
590         if (strcmp (host_name, ""))
591                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
593         /* send user agent */
594         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
595                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
597         /* optionally send the authentication info */
598         if (strcmp (user_auth, "")) {
599                 auth = base64 (user_auth, strlen (user_auth));
600                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
601         }
603         /* either send http POST data */
604         if (strlen (http_post_data)) {
605                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
606                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
607                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
608         }
609         else {
610                 /* or just a newline so the server knows we're done with the request */
611                 asprintf (&buf, "%s%s", buf, CRLF);
612         }
614 #ifdef HAVE_SSL
615         if (use_ssl == TRUE) {
616                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
617                         ERR_print_errors_fp (stderr);
618                         return STATE_CRITICAL;
619                 }
620         }
621         else {
622 #endif
623                 send (sd, buf, strlen (buf), 0);
624 #ifdef HAVE_SSL
625         }
626 #endif
628         /* fetch the page */
629         while ((i = my_recv ()) > 0) {
630                 buffer[i] = '\0';
631                 asprintf (&full_page, "%s%s", full_page, buffer);
632                 pagesize += i;
633         }
635         if (i < 0 && errno != ECONNRESET) {
636 #ifdef HAVE_SSL
637                 if (use_ssl) {
638                         sslerr=SSL_get_error(ssl, i);
639                         if ( sslerr == SSL_ERROR_SSL ) {
640                                 terminate (STATE_WARNING, "Client Certificate Required\n");
641                         } else {
642                                 terminate (STATE_CRITICAL, "Error in recv()");
643                         }
644                 }
645                 else {
646 #endif
647                         terminate (STATE_CRITICAL, "Error in recv()");
648 #ifdef HAVE_SSL
649                 }
650 #endif
651         }
653         /* return a CRITICAL status if we couldn't read any data */
654         if (pagesize == (size_t) 0)
655                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
657         /* close the connection */
658         my_close ();
660         /* reset the alarm */
661         alarm (0);
663         /* leave full_page untouched so we can free it later */
664         page = full_page;
666         if (verbose)
667                 printf ("Page is %d characters\n", pagesize);
669         /* find status line and null-terminate it */
670         status_line = page;
671         page += (size_t) strcspn (page, "\r\n");
672         pos = page;
673         page += (size_t) strspn (page, "\r\n");
674         status_line[strcspn(status_line, "\r\n")] = 0;
675         strip (status_line);
676         if (verbose)
677                 printf ("STATUS: %s\n", status_line);
679         /* find header info and null terminate it */
680         header = page;
681         while (strcspn (page, "\r\n") > 0) {
682                 page += (size_t) strcspn (page, "\r\n");
683                 pos = page;
684                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
685                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
686                         page += (size_t) 2;
687                 else
688                         page += (size_t) 1;
689         }
690         page += (size_t) strspn (page, "\r\n");
691         header[pos - header] = 0;
692         if (verbose)
693                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
695         /* make sure the status line matches the response we are looking for */
696         if (!strstr (status_line, server_expect)) {
697                 if (server_port == HTTP_PORT)
698                         asprintf (&msg, "Invalid HTTP response received from host\n");
699                 else
700                         asprintf (&msg,
701                                         "Invalid HTTP response received from host on port %d\n",
702                                         server_port);
703                 terminate (STATE_CRITICAL, msg);
704         }
707         /* Exit here if server_expect was set by user and not default */
708         if ( server_expect_yn  )  {
709                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
710                           server_expect);
711                 if (verbose)
712                         printf ("%s\n",msg);
714         }
715         else {
716         
718                 /* check the return code */
719                 /* server errors result in a critical state */
720                 if (strstr (status_line, "500") ||
721                   strstr (status_line, "501") ||
722                 strstr (status_line, "502") ||
723                     strstr (status_line, "503")) {
724                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
725                 }
727                 /* client errors result in a warning state */
728                 if (strstr (status_line, "400") ||
729                   strstr (status_line, "401") ||
730                 strstr (status_line, "402") ||
731                     strstr (status_line, "403") ||
732                     strstr (status_line, "404")) {
733                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
734                 }
736                 /* check redirected page if specified */
737                 if (strstr (status_line, "300") ||
738                   strstr (status_line, "301") ||
739                 strstr (status_line, "302") ||
740                     strstr (status_line, "303") ||
741                     strstr (status_line, "304")) {
742                         if (onredirect == STATE_DEPENDENT) {
744                                 asprintf (&orig_url, "%s", server_url);
745                                 pos = header;
746                                 while (pos) {
747                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
748                                         if (server_address == NULL)
749                                                 terminate (STATE_UNKNOWN,
750                                                                                  "HTTP UNKNOWN: could not allocate server_address");
751                                         if (strcspn (pos, "\r\n") > server_url_length) {
752                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
753                                                 if (server_url == NULL)
754                                                         terminate (STATE_UNKNOWN,
755                                                                    "HTTP UNKNOWN: could not allocate server_url");
756                                                 server_url_length = strcspn (pos, "\r\n");
757                                         }
758                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
759                                                 asprintf (&host_name, "%s", server_address);
760                                                 use_ssl = server_type_check (server_type);
761                                                 server_port = atoi (server_port_text);
762                                                 check_http ();
763                                         }
764                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
765                                                 asprintf (&host_name, "%s", server_address);
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_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
771                                                 asprintf (&host_name, "%s", server_address);
772                                                 strcpy (server_url, "/");
773                                                 use_ssl = server_type_check (server_type);
774                                                 server_port = atoi (server_port_text);
775                                                 check_http ();
776                                         }
777                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
778                                                 asprintf (&host_name, "%s", server_address);
779                                                 strcpy (server_url, "/");
780                                                 use_ssl = server_type_check (server_type);
781                                                 server_port = server_port_check (use_ssl);
782                                                 check_http ();
783                                         }
784                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
785                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
786                                                         *x = '\0';
787                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
788                                                 }
789                                                 check_http ();
790                                         }                                       
791                                         pos += (size_t) strcspn (pos, "\r\n");
792                                         pos += (size_t) strspn (pos, "\r\n");
793                                 } /* end while (pos) */
794                                 printf ("UNKNOWN - Could not find redirect location - %s%s",
795                                         status_line, (display_html ? "</A>" : ""));
796                                 exit (STATE_UNKNOWN);
797                         } /* end if (onredirect == STATE_DEPENDENT) */
798                         
799                         else if (onredirect == STATE_UNKNOWN)
800                                 printf ("UNKNOWN");
801                         else if (onredirect == STATE_OK)
802                                 printf ("OK");
803                         else if (onredirect == STATE_WARNING)
804                                 printf ("WARNING");
805                         else if (onredirect == STATE_CRITICAL)
806                                 printf ("CRITICAL");
807                         elapsed_time = delta_time (tv);
808                         asprintf (&msg, " - %s - %.3f second response time %s%s|time=%.3f\n",
809                                  status_line, elapsed_time, timestamp,
810                            (display_html ? "</A>" : ""), elapsed_time);
811                         terminate (onredirect, msg);
812                 } /* end if (strstr (status_line, "30[0-4]") */
815         } /* end else (server_expect_yn)  */
817                 
818         /* check elapsed time */
819         elapsed_time = delta_time (tv);
820         asprintf (&msg, "HTTP problem: %s - %.3f second response time %s%s|time=%.3f\n",
821                        status_line, elapsed_time, timestamp,
822                        (display_html ? "</A>" : ""), elapsed_time);
823         if (check_critical_time == TRUE && elapsed_time > critical_time)
824                 terminate (STATE_CRITICAL, msg);
825         if (check_warning_time == TRUE && elapsed_time > warning_time)
826                 terminate (STATE_WARNING, msg);
828         /* Page and Header content checks go here */
829         /* these checks should be last */
831         if (strlen (string_expect)) {
832                 if (strstr (page, string_expect)) {
833                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
834                                 status_line, elapsed_time,
835                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
836                         exit (STATE_OK);
837                 }
838                 else {
839                         printf ("CRITICAL - string not found%s|time=%.3f\n",
840                                 (display_html ? "</A>" : ""), elapsed_time);
841                         exit (STATE_CRITICAL);
842                 }
843         }
844 #ifdef HAVE_REGEX_H
845         if (strlen (regexp)) {
846                 errcode = regexec (&preg, page, REGS, pmatch, 0);
847                 if (errcode == 0) {
848                         printf ("HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
849                                 status_line, elapsed_time,
850                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
851                         exit (STATE_OK);
852                 }
853                 else {
854                         if (errcode == REG_NOMATCH) {
855                                 printf ("CRITICAL - pattern not found%s|time=%.3f\n",
856                                         (display_html ? "</A>" : ""), elapsed_time);
857                                 exit (STATE_CRITICAL);
858                         }
859                         else {
860                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
861                                 printf ("CRITICAL - Execute Error: %s\n", errbuf);
862                                 exit (STATE_CRITICAL);
863                         }
864                 }
865         }
866 #endif
868         /* make sure the page is of an appropriate size */
869         page_len = strlen (page);
870         if ((min_page_len > 0) && (page_len < min_page_len)) {
871                 printf ("HTTP WARNING: page size too small%s|size=%i\n",
872                         (display_html ? "</A>" : ""), page_len );
873                 exit (STATE_WARNING);
874         }
875         /* We only get here if all tests have been passed */
876         asprintf (&msg, "HTTP OK %s - %.3f second response time %s%s|time=%.3f\n",
877                         status_line, (float)elapsed_time,
878                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
879         terminate (STATE_OK, msg);
880         return STATE_UNKNOWN;
885 #ifdef HAVE_SSL
886 int connect_SSL (void)
888         SSL_METHOD *meth;
890         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
891         RAND_seed (randbuff, strlen (randbuff));
892         if (verbose)
893                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
895         /* Initialize SSL context */
896         SSLeay_add_ssl_algorithms ();
897         meth = SSLv23_client_method ();
898         SSL_load_error_strings ();
899         if ((ctx = SSL_CTX_new (meth)) == NULL) {
900                 printf ("CRITICAL -  Cannot create SSL context.\n");
901                 return STATE_CRITICAL;
902         }
904         /* Initialize alarm signal handling */
905         signal (SIGALRM, socket_timeout_alarm_handler);
907         /* Set socket timeout */
908         alarm (socket_timeout);
910         /* Save start time */
911         gettimeofday (&tv, NULL);
913         /* Make TCP connection */
914         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
915                 /* Do the SSL handshake */
916                 if ((ssl = SSL_new (ctx)) != NULL) {
917                         SSL_set_cipher_list(ssl, "ALL");
918                         SSL_set_fd (ssl, sd);
919                         if (SSL_connect (ssl) != -1)
920                                 return OK;
921                         ERR_print_errors_fp (stderr);
922                 }
923                 else {
924                         printf ("CRITICAL - Cannot initiate SSL handshake.\n");
925                 }
926                 SSL_free (ssl);
927         }
929         SSL_CTX_free (ctx);
930         close (sd);
932         return STATE_CRITICAL;
934 #endif
936 #ifdef HAVE_SSL
937 int
938 check_certificate (X509 ** certificate)
940         ASN1_STRING *tm;
941         int offset;
942         struct tm stamp;
943         int days_left;
946         /* Retrieve timestamp of certificate */
947         tm = X509_get_notAfter (*certificate);
949         /* Generate tm structure to process timestamp */
950         if (tm->type == V_ASN1_UTCTIME) {
951                 if (tm->length < 10) {
952                         printf ("CRITICAL - Wrong time format in certificate.\n");
953                         return STATE_CRITICAL;
954                 }
955                 else {
956                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
957                         if (stamp.tm_year < 50)
958                                 stamp.tm_year += 100;
959                         offset = 0;
960                 }
961         }
962         else {
963                 if (tm->length < 12) {
964                         printf ("CRITICAL - Wrong time format in certificate.\n");
965                         return STATE_CRITICAL;
966                 }
967                 else {
968                         stamp.tm_year =
969                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
970                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
971                         stamp.tm_year -= 1900;
972                         offset = 2;
973                 }
974         }
975         stamp.tm_mon =
976                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
977         stamp.tm_mday =
978                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
979         stamp.tm_hour =
980                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
981         stamp.tm_min =
982                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
983         stamp.tm_sec = 0;
984         stamp.tm_isdst = -1;
986         days_left = (mktime (&stamp) - time (NULL)) / 86400;
987         snprintf
988                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
989                  stamp.tm_mon + 1,
990                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
992         if (days_left > 0 && days_left <= days_till_exp) {
993                 printf ("WARNING - Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
994                 return STATE_WARNING;
995         }
996         if (days_left < 0) {
997                 printf ("CRITICAL - Certificate expired on %s.\n", timestamp);
998                 return STATE_CRITICAL;
999         }
1001         if (days_left == 0) {
1002                 printf ("WARNING - Certificate expires today (%s).\n", timestamp);
1003                 return STATE_WARNING;
1004         }
1006         printf ("OK - Certificate will expire on %s.\n", timestamp);
1008         return STATE_OK;
1010 #endif
1011 \f
1014 int
1015 my_recv (void)
1017         int i;
1018 #ifdef HAVE_SSL
1019         if (use_ssl) {
1020                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1021         }
1022         else {
1023                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1024         }
1025 #else
1026         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1027 #endif
1028         return i;
1032 int
1033 my_close (void)
1035 #ifdef HAVE_SSL
1036         if (use_ssl == TRUE) {
1037                 SSL_shutdown (ssl);
1038                 SSL_free (ssl);
1039                 SSL_CTX_free (ctx);
1040                 return 0;
1041         }
1042         else {
1043 #endif
1044                 return close (sd);
1045 #ifdef HAVE_SSL
1046         }
1047 #endif
1049 \f
1052 void
1053 print_help (void)
1055         print_revision (progname, REVISION);
1056         printf
1057                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1058                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1059         print_usage ();
1060         printf ("NOTE: One or both of -H and -I must be specified\n");
1061         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1062                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1063 #ifdef HAVE_SSL
1064         printf (SSLDESCRIPTION);
1065 #endif
1069 void
1070 print_usage (void)
1072         printf ("\
1073 Usage:\n\
1074  %s %s\n\
1075  %s (-h | --help) for detailed help\n\
1076  %s (-V | --version) for version information\n",
1077         progname, OPTIONS, progname, progname);