POE and Port Redirection

T

Theo James

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:

#!/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 );

# The redirection table.

my %redirects =
qw( 1.6.20.15:6666 12.1.2.135:6666
);

### This is a stream-based tcp forwarder session. It passes data
### between two sockets, and that's about all.

# Create a session that will forward data between two sockets.

sub forwarder_create {
my ( $handle, $peer_host, $peer_port, $remote_addr, $remote_port )
= @_;

POE::Session->new
( _start => \&forwarder_start,
_stop => \&forwarder_stop,
client_input => \&forwarder_client_input, # Client sent
something.
client_error => \&forwarder_client_error, # Error on
client socket.
server_connect => \&forwarder_server_connect, # Connected to
server.
server_input => \&forwarder_server_input, # Server sent
something.
server_error => \&forwarder_server_error, # Error on
server socket.

# Pass some things to forwarder_start():
# ARG0, ARG1, ARG2, ARG3, ARG4
[ $handle, $peer_host, $peer_port, $remote_addr, $remote_port
]
);
}


sub forwarder_start {
my ( $heap, $session,
$socket, $peer_host, $peer_port, $remote_addr, $remote_port
) =
@_[ HEAP, SESSION,
ARG0, ARG1, ARG2, ARG3, ARG4
];

$heap->{log} = $session->ID;
$peer_host = inet_ntoa($peer_host);
$heap->{peer_host} = $peer_host;
$heap->{peer_port} = $peer_port;
$heap->{remote_addr} = $remote_addr;
$heap->{remote_port} = $remote_port;

print "[$heap->{log}] Accepted connection from
$peer_host:$peer_port\n";

$heap->{state} = 'connecting';
$heap->{queue} = [];

$heap->{wheel_client} = POE::Wheel::ReadWrite->new
( Handle => $socket,
Driver => POE::Driver::SysRW->new,
Filter => POE::Filter::Stream->new,
InputEvent => 'client_input',
ErrorEvent => 'client_error',
);

$heap->{wheel_server} = POE::Wheel::SocketFactory->new
( RemoteAddress => $remote_addr,
RemotePort => $remote_port,
SuccessEvent => 'server_connect',
FailureEvent => 'server_error',
);
}

# The forwarder has stopped. Log that it's done.

sub forwarder_stop {
my $heap = $_[HEAP];
print "[$heap->{log}] Closing redirection session\n";
}

# The forwarder has received data from its client side. Pass the data
# through to the server if it's connected. Otherwise hold the data in
# a queue until the server connects.

sub forwarder_client_input {
my ( $heap, $input ) = @_[ HEAP, ARG0 ];

if ( $heap->{state} eq 'connecting' ) {
push @{ $heap->{queue} }, $input;
}
else {
( exists $heap->{wheel_server} ) &&
$heap->{wheel_server}->put($input);
}
}

# The forwarder has received an error from the client. Shut down both
# sides of the connection. Log the error in a manner appropriate to
# its type.

sub forwarder_client_error {
my ( $kernel, $heap, $operation, $errnum, $errstr ) =
@_[ KERNEL, HEAP, ARG0, ARG1, ARG2 ];

if ($errnum) {
print( "[$heap->{log}] Client connection encountered ",
"$operation error $errnum: $errstr\n"
);
}
else {
print "[$heap->{log}] Client closed connection.\n";
}

delete $heap->{wheel_client};
delete $heap->{wheel_server};
}

# The forwarder's SocketFactory has successfully connected to the
# server. Log the success, and create a ReadWrite wheel to interact
# with the server socket. If the client sent anything during the
# connection process, pass it through to the server now.

sub forwarder_server_connect {
my ( $kernel, $session, $heap, $socket ) = @_[ KERNEL, SESSION,
HEAP, ARG0 ];

my ( $local_port, $local_addr ) = unpack_sockaddr_in(
getsockname($socket) );
$local_addr = inet_ntoa($local_addr);
print( "[$heap->{log}] Established forward from local ",
"$local_addr:$local_port to remote ",
$heap->{remote_addr}, ':', $heap->{remote_port}, "\n"
);

# Replace the SocketFactory wheel with a ReadWrite wheel.

$heap->{wheel_server} = POE::Wheel::ReadWrite->new
( Handle => $socket,
Driver => POE::Driver::SysRW->new,
Filter => POE::Filter::Stream->new,
InputEvent => 'server_input',
ErrorEvent => 'server_error',
);

$heap->{state} = 'connected';
foreach my $pending ( @{ $heap->{queue} } ) {
$kernel->call( $session, 'client_input', $pending );
}
$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 ];

