I recently discovered Proxytunnel, a great tool to build tunnels through HTTP and/or HTTPS proxies, which is even able to chain two proxies. It can seem a bit overkill at first sight, but it’s really useful in some configurations. This tool is well documented on its homepage and a very nice use case is detailed SSH HTTP Tunneling.

I made a patch (named -sl1) against the latest version (1.9.0), which brings the following changes:

  • A command-line option to use HTTP/1.1 CONNECT instead of HTTP/1.0. This also adds the required Host: header. This is needed for some corporate proxies that refuse to serve HTTP/1.0 CONNECT requests and is the primary reason I made this patch.
  • Under Cygwin, the “verbose” switch now outputs to stderr, instead of trying to use syslog, which doesn’t seem to work or make sense under Cygwin
  • Compilation with FORTIFY_SOURCE, which secures some potentially dangerous libc functions, for the gcc’s that support it.
  • Cygwin autodetection at compile time, manually editing the Makefile is no longer needed.

Edit: Released version -sl2, now HTTP/1.0 vs HTTP/1.1 is properly enforced in all cases (one or two proxies, with or without SSL).

Share and enjoy. :)

diff -ruN proxytunnel-1.9.0/cmdline.c proxytunnel-1.9.0-sl2/cmdline.c
--- proxytunnel-1.9.0/cmdline.c 2008-03-03 23:06:28.000000000 +0100
+++ proxytunnel-1.9.0-sl2/cmdline.c     2009-09-23 10:49:42.000000000 +0200
@@ -75,6 +75,7 @@
 #ifdef SETPROCTITLE
 " -x, --proctitle=STRING    Use a different process title\n"
 #endif
+" -1, --http11              Use HTTP/1.1 instead of 1.0, and add 'Host:' header\n"
 "\n"
 "Miscellaneous options:\n"
 " -v, --verbose             Turn on verbosity\n"
@@ -132,6 +133,7 @@
        args_info->encryptproxy_given = 0;
        args_info->encryptremproxy_given = 0;
        args_info->proctitle_given = 0;
+       args_info->http_minorversion = 0;
 /* No... we can't make this a function... -- Maniac */
 #define clear_args() \
@@ -157,6 +159,7 @@
        args_info->encryptproxy_flag = 0; \
        args_info->encryptremproxy_flag = 0; \
        args_info->proctitle_arg = NULL; \
+       args_info->http_minorversion = 0; \
 } 
        clear_args();
@@ -198,12 +201,13 @@
                        { "encrypt",            0, NULL, 'e' },
                        { "encrypt-proxy",      0, NULL, 'E' },
                        { "encrypt-remproxy",0,NULL, 'X' },
+                       { "http11",         0, NULL, '1' },
                        { NULL,                         0, NULL, 0 }
                };
-               c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXq", long_options, &option_index);
+               c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXq1", long_options, &option_index);
 #else
-               c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXq" );
+               c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXq1" );
 #endif
                if (c == -1)
@@ -403,6 +407,10 @@
                                args_info->quiet_flag = !(args_info->quiet_flag);
                                break;
+                       case '1':       /* use HTTP/1.1 instead of HTTP/1.0 */
+                               args_info->http_minorversion = 1;
+                               break;
+
                        case 0: /* Long option with no short option */
                        case '?':       /* Invalid option.  */
@@ -418,7 +426,7 @@
        } /* while */
 /* For Windows quiet is the default output. -- Dag */
-#ifdef CYGWIN
+#ifdef __CYGWIN__
        if (! args_info->verbose_flag ) {
                args_info->quiet_flag = 1;
        }
diff -ruN proxytunnel-1.9.0/cmdline.h proxytunnel-1.9.0-sl2/cmdline.h
--- proxytunnel-1.9.0/cmdline.h 2008-02-22 22:25:02.000000000 +0100
+++ proxytunnel-1.9.0-sl2/cmdline.h     2009-09-23 09:39:00.000000000 +0200
@@ -71,6 +71,7 @@
        int encryptproxy_given; /* Whether encrypt was given */
        int encryptremproxy_given;   /* Whether encrypt was given */
        int proctitle_given;    /* Whether to override process title */
+       int http_minorversion;  /* Whether to use HTTP/1.1 or 1.0 for CONNECT */
 };
 int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *args_info );
diff -ruN proxytunnel-1.9.0/config.h proxytunnel-1.9.0-sl2/config.h
--- proxytunnel-1.9.0/config.h  2008-03-03 22:50:32.000000000 +0100
+++ proxytunnel-1.9.0-sl2/config.h      2009-09-23 09:39:00.000000000 +0200
@@ -17,7 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-#define VERSION "1.9.0"
+#define VERSION "1.9.0-sl1"
 #define PACKAGE "proxytunnel"
 #define PURPOSE "Build generic tunnels through HTTPS proxies"
 #define AUTHORS "Jos Visser (Muppet) <josv@osp.nl>, Mark Janssen (Maniac) <maniac@maniac.nl>"
diff -ruN proxytunnel-1.9.0/http.c proxytunnel-1.9.0-sl2/http.c
--- proxytunnel-1.9.0/http.c    2008-02-27 00:31:01.000000000 +0100
+++ proxytunnel-1.9.0-sl2/http.c        2009-09-23 12:11:12.000000000 +0200
@@ -97,6 +97,19 @@
 }
 /*
+ * Return the "host" portion of a "host:port" string
+ * Don't forget to free() the returned string!
+ */
+
+char *get_host_only(const char *hostport) {
+       if( !hostport ) { return NULL; }
+       char *hostonly = strdup( hostport );
+       char *sep = strstr( hostonly, ":" );
+       if( sep ) { *sep = 0; } // end the string before the ':'
+       return hostonly;
+}
+
+/*
  * Execute the basic proxy protocol of CONNECT and response, until the
  * last line of the response has been read. The tunnel is then open.
  */
