Code

Clean up of output from plugin
[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         return TRUE;
491 \f
494 /* written by lauri alanko */
495 static char *
496 base64 (char *bin, int len)
499         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
500         int i = 0, j = 0;
502         char BASE64_END = '=';
503         char base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
505         while (j < len - 2) {
506                 buf[i++] = base64_table[bin[j] >> 2];
507                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
508                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
509                 buf[i++] = base64_table[bin[j + 2] & 63];
510                 j += 3;
511         }
513         switch (len - j) {
514         case 1:
515                 buf[i++] = base64_table[bin[j] >> 2];
516                 buf[i++] = base64_table[(bin[j] & 3) << 4];
517                 buf[i++] = BASE64_END;
518                 buf[i++] = BASE64_END;
519                 break;
520         case 2:
521                 buf[i++] = base64_table[bin[j] >> 2];
522                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
523                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
524                 buf[i++] = BASE64_END;
525                 break;
526         case 0:
527                 break;
528         }
530         buf[i] = '\0';
531         return buf;
533 \f
536 int
537 check_http (void)
539         char *msg = NULL;
540         char *status_line = "";
541         char *header = NULL;
542         char *page = "";
543         char *auth = NULL;
544         int i = 0;
545         size_t pagesize = 0;
546         char *full_page = "";
547         char *buf = NULL;
548         char *pos = "";
549         char *x = NULL;
550         char *orig_url = NULL;
551         double elapsed_time;
552         int page_len = 0;
553 #ifdef HAVE_SSL
554         int sslerr;
555 #endif
557         /* try to connect to the host at the given port number */
558 #ifdef HAVE_SSL
559         if (use_ssl == TRUE) {
561                 if (connect_SSL () != OK) {
562                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
563                 }
565                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
566                         X509_free (server_cert);
567                 }
568                 else {
569                         printf ("ERROR: Cannot retrieve server certificate.\n");
570                         return STATE_CRITICAL;
571                 }
573         }
574         else {
575 #endif
576                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
577                         terminate (STATE_CRITICAL, "Unable to open TCP socket");
578 #ifdef HAVE_SSL
579         }
580 #endif
582         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
584         /* optionally send the host header info (not clear if it's usable) */
585         if (strcmp (host_name, ""))
586                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
588         /* send user agent */
589         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
590                   buf, clean_revstring (REVISION), PACKAGE_VERSION);
592         /* optionally send the authentication info */
593         if (strcmp (user_auth, "")) {
594                 auth = base64 (user_auth, strlen (user_auth));
595                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
596         }
598         /* either send http POST data */
599         if (strlen (http_post_data)) {
600                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
601                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
602                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
603         }
604         else {
605                 /* or just a newline so the server knows we're done with the request */
606                 asprintf (&buf, "%s%s", buf, CRLF);
607         }
609 #ifdef HAVE_SSL
610         if (use_ssl == TRUE) {
611                 if (SSL_write (ssl, buf, strlen (buf)) == -1) {
612                         ERR_print_errors_fp (stderr);
613                         return STATE_CRITICAL;
614                 }
615         }
616         else {
617 #endif
618                 send (sd, buf, strlen (buf), 0);
619 #ifdef HAVE_SSL
620         }
621 #endif
623         /* fetch the page */
624         while ((i = my_recv ()) > 0) {
625                 buffer[i] = '\0';
626                 asprintf (&full_page, "%s%s", full_page, buffer);
627                 pagesize += i;
628         }
630         if (i < 0) {
631 #ifdef HAVE_SSL
632                 sslerr=SSL_get_error(ssl, i);
633                 if ( sslerr == SSL_ERROR_SSL ) {
634                         terminate (STATE_WARNING, "Client Certificate Required\n");
635                 } else {
636                         terminate (STATE_CRITICAL, "Error in recv()");
637                 }
638 #else
639                 terminate (STATE_CRITICAL, "Error in recv()");
640 #endif
641         }
643         /* return a CRITICAL status if we couldn't read any data */
644         if (pagesize == (size_t) 0)
645                 terminate (STATE_CRITICAL, "No data received %s", timestamp);
647         /* close the connection */
648         my_close ();
650         /* reset the alarm */
651         alarm (0);
653         /* leave full_page untouched so we can free it later */
654         page = full_page;
656         if (verbose)
657                 printf ("Page is %d characters\n", pagesize);
659         /* find status line and null-terminate it */
660         status_line = page;
661         page += (size_t) strcspn (page, "\r\n");
662         pos = page;
663         page += (size_t) strspn (page, "\r\n");
664         status_line[strcspn(status_line, "\r\n")] = 0;
665         strip (status_line);
666         if (verbose)
667                 printf ("STATUS: %s\n", status_line);
669         /* find header info and null terminate it */
670         header = page;
671         while (strcspn (page, "\r\n") > 0) {
672                 page += (size_t) strcspn (page, "\r\n");
673                 pos = page;
674                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
675                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
676                         page += (size_t) 2;
677                 else
678                         page += (size_t) 1;
679         }
680         page += (size_t) strspn (page, "\r\n");
681         header[pos - header] = 0;
682         if (verbose)
683                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
685         /* make sure the status line matches the response we are looking for */
686         if (!strstr (status_line, server_expect)) {
687                 if (server_port == HTTP_PORT)
688                         asprintf (&msg, "Invalid HTTP response received from host\n");
689                 else
690                         asprintf (&msg,
691                                         "Invalid HTTP response received from host on port %d\n",
692                                         server_port);
693                 terminate (STATE_CRITICAL, msg);
694         }
697         /* Exit here if server_expect was set by user and not default */
698         if ( server_expect_yn  )  {
699                 asprintf (&msg, "HTTP OK: Status line output matched \"%s\"\n",
700                           server_expect);
701                 if (verbose)
702                         printf ("%s\n",msg);
704         }
705         else {
706         
708                 /* check the return code */
709                 /* server errors result in a critical state */
710                 if (strstr (status_line, "500") ||
711                   strstr (status_line, "501") ||
712                 strstr (status_line, "502") ||
713                     strstr (status_line, "503")) {
714                         terminate (STATE_CRITICAL, "HTTP CRITICAL: %s\n", status_line);
715                 }
717                 /* client errors result in a warning state */
718                 if (strstr (status_line, "400") ||
719                   strstr (status_line, "401") ||
720                 strstr (status_line, "402") ||
721                     strstr (status_line, "403") ||
722                     strstr (status_line, "404")) {
723                         terminate (STATE_WARNING, "HTTP WARNING: %s\n", status_line);
724                 }
726                 /* check redirected page if specified */
727                 if (strstr (status_line, "300") ||
728                   strstr (status_line, "301") ||
729                 strstr (status_line, "302") ||
730                     strstr (status_line, "303") ||
731                     strstr (status_line, "304")) {
732                         if (onredirect == STATE_DEPENDENT) {
734                                 asprintf (&orig_url, "%s", server_url);
735                                 pos = header;
736                                 while (pos) {
737                                         server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH);
738                                         if (server_address == NULL)
739                                                 terminate (STATE_UNKNOWN,
740                                                                                  "HTTP UNKNOWN: could not allocate server_address");
741                                         if (strcspn (pos, "\r\n") > server_url_length) {
742                                                 server_url = realloc (server_url, strcspn (pos, "\r\n"));
743                                                 if (server_url == NULL)
744                                                         terminate (STATE_UNKNOWN,
745                                                                    "HTTP UNKNOWN: could not allocate server_url");
746                                                 server_url_length = strcspn (pos, "\r\n");
747                                         }
748                                         if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT URI_PATH, server_type, server_address, server_port_text, server_url) == 4) {
749                                                 asprintf (&host_name, "%s", server_address);
750                                                 use_ssl = server_type_check (server_type);
751                                                 server_port = atoi (server_port_text);
752                                                 check_http ();
753                                         }
754                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PATH, server_type, server_address, server_url) == 3 ) { 
755                                                 asprintf (&host_name, "%s", server_address);
756                                                 use_ssl = server_type_check (server_type);
757                                                 server_port = server_port_check (use_ssl);
758                                                 check_http ();
759                                         }
760                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST URI_PORT, server_type, server_address, server_port_text) == 3) {
761                                                 asprintf (&host_name, "%s", server_address);
762                                                 strcpy (server_url, "/");
763                                                 use_ssl = server_type_check (server_type);
764                                                 server_port = atoi (server_port_text);
765                                                 check_http ();
766                                         }
767                                         else if (sscanf (pos, HDR_LOCATION URI_HTTP URI_HOST, server_type, server_address) == 2) {
768                                                 asprintf (&host_name, "%s", server_address);
769                                                 strcpy (server_url, "/");
770                                                 use_ssl = server_type_check (server_type);
771                                                 server_port = server_port_check (use_ssl);
772                                                 check_http ();
773                                         }
774                                         else if (sscanf (pos, HDR_LOCATION URI_PATH, server_url) == 1) {
775                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
776                                                         *x = '\0';
777                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
778                                                 }
779                                                 check_http ();
780                                         }                                       
781                                         pos += (size_t) strcspn (pos, "\r\n");
782                                         pos += (size_t) strspn (pos, "\r\n");
783                                 } /* end while (pos) */
784                                 printf ("UNKNOWN - Could not find redirect location - %s%s",
785                                         status_line, (display_html ? "</A>" : ""));
786                                 exit (STATE_UNKNOWN);
787                         } /* end if (onredirect == STATE_DEPENDENT) */
788                         
789                         else if (onredirect == STATE_UNKNOWN)
790                                 printf ("UNKNOWN");
791                         else if (onredirect == STATE_OK)
792                                 printf ("OK");
793                         else if (onredirect == STATE_WARNING)
794                                 printf ("WARNING");
795                         else if (onredirect == STATE_CRITICAL)
796                                 printf ("CRITICAL");
797                         elapsed_time = delta_time (tv);
798                         asprintf (&msg, " - %s - %7.3f second response time %s%s|time=%7.3f\n",
799                                  status_line, elapsed_time, timestamp,
800                            (display_html ? "</A>" : ""), elapsed_time);
801                         terminate (onredirect, msg);
802                 } /* end if (strstr (status_line, "30[0-4]") */
805         } /* end else (server_expect_yn)  */
807                 
808         /* check elapsed time */
809         elapsed_time = delta_time (tv);
810         asprintf (&msg, "HTTP problem: %s - %7.3f second response time %s%s|time=%7.3f\n",
811                        status_line, elapsed_time, timestamp,
812                        (display_html ? "</A>" : ""), elapsed_time);
813         if (check_critical_time == TRUE && elapsed_time > critical_time)
814                 terminate (STATE_CRITICAL, msg);
815         if (check_warning_time == TRUE && elapsed_time > warning_time)
816                 terminate (STATE_WARNING, msg);
818         /* Page and Header content checks go here */
819         /* these checks should be last */
821         if (strlen (string_expect)) {
822                 if (strstr (page, string_expect)) {
823                         printf ("HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
824                                 status_line, elapsed_time,
825                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
826                         exit (STATE_OK);
827                 }
828                 else {
829                         printf ("CRITICAL - string not found%s|time=%7.3f\n",
830                                 (display_html ? "</A>" : ""), elapsed_time);
831                         exit (STATE_CRITICAL);
832                 }
833         }
834 #ifdef HAVE_REGEX_H
835         if (strlen (regexp)) {
836                 errcode = regexec (&preg, page, REGS, pmatch, 0);
837                 if (errcode == 0) {
838                         printf ("HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
839                                 status_line, elapsed_time,
840                                 timestamp, (display_html ? "</A>" : ""), elapsed_time);
841                         exit (STATE_OK);
842                 }
843                 else {
844                         if (errcode == REG_NOMATCH) {
845                                 printf ("CRITICAL - pattern not found%s|time=%7.3f\n",
846                                         (display_html ? "</A>" : ""), elapsed_time);
847                                 exit (STATE_CRITICAL);
848                         }
849                         else {
850                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
851                                 printf ("CRITICAL - Execute Error: %s\n", errbuf);
852                                 exit (STATE_CRITICAL);
853                         }
854                 }
855         }
856 #endif
858         /* make sure the page is of an appropriate size */
859         page_len = strlen (page);
860         if ((min_page_len > 0) && (page_len < min_page_len)) {
861                 printf ("HTTP WARNING: page size too small%s|size=%i\n",
862                         (display_html ? "</A>" : ""), page_len );
863                 exit (STATE_WARNING);
864         }
865         /* We only get here if all tests have been passed */
866         asprintf (&msg, "HTTP OK %s - %7.3f second response time %s%s|time=%7.3f\n",
867                         status_line, (float)elapsed_time,
868                         timestamp, (display_html ? "</A>" : ""), elapsed_time);
869         terminate (STATE_OK, msg);
870         return STATE_UNKNOWN;
875 #ifdef HAVE_SSL
876 int connect_SSL (void)
878         SSL_METHOD *meth;
880         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
881         RAND_seed (randbuff, strlen (randbuff));
882         if (verbose)
883                 printf("SSL seeding: %s\n", (RAND_status()==1 ? "OK" : "Failed") );
885         /* Initialize SSL context */
886         SSLeay_add_ssl_algorithms ();
887         meth = SSLv23_client_method ();
888         SSL_load_error_strings ();
889         if ((ctx = SSL_CTX_new (meth)) == NULL) {
890                 printf ("CRITICAL -  Cannot create SSL context.\n");
891                 return STATE_CRITICAL;
892         }
894         /* Initialize alarm signal handling */
895         signal (SIGALRM, socket_timeout_alarm_handler);
897         /* Set socket timeout */
898         alarm (socket_timeout);
900         /* Save start time */
901         gettimeofday (&tv, NULL);
903         /* Make TCP connection */
904         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
905                 /* Do the SSL handshake */
906                 if ((ssl = SSL_new (ctx)) != NULL) {
907                         SSL_set_cipher_list(ssl, "ALL");
908                         SSL_set_fd (ssl, sd);
909                         if (SSL_connect (ssl) != -1)
910                                 return OK;
911                         ERR_print_errors_fp (stderr);
912                 }
913                 else {
914                         printf ("CRITICAL - Cannot initiate SSL handshake.\n");
915                 }
916                 SSL_free (ssl);
917         }
919         SSL_CTX_free (ctx);
920         close (sd);
922         return STATE_CRITICAL;
924 #endif
926 #ifdef HAVE_SSL
927 int
928 check_certificate (X509 ** certificate)
930         ASN1_STRING *tm;
931         int offset;
932         struct tm stamp;
933         int days_left;
936         /* Retrieve timestamp of certificate */
937         tm = X509_get_notAfter (*certificate);
939         /* Generate tm structure to process timestamp */
940         if (tm->type == V_ASN1_UTCTIME) {
941                 if (tm->length < 10) {
942                         printf ("CRITICAL - Wrong time format in certificate.\n");
943                         return STATE_CRITICAL;
944                 }
945                 else {
946                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
947                         if (stamp.tm_year < 50)
948                                 stamp.tm_year += 100;
949                         offset = 0;
950                 }
951         }
952         else {
953                 if (tm->length < 12) {
954                         printf ("CRITICAL - Wrong time format in certificate.\n");
955                         return STATE_CRITICAL;
956                 }
957                 else {
958                         stamp.tm_year =
959                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
960                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
961                         stamp.tm_year -= 1900;
962                         offset = 2;
963                 }
964         }
965         stamp.tm_mon =
966                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
967         stamp.tm_mday =
968                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
969         stamp.tm_hour =
970                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
971         stamp.tm_min =
972                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
973         stamp.tm_sec = 0;
974         stamp.tm_isdst = -1;
976         days_left = (mktime (&stamp) - time (NULL)) / 86400;
977         snprintf
978                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
979                  stamp.tm_mon + 1,
980                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
982         if (days_left > 0 && days_left <= days_till_exp) {
983                 printf ("WARNING - Certificate expires in %d day(s) (%s).\n", days_left, timestamp);
984                 return STATE_WARNING;
985         }
986         if (days_left < 0) {
987                 printf ("CRITICAL - Certificate expired on %s.\n", timestamp);
988                 return STATE_CRITICAL;
989         }
991         if (days_left == 0) {
992                 printf ("WARNING - Certificate expires today (%s).\n", timestamp);
993                 return STATE_WARNING;
994         }
996         printf ("OK - Certificate will expire on %s.\n", timestamp);
998         return STATE_OK;
1000 #endif
1001 \f
1004 int
1005 my_recv (void)
1007         int i;
1008 #ifdef HAVE_SSL
1009         if (use_ssl) {
1010                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1011         }
1012         else {
1013                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1014         }
1015 #else
1016         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1017 #endif
1018         return i;
1022 int
1023 my_close (void)
1025 #ifdef HAVE_SSL
1026         if (use_ssl == TRUE) {
1027                 SSL_shutdown (ssl);
1028                 SSL_free (ssl);
1029                 SSL_CTX_free (ctx);
1030                 return 0;
1031         }
1032         else {
1033 #endif
1034                 return close (sd);
1035 #ifdef HAVE_SSL
1036         }
1037 #endif
1039 \f
1042 void
1043 print_help (void)
1045         print_revision (progname, REVISION);
1046         printf
1047                 ("Copyright (c) %s %s <%s>\n\n%s\n",
1048                  COPYRIGHT, AUTHORS, EMAIL, SUMMARY);
1049         print_usage ();
1050         printf ("NOTE: One or both of -H and -I must be specified\n");
1051         printf ("\nOptions:\n" LONGOPTIONS "\n", HTTP_EXPECT, HTTP_PORT,
1052                 DEFAULT_SOCKET_TIMEOUT, SSLOPTIONS, REGOPTIONS);
1053 #ifdef HAVE_SSL
1054         printf (SSLDESCRIPTION);
1055 #endif
1059 void
1060 print_usage (void)
1062         printf ("Usage:\n" " %s %s\n"
1063                 " %s (-h | --help) for detailed help\n"
1064                 " %s (-V | --version) for version information\n",
1065         progname, OPTIONS, progname, progname);