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

On 2014-02-21 18:14, Gunnar Strand wrote:
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

Reply via email to