Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Need help for passing data back and forth data over a persistent SSL TCP socket

0 views
Skip to first unread message

k...@angry-monk.com

unread,
Feb 20, 2014, 7:48:25 PM2/20/14
to p...@perl.org
Hi there,

I'm trying to write a POE based server/client combo that uses SSL authed
persistent connections for comunication between a client and server.

Basically I have a server running that listens on port 2001. A client
connects to the server. Sets up an SSL connection with client
certificate auth and then what I _want_ to happen is that the client
then (every 10 seconds) asks for new temperature data from the server
and is served the current value. I have the client asking in a loop
every 10 seconds but the server isn't triggering and responding past the
first connection event. I'm not sure I'm doing this right (tm) can
anyone help me? I have some inline comments in the code.

client :

#!/usr/bin/perl

### very simple connect to server with auth certs and when connected
sends the "temp" command.
### then when it receives input it fires off Server input and wait 10
then sends again. But
### should it trigger a input event again? I think I'm looping in POE
incorrectly.

use warnings;
use strict;

use POE qw(Component::Client::TCP Filter::SSL);

POE::Component::Client::TCP->new(
RemoteAddress => "localhost",
RemotePort => 2001,
Filter => [ "POE::Filter::SSL", crt => 'keys/client1.crt',
key => 'keys/client1.key', client => 1 ],
Connected => sub {
$_[HEAP]{server}->put("temp");

},
ServerInput => sub {
my $command = "temp";
while(1) {
print "from server: ".$_[ARG0]."\n";
sleep (10);
print "Sending to server : $command\n";
$_[HEAP]{server}->put($command);
}

},
);

POE::Kernel->run();
exit;


Server code :

#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use POE qw(
Wheel::SocketFactory
Wheel::ReadWrite
Driver::SysRW
Filter::SSL
Filter::Stackable
Filter::HTTPD
);


