NAME
    POE::Filter::SSL - The easiest and flexiblest way to SSL in POE!

VERSION
    Version 0.03

DESCRIPTION
    This module allows to make a SSL TCP server or SSL TCP client via a
    filter for POE::Wheel::ReadWrite.

    The SSL Filter can be switched during runtime, for example if you want
    to first make plain text and then STARTTLS. You also can combine
    POE::Filter::SSL with an other filter, for example POE::Filter::HTTPD
    (see *ADVANCED EXAMPLE* later on this site), and have a HTTPS server.

    POE::Filter::SSL is mainly based on Net::SSLeay, but has implemented
    some Net::SSLeay missing calls by itself. Further there is an own BIO
    implementation, which replaces the normal socket interface of OpenSSL.

    Included Features

          Full non-blocking processing

          No use of sockets at all

          Server and client mode

          Client certificate verification

          Allows to accept connections with invalid or missing client
          certificate and return custom error data

          CRL check of client certificates

          Retrieve client certificate details (subject name, issuer name,
          certificate serial)

    Upcomming Features

          Direct cipher encryption without SSL or TLS protocol, for example
          with static AES encryption

SYNOPSIS
    Examples
      Basically is server and client the same; you have to add the option
      *client* to *new* instead of the certification files.

      Client
             #!/usr/bin/perl

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

             my $host = "your.test.de";

             POE::Session->create(
                inline_states => {
                   _start       => sub {
                      my $heap = $_[HEAP];
                      $heap->{listener} = POE::Wheel::SocketFactory->new(
                         RemoteAddress => $host,
                         RemotePort    => 443,
                         SuccessEvent => 'socket_birth',
                         FailureEvent => '_stop',
                      );
                   },
                   _stop => sub {
                      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->{socket_wheel} = POE::Wheel::ReadWrite->new(
                                  Handle     => $connected_socket,
                                  Driver     => POE::Driver::SysRW->new(),
                                  Filter     => POE::Filter::SSL->new({client => 1}),            ### HERE WE ARE!!!
                                  InputEvent => 'socket_input',
                                  ErrorEvent => '_stop',
                               );
                               $heap->{socket_wheel}->put("GET / HTTP/1.0\r\nHost: ".$host."\r\n\r\n")
                            },
                            socket_input => sub {
                               my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0];
                               # This following line is needed to do the SSL handshake!
                               return $heap->{socket_wheel}->put()
                                  unless $heap->{socket_wheel}->get_input_filter()->handshakeDone();
                               print "Received: ".$buf."\n";
                            },
                            _stop => sub {
                               delete $_[HEAP]->{socket_wheel};
                            }
                         },
                         args => [$socket],
                      );
                   }
                }
             );
             $poe_kernel->run();

      Server
             #!/usr/bin/perl

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

             POE::Session->create(
                inline_states => {
                   _start       => sub {
                      my $heap = $_[HEAP];
                      $heap->{listener} = POE::Wheel::SocketFactory->new(
                         BindAddress  => '0.0.0.0',
                         BindPort     => 443,
                         Reuse        => 'yes',
                         SuccessEvent => 'socket_birth',
                         FailureEvent => '_stop',
                      );
                   },
                   _stop => sub {
                      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->{socket_wheel} = POE::Wheel::ReadWrite->new(
                                  Handle     => $connected_socket,
                                  Driver     => POE::Driver::SysRW->new(),
                                  Filter     => POE::Filter::SSL->new({crt => 'server.crt', key => 'server.key'}), ### HERE WE ARE!!!
                                  InputEvent => 'socket_input',
                                  ErrorEvent => '_stop',
                               );
                            },
                            socket_input => sub {
                               my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0];
                               # This following line is needed to do the SSL handshake!
                               return $heap->{socket_wheel}->put()
                                  unless $heap->{socket_wheel}->get_input_filter()->handshakeDone();
                               my $content = "HTTP/1.0 OK\r\nContent-type: text/html\r\n\r\n";
                               $content .= "Welcome on the SSL encrypted TCP connection!<br>\r\n";
                               $content .= localtime(time());
                               $heap->{socket_wheel}->put($content);
                               print "READ ".length($buf)." Bytes.\n";
                               $kernel->delay(_stop => 1);
                            },
                            _stop => sub {
                               delete $_[HEAP]->{socket_wheel};
                            }
                         },
                         args => [$socket],
                      );
                   }
                }
             );
             $poe_kernel->run();

