Code

Added Content-type when POSTing (Shawn Wills)
[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 ******************************************************************************/
18 /* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
20 const char *progname = "check_http";
21 const char *revision = "$Revision$";
22 const char *copyright = "1999-2001";
23 const char *email = "nagiosplug-devel@lists.sourceforge.net";
25 #include "common.h"
26 #include "netutils.h"
27 #include "utils.h"
29 #define HTTP_EXPECT "HTTP/1."
30 enum {
31         MAX_IPV4_HOSTLENGTH = 255,
32         HTTP_PORT = 80,
33         HTTPS_PORT = 443
34 };
36 #ifdef HAVE_SSL_H
37 #include <rsa.h>
38 #include <crypto.h>
39 #include <x509.h>
40 #include <pem.h>
41 #include <ssl.h>
42 #include <err.h>
43 #include <rand.h>
44 #else
45 # ifdef HAVE_OPENSSL_SSL_H
46 # include <openssl/rsa.h>
47 # include <openssl/crypto.h>
48 # include <openssl/x509.h>
49 # include <openssl/pem.h>
50 # include <openssl/ssl.h>
51 # include <openssl/err.h>
52 # include <openssl/rand.h>
53 # endif
54 #endif
56 #ifdef HAVE_SSL
57 int check_cert = FALSE;
58 int days_till_exp;
59 char *randbuff;
60 SSL_CTX *ctx;
61 SSL *ssl;
62 X509 *server_cert;
63 int connect_SSL (void);
64 int check_certificate (X509 **);
65 #endif
67 #ifdef HAVE_REGEX_H
68 enum {
69         REGS = 2,
70         MAX_RE_SIZE = 256
71 };
72 #include <regex.h>
73 regex_t preg;
74 regmatch_t pmatch[REGS];
75 char regexp[MAX_RE_SIZE];
76 char errbuf[MAX_INPUT_BUFFER];
77 int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
78 int errcode;
79 #endif
81 struct timeval tv;
83 #define HTTP_URL "/"
84 #define CRLF "\r\n"
86 char timestamp[17] = "";
87 int specify_port = FALSE;
88 int server_port = HTTP_PORT;
89 char server_port_text[6] = "";
90 char server_type[6] = "http";
91 char *server_address;
92 char *host_name;
93 char *server_url;
94 char *user_agent;
95 int server_url_length;
96 int server_expect_yn = 0;
97 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
98 char string_expect[MAX_INPUT_BUFFER] = "";
99 double warning_time = 0;
100 int check_warning_time = FALSE;
101 double critical_time = 0;
102 int check_critical_time = FALSE;
103 char user_auth[MAX_INPUT_BUFFER] = "";
104 int display_html = FALSE;
105 int onredirect = STATE_OK;
106 int use_ssl = FALSE;
107 int verbose = FALSE;
108 int sd;
109 int min_page_len = 0;
110 int redir_depth = 0;
111 int max_depth = 15;
112 char *http_method;
113 char *http_post_data;
114 char *http_content_type;
115 char buffer[MAX_INPUT_BUFFER];
117 int process_arguments (int, char **);
118 static char *base64 (const char *bin, size_t len);
119 int check_http (void);
120 void redir (char *pos, char *status_line);
121 int server_type_check(const char *type);
122 int server_port_check(int ssl_flag);
123 char *perfd_time (long microsec);
124 char *perfd_size (int page_len);
125 int my_recv (void);
126 int my_close (void);
127 void print_help (void);
128 void print_usage (void);
130 int
131 main (int argc, char **argv)
133         int result = STATE_UNKNOWN;
135         /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
136         server_url = strdup(HTTP_URL);
137         server_url_length = strlen(server_url);
138         asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
139                   clean_revstring (revision), VERSION);
141         if (process_arguments (argc, argv) == ERROR)
142                 usage (_("check_http: could not parse arguments\n"));
144         if (strstr (timestamp, ":")) {
145                 if (strstr (server_url, "?"))
146                         asprintf (&server_url, "%s&%s", server_url, timestamp);
147                 else
148                         asprintf (&server_url, "%s?%s", server_url, timestamp);
149         }
151         if (display_html == TRUE)
152                 printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
153                         host_name, server_port, server_url);
155         /* initialize alarm signal handling, set socket timeout, start timer */
156         (void) signal (SIGALRM, socket_timeout_alarm_handler);
157         (void) alarm (socket_timeout);
158         gettimeofday (&tv, NULL);
160 #ifdef HAVE_SSL
161         if (use_ssl && check_cert == TRUE) {
162                 if (connect_SSL () != OK)
163                         die (STATE_CRITICAL, _("HTTP CRITICAL - Could not make SSL connection\n"));
164                 if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
165                         result = check_certificate (&server_cert);
166                         X509_free (server_cert);
167                 }
168                 else {
169                         printf (_("ERROR: Cannot retrieve server certificate.\n"));
170                         result = STATE_CRITICAL;
171                 }
172                 SSL_shutdown (ssl);
173                 SSL_free (ssl);
174                 SSL_CTX_free (ctx);
175                 close (sd);
176         }
177         else {
178                 result = check_http ();
179         }
180 #else
181         result = check_http ();
182 #endif
183         return result;
185 \f
188 /* process command-line arguments */
189 int
190 process_arguments (int argc, char **argv)
192         int c = 1;
194         int option = 0;
195         static struct option longopts[] = {
196                 STD_LONG_OPTS,
197                 {"file",required_argument,0,'F'},
198                 {"link", no_argument, 0, 'L'},
199                 {"nohtml", no_argument, 0, 'n'},
200                 {"ssl", no_argument, 0, 'S'},
201                 {"verbose", no_argument, 0, 'v'},
202                 {"post", required_argument, 0, 'P'},
203                 {"IP-address", required_argument, 0, 'I'},
204                 {"string", required_argument, 0, 's'},
205                 {"regex", required_argument, 0, 'r'},
206                 {"ereg", required_argument, 0, 'r'},
207                 {"eregi", required_argument, 0, 'R'},
208                 {"linespan", no_argument, 0, 'l'},
209                 {"onredirect", required_argument, 0, 'f'},
210                 {"certificate", required_argument, 0, 'C'},
211                 {"content-type", required_argument, 0, 'T'},
212                 {"min", required_argument, 0, 'm'},
213                 {"use-ipv4", no_argument, 0, '4'},
214                 {"use-ipv6", no_argument, 0, '6'},
215                 {0, 0, 0, 0}
216         };
218         if (argc < 2)
219                 return ERROR;
221         for (c = 1; c < argc; c++) {
222                 if (strcmp ("-to", argv[c]) == 0)
223                         strcpy (argv[c], "-t");
224                 if (strcmp ("-hn", argv[c]) == 0)
225                         strcpy (argv[c], "-H");
226                 if (strcmp ("-wt", argv[c]) == 0)
227                         strcpy (argv[c], "-w");
228                 if (strcmp ("-ct", argv[c]) == 0)
229                         strcpy (argv[c], "-c");
230                 if (strcmp ("-nohtml", argv[c]) == 0)
231                         strcpy (argv[c], "-n");
232         }
234         while (1) {
235                 c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
236                 if (c == -1 || c == EOF)
237                         break;
239                 switch (c) {
240                 case '?': /* usage */
241                         usage3 (_("unknown argument"), optopt);
242                         break;
243                 case 'h': /* help */
244                         print_help ();
245                         exit (STATE_OK);
246                         break;
247                 case 'V': /* version */
248                         print_revision (progname, revision);
249                         exit (STATE_OK);
250                         break;
251                 case 't': /* timeout period */
252                         if (!is_intnonneg (optarg))
253                                 usage2 (_("timeout interval must be a non-negative integer"), optarg);
254                         else
255                                 socket_timeout = atoi (optarg);
256                         break;
257                 case 'c': /* critical time threshold */
258                         if (!is_intnonneg (optarg))
259                                 usage2 (_("invalid critical threshold"), optarg);
260                         else {
261                                 critical_time = strtod (optarg, NULL);
262                                 check_critical_time = TRUE;
263                         }
264                         break;
265                 case 'w': /* warning time threshold */
266                         if (!is_intnonneg (optarg))
267                                 usage2 (_("invalid warning threshold"), optarg);
268                         else {
269                                 warning_time = strtod (optarg, NULL);
270                                 check_warning_time = TRUE;
271                         }
272                         break;
273                 case 'L': /* show html link */
274                         display_html = TRUE;
275                         break;
276                 case 'n': /* do not show html link */
277                         display_html = FALSE;
278                         break;
279                 case 'S': /* use SSL */
280 #ifndef HAVE_SSL
281                         usage (_("check_http: invalid option - SSL is not available\n"));
282 #endif
283                         use_ssl = TRUE;
284                         if (specify_port == FALSE)
285                                 server_port = HTTPS_PORT;
286                         break;
287                 case 'C': /* Check SSL cert validity */
288 #ifdef HAVE_SSL
289                         if (!is_intnonneg (optarg))
290                                 usage2 (_("invalid certificate expiration period"), optarg);
291                         else {
292                                 days_till_exp = atoi (optarg);
293                                 check_cert = TRUE;
294                         }
295 #else
296                         usage (_("check_http: invalid option - SSL is not available\n"));
297 #endif
298                         break;
299                 case 'f': /* onredirect */
300                         if (!strcmp (optarg, "follow"))
301                                 onredirect = STATE_DEPENDENT;
302                         if (!strcmp (optarg, "unknown"))
303                                 onredirect = STATE_UNKNOWN;
304                         if (!strcmp (optarg, "ok"))
305                                 onredirect = STATE_OK;
306                         if (!strcmp (optarg, "warning"))
307                                 onredirect = STATE_WARNING;
308                         if (!strcmp (optarg, "critical"))
309                                 onredirect = STATE_CRITICAL;
310                         if (verbose)
311                                 printf(_("option f:%d \n"), onredirect);  
312                         break;
313                 /* Note: H, I, and u must be malloc'd or will fail on redirects */
314                 case 'H': /* Host Name (virtual host) */
315                         host_name = strdup (optarg);
316                         break;
317                 case 'I': /* Server IP-address */
318                         server_address = strdup (optarg);
319                         break;
320                 case 'u': /* URL path */
321                         server_url = strdup (optarg);
322                         server_url_length = strlen (server_url);
323                         break;
324                 case 'p': /* Server port */
325                         if (!is_intnonneg (optarg))
326                                 usage2 (_("invalid port number"), optarg);
327                         else {
328                                 server_port = atoi (optarg);
329                                 specify_port = TRUE;
330                         }
331                         break;
332                 case 'a': /* authorization info */
333                         strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
334                         user_auth[MAX_INPUT_BUFFER - 1] = 0;
335                         break;
336                 case 'P': /* HTTP POST data in URL encoded format */
337                         if (http_method || http_post_data) break;
338                         http_method = strdup("POST");
339                         http_post_data = strdup (optarg);
340                         break;
341                 case 's': /* string or substring */
342                         strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
343                         string_expect[MAX_INPUT_BUFFER - 1] = 0;
344                         break;
345                 case 'e': /* string or substring */
346                         strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
347                         server_expect[MAX_INPUT_BUFFER - 1] = 0;
348                         server_expect_yn = 1;
349                         break;
350                 case 'T': /* Content-type */
351                         asprintf (&http_content_type, "%s", optarg);
352                         break;
353 #ifndef HAVE_REGEX_H
354                 case 'l': /* linespan */
355                 case 'r': /* linespan */
356                 case 'R': /* linespan */
357                         usage (_("check_http: call for regex which was not a compiled option\n"));
358                         break;
359 #else
360                 case 'l': /* linespan */
361                         cflags &= ~REG_NEWLINE;
362                         break;
363                 case 'R': /* regex */
364                         cflags |= REG_ICASE;
365                 case 'r': /* regex */
366                         strncpy (regexp, optarg, MAX_RE_SIZE - 1);
367                         regexp[MAX_RE_SIZE - 1] = 0;
368                         errcode = regcomp (&preg, regexp, cflags);
369                         if (errcode != 0) {
370                                 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
371                                 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
372                                 return ERROR;
373                         }
374                         break;
375 #endif
376                 case '4':
377                         address_family = AF_INET;
378                         break;
379                 case '6':
380 #ifdef USE_IPV6
381                         address_family = AF_INET6;
382 #else
383                         usage (_("IPv6 support not available\n"));
384 #endif
385                         break;
386                 case 'v': /* verbose */
387                         verbose = TRUE;
388                         break;
389                 case 'm': /* min_page_length */
390                         min_page_len = atoi (optarg);
391                         break;
392                 }
393         }
395         c = optind;
397         if (server_address == NULL && c < argc)
398                 server_address = strdup (argv[c++]);
400         if (host_name == NULL && c < argc)
401                 host_name = strdup (argv[c++]);
403         if (server_address == NULL) {
404                 if (host_name == NULL)
405                         usage (_("check_http: you must specify a server address or host name\n"));
406                 else
407                         server_address = strdup (host_name);
408         }
410         if (check_critical_time && critical_time>(double)socket_timeout)
411                 socket_timeout = (int)critical_time + 1;
413         if (http_method == NULL)
414                 http_method = strdup ("GET");
416         return TRUE;
418 \f
421 /* written by lauri alanko */
422 static char *
423 base64 (const char *bin, size_t len)
426         char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
427         size_t i = 0, j = 0;
429         char BASE64_END = '=';
430         char base64_table[64];
431         strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
433         while (j < len - 2) {
434                 buf[i++] = base64_table[bin[j] >> 2];
435                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
436                 buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
437                 buf[i++] = base64_table[bin[j + 2] & 63];
438                 j += 3;
439         }
441         switch (len - j) {
442         case 1:
443                 buf[i++] = base64_table[bin[j] >> 2];
444                 buf[i++] = base64_table[(bin[j] & 3) << 4];
445                 buf[i++] = BASE64_END;
446                 buf[i++] = BASE64_END;
447                 break;
448         case 2:
449                 buf[i++] = base64_table[bin[j] >> 2];
450                 buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
451                 buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
452                 buf[i++] = BASE64_END;
453                 break;
454         case 0:
455                 break;
456         }
458         buf[i] = '\0';
459         return buf;
461 \f
466 int
467 check_http (void)
469         char *msg;
470         char *status_line;
471         char *status_code;
472         char *header;
473         char *page;
474         char *auth;
475         int http_status;
476         int i = 0;
477         size_t pagesize = 0;
478         char *full_page;
479         char *buf;
480         char *pos;
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\n"));
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\n"));
509 #ifdef HAVE_SSL
510         }
511 #endif
513         asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
515         /* optionally send the host header info */
516         if (host_name)
517                 asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
519         /* optionally send the authentication info */
520         if (strlen(user_auth)) {
521                 auth = base64 (user_auth, strlen (user_auth));
522                 asprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth);
523         }
525         /* either send http POST data */
526         if (http_post_data) {
527                 if (http_content_type) {
528                         asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
529                 } else {
530                         asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
531                 }
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         if (verbose)
541                 printf ("%s\n", buf);
543 #ifdef HAVE_SSL
544         if (use_ssl == TRUE) {
545                 if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
546                         ERR_print_errors_fp (stderr);
547                         return STATE_CRITICAL;
548                 }
549         }
550         else {
551 #endif
552                 send (sd, buf, strlen (buf), 0);
553 #ifdef HAVE_SSL
554         }
555 #endif
557         /* fetch the page */
558         full_page = strdup("");
559         while ((i = my_recv ()) > 0) {
560                 buffer[i] = '\0';
561                 asprintf (&full_page, "%s%s", full_page, buffer);
562                 pagesize += i;
563         }
565         if (i < 0 && errno != ECONNRESET) {
566 #ifdef HAVE_SSL
567                 if (use_ssl) {
568                         sslerr=SSL_get_error(ssl, i);
569                         if ( sslerr == SSL_ERROR_SSL ) {
570                                 die (STATE_WARNING, _("Client Certificate Required\n"));
571                         } else {
572                                 die (STATE_CRITICAL, _("Error in recv()\n"));
573                         }
574                 }
575                 else {
576 #endif
577                         die (STATE_CRITICAL, _("Error in recv()\n"));
578 #ifdef HAVE_SSL
579                 }
580 #endif
581         }
583         /* return a CRITICAL status if we couldn't read any data */
584         if (pagesize == (size_t) 0)
585                 die (STATE_CRITICAL, _("No data received %s\n"), timestamp);
587         /* close the connection */
588         my_close ();
590         /* reset the alarm */
591         alarm (0);
593         /* leave full_page untouched so we can free it later */
594         page = full_page;
596         if (verbose)
597                 printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
599         /* find status line and null-terminate it */
600         status_line = page;
601         page += (size_t) strcspn (page, "\r\n");
602         pos = page;
603         page += (size_t) strspn (page, "\r\n");
604         status_line[strcspn(status_line, "\r\n")] = 0;
605         strip (status_line);
606         if (verbose)
607                 printf ("STATUS: %s\n", status_line);
609         /* find header info and null-terminate it */
610         header = page;
611         while (strcspn (page, "\r\n") > 0) {
612                 page += (size_t) strcspn (page, "\r\n");
613                 pos = page;
614                 if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) ||
615                     (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2))
616                         page += (size_t) 2;
617                 else
618                         page += (size_t) 1;
619         }
620         page += (size_t) strspn (page, "\r\n");
621         header[pos - header] = 0;
622         if (verbose)
623                 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
625         /* make sure the status line matches the response we are looking for */
626         if (!strstr (status_line, server_expect)) {
627                 if (server_port == HTTP_PORT)
628                         asprintf (&msg,
629                             _("Invalid HTTP response received from host\n"));
630                 else
631                         asprintf (&msg,
632                                   _("Invalid HTTP response received from host on port %d\n"),
633                                   server_port);
634                 die (STATE_CRITICAL, "%s", msg);
635         }
637         /* Exit here if server_expect was set by user and not default */
638         if ( server_expect_yn  )  {
639                 asprintf (&msg,
640                           _("HTTP OK: Status line output matched \"%s\"\n"),
641                           server_expect);
642                 if (verbose)
643                         printf ("%s\n",msg);
644         }
645         else {
646                 /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
647                 /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
648     /* Status-Code = 3 DIGITS */
650                 status_code = strchr (status_line, ' ') + sizeof (char);
651                 if (strspn (status_code, "1234567890") != 3)
652                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
654                 http_status = atoi (status_code);
656                 /* check the return code */
658                 if (http_status >= 600 || http_status < 100)
659                         die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
661                 /* server errors result in a critical state */
662                 else if (http_status >= 500)
663                         die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
665                 /* client errors result in a warning state */
666                 else if (http_status >= 400)
667                         die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
669                 /* check redirected page if specified */
670                 else if (http_status >= 300) {
672                         if (onredirect == STATE_DEPENDENT)
673                                 redir (header, status_line);
674                         else if (onredirect == STATE_UNKNOWN)
675                                 printf (_("UNKNOWN"));
676                         else if (onredirect == STATE_OK)
677                                 printf (_("OK"));
678                         else if (onredirect == STATE_WARNING)
679                                 printf (_("WARNING"));
680                         else if (onredirect == STATE_CRITICAL)
681                                 printf (_("CRITICAL"));
682                         microsec = deltime (tv);
683                         elapsed_time = (double)microsec / 1.0e6;
684                         die (onredirect,
685                              _(" - %s - %.3f second response time %s%s|%s %s\n"),
686                              status_line, elapsed_time, timestamp,
687                              (display_html ? "</A>" : ""),
688                                          perfd_time (microsec), perfd_size (pagesize));
689                 } /* end if (http_status >= 300) */
691         } /* end else (server_expect_yn)  */
692                 
693         /* check elapsed time */
694         microsec = deltime (tv);
695         elapsed_time = (double)microsec / 1.0e6;
696         asprintf (&msg,
697                   _("HTTP problem: %s - %.3f second response time %s%s|%s %s\n"),
698                   status_line, elapsed_time, timestamp,
699                   (display_html ? "</A>" : ""),
700                                                 perfd_time (microsec), perfd_size (pagesize));
701         if (check_critical_time == TRUE && elapsed_time > critical_time)
702                 die (STATE_CRITICAL, "%s", msg);
703         if (check_warning_time == TRUE && elapsed_time > warning_time)
704                 die (STATE_WARNING, "%s", msg);
706         /* Page and Header content checks go here */
707         /* these checks should be last */
709         if (strlen (string_expect)) {
710                 if (strstr (page, string_expect)) {
711                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
712                                 status_line, elapsed_time,
713                                 timestamp, (display_html ? "</A>" : ""),
714                                 perfd_time (microsec), perfd_size (pagesize));
715                         exit (STATE_OK);
716                 }
717                 else {
718                         printf (_("CRITICAL - string not found%s|%s %s\n"),
719                                 (display_html ? "</A>" : ""),
720                                 perfd_time (microsec), perfd_size (pagesize));
721                         exit (STATE_CRITICAL);
722                 }
723         }
724 #ifdef HAVE_REGEX_H
725         if (strlen (regexp)) {
726                 errcode = regexec (&preg, page, REGS, pmatch, 0);
727                 if (errcode == 0) {
728                         printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
729                                 status_line, elapsed_time,
730                                 timestamp, (display_html ? "</A>" : ""),
731                                 perfd_time (microsec), perfd_size (pagesize));
732                         exit (STATE_OK);
733                 }
734                 else {
735                         if (errcode == REG_NOMATCH) {
736                                 printf (_("CRITICAL - pattern not found%s|%s %s\n"),
737                                         (display_html ? "</A>" : ""),
738                                         perfd_time (microsec), perfd_size (pagesize));
739                                 exit (STATE_CRITICAL);
740                         }
741                         else {
742                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
743                                 printf (_("CRITICAL - Execute Error: %s\n"), errbuf);
744                                 exit (STATE_CRITICAL);
745                         }
746                 }
747         }
748 #endif
750         /* make sure the page is of an appropriate size */
751         page_len = strlen (page);
752         if ((min_page_len > 0) && (page_len < min_page_len)) {
753                 printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
754                         page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
755                 exit (STATE_WARNING);
756         }
757         /* We only get here if all tests have been passed */
758         asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
759                   status_line, page_len, elapsed_time,
760                   timestamp, (display_html ? "</A>" : ""),
761                                                 perfd_time (microsec), perfd_size (page_len));
762         die (STATE_OK, "%s", msg);
763         return STATE_UNKNOWN;
769 /* per RFC 2396 */
770 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
771 #define URI_HTTP "%[HTPShtps]://"
772 #define URI_HOST "%[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
773 #define URI_PORT ":%[0123456789]"
774 #define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]"
775 #define HD1 URI_HTTP URI_HOST URI_PORT URI_PATH
776 #define HD2 URI_HTTP URI_HOST URI_PATH
777 #define HD3 URI_HTTP URI_HOST URI_PORT
778 #define HD4 URI_HTTP URI_HOST
779 #define HD5 URI_PATH
781 void
782 redir (char *pos, char *status_line)
784         int i = 0;
785         char *x;
786         char xx[2];
787         char type[6];
788         char *addr;
789         char port[6];
790         char *url;
792         addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
793         if (addr == NULL)
794                 die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
795         
796         url = malloc (strcspn (pos, "\r\n"));
797         if (url == NULL)
798                 die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
800         while (pos) {
802                 if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
804                         pos += (size_t) strcspn (pos, "\r\n");
805                         pos += (size_t) strspn (pos, "\r\n");
806                         if (strlen(pos) == 0) 
807                                 die (STATE_UNKNOWN,
808                                                  _("UNKNOWN - Could not find redirect location - %s%s\n"),
809                                                  status_line, (display_html ? "</A>" : ""));
810                         continue;
811                 }
813                 pos += i;
814                 pos += strspn (pos, " \t\r\n");
816                 url = realloc (url, strcspn (pos, "\r\n"));
817                 if (url == NULL)
818                         die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
820                 /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
821                 if (sscanf (pos, HD1, type, addr, port, url) == 4) {
822                         use_ssl = server_type_check (type);
823                         i = atoi (port);
824                 }
826                 /* URI_HTTP URI_HOST URI_PATH */
827                 else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
828                         use_ssl = server_type_check (type);
829                         i = server_port_check (use_ssl);
830                 }
832                 /* URI_HTTP URI_HOST URI_PORT */
833                 else if(sscanf (pos, HD3, type, addr, port) == 3) {
834                         strcpy (url, HTTP_URL);
835                         use_ssl = server_type_check (type);
836                         i = atoi (port);
837                 }
839                 /* URI_HTTP URI_HOST */
840                 else if(sscanf (pos, HD4, type, addr) == 2) {
841                         strcpy (url, HTTP_URL);
842                         use_ssl = server_type_check (type);
843                         i = server_port_check (use_ssl);
844                 }
846                 /* URI_PATH */
847                 else if (sscanf (pos, HD5, url) == 1) {
848                         /* relative url */
849                         if ((url[0] != '/')) {
850                                 if ((x = strrchr(server_url, '/')))
851                                         *x = '\0';
852                                 asprintf (&url, "%s/%s", server_url, url);
853                         }
854                         i = server_port;
855                         strcpy (type, server_type);
856                         strcpy (addr, host_name);
857                 }                                       
859                 else {
860                         die (STATE_UNKNOWN,
861                                          _("UNKNOWN - Could not parse redirect location - %s%s\n"),
862                                          pos, (display_html ? "</A>" : ""));
863                 }
865                 break;
867         } /* end while (pos) */
869         if (++redir_depth > max_depth)
870                 die (STATE_WARNING,
871                      _("WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"),
872                      max_depth, type, addr, i, url, (display_html ? "</A>" : ""));
874         if (server_port==i &&
875             !strcmp(server_address, addr) &&
876             (host_name && !strcmp(host_name, addr)) &&
877             !strcmp(server_url, url))
878                 die (STATE_WARNING,
879                      _("WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
880                      type, addr, i, url, (display_html ? "</A>" : ""));
882         server_port = i;
883         strcpy (server_type, type);
885         free (host_name);
886         host_name = strdup (addr);
888         free (server_address);
889         server_address = strdup (addr);
891         free (server_url);
892         server_url = strdup (url);
894         check_http ();
899 int
900 server_type_check (const char *type)
902         if (strcmp (type, "https"))
903                 return FALSE;
904         else
905                 return TRUE;
908 int
909 server_port_check (int ssl_flag)
911         if (ssl_flag)
912                 return HTTPS_PORT;
913         else
914                 return HTTP_PORT;
919 #ifdef HAVE_SSL
920 int connect_SSL (void)
922         SSL_METHOD *meth;
924         asprintf (&randbuff, "%s", "qwertyuiopasdfghjklqwertyuiopasdfghjkl");
925         RAND_seed (randbuff, (int)strlen(randbuff));
926         if (verbose)
927                 printf(_("SSL seeding: %s\n"), (RAND_status()==1 ? _("OK") : _("Failed")) );
929         /* Initialize SSL context */
930         SSLeay_add_ssl_algorithms ();
931         meth = SSLv23_client_method ();
932         SSL_load_error_strings ();
933         if ((ctx = SSL_CTX_new (meth)) == NULL) {
934                 printf (_("CRITICAL -  Cannot create SSL context.\n"));
935                 return STATE_CRITICAL;
936         }
938         /* Initialize alarm signal handling */
939         signal (SIGALRM, socket_timeout_alarm_handler);
941         /* Set socket timeout */
942         alarm (socket_timeout);
944         /* Save start time */
945         gettimeofday (&tv, NULL);
947         /* Make TCP connection */
948         if (my_tcp_connect (server_address, server_port, &sd) == STATE_OK) {
949                 /* Do the SSL handshake */
950                 if ((ssl = SSL_new (ctx)) != NULL) {
951                         SSL_set_cipher_list(ssl, "ALL");
952                         SSL_set_fd (ssl, sd);
953                         if (SSL_connect (ssl) != -1)
954                                 return OK;
955                         ERR_print_errors_fp (stderr);
956                 }
957                 else {
958                         printf (_("CRITICAL - Cannot initiate SSL handshake.\n"));
959                 }
960                 SSL_free (ssl);
961         }
963         SSL_CTX_free (ctx);
964         close (sd);
966         return STATE_CRITICAL;
968 #endif
970 #ifdef HAVE_SSL
971 int
972 check_certificate (X509 ** certificate)
974         ASN1_STRING *tm;
975         int offset;
976         struct tm stamp;
977         int days_left;
980         /* Retrieve timestamp of certificate */
981         tm = X509_get_notAfter (*certificate);
983         /* Generate tm structure to process timestamp */
984         if (tm->type == V_ASN1_UTCTIME) {
985                 if (tm->length < 10) {
986                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
987                         return STATE_CRITICAL;
988                 }
989                 else {
990                         stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0');
991                         if (stamp.tm_year < 50)
992                                 stamp.tm_year += 100;
993                         offset = 0;
994                 }
995         }
996         else {
997                 if (tm->length < 12) {
998                         printf (_("CRITICAL - Wrong time format in certificate.\n"));
999                         return STATE_CRITICAL;
1000                 }
1001                 else {
1002                         stamp.tm_year =
1003                                 (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 +
1004                                 (tm->data[2] - '0') * 10 + (tm->data[3] - '0');
1005                         stamp.tm_year -= 1900;
1006                         offset = 2;
1007                 }
1008         }
1009         stamp.tm_mon =
1010                 (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1;
1011         stamp.tm_mday =
1012                 (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0');
1013         stamp.tm_hour =
1014                 (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0');
1015         stamp.tm_min =
1016                 (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0');
1017         stamp.tm_sec = 0;
1018         stamp.tm_isdst = -1;
1020         days_left = (mktime (&stamp) - time (NULL)) / 86400;
1021         snprintf
1022                 (timestamp, 17, "%02d/%02d/%04d %02d:%02d",
1023                  stamp.tm_mon + 1,
1024                  stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min);
1026         if (days_left > 0 && days_left <= days_till_exp) {
1027                 printf (_("WARNING - Certificate expires in %d day(s) (%s).\n"), days_left, timestamp);
1028                 return STATE_WARNING;
1029         }
1030         if (days_left < 0) {
1031                 printf (_("CRITICAL - Certificate expired on %s.\n"), timestamp);
1032                 return STATE_CRITICAL;
1033         }
1035         if (days_left == 0) {
1036                 printf (_("WARNING - Certificate expires today (%s).\n"), timestamp);
1037                 return STATE_WARNING;
1038         }
1040         printf (_("OK - Certificate will expire on %s.\n"), timestamp);
1042         return STATE_OK;
1044 #endif
1045 \f
1048 char *perfd_time (long microsec)
1050         return perfdata ("time", microsec, "us",
1051                   check_warning_time, (int)(1e6*warning_time),
1052                   check_critical_time, (int)(1e6*critical_time),
1053                   TRUE, 0, FALSE, 0);
1057 char *perfd_size (int page_len)
1059         return perfdata ("size", page_len, "B",
1060                   (min_page_len>0?TRUE:FALSE), min_page_len,
1061                   (min_page_len>0?TRUE:FALSE), 0,
1062                   TRUE, 0, FALSE, 0);
1066 int
1067 my_recv (void)
1069         int i;
1070 #ifdef HAVE_SSL
1071         if (use_ssl) {
1072                 i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
1073         }
1074         else {
1075                 i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1076         }
1077 #else
1078         i = recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0);
1079 #endif
1080         return i;
1084 int
1085 my_close (void)
1087 #ifdef HAVE_SSL
1088         if (use_ssl == TRUE) {
1089                 SSL_shutdown (ssl);
1090                 SSL_free (ssl);
1091                 SSL_CTX_free (ctx);
1092                 return 0;
1093         }
1094         else {
1095 #endif
1096                 return close (sd);
1097 #ifdef HAVE_SSL
1098         }
1099 #endif
1106 \f
1107 void
1108 print_help (void)
1110         print_revision (progname, revision);
1112         printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
1113         printf (_(COPYRIGHT), copyright, email);
1115         printf (_("\
1116 This plugin tests the HTTP service on the specified host. It can test\n\
1117 normal (http) and secure (https) servers, follow redirects, search for\n\
1118 strings and regular expressions, check connection times, and report on\n\
1119 certificate expiration times.\n"));
1121         print_usage ();
1123         printf (_("NOTE: One or both of -H and -I must be specified\n"));
1125         printf (_(UT_HELP_VRSN));
1127         printf (_("\
1128  -H, --hostname=ADDRESS\n\
1129     Host name argument for servers using host headers (virtual host)\n\
1130  -I, --IP-address=ADDRESS\n\
1131    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
1132  -p, --port=INTEGER\n\
1133    Port number (default: %d)\n"), HTTP_PORT);
1135         printf (_(UT_IPv46));
1137 #ifdef HAVE_SSL
1138         printf (_("\
1139  -S, --ssl\n\
1140     Connect via SSL\n\
1141  -C, --certificate=INTEGER\n\
1142     Minimum number of days a certificate has to be valid.\n\
1143     (when this option is used the url is not checked.)\n"));
1144 #endif
1146         printf (_("\
1147  -e, --expect=STRING\n\
1148    String to expect in first (status) line of server response (default: %s)\n\
1149    If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)\n\
1150  -s, --string=STRING\n\
1151    String to expect in the content\n\
1152  -u, --url=PATH\n\
1153    URL to GET or POST (default: /)\n\
1154  -P, --post=STRING\n\
1155    URL encoded http POST data\n\
1156  -T, --content-type=STRING\n\
1157    specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
1159 #ifdef HAVE_REGEX_H
1160         printf (_("\
1161  -l, --linespan\n\
1162     Allow regex to span newlines (must precede -r or -R)\n\
1163  -r, --regex, --ereg=STRING\n\
1164     Search page for regex STRING\n\
1165  -R, --eregi=STRING\n\
1166     Search page for case-insensitive regex STRING\n"));
1167 #endif
1169         printf (_("\
1170  -a, --authorization=AUTH_PAIR\n\
1171    Username:password on sites with basic authentication\n\
1172  -L, --link=URL\n\
1173    Wrap output in HTML link (obsoleted by urlize)\n\
1174  -f, --onredirect=<ok|warning|critical|follow>\n\
1175    How to handle redirected pages\n\
1176  -m, --min=INTEGER\n\
1177    Minimum page size required (bytes)\n"));
1179         printf (_(UT_WARN_CRIT));
1181         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1183         printf (_(UT_VERBOSE));
1185                                         printf (_("\
1186 This plugin will attempt to open an HTTP connection with the host. Successful\n\
1187 connects return STATE_OK, refusals and timeouts return STATE_CRITICAL, other\n\
1188 errors return STATE_UNKNOWN.  Successful connects, but incorrect reponse\n\
1189 messages from the host result in STATE_WARNING return values.  If you are\n\
1190 checking a virtual server that uses 'host headers' you must supply the FQDN\n\
1191 (fully qualified domain name) as the [host_name] argument.\n"));
1193 #ifdef HAVE_SSL
1194         printf (_("\n\
1195 This plugin can also check whether an SSL enabled web server is able to\n\
1196 serve content (optionally within a specified time) or whether the X509 \n\
1197 certificate is still valid for the specified number of days.\n"));
1198         printf (_("\n\
1199 CHECK CONTENT: check_http -w 5 -c 10 --ssl www.verisign.com\n\n\
1200 When the 'www.verisign.com' server returns its content within 5 seconds, a\n\
1201 STATE_OK will be returned. When the server returns its content but exceeds\n\
1202 the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,\n\
1203 a STATE_CRITICAL will be returned.\n\n"));
1205         printf (_("\
1206 CHECK CERTIFICATE: check_http www.verisign.com -C 14\n\n\
1207 When the certificate of 'www.verisign.com' is valid for more than 14 days, a\n\
1208 STATE_OK is returned. When the certificate is still valid, but for less than\n\
1209 14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when\n\
1210 the certificate is expired.\n"));
1211 #endif
1213         printf (_(UT_SUPPORT));
1220 void
1221 print_usage (void)
1223         printf (_("\
1224 Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
1225   [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
1226   [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
1227   [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
1228   [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
1229         printf (_(UT_HLP_VRS), progname, progname);