From 6b28ae739b1ca9f20519f7522b52d055ce3a0902 Mon Sep 17 00:00:00 2001
From: Holger Weiss <holger@zedat.fu-berlin.de>
Date: Tue, 10 Sep 2013 11:09:22 +0200
Subject: check_http: Support HTTP CONNECT method

Add the -Y/--http-connect[=<port>] option which tells check_http to use
the HTTP CONNECT method for tunneling an HTTPS connection through a
proxy server.

This is a modified version of a patch provided by Mark Frost in
SourceForge tracker item #2975393, updated for the current check_http
code.  Changes include:

- Let the new --http-connect option imply --ssl.
- Allow for specifying the server port the proxy should connect to.

diff --git a/NEWS b/NEWS
index 6c01776..17acce2 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,7 @@ This file documents the major additions and syntax changes between releases.
 	New switch -E/--extended-perfdata for check_http to print additional performance data (Sebastian Nohn)
 	New check_http -d option to specify a string to expect within the response headers
 	New check_http -J/-K options for client certificate authentication support
+	New check_http -Y/--http-connect[=<port>] option for tunneling SSL connections through proxies
 	Add support for executing queries to check_pgsql
 	Let check_pgsql accept a UNIX socket directory as hostname
 	New check_pgsql -o option to specify additional connection parameters
diff --git a/THANKS.in b/THANKS.in
index 6118143..a6caa46 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -286,3 +286,4 @@ Fabio Rueda
 Gabriele Tozzi
 Sebastian Nohn
 Emmanuel Dreyfus
+Mark Frost
diff --git a/plugins/check_http.c b/plugins/check_http.c
index c44bb3a..43c37e0 100644
--- a/plugins/check_http.c
+++ b/plugins/check_http.c
@@ -118,6 +118,8 @@ int use_ssl = FALSE;
 int use_sni = FALSE;
 int verbose = FALSE;
 int show_extended_perfdata = FALSE;
+int http_connect = FALSE;
+int connect_port = HTTPS_PORT;
 int sd;
 int min_page_len = 0;
 int max_page_len = 0;
@@ -133,6 +135,7 @@ char *client_privkey = NULL;
 int process_arguments (int, char **);
 int check_http (void);
 void redir (char *pos, char *status_line);
+int http_connect_through_proxy (char *host_name, int port, char *user_agent, int sd);
 int server_type_check(const char *type);
 int server_port_check(int ssl_flag);
 char *perfd_time (double microsec);
@@ -209,6 +212,7 @@ process_arguments (int argc, char **argv)
     {"nohtml", no_argument, 0, 'n'},
     {"ssl", optional_argument, 0, 'S'},
     {"sni", no_argument, 0, SNI_OPTION},
+    {"http-connect", optional_argument, 0, 'Y'},
     {"post", required_argument, 0, 'P'},
     {"method", required_argument, 0, 'j'},
     {"IP-address", required_argument, 0, 'I'},
@@ -257,7 +261,7 @@ process_arguments (int argc, char **argv)
   }
 
   while (1) {
-    c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:N:E", longopts, &option);
+    c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:Y::T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:N:E", longopts, &option);
     if (c == -1 || c == EOF)
       break;
 
@@ -336,6 +340,13 @@ process_arguments (int argc, char **argv)
       client_privkey = optarg;
       goto enable_ssl;
 #endif
+    case 'Y': /* Use HTTP CONNECT */
+#ifdef HAVE_SSL
+      if (optarg != NULL && ((connect_port = atoi (optarg)) < 1 || connect_port > 65535))
+        usage2 (_("Invalid HTTP CONNECT port number"), optarg);
+      http_connect = TRUE;
+      goto enable_ssl;
+#endif
     case 'S': /* use SSL */
 #ifdef HAVE_SSL
     enable_ssl:
@@ -879,6 +890,8 @@ check_http (void)
   elapsed_time_connect = (double)microsec_connect / 1.0e6;
   if (use_ssl == TRUE) {
     gettimeofday (&tv_temp, NULL);
+    if (http_connect == TRUE && http_connect_through_proxy (host_name, connect_port, user_agent, sd) != STATE_OK)
+      die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open proxy tunnel TCP socket\n"));
     result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey);
     if (result != STATE_OK)
       return result;
@@ -1369,6 +1382,41 @@ redir (char *pos, char *status_line)
   check_http ();
 }
 
+/* start the HTTP CONNECT method exchange with a proxy host */
+int
+http_connect_through_proxy (char *host_name, int port, char *user_agent, int sd)
+{
+  int result;
+  char *send_buffer=NULL;
+  char recv_buffer[MAX_INPUT_BUFFER];
+  char *status_line;
+  char *status_code;
+  int http_status;
+
+  asprintf( &send_buffer, "CONNECT %s:%d HTTP/1.0\r\nUser-agent: %s\r\n\r\n", host_name, port, user_agent);
+
+  result = STATE_OK;
+  result = send_tcp_request (sd, send_buffer, recv_buffer, sizeof(recv_buffer));
+  if (result != STATE_OK)
+    return result;
+
+  status_line = recv_buffer;
+  status_line[strcspn(status_line, "\r\n")] = 0;
+  strip (status_line);
+  if (verbose)
+    printf ("HTTP_CONNECT STATUS: %s\n", status_line);
+
+  status_code = strchr (status_line, ' ') + sizeof (char);
+  if (strspn (status_code, "1234567890") != 3)
+    die (STATE_CRITICAL, _("HTTP CRITICAL: HTTP_CONNECT Returns Invalid Status Line (%s)\n"), status_line);
+
+  http_status = atoi (status_code);
+
+  if (http_status != 200)
+    die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid HTTP Connect Proxy Status (%s)\n"), status_line);
+
+  return STATE_OK;
+}
 
 int
 server_type_check (const char *type)
@@ -1479,6 +1527,10 @@ print_help (void)
   printf (" %s\n", "-K, --private-key=FILE");
   printf ("   %s\n", _("Name of file containing the private key (PEM format)"));
   printf ("   %s\n", _("matching the client certificate"));
+  printf (" %s\n", "-Y, --http-connect=PORT");
+  printf ("   %s\n", _("Connect to a proxy using the HTTP CONNECT method (SSL tunnel)."));
+  printf ("   %s\n", _("Implies -S. The optional PORT number specifies the port on the server the"));
+  printf ("   %s\n\n", _("proxy should connect to (default: 443)."));
 #endif
 
   printf (" %s\n", "-e, --expect=STRING");
@@ -1589,7 +1641,7 @@ print_usage (void)
 {
   printf ("%s\n", _("Usage:"));
   printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
-  printf ("       [-J <client certificate file>] [-K <private key>]\n");
+  printf ("       [-J <client certificate file>] [-K <private key>] [-Y <port>]\n");
   printf ("       [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
   printf ("       [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport>]\n");
   printf ("       [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
-- 
cgit v0.10-9-g596f