FUNCTIONS
    new({option => "value", option2 => "value2", ...})>
        Returns a new POE::Filter::SSL object. It accepts as a hash with the
        following options:

        debug
          Get debug messages during ssl.

        client
          The filter is not a SSL server but a SSL client.

        crt
          The certificate for the server, normale file.crt.

        key
          The key of the certificate for the server, normale file.key.

        clientcertrequest
          The client gets requested for a client certificat during ssl
          handshake

        cacrt
          The ca certificate, which is used to verificated the client
          certificates against a CA. Normaly a file like ca.crt.

        cacrl
          Configures a CRL against the client certificate is proofed by
          clientCertValid().

        cipher
          Specify which ciphers are allowed.

          Example:

             cipher => 'AES256-SHA'

    handshakeDone({option => 1})
        Returns true if the handshake is done and all data for hanshake has
        been written out. It accepts as a hash with the following options:

        ignorebuf
          Ignores buffer states, just returns if OpenSSL established the
          connection. Needed if you want to exchange the Filter of
          POE::Wheel::ReadWrite before the first data comes in.

    clientCertNotOnCRL($file)
        Opens a CRL file, and verify if the serial of the client certificate
        is not contained in the CRL file. No file caching is done, each call
        opens the file again.

    clientCertIds()
        Returns a array of every certificate found by OpenSSL. Each element
        is again a array: First element is the value of
        X509_get_subject_name, second is the value of X509_get_issuer_name
        and third element is the serial of the certificate in binary form.
        You have to use split and use "ord" to convert it to a readable
        form. Example:

           my ($certid) = ($heap->{sslfilter}->clientCertIds());
           $certid = $certid ? $certid->[0]."<br>".$certid->[1]."<br>SERIAL=".ord($certid->[2]) : 'No client certificate';

    clientCertValid()
        Returns true if there is a client certificate that is valid. It also
        tests against the crl, if you have set the *cacrl* option on new().

    clientCertExists()
        Returns true if there is a client certificate, that maybe is
        untrusted.

    hexdump($string)
        Returns string data in hex format.

        For example:

          perl -e 'use POE::Filter::SSL; print POE::Filter::SSL::hexdump("test")."\n";'
          74:65:73:74

  Internaly used to access OpenSSL
    BIO_get_handler()
    BIO_read()
    BIO_write()
    VERIFY()
    X509_get_serialNumber()
    clone()
    doSSL()
    get()
    get_one()
    get_one_start()
    get_pending()
    hello()
    put()
    verify_serial_against_crl_file()

ADVANCED EXAMPLE
       #!/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->{listener} = POE::Wheel::SocketFactory->new(
                   BindAddress  => '0.0.0.0',
                   BindPort     => 443,
                   Reuse        => 'yes',
                   SuccessEvent => 'socket_birth',
                   FailureEvent => '_stop',
                );
             },
             _stop => sub {
                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->{socket_wheel} = POE::Wheel::ReadWrite->new(
                            Handle     => $connected_socket,
                            Driver     => POE::Driver::SysRW->new(),
                            Filter     => POE::Filter::SSL->new({           ### HERE WE ARE!!!
                               crt    => 'server.crt',
                               key    => 'server.key',
                               cactr  => 'ca.crt',
                               cipher => 'AES256-SHA',
                               cacrl  => 'ca.crl',
                               debug  => 1,
                               clientcertrequest => 1
                            }),
                            InputEvent => 'socket_input',
                            ErrorEvent => '_stop',
                         );
                         $heap->{sslfilter} = $heap->{socket_wheel}->get_input_filter();
                      },
                      socket_input => sub {
                         my ($kernel, $heap, $buf) = @_[KERNEL, HEAP, ARG0];
                         ### Uncomment the follwing lines if you want to use POE::Filter::HTTPD after the SSL handshake
                         #if (ref($heap->{socket_wheel}->get_input_filter()) eq "POE::Filter::SSL") {
                         #   if ($heap->{sslfilter}->handshakeDone({ignorebuf => 1})) {
                         #      $heap->{socket_wheel}->set_input_filter(POE::Filter::Stackable->new(
                         #         Filters => [
                         #            $heap->{sslfilter},
                         #            POE::Filter::HTTPD->new()
                         #         ])
                         #      );
                         #   }
                         #}
                         # This following line is needed to do the SSL handshake!
                         return $heap->{socket_wheel}->put()
                            unless $heap->{sslfilter}->handshakeDone();
                         my ($certid) = ($heap->{sslfilter}->clientCertIds());
                         $certid = $certid ? $certid->[0]."<br>".$certid->[1]."<br>SERIAL=".ord($certid->[2]) : 'No client certificate';
                         my $content = "HTTP/1.0 OK\r\nContent-type: text/html\r\n\r\n";
                         if ($heap->{sslfilter}->clientCertValid()) {
                            $content .= "Hello <font color=green>valid</font> client Certifcate:";
                         } else {
                            $content .= "None or <font color=red>invalid</font> client certificate:";
                         }
                         $content .= "<hr>".$certid."<hr>";
                         $content .= "Your URL was: ".$buf->uri."<hr>" # This line will only appear if you uncomment the lines above!
                            if (ref($buf) eq "HTTP::Request");
                         $content .= localtime(time());
                         $heap->{socket_wheel}->put($content);
                         $kernel->delay(_stop => 1);
                      },
                      _stop => sub {
                         delete $_[HEAP]->{socket_wheel};
                      }
                   },
                   args => [$socket],
                );
             }
          }
       );
       $poe_kernel->run();

AUTHOR
    Markus Mueller, "<privi at cpan.org>"

BUGS
    Please report any bugs or feature requests to "bug-poe-filter-sslsupport
    at rt.cpan.org", or through the web interface at
    <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Filter-SSL>. I will
    be notified, and then you'll automatically be notified of progress on
    your bug as I make changes.

SUPPORT
    You can find documentation for this module with the perldoc command.

        perldoc POE::Filter::SSL

    You can also look for information at:

    *   RT: CPAN's request tracker

        <http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Filter-SSL>

    *   AnnoCPAN: Annotated CPAN documentation

        <http://annocpan.org/dist/POE-Filter-SSL>

    *   CPAN Ratings

        <http://cpanratings.perl.org/d/POE-Filter-SSL>

    *   Search CPAN

        <http://search.cpan.org/dist/POE-Filter-SSL>

Commercial support
    Commercial support can be gained at <ssl at priv.de>

COPYRIGHT & LICENSE
    Copyright 2010 Markus Mueller, all rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