exists( $heap->{wheel_client} ) and
$heap->{wheel_client}->put($input);
}

# The forwarder has received an error from the server. Shut down both
# sides of the connection. Log the error in a manner appropriate to
# its type.

sub forwarder_server_error {
my ( $kernel, $heap, $operation, $errnum, $errstr ) =
@_[ KERNEL, HEAP, ARG0, ARG1, ARG2 ];

if ($errnum) {
print( "[$heap->{log}] Server connection encountered ",
"$operation error $errnum: $errstr\n"
);
}
else {
print "[$heap->{log}] Server closed connection.\n";
}

delete $heap->{wheel_client};
delete $heap->{wheel_server};
}

### This is a stream-based forwarder server. It listens on TCP ports,
### and it spawns new forwarders to redirect incoming connections.

# Create a session that acts as the forwarder server.

sub server_create {
my ( $local_address, $local_port, $remote_address, $remote_port )
= @_;

POE::Session->new
( _start => \&server_start,
_stop => \&server_stop,
accept_success => \&server_accept_success,
accept_failure => \&server_accept_failure,

# Pass this function's parameters to the server_start().
# ARG0, ARG1, ARG2, ARG3
[ $local_address, $local_port, $remote_address, $remote_port ]
);
}

# Start the server. This records where the server should connect, and
# it creates the listening socket factory.

sub server_start {
my ( $heap, $local_addr, $local_port, $remote_addr, $remote_port )
=
@_[ HEAP, ARG0, ARG1, ARG2, ARG3 ];

print "+ Redirecting $local_addr:$local_port to
$remote_addr:$remote_port\n";

$heap->{local_addr} = $local_addr;
$heap->{local_port} = $local_port;
$heap->{remote_addr} = $remote_addr;
$heap->{remote_port} = $remote_port;

$heap->{server_wheel} = POE::Wheel::SocketFactory->new
( BindAddress => $local_addr, # bind to this address
BindPort => $local_port, # and bind to this port
Reuse => 'yes', # reuse immediately
SuccessEvent => 'accept_success', # generate this event on
connection
FailureEvent => 'accept_failure', # generate this event on
error
);
}

# The server is stopping. Log that fact.

sub server_stop {
my $heap = $_[HEAP];
print( "- Redirection from $heap->{local_addr}:$heap->{local_port}
to ",
"$heap->{remote_addr}:$heap->{remote_port} has stopped.\n"
);
}

# The server has accepted a client connection. Pass the details about
# it to the function that creates a new forwarder. This is as
# unnecessary step. The contents of forwarder_create() could have
# been placed directly into server_accept_success().

sub server_accept_success {
my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0,
ARG1, ARG2 ];
&forwarder_create( $socket, $peer_addr, $peer_port,
$heap->{remote_addr}, $heap->{remote_port}
);
}

# The server encountered an error. Log the error. If we've run out
# of file descriptors, we'll have to shut down the server. A serious
# port redirector should just restart the server here.

sub server_accept_failure {
my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1,
ARG2 ];

print( "! Redirection from $heap->{local_addr}:$heap->{local_port}
to ",
"$heap->{remote_addr}:$heap->{remote_port} encountered
$operation ",
"error $errnum: $errstr\n"
);

delete $heap->{server_wheel} if $errnum == ENFILE or $errnum ==
EMFILE;
}

### Main loop. Create a new server for each record in %redirects.
### Run POE until all the servers (and their forwarders) shut down.

while ( my ( $from, $to ) = each %redirects ) {
my ( $from_address, $from_port ) = split ( /:/, $from );
my ( $to_address, $to_port ) = split ( /:/, $to );

&server_create( $from_address, $from_port, $to_address, $to_port
);
}

$poe_kernel->run();
 
R

Rocco Caputo

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;
 
T

Theo James

Thanks for the quick response. I have placed it on my Linux box and
am trying it out now!

--Theo

Rocco Caputo said:
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;
 
N

news.hinet.net

The POE is very powerful. But i have some question about it.
Is the POE applicable for do this job??
If i have 100 concurrent connection to do this job .
Example 211.11.22.31:389 211.11.22.32 389


How to do POE benchmark for this situation?



Rocco Caputo said:
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;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top