Re: POE and Port Redirection

From: Rocco Caputo (troc_at_pobox.com)
Date: 09/18/04


Date: Sat, 18 Sep 2004 04:58:26 GMT

On 17 Sep 2004 20:29:09 -0700, Theo James wrote:
> Bear with me folks, I have been working with POE for about 60 minutes.
> I am in need of a port redirector that works like this:
>
> Client -> Server -> Host
>
> The client will create many session on the same port on the Server.
> For each connection, the server needs to create a redirection on one
> of 20 predefined ports to the Host (call it 9600-9619). The
> connections will last less than 2 seconds each and if all 20 sessions
> are in use, the servers port (call it 6666) needs to be block.
>
> I found some code and tweaked it a bit to get a single port to
> redirect, but I am at a loss from where to from here. Any help would
> be appreciated:

Here's a version that accepts connections on one port and forwards them to a
range of ports on another machine. It's untested, but I have verified that it
passes "perl -T -c".

-- 
Rocco Caputo - http://poe.perl.org/
#!/usr/bin/perl -Tw
use warnings;
use strict;
use Socket;
use POSIX qw(errno_h);
# Import POE and the extra modules we'll need within it.
use POE qw( Wheel::ReadWrite Wheel::SocketFactory Filter::Stream );
# Some aspects of redirection.
use constant LOCAL_ADDRESS  => "1.6.20.15";
use constant LOCAL_PORT     => 6666;
use constant REMOTE_ADDRESS => "12.1.2.135";
my @open_redirects = (9600..9619);
# Create a session that will forward data between two sockets.
sub forwarder_create {
  my ($handle, $port) = @_;
  POE::Session->create(
    inline_states => {
      _start         => \&forwarder_start,
      _stop          => \&forwarder_stop,
      client_input   => \&forwarder_client_input,
      error          => \&forwarder_error,
      server_connect => \&forwarder_server_connect,
      server_input   => \&forwarder_server_input,
    },
    args => [ $handle, $port ]
  );
}
# The forwarder is actually starting.  Begin interacting with the
# client, and begin connecting to the server.
sub forwarder_start {
  my ($heap, $socket, $remote_port) = @_[HEAP, ARG0, ARG1];
  $heap->{state} = 'connecting';
  $heap->{queue} = [];
  $heap->{port}  = $remote_port;
  $heap->{wheel_client} = POE::Wheel::ReadWrite->new(
    Handle     => $socket,
    Filter     => POE::Filter::Stream->new,
    InputEvent => 'client_input',
    ErrorEvent => 'error',
  );
  $heap->{wheel_server} = POE::Wheel::SocketFactory->new(
    RemoteAddress => REMOTE_ADDRESS,
    RemotePort    => $remote_port,
    SuccessEvent  => 'server_connect',
    FailureEvent  => 'error',
  );
}
# The forwarder has stopped.  Put its port at the end of the open
# redirects list so it can be reused.
sub forwarder_stop {
  push @open_redirects, $_[HEAP]->{port};
}
# The forwarder has received data from its client side.  Queue the
# data if the server connection hasn't been established yet.
# Otherwise send it through to the server.
sub forwarder_client_input {
  my ( $heap, $input ) = @_[ HEAP, ARG0 ];
  if ( $heap->{state} eq 'connecting' ) {
    push @{ $heap->{queue} }, $input;
    return;
  }
  return unless exists $heap->{wheel_server};
  $heap->{wheel_server}->put($input);
}
# A server connection was successfully made.  Send any pending data to
# it.
sub forwarder_server_connect {
  my ( $kernel, $session, $heap, $socket ) = @_[ KERNEL, SESSION, HEAP, ARG0 ];
  # Replace the SocketFactory wheel with a ReadWrite wheel, so we can
  # interact with the server.
  $heap->{wheel_server} = POE::Wheel::ReadWrite->new(
    Handle     => $socket,
    Filter     => POE::Filter::Stream->new,
    InputEvent => 'server_input',
    ErrorEvent => 'server_error',
  );
  # Send pending data to the server.
  if (@{$heap->{queue}}) {
    $heap->{wheel_server}->put( @{$heap->{queue}} );
    $heap->{queue} = [];
  }
}
# The forwarder has received data from its server side.  Pass that
# through to the client.
sub forwarder_server_input {
  my ( $heap, $input ) = @_[ HEAP, ARG0 ];
  return unless exists $heap->{wheel_client};
  $heap->{wheel_client}->put($input);
}
# The forwarder has received an error from either the client or
# server.  Shut it all down.
sub forwarder_error {
    my $heap = $_[HEAP];
    delete $heap->{wheel_client};
    delete $heap->{wheel_server};
}
###--------------------------------------------------------------
### This is the listening part of the redirector.
# Create a session that acts as the forwarder server.
sub server_create {
  POE::Session->new(
    _start         => \&server_start,
    _child         => \&forwarder_status,
    accept_success => \&server_accept_success,
    accept_failure => \&server_accept_failure,
  );
}
# Start the server.
sub server_start {
  my $heap = $_[HEAP];
  $heap->{server_wheel} = POE::Wheel::SocketFactory->new(
    BindAddress  => LOCAL_ADDRESS,
    BindPort     => LOCAL_PORT,
    Reuse        => 'yes',
    SuccessEvent => 'accept_success',
    FailureEvent => 'accept_failure',
  );
}
# The status of a forwarder has changed.  If a forwarder is going
# away, it means we have one more redirect port open.  Check to see if
# the server should resume accepting connections.
sub forwarder_status {
  my $op = $_[ARG0];
  return unless $op eq "lose";
  $_[HEAP]->resume_accept() if @open_redirects == 1;
}
# The server has accepted a client connection.  Start forwarding.
sub server_accept_success {
  my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
  # Grab the next open redirect port in our list.
  my $next_remote_port = shift @open_redirects;
  forwarder_create($socket, $next_remote_port);
  # Temporarily stop accepting connections if there are no more
  # redirect ports.
  $heap->{server_wheel}->pause_accept() unless @open_redirects;
}
# The server encountered an error.  Shut it down if we've run out of
# file descriptiors.  A serious port redirector would handle this more
# gracefully.
sub server_accept_failure {
  my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
  delete $heap->{server_wheel} if $errnum == ENFILE or $errnum == EMFILE;
}
# Main loop.  Create a single listening socket that redirects
# connections to one of a pool of remote sockets.
server_create();
POE::Kernel->run;
exit;


Relevant Pages

  • Virtual host "lite"?
    ... redirect an incoming we request based on DNS name, ... "http://webmail.domain.com " will automatically be redirected to port ... fall over and the Boss works out what a "server" is.. ...
    (comp.os.linux.networking)
  • Re: Need assistance w/ Checkpoint NG, URL based redirection
    ... Where users who hit the external IP of my firewall on port 80 redirect ... to my internal server at 192.168.1.10, ...
    (comp.security.firewalls)
  • Re: Wierd 301 Moved Loop in OWA
    ... appears to be some sort of endless redirect loop. ... The server responds with a 301 ... a non standard port for regular HTTP and then the usual 443 port for ... redirects all requests to HTTPS. ...
    (microsoft.public.exchange.admin)
  • Re: Port 443 Question
    ... router and more particularly the SBS box? ... What do I do to the SBS for the redirect to the other port? ... is a completely different server on the ...
    (microsoft.public.windows.server.sbs)
  • POE and Port Redirection
    ... I am in need of a port redirector that works like this: ... The client will create many session on the same port on the Server. ... sub forwarder_create { ...
    (comp.lang.perl.misc)