#!/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 '' && $serverhost ne $uri_host) { $serverhost = $uri_host; $serverport = $uri_port } if (!$serverport) { if ($uri_schema eq 'https' || $req{METHOD} eq 'CONNECT') { $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|OPTIONS|PATCH|CONNECT/) { $req{URI} = $uri_path . ($uri_query ? "?$uri_query":''); print "[INFO]\t" . $browser->peerhost . "\tCID:" . $browser->peerport . "\t$serverhost:$serverport $req{PROTO} $req{METHOD} $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 established\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_ORIG}{$list}\015\012" } else { $respond .= "$list: $req{HEADER_ORIG}{$list}\015\012" } } $respond .= "X-Forwarded-For: " . $browser->peerhost . "\015\012\015\012"; if ($req{HEADER_SYST}{'content-length'}) { my $content = substr($buffer, $crlf || -$req{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($buffer) } } 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 }