Apache-HTTunnel
view release on metacpan or search on metacpan
lib/Apache/HTTunnel/Handler.pm view on Meta::CPAN
my @params = split(/\//, $path_info) ;
my $cmd = shift @params ;
$slog->info("HTTunnel Handler: Processing '$cmd' command ($path_info)") ;
if ($cmd eq 'connect'){
($resp, $timeout, $extra) = connect_cmd($r, @params) ;
}
elsif ($cmd eq 'read'){
($resp, $timeout, $extra) = read_cmd($r, @params) ;
}
elsif ($cmd eq 'write'){
($resp, $timeout, $extra) = write_cmd($r, @params) ;
}
elsif ($cmd eq 'close'){
($resp, $timeout, $extra) = close_cmd($r, @params) ;
}
else {
die("Invalid command $cmd ($path_info)") ;
}
} ;
if ($@){
# TODO: Handle APR::Error
$resp = 'err'. $@ ;
$slog->error("HTTunnel Handler: $@") ;
}
else {
if (defined($extra)){
$extra .= ':' ;
}
if ($timeout){
$resp = 'okt' . $extra ;
}
elsif (length($resp) == 0){
$resp = 'okn' . $extra ;
}
else {
$resp = 'okd' . $extra . $resp ;
}
}
$r->print($resp) or
$slog->error("HTTunnel Handler: Error writing response to client: $!") ;
$r->rflush() ;
my $cnt = $fdk->cnt() ;
$slog->info("HTTunnel Handler: $cnt handles remaining in Keeper") ;
return OK ;
}
sub connect_cmd {
my $r = shift ;
my @params = @_ ;
my $slog = $r->log() ;
my $proto = shift @params ;
my $host = shift @params ;
my $port = shift @params ;
my $timeout = shift @params || 15 ;
my $max_timeout = $r->dir_config('HTTunnelMaxConnectTimeout') || 15 ;
if ($timeout > $max_timeout){
$slog->notice("HTTunnel Handler: Requested connect timeout ($timeout) decreased " .
"to HTTunnelMaxConnectTimeout ($max_timeout)") ;
$timeout = $max_timeout ;
}
check_access($r, $host, $port) ;
$slog->info("HTTunnel Handler: Connecting to $host:$port...") ;
my $sock = undef ;
my $peer_info = undef ;
eval {
local $SIG{ALRM} = sub {die "timeout\n"} ;
alarm($timeout) ;
$sock = new IO::Socket::INET(
Proto => $proto,
PeerAddr => $host,
PeerPort => $port,
) ;
die("Error connecting to $host:$port: $!") unless defined($sock) ;
if ($proto eq 'tcp'){
my $peer = getpeername($sock) ;
my ($port, $addr) = sockaddr_in($peer) ;
$peer_info = join(':', inet_ntoa($addr), $port) ;
}
alarm(0) ;
} ;
if ($@){
if ($@ eq "timeout\n"){
$slog->notice("HTTunnel Handler: Connection to $host:$port timed out " .
"after $timeout seconds.") ;
return (undef, 0) ;
}
else {
alarm(0) ;
die("$@\n") ;
}
}
die("Can't connect to $host:$port: $!") unless $sock ;
$slog->notice("HTTunnel Handler: Connected to $host:$port") ;
$slog->notice("HTTunnel Handler: Putting filehandle...") ;
my $fhid = $fdk->put($sock) ;
$slog->notice("HTTunnel Handler: Filehandle '$fhid' put") ;
return ($fhid, 0, $peer_info) ;
}
sub check_access {
my $r = shift ;
my $host = shift ;
my $port = shift ;
my $slog = $r->log() ;
my $rules = $r->dir_config('HTTunnelAllowedTunnels') or die("HTTunnelAllowedTunnels not defined in Apache configuration file") ;
$rules =~ s/^\s+// ;
$rules =~ s/\s+$// ;
my @rules = split(/\s*,\s*/, $rules) ;
foreach my $r (@rules){
$slog->debug("HTTunnel Handler: Allowed (raw): $r") ;
}
my %allowed = () ;
foreach my $r (@rules){
my ($hosts, $ports) = split(/\s*=>\s*/, $r) ;
my @hosts = split(/\s*\|\s*/, $hosts) ;
my @ports = split(/\s*\|\s*/, $ports) ;
foreach my $h (@hosts){
foreach my $p (@ports){
my @addrs = ($h) ;
if ($h ne '*'){
my @info = gethostbyname($h) ;
for (my $i = 4 ; $i < scalar(@info) ; $i++){
push @addrs, inet_ntoa($info[$i]) ;
}
}
foreach my $a (@addrs){
$allowed{"$a:$p"} = 1 ;
}
}
}
}
foreach my $r (@rules){
$slog->debug("HTTunnel Handler: Allowed (expanded): $r") ;
}
if (($allowed{"$host:$port"})||($allowed{"$host:*"})||
($allowed{"*:$port"})||($allowed{"*:*"})){
$slog->notice("HTTunnel Handler: $host:$port is allowed by configuration") ;
}
else{
die("Permission denied for $host:$port") ;
}
}
sub read_cmd {
my $r = shift ;
my @params = @_ ;
my $slog = $r->log() ;
my $fhid = shift @params ;
my $proto = shift @params ;
my $len = shift @params ;
my $timeout = shift @params || 15 ;
my $max_len = $r->dir_config('HTTunnelMaxReadLength') || 131072 ;
if ($len > $max_len){
$slog->notice("HTTunnel Handler: Requested read length ($len) decreased " .
"to HTTunnelMaxReadLength ($max_len)") ;
$len = $max_len ;
}
my $max_timeout = $r->dir_config('HTTunnelMaxReadTimeout') || 15 ;
if ($timeout > $max_timeout){
$slog->notice("HTTunnel Handler: Requested read timeout ($timeout) decreased " .
"to HTTunnelMaxReadTimeout ($max_timeout)") ;
$timeout = $max_timeout ;
}
my $data = undef ;
$slog->notice("HTTunnel Handler: Getting filehandle '$fhid'...") ;
my $fh = $fdk->get($fhid) or die("Unknown filehandle '$fhid'") ;
$slog->notice("HTTunnel Handler: Filehandle '$fhid' gotten") ;
my $timed_out = 0 ;
my $peer_info = undef ;
eval {
local $SIG{ALRM} = sub {die "timeout\n"} ;
alarm($timeout) ;
$slog->info("HTTunnel Handler: Reading up to $len bytes from filehandle '$fhid'") ;
if ($proto eq 'udp'){
my $peer = undef ;
($peer, $data) = recv_from($fh, $len) ;
my ($port, $addr) = sockaddr_in($peer) ;
$peer_info = join(':', inet_ntoa($addr), $port) ;
}
else{
$data = read_from($fh, $len) ;
}
if (! defined($data)){
$slog->notice("HTTunnel Handler: EOF detected on filehandle '$fhid'") ;
}
else {
my $l = length($data) ;
$slog->notice("HTTunnel Handler: Read $l bytes from filehandle '$fhid'") ;
}
alarm(0) ;
} ;
if ($@){
if ($@ eq "timeout\n"){
$slog->notice("HTTunnel Handler: Read timed out on purpose after $timeout seconds.") ;
$timed_out = 1 ;
}
else {
alarm(0) ;
die("$@\n") ;
}
}
return ($data, $timed_out, $peer_info) ;
}
sub write_cmd {
my $r = shift ;
my @params = @_ ;
my $slog = $r->log() ;
my $fhid = shift @params ;
my $proto = shift @params ;
my $cl = $r->headers_in->{'Content-Length'} ;
defined($cl) or die("Content-Length is not defined") ;
$slog->notice("HTTunnel Handler: Content-Length is $cl bytes") ;
my $data = '' ;
( run in 0.613 second using v1.01-cache-2.11-cpan-f56aa216473 )