#!/usr/bin/perl -w
# -*- perl -*-
#
# (c) 1999 Chris Chiappa <chris+ssh-https@chiappa.net>
# This code is released to the public domain.  I take no responsibility
# for anything done with it.  It is supplied without any warranty.
#
# V1.1
# V1.0  6/13/1999
#
# $Id: stunnel,v 1.2 2000/03/07 20:21:23 griffon Exp $
#
#######################################################################
# Proxy through an https tunnel.  sshd probably needs to be running on
# port 443 unless the proxy is very lenient.
# You should add an entry to your .ssh/config such as the following:
#
# host remote.host.com
#   ProxyCommand /home/user/bin/stunnel %h %p
#   Port 443
#
# Thereafter 'ssh remote.host.com' will use this program to tunnel
# through the firewall to the remote host.
#
# You may need to tweak it a little to cope with the peculiarities
# of your firewall.
#######################################################################

use strict;

use Socket;
use FileHandle;

#### Configuration ####
#my $proxy      = "proxy.dr.sncf.fr";
#my $proxy_port = 8080;
#my $proxy      = "proxy.univ-lyon1.fr";
#my $proxy_port = 3128;
my $proxy      = "proxy.efrei.fr";
my $proxy_port = 3128;

#################################################################

my ($rem_host, $rem_port) = ($ARGV[0], $ARGV[1]);

$| = 1;

$rem_host || die "Remote host not supplied";
$rem_port || die "Remote port not supplied";

my $pSOCK = proxy_connect();
$pSOCK || die "Couldn't connect to proxy!";
my $SOCK = $$pSOCK;

# These hashes hold the source and destination file handles for the
# file numbers we use in select()
my (%rfds, %wfds);
my ($stdin_fileno, $sock_fileno) = (fileno(*STDIN), fileno($SOCK));
$rfds{$stdin_fileno} = \*STDIN;
$rfds{$sock_fileno}  = $SOCK;
$wfds{$stdin_fileno} = $SOCK;
$wfds{$sock_fileno}  = \*STDOUT;

$SOCK->print("CONNECT $rem_host:$rem_port HTTP/1.0\n\n");

SCANHEADERS: while(<$SOCK>) {
  /^\s+$/ && last SCANHEADERS;
}

#(! m,^HTTP/1.. 2.. Connection established,) &&
#  die "Unexpected response from firewall: $_";

# Set up for select
my $rin = "";
vec($rin, $stdin_fileno, 1) = 1;
vec($rin,  $sock_fileno, 1) = 1;
my $ein = $rin;

my ($rout, $eout);

# Loop, reading from one socket and writing to the other like a good
# proxy program
SELECT: while(1) {
  my $nfound = select($rout = $rin, undef, $eout = $ein, 1024);
  $nfound || next SELECT;

  (vec($eout, $stdin_fileno, 1) || vec($eout, $sock_fileno, 1)) && last SELECT;

  foreach my $key (keys(%rfds)) {
    if(vec($rout, $key, 1)) {
      do_copy($rfds{$key}, $wfds{$key}) || last SELECT;
    }
  }
  # next SELECT;
}

$SOCK->close();

exit(0);

#######################################################################

# Copy stuff from one filehandle to another
sub do_copy {
  my($pIn, $pOut) = @_;
  my ($buf, $len, $offset, $br);
  
  $len = sysread($$pIn, $buf, 1024);
  $len || return 0;

  $offset = 0;
  while($len)  {
    $br = syswrite($$pOut, $buf, $len, $offset);
    $br || return 0;
    $offset += $br;
    $len    -= $br;
  }
  return 1;
}

# Connect to the proxy and return a filehandle for it.
sub proxy_connect {
  my $iaddr = inet_aton($proxy);
  my $paddr = sockaddr_in($proxy_port, $iaddr);
  my $proto = getprotobyname('tcp');
  my $fh    = new FileHandle;
  
  socket($fh, PF_INET, SOCK_STREAM, $proto) or return;
  connect($fh, $paddr) or return;
  $fh->autoflush(1);

  return \$fh;
}
