Code

*BUGFIX: LWS is not required betwwen "Location:" header field name and field value
[nagiosplug.git] / plugins / check_http.c
1 /******************************************************************************
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; either version 2 of the License, or
6  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 ******************************************************************************/
19 const char *progname = "check_http";
20 const char *revision = "$Revision$";
21 const char *copyright = "1999-2001";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "netutils.h"
26 #include "utils.h"
28 #define HTTP_EXPECT "HTTP/1."
29 enum {
30         MAX_IPV4_HOSTLENGTH = 255,
31         HTTP_PORT = 80,
32         HTTPS_PORT = 443
33 };
35 #ifdef HAVE_SSL_H
36 #include <rsa.h>
37 #include <crypto.h>
38 #include <x509.h>
39 #include <pem.h>
40 #include <ssl.h>
41 #include <err.h>
42 #include <rand.h>
43 #else
44 # ifdef HAVE_OPENSSL_SSL_H
45 # include <openssl/rsa.h>
46 # include <openssl/crypto.h>
47 # include <openssl/x509.h>
48 # include <openssl/pem.h>
49 # include <openssl/ssl.h>
50 # include <openssl/err.h>
51 # include <openssl/rand.h>
52 # endif
53 #endif
55 #ifdef HAVE_SSL
56 int check_cert = FALSE;
57 int days_till_exp;
58 char *randbuff;
59 SSL_CTX *ctx;
60 SSL *ssl;
61 X509 *server_cert;
62 int connect_SSL (void);
63 int check_certificate (X509 **);
64 #endif
66 #ifdef HAVE_REGEX_H
67 enum {
68         REGS = 2,
69         MAX_RE_SIZE = 256
70 };
71 #include <regex.h>
72 regex_t preg;
73 regmatch_t pmatch[REGS];
74 char regexp[MAX_RE_SIZE];
75 char errbuf[MAX_INPUT_BUFFER];
76 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
77 int errcode;
78 #endif
80 struct timeval tv;
82 #define server_type_check(server_type) \
83 (strcmp (server_type, "https") ? FALSE : TRUE)
85 #define server_port_check(use_ssl) (use_ssl ? HTTPS_PORT : HTTP_PORT)
87 #define HTTP_URL "/"
88 #define CRLF "\r\n"
90 char timestamp[17] = "";
91 int specify_port = FALSE;
92 int server_port = HTTP_PORT;
93 char server_port_text[6] = "";
94 char server_type[6] = "http";
95 char *server_address;
96 char *host_name;
97 char *server_url;
98 int server_url_length;
99 int server_expect_yn = 0;
100 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
101 char string_expect[MAX_INPUT_BUFFER] = "";
102 double warning_time = 0;
103 int check_warning_time = FALSE;
104 double critical_time = 0;
105 int check_critical_time = FALSE;
106 char user_auth[MAX_INPUT_BUFFER] = "";
107 int display_html = FALSE;
108 int onredirect = STATE_OK;
109 int use_ssl = FALSE;
110 int verbose = FALSE;
111 int sd;
112 int min_page_len = 0;
113 char *http_method;
114 char *http_post_data;
115 char buffer[MAX_INPUT_BUFFER];
117 int process_arguments (int, char **);
118 static char *base64 (char *bin, size_t len);
119 int check_http (void);
120 int my_recv (void);
121 int my_close (void);
122 void print_help (void);
123 void print_usage (void);
125 int
126 main (int argc, char **argv)
128         int result = STATE_UNKNOWN;
130         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
131         server_url = strdup(HTTP_URL);
132         server_url_length = strlen(server_url);
134         if (process_arguments (argc, argv) == ERROR)
135                 usage (_("check_http: could not parse arguments\n"));
137         if (strstr (timestamp, ":")) {
138                 if (strstr (server_url, "?"))
139                         asprintf (&server_url, "%s&%s", server_url, timestamp);
140                 else
141                         asprintf (&server_url, "%s?%s", server_url, timestamp);
142         }
144         if (display_html == TRUE)
145                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
146                         host_name, server_port, server_url);
148         /* initialize alarm signal handling, set socket timeout, start timer */
149         (void) signal (SIGALRM, socket_timeout_alarm_handler);
150         (void) alarm (socket_timeout);
151         gettimeofday (&tv, NULL);
153 #ifdef HAVE_SSL
154         if (use_ssl && check_cert == TRUE) {
155                 if (connect_SSL () != OK)
156                         die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
157                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
158                         result = check_certificate (&server_cert);
159                         X509_free (server_cert);
160                 }
161                 else {
162                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
163                         result = STATE_CRITICAL;
164                 }
165                 SSL_shutdown (ssl);
166                 SSL_free (ssl);
167                 SSL_CTX_free (ctx);
168                 close (sd);
169         }
170         else {
171                 result = check_http ();
172         }
173 #else
174         result = check_http ();
175 #endif
176         return result;
178 \f
181 /* process command-line arguments */
182 int
183 process_arguments (int argc, char **argv)
185         int c = 1;
187         int option = 0;
188         static struct option longopts[] = {
189                 STD_LONG_OPTS,
190                 {"file",required_argument,0,'F'},
191                 {"link", no_argument, 0, 'L'},
192                 {"nohtml", no_argument, 0, 'n'},
193                 {"ssl", no_argument, 0, 'S'},
194                 {"verbose", no_argument, 0, 'v'},
195                 {"post", required_argument, 0, 'P'},
196                 {"IP-address", required_argument, 0, 'I'},
197                 {"string", required_argument, 0, 's'},
198                 {"regex", required_argument, 0, 'r'},
199                 {"ereg", required_argument, 0, 'r'},
200                 {"eregi", required_argument, 0, 'R'},
201                 {"linespan", no_argument, 0, 'l'},
202                 {"onredirect", required_argument, 0, 'f'},
203                 {"certificate", required_argument, 0, 'C'},
204                 {"min", required_argument, 0, 'm'},
205                 {"use-ipv4", no_argument, 0, '4'},
206                 {"use-ipv6", no_argument, 0, '6'},
207                 {0, 0, 0, 0}
208         };
210         if (argc < 2)
211                 return ERROR;
213         for (c = 1; c < argc; c++) {
214                 if (strcmp ("-to", argv[c]) == 0)
215                         strcpy (argv[c], "-t");
216                 if (strcmp ("-hn", argv[c]) == 0)
217                         strcpy (argv[c], "-H");
218                 if (strcmp ("-wt", argv[c]) == 0)
219                         strcpy (argv[c], "-w");
220                 if (strcmp ("-ct", argv[c]) == 0)
221                         strcpy (argv[c], "-c");
222                 if (strcmp ("-nohtml", argv[c]) == 0)
223                         strcpy (argv[c], "-n");
224         }
226         while (1) {
227                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
228                 if (c == -1 || c == EOF)
229                         break;
231                 switch (c) {
232                 case '?': /* usage */
233                         usage3 (_("unknown argument"), optopt);
234                         break;
235                 case 'h': /* help */
236                         print_help ();
237                         exit (STATE_OK);
238                         break;
239                 case 'V': /* version */
240                         print_revision (progname, revision);
241                         exit (STATE_OK);
242                         break;
243                 case 't': /* timeout period */
244                         if (!is_intnonneg (optarg))
245                                 usage2 (_("timeout interval must be a non-negative integer"), optarg);
246                         else
247                                 socket_timeout = atoi (optarg);
248                         break;
249                 case 'c': /* critical time threshold */
250                         if (!is_intnonneg (optarg))
251                                 usage2 (_("invalid critical threshold"), optarg);
252                         else {
253                                 critical_time = strtod (optarg, NULL);
254                                 check_critical_time = TRUE;
255                         }
256                         break;
257                 case 'w': /* warning time threshold */
258                         if (!is_intnonneg (optarg))
259                                 usage2 (_("invalid warning threshold"), optarg);
260                         else {
261                                 warning_time = strtod (optarg, NULL);
262                                 check_warning_time = TRUE;
263                         }
264                         break;
265                 case 'L': /* show html link */
266                         display_html = TRUE;
267                         break;
268                 case 'n': /* do not show html link */
269                         display_html = FALSE;
270                         break;
271                 case 'S': /* use SSL */
272 #ifndef HAVE_SSL
273                         usage (_("check_http: invalid option - SSL is not available\n"));
274 #endif
275                         use_ssl = TRUE;
276                         if (specify_port == FALSE)
277                                 server_port = HTTPS_PORT;
278                         break;
279                 case 'C': /* Check SSL cert validity */
280 #ifdef HAVE_SSL
281                         if (!is_intnonneg (optarg))
282                                 usage2 (_("invalid certificate expiration period"), optarg);
283                         else {
284                                 days_till_exp = atoi (optarg);
285                                 check_cert = TRUE;
286                         }
287 #else
288                         usage (_("check_http: invalid option - SSL is not available\n"));
289 #endif
290                         break;
291                 case 'f': /* onredirect */
292                         if (!strcmp (optarg, "follow"))
293                                 onredirect = STATE_DEPENDENT;
294                         if (!strcmp (optarg, "unknown"))
295                                 onredirect = STATE_UNKNOWN;
296                         if (!strcmp (optarg, "ok"))
297                                 onredirect = STATE_OK;
298                         if (!strcmp (optarg, "warning"))
299                                 onredirect = STATE_WARNING;
300                         if (!strcmp (optarg, "critical"))
301                                 onredirect = STATE_CRITICAL;
302                         if (verbose)
303                                 printf(_("option f:%d \n"), onredirect);  
304                         break;
305                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
306                 case 'H': /* Host Name (virtual host) */
307                         host_name = strdup (optarg);
308                         break;
309                 case 'I': /* Server IP-address */
310                         server_address = strdup (optarg);
311                         break;
312                 case 'u': /* URL path */
313                         server_url = strdup (optarg);
314                         server_url_length = strlen (server_url);
315                         break;
316                 case 'p': /* Server port */
317                         if (!is_intnonneg (optarg))
318                                 usage2 (_("invalid port number"), optarg);
319                         else {
320                                 server_port = atoi (optarg);
321                                 specify_port = TRUE;
322                         }
323                         break;
324                 case 'a': /* authorization info */
325                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
326                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
327                         break;
328                 case 'P': /* HTTP POST data in URL encoded format */
329                         if (http_method || http_post_data) break;
330                         http_method = strdup("POST");
331                         http_post_data = strdup (optarg);
332                         break;
333                 case 's': /* string or substring */
334                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
335                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
336                         break;
337                 case 'e': /* string or substring */
338                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
339                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
340                         server_expect_yn = 1;
341                         break;
342 #ifndef HAVE_REGEX_H
343                 case 'l': /* linespan */
344                 case 'r': /* linespan */
345                 case 'R': /* linespan */
346                         usage (_("check_http: call for regex which was not a compiled option\n"));
347                         break;
348 #else
349                 case 'l': /* linespan */
350                         cflags &= ~REG_NEWLINE;
351                         break;
352                 case 'R': /* regex */
353                         cflags |= REG_ICASE;
354                 case 'r': /* regex */
355                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
356                         regexp[MAX_RE_SIZE - 1] = 0;
357                         errcode = regcomp (&preg, regexp, cflags);
358                         if (errcode != 0) {
359                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
360                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
361                                 return ERROR;
362                         }
363                         break;
364 #endif
365                 case '4':
366                         address_family = AF_INET;
367                         break;
368                 case '6':
369 #ifdef USE_IPV6
370                         address_family = AF_INET6;
371 #else
372                         usage (_("IPv6 support not available\n"));
373 #endif
374                         break;
375                 case 'v': /* verbose */
376                         verbose = TRUE;
377                         break;
378                 case 'm': /* min_page_length */
379                         min_page_len = atoi (optarg);
380                         break;
381                 }
382         }
384         c = optind;
386         if (server_address == NULL && c < argc)
387                 server_address = strdup (argv[c++]);
389         if (host_name == NULL && c < argc)
390                 host_name = strdup (argv[c++]);
392         if (server_address == NULL) {
393                 if (host_name == NULL)
394                         usage (_("check_http: you must specify a server address or host name\n"));
395                 else
396                         server_address = strdup (host_name);
397         }
399         if (check_critical_time && critical_time>(double)socket_timeout)
400                 socket_timeout = (int)critical_time + 1;
402         if (http_method == NULL)
403                 http_method = strdup ("GET");
405         return TRUE;
407 \f
410 /* written by lauri alanko */
411 static char *
412 base64 (char *bin, size_t len)
415         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
416         size_t i = 0, j = 0;
418         char BASE64_END = '=';
419         char base64_table[64];
420         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
422         while (j < len - 2) {
423                 buf[i++] = base64_table[bin[j] >> 2];
424                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
425                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
426                 buf[i++] = base64_table[bin[j + 2] & 63];
427                 j += 3;
428         }
430         switch (len - j) {
431         case 1:
432                 buf[i++] = base64_table[bin[j] >> 2];
433                 buf[i++] = base64_table[(bin[j] & 3) << 4];
434                 buf[i++] = BASE64_END;
435                 buf[i++] = BASE64_END;
436                 break;
437         case 2:
438                 buf[i++] = base64_table[bin[j] >> 2];
439                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
440                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
441                 buf[i++] = BASE64_END;
442                 break;
443         case 0:
444                 break;
445         }
447         buf[i] = '\0';
448         return buf;
450 \f
453 /* per RFC 2396 */
454 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
455 #define URI_HTTP "%[HTPShtps]://"
456 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
457 #define URI_PORT ":%[0123456789]"
458 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
459 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
460 #define HD2 URI_HTTP URI_HOST URI_PATH
461 #define HD3 URI_HTTP URI_HOST URI_PORT
462 #define HD4 URI_HTTP URI_HOST
463 #define HD5 URI_PATH
465 int
466 check_http (void)
468         char *msg;
469         char *status_line;
470         char *header;
471         char *page;
472         char *auth;
473         int i = 0;
474         size_t pagesize = 0;
475         char *full_page;
476         char *buf;
477         char *pos;
478         char *x;
479         char xx[2];
480         char *orig_url;
481         long microsec;
482         double elapsed_time;
483         int page_len = 0;
484 #ifdef HAVE_SSL
485         int sslerr;
486 #endif
488         /* try to connect to the host at the given port number */
489 #ifdef HAVE_SSL
490         if (use_ssl == TRUE) {
492                 if (connect_SSL () != OK) {
493                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
494                 }
496                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
497                         X509_free (server_cert);
498                 }
499                 else {
500                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
501                         return STATE_CRITICAL;
502                 }
504         }
505         else {
506 #endif
507                 if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK)
508                         die (STATE_CRITICAL, _("Unable to open TCP socket"));
509 #ifdef HAVE_SSL
510         }
511 #endif
513         asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
515         /* optionally send the host header info (not clear if it's usable) */
516         if (host_name)
517                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
519         /* send user agent */
520         asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
521                   buf, clean_revstring (revision), VERSION);
523         /* optionally send the authentication info */
524         if (strlen(user_auth)) {
525                 auth = base64 (user_auth, strlen (user_auth));
526                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
527         }
529         /* either send http POST data */
530         if (http_post_data) {
531                 asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
532                 asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
533                 asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
534         }
535         else {
536                 /* or just a newline so the server knows we're done with the request */
537                 asprintf (&buf, "%s%s", buf, CRLF);
538         }
540 #ifdef HAVE_SSL
541         if (use_ssl == TRUE) {
542                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
543                         ERR_print_errors_fp (stderr);
544                         return STATE_CRITICAL;
545                 }
546         }
547         else {
548 #endif
549                 send (sd, buf, strlen (buf), 0);
550 #ifdef HAVE_SSL
551         }
552 #endif
554         /* fetch the page */
555         full_page = strdup("");
556         while ((i = my_recv ()) > 0) {
557                 buffer[i] = '\0';
558                 asprintf (&full_page, "%s%s", full_page, buffer);
559                 pagesize += i;
560         }
562         if (i < 0 && errno != ECONNRESET) {
563 #ifdef HAVE_SSL
564                 if (use_ssl) {
565                         sslerr=SSL_get_error(ssl, i);
566                         if ( sslerr == SSL_ERROR_SSL ) {
567                                 die (STATE_WARNING, _("Client Certificate Required\n"));
568                         } else {
569                                 die (STATE_CRITICAL, _("Error in recv()"));
570                         }
571                 }
572                 else {
573 #endif
574                         die (STATE_CRITICAL, _("Error in recv()"));
575 #ifdef HAVE_SSL
576                 }
577 #endif
578         }
580         /* return a CRITICAL status if we couldn't read any data */
581         if (pagesize == (size_t) 0)
582                 die (STATE_CRITICAL, _("No data received %s"), timestamp);
584         /* close the connection */
585         my_close ();
587         /* reset the alarm */
588         alarm (0);
590         /* leave full_page untouched so we can free it later */
591         page = full_page;
593         if (verbose)
594                 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
596         /* find status line and null-terminate it */
597         status_line = page;
598         page += (size_t) strcspn (page, "\r\n");
599         pos = page;
600         page += (size_t) strspn (page, "\r\n");
601         status_line[strcspn(status_line, "\r\n")] = 0;
602         strip (status_line);
603         if (verbose)
604                 printf ("STATUS: %s\n", status_line);
606         /* find header info and null-terminate it */
607         header = page;
608         while (strcspn (page, "\r\n") > 0) {
609                 page += (size_t) strcspn (page, "\r\n");
610                 pos = page;
611                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
612                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
613                         page += (size_t) 2;
614                 else
615                         page += (size_t) 1;
616         }
617         page += (size_t) strspn (page, "\r\n");
618         header[pos - header] = 0;
619         if (verbose)
620                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
622         /* make sure the status line matches the response we are looking for */
623         if (!strstr (status_line, server_expect)) {
624                 if (server_port == HTTP_PORT)
625                         asprintf (&msg, _("Invalid HTTP response received from host\n"));
626                 else
627                         asprintf (&msg,
628                                         _("Invalid HTTP response received from host on port %d\n"),
629                                         server_port);
630                 die (STATE_CRITICAL, "%s", msg);
631         }
634         /* Exit here if server_expect was set by user and not default */
635         if ( server_expect_yn  )  {
636                 asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
637                           server_expect);
638                 if (verbose)
639                         printf ("%s\n",msg);
641         }
642         else {
643         
645                 /* check the return code */
646                 /* server errors result in a critical state */
647                 if (strstr (status_line, "500") || strstr (status_line, "501") ||
648                     strstr (status_line, "502") || strstr (status_line, "503") ||
649                     strstr (status_line, "504") || strstr (status_line, "505")) {
650                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
651                 }
653                 /* client errors result in a warning state */
654                 if (strstr (status_line, "400") || strstr (status_line, "401") ||
655                     strstr (status_line, "402") || strstr (status_line, "403") ||
656                     strstr (status_line, "404") || strstr (status_line, "405") ||
657                     strstr (status_line, "406") || strstr (status_line, "407") ||
658                     strstr (status_line, "408") || strstr (status_line, "409") ||
659                     strstr (status_line, "410") || strstr (status_line, "411") ||
660                     strstr (status_line, "412") || strstr (status_line, "413") ||
661                     strstr (status_line, "414") || strstr (status_line, "415") ||
662                     strstr (status_line, "416") || strstr (status_line, "417")) {
663                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
664                 }
666                 /* check redirected page if specified */
667                 if (strstr (status_line, "300") || strstr (status_line, "301") ||
668                     strstr (status_line, "302") || strstr (status_line, "303") ||
669                     strstr (status_line, "304") || strstr (status_line, "305") ||
670                     strstr (status_line, "306")) {
671                         if (onredirect == STATE_DEPENDENT) {
673                                 server_address = realloc (server_address, MAX_IPV4_HOSTLENGTH + 1);
674                                 if (server_address == NULL)
675                                         die (STATE_UNKNOWN,_("ERROR: could not allocate server_address"));
677                                 asprintf (&orig_url, "%s", server_url);
678                                 if (strcspn (pos, "\r\n") > (size_t)server_url_length) {
679                                         server_url = realloc (server_url, strcspn (pos, "\r\n"));
680                                         if (server_url == NULL)
681                                                 die (STATE_UNKNOWN, _("ERROR: could not allocate server_url"));
682                                         server_url_length = strcspn (pos, "\r\n");
683                                 }
685                                 pos = header;
686                                 while (pos) {
687                                         if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) > 0) {
688                                                 pos += i;
689                                                 pos += strspn (pos, " \t\r\n");
690                                         } else {
691                                                 pos += (size_t) strcspn (pos, "\r\n");
692                                                 pos += (size_t) strspn (pos, "\r\n");
693                                                 continue;
694                                         }
695                                         /* HDR_LOCATION, URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
696                                         if (sscanf (pos, HD1, server_type, server_address, server_port_text, server_url) == 4) {
697                                                 if (host_name != NULL) free(host_name);
698                                                 host_name = strdup(server_address);
699                                                 use_ssl = server_type_check (server_type);
700                                                 server_port = atoi (server_port_text);
701                                                 check_http ();
702                                         }
703                                         /* HDR_LOCATION URI_HTTP URI_HOST URI_PATH */
704                                         else if (sscanf (pos, HD2, server_type, server_address, server_url) == 3 ) { 
705                                                 if (host_name != NULL) free(host_name);
706                                                 host_name = strdup(server_address);
707                                                 use_ssl = server_type_check (server_type);
708                                                 server_port = server_port_check (use_ssl);
709                                                 check_http ();
710                                         }
711                                         /* HDR_LOCATION URI_HTTP URI_HOST URI_PORT */
712                                         else if(sscanf (pos, HD3, server_type, server_address, server_port_text) == 3) {
713                                                 if (host_name != NULL) free(host_name);
714                                                 host_name = strdup(server_address);
715                                                 strcpy (server_url, "/");
716                                                 use_ssl = server_type_check (server_type);
717                                                 server_port = atoi (server_port_text);
718                                                 check_http ();
719                                         }
720                                         /* HDR_LOCATION URI_HTTP URI_HOST */
721                                         else if(sscanf (pos, HD4, server_type, server_address) == 2) {
722                                                 if (host_name != NULL) free(host_name);
723                                                 host_name = strdup(server_address);
724                                                 strcpy (server_url, "/");
725                                                 use_ssl = server_type_check (server_type);
726                                                 server_port = server_port_check (use_ssl);
727                                                 check_http ();
728                                         }
729                                         /* HDR_LOCATION URI_PATH */
730                                         else if (sscanf (pos, HD5, server_url) == 1) {
731                                                 if ((server_url[0] != '/') && (x = strrchr(orig_url, '/'))) {
732                                                         *x = '\0';
733                                                         asprintf (&server_url, "%s/%s", orig_url, server_url);
734                                                 }
735                                                 check_http ();
736                                         }                                       
737                                 } /* end while (pos) */
738                                 printf (_("UNKNOWN - Could not find redirect location - %s%s"),
739                                         status_line, (display_html ? "</A>" : ""));
740                                 exit (STATE_UNKNOWN);
741                         } /* end if (onredirect == STATE_DEPENDENT) */
742                         
743                         else if (onredirect == STATE_UNKNOWN)
744                                 printf (_("UNKNOWN"));
745                         else if (onredirect == STATE_OK)
746                                 printf (_("OK"));
747                         else if (onredirect == STATE_WARNING)
748                                 printf (_("WARNING"));
749                         else if (onredirect == STATE_CRITICAL)
750                                 printf (_("CRITICAL"));
751                         microsec = deltime (tv);
752                         elapsed_time = (double)microsec / 1.0e6;
753                         asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
754                                  status_line, elapsed_time, timestamp,
755                            (display_html ? "</A>" : ""), microsec, pagesize);
756                         die (onredirect, "%s", msg);
757                 } /* end if (strstr (status_line, "30[0-4]") */
760         } /* end else (server_expect_yn)  */
762                 
763         /* check elapsed time */
764         microsec = deltime (tv);
765         elapsed_time = (double)microsec / 1.0e6;
766         asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
767                        status_line, elapsed_time, timestamp,
768                        (display_html ? "</A>" : ""), microsec, pagesize);
769         if (check_critical_time == TRUE && elapsed_time > critical_time)
770                 die (STATE_CRITICAL, "%s", msg);
771         if (check_warning_time == TRUE && elapsed_time > warning_time)
772                 die (STATE_WARNING, "%s", msg);
774         /* Page and Header content checks go here */
775         /* these checks should be last */
777         if (strlen (string_expect)) {
778                 if (strstr (page, string_expect)) {
779                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
780                                 status_line, elapsed_time,
781                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
782                         exit (STATE_OK);
783                 }
784                 else {
785                         printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
786                                 (display_html ? "</A>" : ""), microsec, pagesize);
787                         exit (STATE_CRITICAL);
788                 }
789         }
790 #ifdef HAVE_REGEX_H
791         if (strlen (regexp)) {
792                 errcode = regexec (&preg, page, REGS, pmatch, 0);
793                 if (errcode == 0) {
794                         printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
795                                 status_line, elapsed_time,
796                                 timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
797                         exit (STATE_OK);
798                 }
799                 else {
800                         if (errcode == REG_NOMATCH) {
801                                 printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
802                                         (display_html ? "</A>" : ""), microsec, pagesize);
803                                 exit (STATE_CRITICAL);
804                         }
805                         else {
806                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
807                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
808                                 exit (STATE_CRITICAL);
809                         }
810                 }
811         }
812 #endif
814         /* make sure the page is of an appropriate size */
815         page_len = strlen (page);
816         if ((min_page_len > 0) && (page_len < min_page_len)) {
817                 printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
818                         (display_html ? "</A>" : ""), page_len );
819                 exit (STATE_WARNING);
820         }
821         /* We only get here if all tests have been passed */
822         asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
823                         status_line, elapsed_time,
824                         timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
825         die (STATE_OK, "%s", msg);
826         return STATE_UNKNOWN;
831 #ifdef HAVE_SSL
832 int connect_SSL (void)
834         SSL_METHOD *meth;
836         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
837         RAND_seed (randbuff, (int)strlen(randbuff));
838         if (verbose)
839                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
841         /* Initialize SSL context */
842         SSLeay_add_ssl_algorithms ();
843         meth = SSLv23_client_method ();
844         SSL_load_error_strings ();
845         if ((ctx = SSL_CTX_new (meth)) == NULL) {
846                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
847                 return STATE_CRITICAL;
848         }
850         /* Initialize alarm signal handling */
851         signal (SIGALRM, socket_timeout_alarm_handler);
853         /* Set socket timeout */
854         alarm (socket_timeout);
856         /* Save start time */
857         gettimeofday (&tv, NULL);
859         /* Make TCP connection */
860         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
861                 /* Do the SSL handshake */
862                 if ((ssl = SSL_new (ctx)) != NULL) {
863                         SSL_set_cipher_list(ssl, "ALL");
864                         SSL_set_fd (ssl, sd);
865                         if (SSL_connect (ssl) != -1)
866                                 return OK;
867                         ERR_print_errors_fp (stderr);
868                 }
869                 else {
870                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
871                 }
872                 SSL_free (ssl);
873         }
875         SSL_CTX_free (ctx);
876         close (sd);
878         return STATE_CRITICAL;
880 #endif
882 #ifdef HAVE_SSL
883 int
884 check_certificate (X509 ** certificate)
886         ASN1_STRING *tm;
887         int offset;
888         struct tm stamp;
889         int days_left;
892         /* Retrieve timestamp of certificate */
893         tm = X509_get_notAfter (*certificate);
895         /* Generate tm structure to process timestamp */
896         if (tm->type == V_ASN1_UTCTIME) {
897                 if (tm->length < 10) {
898                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
899                         return STATE_CRITICAL;
900                 }
901                 else {
902                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
903                         if (stamp.tm_year < 50)
904                                 stamp.tm_year += 100;
905                         offset = 0;
906                 }
907         }
908         else {
909                 if (tm->length < 12) {
910                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
911                         return STATE_CRITICAL;
912                 }
913                 else {
914                         stamp.tm_year =
915                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
916                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
917                         stamp.tm_year -= 1900;
918                         offset = 2;
919                 }
920         }
921         stamp.tm_mon =
922                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
923         stamp.tm_mday =
924                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
925         stamp.tm_hour =
926                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
927         stamp.tm_min =
928                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
929         stamp.tm_sec = 0;
930         stamp.tm_isdst = -1;
932         days_left = (mktime (&stamp) - time (NULL)) / 86400;
933         snprintf
934                 (timestamp, 16, "%02d/%02d/%04d %02d:%02d",
935                  stamp.tm_mon + 1,
936                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
938         if (days_left > 0 && days_left <= days_till_exp) {
939                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
940                 return STATE_WARNING;
941         }
942         if (days_left < 0) {
943                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
944                 return STATE_CRITICAL;
945         }
947         if (days_left == 0) {
948                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
949                 return STATE_WARNING;
950         }
952         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
954         return STATE_OK;
956 #endif
957 \f
960 int
961 my_recv (void)
963         int i;
964 #ifdef HAVE_SSL
965         if (use_ssl) {
966                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
967         }
968         else {
969                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
970         }
971 #else
972         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
973 #endif
974         return i;
978 int
979 my_close (void)
981 #ifdef HAVE_SSL
982         if (use_ssl == TRUE) {
983                 SSL_shutdown (ssl);
984                 SSL_free (ssl);
985                 SSL_CTX_free (ctx);
986                 return 0;
987         }
988         else {
989 #endif
990                 return close (sd);
991 #ifdef HAVE_SSL
992         }
993 #endif
1000 \f
1001 void
1002 print_help (void)
1004         print_revision (progname, revision);
1006         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1007         printf (_(COPYRIGHT), copyright, email);
1009         printf (_("\
1010 This plugin tests the HTTP service on the specified host. It can test\n\
1011 normal (http) and secure (https) servers, follow redirects, search for\n\
1012 strings and regular expressions, check connection times, and report on\n\
1013 certificate expiration times.\n"));
1015         print_usage ();
1017         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1019         printf (_(UT_HELP_VRSN));
1021         printf (_("\
1022  -H, --hostname=ADDRESS\n\
1023     Host name argument for servers using host headers (virtual host)\n\
1024  -I, --IP-address=ADDRESS\n\
1025    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1026  -p, --port=INTEGER\n\
1027    Port number (default: %d)\n"), HTTP_PORT);
1029         printf (_(UT_IPv46));
1031 #ifdef HAVE_SSL
1032         printf (_("\
1033  -S, --ssl\n\
1034     Connect via SSL\n\
1035  -C, --certificate=INTEGER\n\
1036     Minimum number of days a certificate has to be valid.\n\
1037     (when this option is used the url is not checked.)\n"));
1038 #endif
1040         printf (_("\
1041  -e, --expect=STRING\n\
1042    String to expect in first (status) line of server response (default: %s)\n\
1043    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1044  -s, --string=STRING\n\
1045    String to expect in the content\n\
1046  -u, --url=PATH\n\
1047    URL to GET or POST (default: /)\n\
1048  -P, --post=STRING\n\
1049    URL encoded http POST data\n"), HTTP_EXPECT);
1051 #ifdef HAVE_REGEX_H
1052         printf (_("\
1053  -l, --linespan\n\
1054     Allow regex to span newlines (must precede -r or -R)\n\
1055  -r, --regex, --ereg=STRING\n\
1056     Search page for regex STRING\n\
1057  -R, --eregi=STRING\n\
1058     Search page for case-insensitive regex STRING\n"));
1059 #endif
1061         printf (_("\
1062  -a, --authorization=AUTH_PAIR\n\
1063    Username:password on sites with basic authentication\n\
1064  -L, --link=URL\n\
1065    Wrap output in HTML link (obsoleted by urlize)\n\
1066  -f, --onredirect=<ok|warning|critical|follow>\n\
1067    How to handle redirected pages\n\
1068  -m, --min=INTEGER\n\
1069    Minimum page size required (bytes)\n"));
1071         printf (_(UT_WARN_CRIT));
1073         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1075         printf (_(UT_VERBOSE));
1077                                         printf (_("\
1078 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1079 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1080 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1081 messages from the host result in STATE_WARNING return values.  If you are\n\
1082 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1083 (fully qualified domain name) as the [host_name] argument.\n"));
1085 #ifdef HAVE_SSL
1086         printf (_("\n\
1087 This plugin can also check whether an SSL enabled web server is able to\n\
1088 serve content (optionally within a specified time) or whether the X509 \n\
1089 certificate is still valid for the specified number of days.\n"));
1090         printf (_("\n\
1091 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1092 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1093 STATE_OK will be returned. When the server returns its content but exceeds\n\
1094 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1095 a STATE_CRITICAL will be returned.\n\n"));
1097         printf (_("\
1098 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1099 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1100 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1101 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1102 the certificate is expired.\n"));
1103 #endif
1105         printf (_(UT_SUPPORT));
1112 void
1113 print_usage (void)
1115         printf (_("\
1116 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1117   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1118   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1119   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1120   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1121         printf (_(UT_HLP_VRS), progname, progname);