POE::Session->create(
inline_states => {
_start => sub {
my $heap = $_[HEAP];
$heap->{debug} = 1;
if ($heap->{debug}) { print "Stating POE session and
initialising socket\n"};
$heap->{listener} = POE::Wheel::SocketFactory->new(
BindAddress => 'localhost',
BindPort => 2001,
Reuse => 'yes',
SuccessEvent => 'socket_birth',
FailureEvent => '_stop',
);
if ($heap->{debug}) { print "Socket initialised Waiting for
connections\n"};
},
_stop => sub {
if ($_[HEAP]->{debug}) { print "Socket Binding Failed!\n"};
delete $_[HEAP]->{listener};
},
socket_birth => sub {
my ($socket) = $_[ARG0];
POE::Session->create(
inline_states => {
_start => sub {
my ($heap, $kernel, $connected_socket, $address, $port) =
@_[HEAP, KERNEL, ARG0, ARG1, ARG2];
$heap->{debug} = 1;
if ($_[HEAP]->{debug}) { print "Creating SSL Object\n"};
$heap->{sslfilter} = POE::Filter::SSL->new(
crt => 'keys/server.crt',
key => 'keys/server.key',
cacrt => 'keys/ca.crt',
cipher => 'DHE-RSA-AES256-GCM-SHA384:AES256-SHA',
debug => 1,
clientcert => 1
);
$heap->{socket_wheel} = POE::Wheel::ReadWrite->new(
Handle => $connected_socket,
Driver => POE::Driver::SysRW->new(),
Filter => $heap->{sslfilter},
InputEvent => 'socket_input',
ErrorEvent => '_stop',
);
if ($_[HEAP]->{debug}) { print "Connection made!\n"};
},
socket_input => sub {
my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0];
print "RECEIVE : $buf\n";
my (@certid) = ($heap->{sslfilter}->clientCertIds());
my $content = '';
if ($_[HEAP]->{debug}) { print "Authing Client\n"};
if ($heap->{sslfilter}->clientCertValid()) {
if ($_[HEAP]->{debug}) { print "Client Certificate
Valid, Authorised\n"};
if ($buf eq "temp") {
$content =
`/home/pi/c0de/sht15/temp-munin.py`;
} else {
$content = "Unknown request\n";
}
my $response = $content;
if ($_[HEAP]->{debug}) { print "Sending Client
$content\n"};
$heap->{socket_wheel}->put($response);
return (1);
#$kernel->delay(_stop => 1);
} else {
if ($_[HEAP]->{debug}) { print "Client Certificate
Invalid, Rejecting connection\n"};
$content .= "INVALID CERT! Connection rejected!\n";
my $response = $content;
$heap->{socket_wheel}->put($response);
$kernel->delay(_stop => 1);
}
},
_stop => sub {
if ($_[HEAP]->{debug}) { print "Client Disconnected\n"};
delete $_[HEAP]->{socket_wheel};
}
},
args => [$socket],
);
}
}
);

$poe_kernel->run();

Any help anyone can provide would be awesome. I want to avoid
disconnecting and reconnecting ever time I want to get a reading. So
once the connection is made I want to be able to just send a request and
get an answer in an endless loop. I think this should work but can't
figure out why.

Thanks!

Gunnar Strand

unread,
Feb 21, 2014, 5:14:00 AM2/21/14
to k...@angry-monk.com, p...@perl.org
Hi,



2014-02-21 1:48 GMT+01:00 <k...@angry-monk.com>:
> Hi there,
>
> I'm trying to write a POE based server/client combo that uses SSL authed
> persistent connections for comunication between a client and server.
>
> Basically I have a server running that listens on port 2001. A client
> connects to the server. Sets up an SSL connection with client certificate
> auth and then what I _want_ to happen is that the client then (every 10
> seconds) asks for new temperature data from the server and is served the
> current value. I have the client asking in a loop every 10 seconds but the
> server isn't triggering and responding past the first connection event. I'm
> not sure I'm doing this right (tm) can anyone help me? I have some inline
> comments in the code.

You need to have a look at timers for POE:

http://poe.perl.org/?POE_Cookbook/Recurring_Alarms

>
> client :
>
> #!/usr/bin/perl
>
> ### very simple connect to server with auth certs and when connected sends
> the "temp" command.
> ### then when it receives input it fires off Server input and wait 10 then
> sends again. But
> ### should it trigger a input event again? I think I'm looping in POE
> incorrectly.
>
> ServerInput => sub {
> my $command = "temp";
> while(1) {
> print "from server: ".$_[ARG0]."\n";
> sleep (10);
> print "Sending to server : $command\n";
> $_[HEAP]{server}->put($command);
> }

You are correct that you are "looping" incorrectly. POE is
event-driven and uses run-to-completion scheduling
which means that each subroutine must exit before the POE Kernel can
schedule the next event.

You need to 1) set up a recurring alarm and corresponding event
handler which sends "$command" to the server,
and 2) handle data from the server in "ServerInput" and then return
from the subroutine.

Add an alarm to "Connected":

$_[HEAP]->{next_alarm_time} = int(time()); # Immediately
trigger an alarm
$_[KERNEL]->alarm(tick => $_[HEAP]->{next_alarm_time});

Add an inline state "tick" event:

tick => sub {
my $command = "temp";
print "Sending to server : $command\n";
$_[HEAP]{server}->put($command);
$_[HEAP]->{next_alarm_time}+=10;
$_[KERNEL]->alarm(tick => $_[HEAP]->{next_alarm_time});
},

See http://search.cpan.org/dist/POE/lib/POE/Component/Client/TCP.pm#InlineStates

Remove *everything* from the "while" loop except the "print".

I have never dabbled in SSL, so I can't verify the server function, but perhaps
POE::Component::Server::TCP could relieve you of some of the socket handling
in the server part.

http://search.cpan.org/~rcaputo/POE/lib/POE/Component/Server/TCP.pm

BR
Gunnar

k...@angry-monk.com

unread,
Feb 24, 2014, 7:39:21 AM2/24/14
to p...@perl.org
Thanks for this, works perfectly. And since I had so much trouble
finding examples of working code with SSL authentication and a
persistent connection to pass data over I figured I would post the
working code back so it's in the archive for others to poke at and
steal. This is mostly just a combo of examples from CPAN and the perl
POE cookbook but it seems to work as I wanted and allows multiple
persistent clients etc. With this sever I am authenticating every
command sent by the client against it's SSL cert. Probably un-needed
beyond the SSLifying of the initial connection but colour me paranoid.

Server code :

#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use POE qw(
Wheel::SocketFactory
Wheel::ReadWrite
Driver::SysRW
Filter::SSL
Filter::Stackable
Filter::Stream
);


my $serverport = 2001;
my $debug = 1;

POE::Session->create(
inline_states => {
_start => \&parent_start,
_stop => \&parent_stop,

socket_birth => \&socket_birth,
socket_death => \&socket_death,
}
);

sub parent_start {
my $heap = $_[HEAP];
$heap->{debug} = 1;
if ($heap->{debug}) { print "= I = Starting POE session and
initialising socket\n"};

$heap->{listener} = POE::Wheel::SocketFactory->new(
BindAddress => '127.0.0.1',
BindPort => $serverport,
Reuse => 'yes',
SuccessEvent => 'socket_birth',
FailureEvent => 'socket_death',
);
if ($heap->{debug}) { print "= I = Socket initialised Waiting for
connections\n"};
}

# clean up if we shut down the server
sub parent_stop {
my $heap = $_[HEAP];
delete $heap->{listener};
delete $heap->{session};
if ($_[HEAP]->{debug}) { print "= I = Listener Death!\n"};
}

# open the socket for the remote session.
sub socket_birth {
my ($socket, $address, $port) = @_[ARG0, ARG1, ARG2];
$address = inet_ntoa($address);

print "\n= S = Socket birth client connecting\n" if $debug;

POE::Session->create(
inline_states => {
_start => \&socket_success,
_stop => \&socket_death,

socket_input => \&socket_input,
socket_death => \&socket_death,
},
args => [$socket, $address, $port],
);

}

# close the socket session when the user exits.
sub socket_death {
my $heap = $_[HEAP];
if ($heap->{socket_wheel}) {
print "= S = Socket death, client disconnected\n" if $debug;
delete $heap->{socket_wheel};
}
}

# yay! we sucessfully opened a socket. Set up the session.
sub socket_success {
my ($heap, $kernel, $connected_socket, $address, $port) =
@_[HEAP, KERNEL, ARG0, ARG1, ARG2];


$heap->{debug} = 1;
print "= I = CONNECTION from $address : $port \n" if
$heap->{debug};
if ($_[HEAP]->{debug}) { print "= SSL = Creating SSL Object\n"};
## make your own certificates to use here. Standard Cert
creation. I used easy-rsa which is part of the openvpn package. Makes it
really easy. As defined here:
http://www.hermann-uwe.de/blog/howto-using-openvpn-on-debian-gnu-linux
to the point of building client1 then you can stop.
$heap->{sslfilter} = POE::Filter::SSL->new(
crt => 'keys/server.crt',
key => 'keys/server.key',
cacrt => 'keys/ca.crt',
cipher => 'DHE-RSA-AES256-GCM-SHA384:AES256-SHA',
debug => 1,
clientcert => 1
);
);
$heap->{socket_wheel} = POE::Wheel::ReadWrite->new(
Handle => $connected_socket,
Driver => POE::Driver::SysRW->new(),
Filter => POE::Filter::Stackable->new(Filters => [
$heap->{sslfilter},
POE::Filter::Stream->new(),
]),
InputEvent => 'socket_input',
ErrorEvent => 'socket_death',
);
if ($_[HEAP]->{debug}) { print "= SSL = SSL connection
established!\n"};
}

sub socket_input {

my ($heap, $kernel, $buf) = @_[HEAP, KERNEL, ARG0];
my $response;
my $sub;
my (@certid) = ($heap->{sslfilter}->clientCertIds());
my $content = '';
$heap->{debug} = 1;
chomp($buf);
if ($_[HEAP]->{debug}) {print "= I = Clint command received :
$buf\n"};
if ($_[HEAP]->{debug}) { print "= SSL = Authing Client
Command\n"};
if ($heap->{sslfilter}->clientCertValid()) {
if ($_[HEAP]->{debug}) { print "= SSL = Client
Certificate Valid, Authorised\n"};
# this is where you take your input from the client, do
something with it and send something back. I am getting temperature
values and sending them to the client. YMMV
if ($buf eq "temp") {
$content = `/home/pi/c0de/sht15/temp-munin.py`;
} else {
$content = "Unknown request\n";
}
my $response = $content;
if ($_[HEAP]->{debug}) { print "= I = Sending Client
Result: $content\n"};
$heap->{socket_wheel}->put($response);
} else {
# User is a bad bad person. No cookie!
if ($_[HEAP]->{debug}) { print "= SSL = Client
Certificate Invalid! Rejecting command and disconnecting!\n"};
$content = "INVALID CERT! Connection rejected!\n";
my $response = $content;
$heap->{socket_wheel}->put($response);
if ($_[HEAP]->{debug}) { print "= I = Sending Client
Result: $content\n"};
$kernel->delay(socket_death => 1);
}
}

$poe_kernel->run();
## END OF LINE

Client code:

#!/usr/bin/perl

use warnings;
use strict;

use POE qw(Component::Client::TCP Filter::SSL);

POE::Component::Client::TCP->new(
RemoteAddress => "localhost",
RemotePort => 2001,
## make your own certificates to use here. Standard Cert creation. I
used easy-rsa which is part of the openvpn package. Makes it really
easy. As defined here:
http://www.hermann-uwe.de/blog/howto-using-openvpn-on-debian-gnu-linux
to the point of building client1 then you can stop.
Filter => [ "POE::Filter::SSL", crt => 'keys/client1.crt',
key => 'keys/client1.key', client => 1 ],
Connected => sub {
$_[HEAP]->{next_alarm_time} = int(time()); # Immediately
trigger an alarm
$_[KERNEL]->alarm(tick => $_[HEAP]->{next_alarm_time});

},
ServerInput => sub {
print "from server: ".$_[ARG0]."\n";
},
InlineStates => {
tick => sub {
#send the server a command server input will
then capture the response while this bit waits 10 seconds and then does
it all again. Thanks to Gunnar on the POE mailing list for help with
this.
my $command = "temp";
print "Sending to server : $command\n";
$_[HEAP]{server}->put($command);
$_[HEAP]->{next_alarm_time}+=10;
$_[KERNEL]->alarm(tick =>
$_[HEAP]->{next_alarm_time});
},
}
);

POE::Kernel->run();
## END OF LINE

Thanks for the help and I hope this helps someone in the future.

Kai

Michael R. Davis

unread,
Feb 24, 2014, 10:39:21 AM2/24/14
to p...@perl.org
POE Folks,
I'd like to use POE::Component::Server::TCP to read data from active network sensors but they have a proprietary protocol buffer like format.  Is there a way to build a ClientInputFilter with multiple fixed length reads building on each other.
 
byte-type, byte-payload-length, payload-of-specified-length
 
An example
 
"\123\005ABCDE"
"\124\000"
"\125\001A"
"\126\007ABCDEFG"
 
There is no "line" separator in the protocol.
 
I currently already have a deserializer for each type that I'd like to be able to plug into ClientInput $_[ARG0] somehow;
 
In non-POE I just
 
my $type=read_byte;
my $length=unpack("C", read_byte);
my $payload=read_bytes($length);
my $object=My::Object::Factory->new(type=>$type, data=>$payload);
 
Any pointers and help would be appreciated on how to get from here to there.
Thanks,
Mike
 
   
0 new messages