Nazar78 has asked for the wisdom of the Perl Monks concerning the following question:
Greetings all!
I'm new here, please excuse my noob post or/and codings. I've been searching for months to solve my little issue hope that you monks can help out.
From many resources and examples from the net, I tried to do a threaded http proxy script to speedup my personal needs which intercepts certain slow urls to perform local caching by retrieving from multiple mirrors. Auth basic digest, FTP, HTTP POST/GET etc² including HTTPS CONNECT are already working great as I expected but I'm having problem with sites that needs TSL handshakes.
Do I have to upgrade the accepted listening and remote sockets with IO::Socket::SSL to perform the handshakes? Or is there any special needs to handle the handshakes with normal HTTP CONNECT tunnel? I can't find any tutorials on these but as I recall looking in the http://www.privoxy.org/ source codes, it just implement normal HTTP CONNECT, return "HTTP/1.0 200 Connection established\r\n\r\n" when connected and start forwarding request. Privoxy works but mine in perl doesn't.
Below is a small part of my newbie codings. It's a workable http proxy on all http/https sites but not on those which requires proper handshakes. I've checked in Wireshark and saw the handshakes being done between client and remote but eventually the connection got drop. I've tried non-blocking, adjusting buffer/socket sizes yet no luck. Please help to advise. Thank you.
#!/usr/bin/perl use strict; use warnings; use threads; use IO::Socket; use IO::Select; my $daemon = IO::Socket::INET->new ( LocalAddr => '192.168.1.16', LocalPort => 8888, PeerAddr => inet_ntoa(INADDR_BROADCAST), PeerPort => 8888, Type => SOCK_STREAM, Proto => 'tcp', ReuseAddr => 1, Listen => SOMAXCONN ) or do { print "[ERROR]\t$@\n"; exit 1 }; binmode $daemon; $daemon->blocking(0); $daemon->autoflush(1); while (1) { if (my $browser = $daemon->accept) { binmode $browser; $browser->blocking(0); $browser->autoflush(1); eval { threads->create(\&doConnect, $browser)->detach }; if ($@) { print "[ERROR]\t$@\n"; exit 1 } } select(undef, undef, undef, (int(rand(60)) ? '.001':0)) } sub doConnect { my $browser = shift; my ($server, $ftp, $serverhost, $serverport) = ''; binmode $browser; $browser->blocking(0); $browser->autoflush(1); my $select = IO::Select->new($browser); while (1) { for my $connection ($select->can_read(60)) { my %req = (); my ($uri_host, $uri_port, $crlf) = ''; $connection->sysread(my $buffer, 65536) or goto EXIT; if ($connection == $browser && $buffer =~ /^\w+ +\S+ +\S+\ +015\012/s) { foreach my $pipes (split(/\015\012/, $buffer)) { chomp $pipes; $pipes =~ s/\r\n+//sg; $pipes =~ s/\r+|\n+//sg; $pipes =~ s/^\s+|\s+$//sg; $pipes =~ s/\f+//sg; $pipes =~ s/\s+|\t+/ /sg; unless ($req{METHOD}) { if ($pipes =~ /^(\w+) +(\S+) +(\S+)/s) { $req{METHOD} = uc $1; $req{URI} = $2; $req{PROTO} = uc $3 } else { last } } elsif ($pipes =~ /^(.+)\: (.+)$/) { $req{HEADER_ORIG}{$1} = $2; $req{HEADER_SYST}{lc($1)} = $2 } else { last } } if ($req{HEADER_SYST}{'content-length'}) { while ($buffer =~ /\015\012\015\012/g) { $crlf = pos $buffer; last } } my ($uri_schema, $uri_location, $uri_path, $uri_query, + undef) = ($req{URI} =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?: +\?([^#]*))?(?:#(.*))?,); $uri_schema = lc $uri_schema; if ($uri_schema ne 'http' && $uri_schema ne 'https') { $uri_location = $uri_schema; if ($uri_path =~ /^\//) { $uri_schema = 'http' } else { $uri_schema = 'https'; $uri_path = $uri_location } } ($serverhost, $serverport) = split(':', lc $req{HEADER +_SYST}{host}); if ($serverhost eq '' && $uri_host ne '') { $serverhost = $uri_host; $serverport = $uri_port } elsif ($serverhost ne '' && $uri_host ne '' && $server +host ne $uri_host) { $serverhost = $uri_host; $serverport = $uri_port } if (!$serverport) { if ($uri_schema eq 'https' || $req{METHOD} eq 'CON +NECT') { $serverport = 443 } else { $serverport = 80 } } if ($serverhost eq '') { $serverhost = '0.0.0.0'; $serverport = 0 } my $connect_host = $serverhost; my $connect_port = $serverport; if ($req{METHOD} =~ /GET|HEAD|POST|PUT|DELETE|TRACE|OP +TIONS|PATCH|CONNECT/) { $req{URI} = $uri_path . ($uri_query ? "?$uri_query +":''); print "[INFO]\t" . $browser->peerhost . "\tCID:" . + $browser->peerport . "\t$serverhost:$serverport $req{PROTO} $req{MET +HOD} $req{URI}\n"; $server = IO::Socket::INET->new ( PeerAddr => $connect_host, PeerPort => $connect_port, Type => SOCK_STREAM, Proto => 'tcp', ReuseAddr => 1, Timeout => 60 ) or return print "[ERROR]\t$@\n"; $browser->blocking(1); binmode $server; $server->blocking(1); $server->autoflush(1); $select->add($server); my $respond = ''; if ($req{METHOD} eq 'CONNECT') { $respond .= "$req{PROTO} 200 Connection establ +ished\015\012\015\012"; $connection = $server } else { $respond .= "$req{METHOD} $req{URI} $req{PROTO +}\015\012"; for my $list (keys %{$req{HEADER_ORIG}}) { next if $list =~ /^Proxy\-Authorization/i; if ($list =~ /Connection/i) { $respond .= "Connection: $req{HEADER_O +RIG}{$list}\015\012" } else { $respond .= "$list: $req{HEADER_ORIG}{ +$list}\015\012" } } $respond .= "X-Forwarded-For: " . $browser->pe +erhost . "\015\012\015\012"; if ($req{HEADER_SYST}{'content-length'}) { my $content = substr($buffer, $crlf || -$r +eq{HEADER_SYST}{'content-length'}); sysread($connection, $content, $req{HEADER +_SYST}{'content-length'}) if $content eq ''; $respond .= $content if $content ne '' } } $buffer = $respond } } ($connection == $browser ? $server:$browser)->syswrite($bu +ffer) } } EXIT: if ($browser) { $select->remove($browser); $browser->shutdown(2); $browser->close; undef $browser } if ($server) { $select->remove($server); $server->shutdown(2); $server->close; undef $server } threads->exit }
|
|---|