@@ -105,13 +118,28 @@
        if (args_info.remproxy_given ) {
                if( args_info.verbose_flag )
                        message( "\nTunneling to %s (remote proxy)\n", args_info.remproxy_arg );
-               sprintf( buf, "CONNECT %s HTTP/1.0\r\n", args_info.remproxy_arg );
+               sprintf( buf, "CONNECT %s HTTP/1.%d\r\n", args_info.remproxy_arg, args_info.http_minorversion );
+               if( args_info.http_minorversion == 1 ) {
+                       char *hostonly = get_host_only( args_info.remproxy_arg );
+                       if( hostonly ) {
+                               strzcat( buf, "Host: %s\r\n", hostonly );
+                               free( hostonly );
+                       }
+               }
+
        } else {
                if( args_info.verbose_flag )
                        message( "\nTunneling to %s (destination)\n", args_info.dest_arg );
-               sprintf( buf, "CONNECT %s HTTP/1.0\r\n", args_info.dest_arg );
+               sprintf( buf, "CONNECT %s HTTP/1.%d\r\n", args_info.dest_arg, args_info.http_minorversion );
+               if( args_info.http_minorversion == 1 ) {
+                       char *hostonly = get_host_only( args_info.dest_arg );
+                       if( hostonly ) {
+                               strzcat( buf, "Host: %s\r\n", hostonly );
+                               free( hostonly );
+                       }
+               }
        }
-
+
        if ( args_info.user_given && args_info.pass_given ) {
                /* Create connect string including the authorization part */
                if (args_info.ntlm_flag) {
@@ -163,7 +191,15 @@
                if( args_info.verbose_flag )
                        message( "\nTunneling to %s (destination)\n", args_info.dest_arg );
-               sprintf( buf, "CONNECT %s HTTP/1.0\r\n", args_info.dest_arg);
+               sprintf( buf, "CONNECT %s HTTP/1.%d\r\n", args_info.dest_arg, args_info.http_minorversion );
+
+               if( args_info.http_minorversion == 1 ) {
+                       char *hostonly = get_host_only( args_info.dest_arg );
+                       if( hostonly ) {
+                               strzcat( buf, "Host: %s\r\n", hostonly );
+                               free( hostonly );
+                       }
+               }
                if ( args_info.remuser_given && args_info.rempass_given )
                        strzcat( buf, "Proxy-Authorization: Basic %s\r\n", basicauth(args_info.remuser_arg, args_info.rempass_arg ));
diff -ruN proxytunnel-1.9.0/Makefile proxytunnel-1.9.0-sl2/Makefile
--- proxytunnel-1.9.0/Makefile  2008-02-27 00:31:08.000000000 +0100
+++ proxytunnel-1.9.0-sl2/Makefile      2009-09-23 10:50:28.000000000 +0200
@@ -3,7 +3,7 @@
 # Please uncomment the appropriate settings
 CC ?= cc
-CFLAGS ?= -Wall -O2 -ggdb
+CFLAGS ?= -Wall -O2 -ggdb -D_FORTIFY_SOURCE=2
 OPTFLAGS = -DREV=$(shell ./getrev.sh)
@@ -28,6 +28,7 @@
 # DARWIN
 #OPTFLAGS += -DDARWIN
+# No longer needed, Cygwin is now autodetected
 # CYGWIN
 #OPTFLAGS += -DCYGWIN
diff -ruN proxytunnel-1.9.0/messages.c proxytunnel-1.9.0-sl2/messages.c
--- proxytunnel-1.9.0/messages.c        2008-02-22 22:20:41.000000000 +0100
+++ proxytunnel-1.9.0-sl2/messages.c    2009-09-23 10:49:45.000000000 +0200
@@ -35,9 +35,11 @@
        vsnprintf( (char *)buf, sizeof( buf ), s, ap );
        va_end( ap );
+#ifndef __CYGWIN__
        if ( i_am_daemon )
                syslog( LOG_NOTICE, "%s", buf );
        else
+#endif
                fputs( buf, stderr );
 }
diff -ruN proxytunnel-1.9.0/proxytunnel.c proxytunnel-1.9.0-sl2/proxytunnel.c
--- proxytunnel-1.9.0/proxytunnel.c     2008-02-22 22:27:57.000000000 +0100
+++ proxytunnel-1.9.0-sl2/proxytunnel.c 2009-09-23 10:50:05.000000000 +0200
@@ -137,7 +137,7 @@
 /* Leave a goodbye message */
 void closeall() {
-#ifndef CYGWIN
+#ifndef __CYGWIN__
        closelog();
 #endif
@@ -210,7 +210,7 @@
  * Also so we can put logging there, since there's no syslog on cygwin (AFAIK)
  *     -- Maniac
  */
-#ifndef CYGWIN
+#ifndef __CYGWIN__
 /*
        if ( ( pid = fork( ) ) < 0 ) {
                my_perror( "Cannot fork into the background" );
@@ -223,7 +223,7 @@
        openlog( program_name, LOG_CONS|LOG_PID,LOG_DAEMON );
        i_am_daemon = 1;
-#endif /* CYGWIN */
+#endif /* __CYGWIN__ */
        atexit( closeall );
        listen( listen_sd, 8 );