diff --git a/Build.PL b/Build.PL index 6be33be..83cb451 100644 --- a/Build.PL +++ b/Build.PL @@ -1,31 +1,51 @@ -# Build.PL -use strict; use warnings; -use Module::Build; - -my $build = Module::Build->new( - # look up Module::Build::API for the info! - 'dynamic_config' => 0, - 'module_name' => 'POE::Component::SSLify', - 'license' => 'perl', - - 'dist_abstract' => 'SSL in the world of POE made easy', - - 'create_packlist' => 1, - 'create_makefile_pl' => 'traditional', - 'create_readme' => 1, - - 'test_files' => 't/*.t', - - 'add_to_cleanup' => [ 'META.yml', 'Makefile.PL', 'README', 'Manifest' ], # automatically generated - - 'requires' => { - # Networking - 'Net::SSLeay' => '1.30', - - # minimum perl version - 'perl' => '5.006', - }, -); - -# all done! -$build->create_build_script; +# Build.PL +use strict; use warnings; +use Module::Build; + +my $build = Module::Build->new( + # look up Module::Build::API for the info! + 'dynamic_config' => 0, + 'module_name' => 'POE::Component::SSLify', + 'license' => 'perl', + + 'dist_abstract' => 'SSL in the world of POE made easy', + 'dist_author' => 'Apocalypse ', + + 'create_packlist' => 1, + 'create_makefile_pl' => 'traditional', + 'create_readme' => 1, + 'create_license' => 1, + 'sign' => 0, + + 'test_files' => 't/*.t', + + 'add_to_cleanup' => [ 'META.yml', 'Makefile.PL', 'README', 'Makefile', 'LICENSE' ], # automatically generated + + 'requires' => { + # Networking + 'Net::SSLeay' => '1.36', + + # minimum perl version + 'perl' => '5.006', + }, + + 'build_requires' => { + # For the t/simple.t test + 'POE' => '1.267', + 'POE::Component::Client::TCP' => 0, + 'POE::Component::Server::TCP' => 0, + }, + + # include the standard stuff in META.yml + 'meta_merge' => { + 'resources' => { + 'license' => 'http://dev.perl.org/licenses/', + 'homepage' => 'http://search.cpan.org/dist/POE-Component-SSLify', + 'bugtracker' => 'http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-SSLify', + 'repository' => 'http://github.com/apocalypse/perl-poe-sslify', + }, + }, +); + +# all done! +$build->create_build_script; diff --git a/Changes b/Changes index bd260e3..d7b6510 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,23 @@ Revision history for Perl extension POE::Component::SSLify. + +* 0.18 + + Bumped POE dep to at least 1.267 for t/simple.t - thanks CPANTesters! + Minor typo fixes in POD/Build.PL + +* 0.17 + + Fixed the t/simple.t test to PASS on FreeBSD because Net::SSLeay::renegotiate was buggy on it, thanks CPANTesters! + Added note about OpenSSL functions in the POD. + +* 0.16 + + Updated the nonblocking code to be production-ready, thanks ASCENT! + Removed the NONBLOCKING() sub, this module is now always nonblocking. + Added more tests, thanks ASCENT! + Added "mylib/example.crt" and "mylib/example.key" for testing, thanks ASCENT! + Misc kwalitee and POD fixes. + Bumped Net::SSLeay prereq to 1.36 so we have the latest SSL stuff to ensure sanity :) * 0.15 diff --git a/LICENSE b/LICENSE index 198fd38..a5b9367 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,9 @@ -This software is copyright (c) 2009 by Apocalypse. +This software is copyright (c) 2010 by Apocalypse . This is free software; you can redistribute it and/or modify it under -the same terms as perl itself. - -Terms of Perl itself +the same terms as the Perl 5 programming language system itself. + +Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any @@ -12,7 +12,7 @@ --- The GNU General Public License, Version 1, February 1989 --- -This software is Copyright (c) 2008 by the POE authors. +This software is Copyright (c) 2010 by Apocalypse . This is free software, licensed under: @@ -270,7 +270,7 @@ --- The Artistic License 1.0 --- -This software is Copyright (c) 2008 by the POE authors. +This software is Copyright (c) 2010 by Apocalypse . This is free software, licensed under: diff --git a/MANIFEST b/MANIFEST index 5e48667..32d49d3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,10 +1,10 @@ Build.PL -Changes -Makefile.PL MANIFEST MANIFEST.SKIP +README +Makefile.PL META.yml -README +Changes LICENSE lib/POE/Component/SSLify.pm @@ -15,5 +15,9 @@ examples/server.pl examples/serverclient.pl +mylib/example.crt +mylib/example.key + t/1_load.t t/apocalypse.t +t/simple.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index a398351..e4f3ad8 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -1,28 +1,29 @@ -# Avoid Eclipse stuff -\.includepath$ -\.project$ -\.settings/ - -# Avoid version control files. -\B\.svn\b -\B\.git\b - -# Avoid Makemaker generated and utility files. -\bMakefile$ -\bblib/ -\bMakeMaker-\d -\bpm_to_blib$ - -# Avoid Module::Build generated and utility files. -\bBuild$ -\b_build/ - -# Avoid temp and backup files. -~$ -\.old$ -\#$ -\b\.# -\.bak$ - -# our tarballs -\.tar\.gz$ +# skip Eclipse IDE stuff +\.includepath$ +\.project$ +\.settings/ + +# Avoid version control files. +\B\.svn\b +\B\.git\b + +# Avoid Makemaker generated and utility files. +\bMakefile$ +\bblib/ +\bMakeMaker-\d +\bpm_to_blib$ + +# Avoid Module::Build generated and utility files. +\bBuild$ +\b_build/ +^MYMETA.yml$ + +# Avoid temp and backup files. +~$ +\.old$ +\#$ +\b\.# +\.bak$ + +# our tarballs +\.tar\.gz$ diff --git a/META.yml b/META.yml index 2371aaa..5ce1bfc 100644 --- a/META.yml +++ b/META.yml @@ -1,27 +1,36 @@ --- +abstract: 'SSL in the world of POE made easy' +author: + - 'Apocalypse ' +build_requires: + POE: 1.267 + POE::Component::Client::TCP: 0 + POE::Component::Server::TCP: 0 +configure_requires: + Module::Build: 0.36 +dynamic_config: 0 +generated_by: 'Module::Build version 0.3603' +license: perl +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: 1.4 name: POE-Component-SSLify -version: 0.15 -author: - - 'Apocalypse Eapocal@cpan.orgE' -abstract: SSL in the world of POE made easy -license: perl -resources: - license: http://dev.perl.org/licenses/ -requires: - Net::SSLeay: 1.30 - perl: 5.006 -dynamic_config: 0 provides: POE::Component::SSLify: file: lib/POE/Component/SSLify.pm - version: 0.15 + version: 0.18 POE::Component::SSLify::ClientHandle: file: lib/POE/Component/SSLify/ClientHandle.pm - version: 0.15 + version: 0.18 POE::Component::SSLify::ServerHandle: file: lib/POE/Component/SSLify/ServerHandle.pm - version: 0.15 -generated_by: Module::Build version 0.280801 -meta-spec: - url: http://module-build.sourceforge.net/META-spec-v1.2.html - version: 1.2 + version: 0.18 +requires: + Net::SSLeay: 1.36 + perl: 5.006 +resources: + bugtracker: http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-SSLify + homepage: http://search.cpan.org/dist/POE-Component-SSLify + license: http://dev.perl.org/licenses/ + repository: http://github.com/apocalypse/perl-poe-sslify +version: 0.18 diff --git a/Makefile.PL b/Makefile.PL index 3ee5ad9..82469dd 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,15 +1,18 @@ -# Note: this file was auto-generated by Module::Build::Compat version 0.2808_01 +# Note: this file was auto-generated by Module::Build::Compat version 0.3603 require 5.006; use ExtUtils::MakeMaker; WriteMakefile ( - 'PL_FILES' => {}, - 'INSTALLDIRS' => 'site', 'NAME' => 'POE::Component::SSLify', - 'EXE_FILES' => [], 'VERSION_FROM' => 'lib/POE/Component/SSLify.pm', 'PREREQ_PM' => { - 'Net::SSLeay' => '1.30' - } + 'Net::SSLeay' => '1.36', + 'POE' => '1.267', + 'POE::Component::Client::TCP' => 0, + 'POE::Component::Server::TCP' => 0 + }, + 'INSTALLDIRS' => 'site', + 'EXE_FILES' => [], + 'PL_FILES' => {} ) ; diff --git a/README b/README index 2e99164..4b585a3 100644 --- a/README +++ b/README @@ -2,14 +2,17 @@ POE::Component::SSLify - Makes using SSL in the world of POE easy! SYNOPSIS - Client-side usage + # CLIENT-side usage + # Import the module use POE::Component::SSLify qw( Client_SSLify ); # Create a normal SocketFactory wheel or something - my $factory = POE::Wheel::SocketFactory->new( ... ); - + my $factory = POE::Wheel::SocketFactory->new; + + # Time passes, SocketFactory gives you a socket when it connects in SuccessEvent # Converts the socket into a SSL socket POE can communicate with + my $socket = shift; eval { $socket = Client_SSLify( $socket ) }; if ( $@ ) { # Unable to SSLify it... @@ -18,12 +21,16 @@ # Now, hand it off to ReadWrite my $rw = POE::Wheel::ReadWrite->new( Handle => $socket, - ... + # other options as usual ); # Use it as you wish... - - Server-side usage + # End of example + + # --------------------------------------------------------------------------- # + + # SERVER-side usage + # !!! Make sure you have a public key + certificate generated via Net::SSLeay's makecert.pl # excellent howto: http://www.akadia.com/services/ssh_test_certificate.html @@ -37,9 +44,11 @@ } # Create a normal SocketFactory wheel or something - my $factory = POE::Wheel::SocketFactory->new( ... ); - + my $factory = POE::Wheel::SocketFactory->new; + + # Time passes, SocketFactory gives you a socket when it gets a connection in SuccessEvent # Converts the socket into a SSL socket POE can communicate with + my $socket = shift; eval { $socket = Server_SSLify( $socket ) }; if ( $@ ) { # Unable to SSLify it... @@ -48,10 +57,11 @@ # Now, hand it off to ReadWrite my $rw = POE::Wheel::ReadWrite->new( Handle => $socket, - ... + # other options as usual ); # Use it as you wish... + # End of example ABSTRACT Makes SSL use in POE a breeze! @@ -86,22 +96,19 @@ } } - Mixing Server/Client in the same program - Some users have reported success, others failure when they tried to utilize SSLify in both roles. This - would require more investigation, so please tread carefully if you need to use it! - - Blocking mode - Normally, Net::SSLeay requires the socket to be in blocking mode for the initial handshake to work. However, - various users ( especially ASCENT, thanks! ) have reported success in setting nonblocking mode for clients. - - In order to enable nonblocking mode, you need to set the subroutine "NONBLOCKING" to a true value in this - package. - - sub POE::Component::SSLify::NONBLOCKING { 1 } - use POE::Component::SSLify; - - This is a global, and an EXPERIMENTAL feature! Please, pretty please report back to me your experience with - this. Hopefully someday SSLify will be fully nonblocking, thanks to your help! + OpenSSL functions + Theoretically you can do anything that Net::SSLeay exports from the + OpenSSL libs on the socket. However, I have not tested every possible + function against SSLify, so use them carefully! If you have success, + please report back to me so I can update this doc! + + Net::SSLeay::renegotiate + This function has been tested ( it's in t/simple.t ) but it doesn't work + on FreeBSD! I tracked it down to this security advisory: + which + explains it in detail. The test will skip this function if it detects + that you're on a FreeBSD system. However, if you have the updated + OpenSSL library that fixes this you can use it. FUNCTIONS Client_SSLify @@ -179,6 +186,20 @@ Example: print "SSL Cipher is: " . SSLify_GetCipher( $sslified_sock ) . "\n"; + NOTE: Doing this immediately after Client_SSLify or Server_SSLify will result in "(NONE)" because the SSL handshake + is not done yet. The socket is nonblocking, so you will have to wait a little bit for it to get ready. + apoc@blackhole:~/mygit/perl-poe-sslify/examples$ perl serverclient.pl + got connection from: 127.0.0.1 - commencing Server_SSLify() + SSLified: 127.0.0.1 cipher type: ((NONE)) + Connected to server, commencing Client_SSLify() + SSLified the connection to the server + Connected to SSL server + Input: hola + got input from: 127.0.0.1 cipher type: (AES256-SHA) input: 'hola' + Got Reply: hola + Input: ^C + stopped at serverclient.pl line 126. + SSLify_GetSocket Returns the actual socket used by the SSLified socket, useful for stuff like getpeername()/getsockname() @@ -206,13 +227,16 @@ EXPORT Stuffs all of the above functions in @EXPORT_OK so you have to request them directly - head1 SUPPORT - +SUPPORT You can find documentation for this module with the perldoc command. perldoc POE::Component::SSLify Websites + * Search CPAN + + + * AnnoCPAN: Annotated CPAN documentation @@ -221,13 +245,34 @@ - * RT: CPAN's request tracker + * CPAN Forum + + + + * RT: CPAN's Request Tracker - * Search CPAN - - + * CPANTS Kwalitee + + + + * CPAN Testers Results + + + + * CPAN Testers Matrix + + + + * Git Source Code Repository + + This code is currently hosted on github.com under the account + "apocalypse". Please feel free to browse it and pull from it, or + whatever. If you want to contribute patches, please send me a diff + or prod me to pull from your repository :) + + Bugs Please report any bugs or feature requests to "bug-poe-component-sslify @@ -244,19 +289,24 @@ AUTHOR Apocalypse -PROPS Original code is entirely Rocco Caputo ( Creator of POE ) -> I simply packaged up the code into something everyone could use and accepted the burden of maintaining it :) From the PoCo::Client::HTTP code =] - # TODO - This code should probably become a POE::Kernel method, + # This code should probably become a POE::Kernel method, # seeing as it's rather baroque and potentially useful in a number # of places. + ASCENT also helped a lot with the nonblocking mode, without his hard + work this module would still be stuck in the stone age :) + COPYRIGHT AND LICENSE - Copyright 2009 by Apocalypse/Rocco Caputo + Copyright 2010 by Apocalypse/Rocco Caputo/Dariusz Jackowski This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + The full text of the license can be found in the LICENSE file included + with this module. + diff --git a/examples/client.pl b/examples/client.pl index f426d50..4eb8f34 100644 --- a/examples/client.pl +++ b/examples/client.pl @@ -1,8 +1,5 @@ #!/usr/bin/perl use strict; use warnings; - -# to use experimental nonblocking, uncomment this line -#sub POE::Component::SSLify::NONBLOCKING { 1 } use POE; use POE::Component::SSLify qw( Client_SSLify ); @@ -30,7 +27,7 @@ 'do_connect' => sub { # Create the socketfactory wheel to listen for requests $_[HEAP]->{'SOCKETFACTORY'} = POE::Wheel::SocketFactory->new( - 'RemotePort' => 5432, + 'RemotePort' => 9898, 'RemoteAddress' => 'localhost', 'Reuse' => 'yes', 'SuccessEvent' => 'Got_Connection', diff --git a/examples/server.pl b/examples/server.pl index 60e5053..9638309 100644 --- a/examples/server.pl +++ b/examples/server.pl @@ -1,8 +1,5 @@ #!/usr/bin/perl use strict; use warnings; - -# to use experimental nonblocking, uncomment this line -#sub POE::Component::SSLify::NONBLOCKING { 1 } use POE; use Socket qw( inet_ntoa unpack_sockaddr_in ); @@ -15,15 +12,18 @@ POE::Session->create( 'inline_states' => { '_start' => sub { - # Okay, set the SSL options - SSLify_Options( 'server.key', 'server.crt' ); + # Okay, set the SSL certificate info + eval { + SSLify_Options( 'mylib/example.key', 'mylib/example.crt' ); + }; + SSLify_Options( '../mylib/example.key', '../mylib/example.crt' ) if ( $@ ); # Set the alias $_[KERNEL]->alias_set( 'main' ); # Create the socketfactory wheel to listen for requests $_[HEAP]->{'SOCKETFACTORY'} = POE::Wheel::SocketFactory->new( - 'BindPort' => 5432, + 'BindPort' => 9898, 'BindAddress' => 'localhost', 'Reuse' => 'yes', 'SuccessEvent' => 'Got_Connection', @@ -39,7 +39,7 @@ $socket = Server_SSLify( $socket ); # testing stuff - warn "got connection from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: " . SSLify_GetCipher( $socket ); + warn "got connection from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: " . SSLify_GetCipher( $socket ) . "\n"; # Hand it off to ReadWrite my $wheel = POE::Wheel::ReadWrite->new( @@ -65,6 +65,10 @@ 'Got_Input' => sub { # ARG0: The Line, ARG1: Wheel ID + # testing stuff + my $socket = $_[HEAP]->{'WHEELS'}->{ $_[ARG1] }->get_output_handle(); + warn "got input from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: (" . SSLify_GetCipher( $socket ) . ") input: '$_[ARG0]'\n"; + # Send back to the client the line! $_[HEAP]->{'WHEELS'}->{ $_[ARG1] }->put( $_[ARG0] ); return 1; diff --git a/examples/serverclient.pl b/examples/serverclient.pl index cffc014..1861eb6 100755 --- a/examples/serverclient.pl +++ b/examples/serverclient.pl @@ -1,8 +1,5 @@ #!/usr/bin/perl use strict; use warnings; - -# to use experimental nonblocking, uncomment this line -#sub POE::Component::SSLify::NONBLOCKING { 1 } use POE; use Socket qw( inet_ntoa unpack_sockaddr_in ); @@ -17,15 +14,18 @@ POE::Session->create( 'inline_states' => { '_start' => sub { - # Okay, set the SSL options - SSLify_Options( 'server.key', 'server.crt' ); + # Okay, set the SSL certificate info + eval { + SSLify_Options( 'mylib/example.key', 'mylib/example.crt' ); + }; + SSLify_Options( '../mylib/example.key', '../mylib/example.crt' ) if ( $@ ); # Set the alias $_[KERNEL]->alias_set( 'server' ); # Create the socketfactory wheel to listen for requests $_[HEAP]->{'SOCKETFACTORY'} = POE::Wheel::SocketFactory->new( - 'BindPort' => 5432, + 'BindPort' => 9898, 'BindAddress' => 'localhost', 'Reuse' => 'yes', 'SuccessEvent' => 'Got_Connection', @@ -38,13 +38,13 @@ my $socket = $_[ ARG0 ]; # testing stuff - warn "got connection from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( $socket ) ) )[1] ) . " - commencing Server_SSLify()"; + warn "got connection from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( $socket ) ) )[1] ) . " - commencing Server_SSLify()\n"; # SSLify it! $socket = Server_SSLify( $socket ); # testing stuff - warn "SSLified: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: " . SSLify_GetCipher( $socket ); + warn "SSLified: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: (" . SSLify_GetCipher( $socket ) . ")\n"; # Hand it off to ReadWrite my $wheel = POE::Wheel::ReadWrite->new( @@ -68,6 +68,10 @@ }, 'Got_Input' => sub { # ARG0: The Line, ARG1: Wheel ID + + # testing stuff + my $socket = $_[HEAP]->{'WHEELS'}->{ $_[ARG1] }->get_output_handle(); + warn "got input from: " . inet_ntoa( ( unpack_sockaddr_in( getpeername( SSLify_GetSocket( $socket ) ) ) )[1] ) . " cipher type: (" . SSLify_GetCipher( $socket ) . ") input: '$_[ARG0]'\n"; # Send back to the client the line! $_[HEAP]->{'WHEELS'}->{ $_[ARG1] }->put( $_[ARG0] ); @@ -104,7 +108,7 @@ 'do_connect' => sub { # Create the socketfactory wheel to listen for requests $_[HEAP]->{'SOCKETFACTORY'} = POE::Wheel::SocketFactory->new( - 'RemotePort' => 5432, + 'RemotePort' => 9898, 'RemoteAddress' => 'localhost', 'Reuse' => 'yes', 'SuccessEvent' => 'Got_Connection', @@ -128,12 +132,12 @@ # ARG0 = Socket, ARG1 = Remote Address, ARG2 = Remote Port my $socket = $_[ ARG0 ]; - warn "Connected to server, commencing Client_SSLify()"; + warn "Connected to server, commencing Client_SSLify()\n"; # SSLify it! $socket = Client_SSLify( $socket ); - warn "SSLified the connection to the server"; + warn "SSLified the connection to the server\n"; # Hand it off to ReadWrite my $wheel = POE::Wheel::ReadWrite->new( diff --git a/lib/POE/Component/SSLify/ClientHandle.pm b/lib/POE/Component/SSLify/ClientHandle.pm index ca94592..c9f2a1b 100644 --- a/lib/POE/Component/SSLify/ClientHandle.pm +++ b/lib/POE/Component/SSLify/ClientHandle.pm @@ -1,10 +1,9 @@ -# $Id: ClientHandle.pm 53 2008-07-28 03:03:04Z larwan $ package POE::Component::SSLify::ClientHandle; use strict; use warnings; # Initialize our version use vars qw( $VERSION ); -$VERSION = '0.15'; +$VERSION = '0.18'; # Import the SSL death routines use Net::SSLeay qw( die_now die_if_ssl_error ); @@ -20,7 +19,7 @@ # create a context, if necessary if ( ! defined $ctx ) { - $ctx = POE::Component::SSLify::createSSLcontext( undef, undef, $version, $options ); + $ctx = POE::Component::SSLify::_createSSLcontext( undef, undef, $version, $options ); } my $ssl = Net::SSLeay::new( $ctx ) or die_now( "Failed to create SSL $!" ); @@ -29,6 +28,10 @@ Net::SSLeay::set_fd( $ssl, $fileno ); # Must use fileno + # Socket is in non-blocking mode, so connect() will return immediately. + # die_if_ssl_error won't die on non-blocking errors. We don't need to call connect() + # again, because OpenSSL I/O functions (read, write, ...) can handle that entirely + # by self (it's needed to connect() once to determine connection type). my $resp = Net::SSLeay::connect( $ssl ) or die_if_ssl_error( 'ssl connect' ); my $self = bless { @@ -71,7 +74,7 @@ =head1 COPYRIGHT AND LICENSE -Copyright 2009 by Apocalypse +Copyright 2010 by Apocalypse This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff --git a/lib/POE/Component/SSLify/ServerHandle.pm b/lib/POE/Component/SSLify/ServerHandle.pm index 1b85e33..d42d06e 100644 --- a/lib/POE/Component/SSLify/ServerHandle.pm +++ b/lib/POE/Component/SSLify/ServerHandle.pm @@ -1,10 +1,9 @@ -# $Id: ServerHandle.pm 53 2008-07-28 03:03:04Z larwan $ package POE::Component::SSLify::ServerHandle; use strict; use warnings; # Initialize our version use vars qw( $VERSION ); -$VERSION = '0.15'; +$VERSION = '0.18'; # Import the SSL death routines use Net::SSLeay qw( die_now die_if_ssl_error ); @@ -19,6 +18,10 @@ Net::SSLeay::set_fd( $ssl, $fileno ); + # Socket is in non-blocking mode, so accept() will return immediately. + # die_if_ssl_error won't die on non-blocking errors. We don't need to call accept() + # again, because OpenSSL I/O functions (read, write, ...) can handle that entirely + # by self (it's needed to accept() once to determine connection type). my $err = Net::SSLeay::accept( $ssl ) and die_if_ssl_error( 'ssl accept' ); my $self = bless { @@ -85,7 +88,8 @@ my $wrote_len = Net::SSLeay::write( $self->{'ssl'}, substr( $buf, $offset, $len ) ); # Did we get an error or number of bytes written? - # Net::SSLeay::write() returns the number of bytes written, or -1 on error. + # Net::SSLeay::write() returns the number of bytes written, or 0 on unsuccessful + # operation (probably connection closed), or -1 on error. if ( $wrote_len < 0 ) { # The normal syswrite() POE uses expects 0 here. return 0; @@ -186,19 +190,9 @@ Apocalypse Eapocal@cpan.orgE -=head1 PROPS - - Original code is entirely Rocco Caputo ( Creator of POE ) -> I simply - packaged up the code into something everyone could use... - - From the PoCo::Client::HTTP code for blocking sockets =] - # TODO - This code should probably become a POE::Kernel method, - # seeing as it's rather baroque and potentially useful in a number - # of places. - =head1 COPYRIGHT AND LICENSE -Copyright 2009 by Apocalypse/Rocco Caputo +Copyright 2010 by Apocalypse This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff --git a/lib/POE/Component/SSLify.pm b/lib/POE/Component/SSLify.pm index e0411f0..af54b5b 100644 --- a/lib/POE/Component/SSLify.pm +++ b/lib/POE/Component/SSLify.pm @@ -1,10 +1,9 @@ -# $Id: SSLify.pm 53 2008-07-28 03:03:04Z larwan $ package POE::Component::SSLify; use strict; use warnings; # Initialize our version use vars qw( $VERSION ); -$VERSION = '0.15'; +$VERSION = '0.18'; # We need Net::SSLeay or all's a failure! BEGIN { @@ -16,22 +15,21 @@ die $@; } else { # Check to make sure the versions are what we want + # TODO what if Net::SSLeay is upgraded to 1.4? :( if ( ! ( defined $Net::SSLeay::VERSION and $Net::SSLeay::VERSION =~ /^1\.3/ ) ) { warn 'Please upgrade Net::SSLeay to v1.30+ installed: v' . $Net::SSLeay::VERSION; } # Finally, load our subclass :) + # ClientHandle isa ServerHandle so it will get loaded automatically require POE::Component::SSLify::ClientHandle; - require POE::Component::SSLify::ServerHandle; # Initialize Net::SSLeay + # Taken from http://search.cpan.org/~flora/Net-SSLeay-1.36/lib/Net/SSLeay.pm#Low_level_API Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); - - # set nonblocking mode? - if ( ! defined &NONBLOCKING ) { *NONBLOCKING = sub () { 0 } } } } @@ -51,33 +49,28 @@ # The server-side CTX stuff my $ctx = undef; -# Helper sub to set blocking on a handle -sub Set_Blocking { +# Helper sub to set nonblocking on a handle +sub _NonBlocking { my $socket = shift; - # skip this? ( experimental ) - return $socket if NONBLOCKING(); - - # Net::SSLeay needs blocking for setup. - # # ActiveState Perl 5.8.0 dislikes the Win32-specific code to make - # a socket blocking, so we use IO::Handle's blocking(1) method. + # a socket blocking, so we use IO::Handle's blocking(0) method. # Perl 5.005_03 doesn't like blocking(), so we only use it in # 5.8.0 and beyond. if ( $] >= 5.008 and $^O eq 'MSWin32' ) { # From IO::Handle POD # If an error occurs blocking will return undef and $! will be set. - if ( ! $socket->blocking( 1 ) ) { - die "Unable to set blocking mode on socket: $!"; + if ( ! $socket->blocking( 0 ) ) { + die "Unable to set nonblocking mode on socket: $!"; } } else { - # Make the handle blocking, the POSIX way. + # Make the handle nonblocking, the POSIX way. if ( $^O ne 'MSWin32' ) { # Get the old flags my $flags = fcntl( $socket, F_GETFL, 0 ) or die "fcntl( $socket, F_GETFL, 0 ) fails: $!"; - # Okay, we patiently wait until the socket turns blocking mode - until( fcntl( $socket, F_SETFL, $flags & ~O_NONBLOCK ) ) { + # Okay, we patiently wait until the socket turns nonblocking mode + until( fcntl( $socket, F_SETFL, $flags | O_NONBLOCK ) ) { # What was the error? if ( ! ( $! == EAGAIN or $! == EWOULDBLOCK ) ) { # Fatal error... @@ -88,7 +81,7 @@ # Darned MSWin32 way... # Do some ioctl magic here # 126 is FIONBIO ( some docs say 0x7F << 16 ) - my $flag = "0"; + my $flag = "1"; ioctl( $socket, 0x80000000 | ( 4 << 16 ) | ( ord( 'f' ) << 8 ) | 126, $flag ) or die "ioctl( $socket, FIONBIO, $flag ) fails: $!"; } } @@ -107,8 +100,8 @@ die "Did not get a defined socket"; } - # Set blocking on - $socket = Set_Blocking( $socket ); + # Set non-blocking + $socket = _NonBlocking( $socket ); # Now, we create the new socket and bind it to our subclass of Net::SSLeay::Handle my $newsock = gensym(); @@ -134,8 +127,8 @@ die 'Please do SSLify_Options() first ( or pass in a $ctx object )'; } - # Set blocking on - $socket = Set_Blocking( $socket ); + # Set non-blocking + $socket = _NonBlocking( $socket ); # Now, we create the new socket and bind it to our subclass of Net::SSLeay::Handle my $newsock = gensym(); @@ -149,7 +142,7 @@ # Get the key + cert + version + options my( $key, $cert, $version, $options ) = @_; - return createSSLcontext( $key, $cert, $version, $options ); + return _createSSLcontext( $key, $cert, $version, $options ); } sub SSLify_Options { @@ -171,13 +164,13 @@ Net::SSLeay::CTX_free( $ctx ); undef $ctx; } - $ctx = createSSLcontext( $key, $cert, $version, $options ); + $ctx = _createSSLcontext( $key, $cert, $version, $options ); # all done! return 1; } -sub createSSLcontext { +sub _createSSLcontext { my( $key, $cert, $version, $options ) = @_; my $context; @@ -247,24 +240,27 @@ # End of module 1; - __END__ +=for stopwords AnnoCPAN CPAN CPANTS Kwalitee RT SSL com diff github FreeBSD OpenSSL + =head1 NAME POE::Component::SSLify - Makes using SSL in the world of POE easy! =head1 SYNOPSIS -=head2 Client-side usage + # CLIENT-side usage # Import the module use POE::Component::SSLify qw( Client_SSLify ); # Create a normal SocketFactory wheel or something - my $factory = POE::Wheel::SocketFactory->new( ... ); - + my $factory = POE::Wheel::SocketFactory->new; + + # Time passes, SocketFactory gives you a socket when it connects in SuccessEvent # Converts the socket into a SSL socket POE can communicate with + my $socket = shift; eval { $socket = Client_SSLify( $socket ) }; if ( $@ ) { # Unable to SSLify it... @@ -273,12 +269,15 @@ # Now, hand it off to ReadWrite my $rw = POE::Wheel::ReadWrite->new( Handle => $socket, - ... + # other options as usual ); # Use it as you wish... - -=head2 Server-side usage + # End of example + + # --------------------------------------------------------------------------- # + + # SERVER-side usage # !!! Make sure you have a public key + certificate generated via Net::SSLeay's makecert.pl # excellent howto: http://www.akadia.com/services/ssh_test_certificate.html @@ -293,9 +292,11 @@ } # Create a normal SocketFactory wheel or something - my $factory = POE::Wheel::SocketFactory->new( ... ); - + my $factory = POE::Wheel::SocketFactory->new; + + # Time passes, SocketFactory gives you a socket when it gets a connection in SuccessEvent # Converts the socket into a SSL socket POE can communicate with + my $socket = shift; eval { $socket = Server_SSLify( $socket ) }; if ( $@ ) { # Unable to SSLify it... @@ -304,10 +305,11 @@ # Now, hand it off to ReadWrite my $rw = POE::Wheel::ReadWrite->new( Handle => $socket, - ... + # other options as usual ); # Use it as you wish... + # End of example =head1 ABSTRACT @@ -345,24 +347,16 @@ } } -=head2 Mixing Server/Client in the same program - - Some users have reported success, others failure when they tried to utilize SSLify in both roles. This - would require more investigation, so please tread carefully if you need to use it! - -=head2 Blocking mode - - Normally, Net::SSLeay requires the socket to be in blocking mode for the initial handshake to work. However, - various users ( especially ASCENT, thanks! ) have reported success in setting nonblocking mode for clients. - - In order to enable nonblocking mode, you need to set the subroutine "NONBLOCKING" to a true value in this - package. - - sub POE::Component::SSLify::NONBLOCKING { 1 } - use POE::Component::SSLify; - - This is a global, and an EXPERIMENTAL feature! Please, pretty please report back to me your experience with - this. Hopefully someday SSLify will be fully nonblocking, thanks to your help! +=head2 OpenSSL functions + +Theoretically you can do anything that Net::SSLeay exports from the OpenSSL libs on the socket. However, I have not tested every +possible function against SSLify, so use them carefully! If you have success, please report back to me so I can update this doc! + +=head3 Net::SSLeay::renegotiate + +This function has been tested ( it's in t/simple.t ) but it doesn't work on FreeBSD! I tracked it down to this security advisory: +L which explains it in detail. The test will skip this function +if it detects that you're on a FreeBSD system. However, if you have the updated OpenSSL library that fixes this you can use it. =head1 FUNCTIONS @@ -446,6 +440,20 @@ Example: print "SSL Cipher is: " . SSLify_GetCipher( $sslified_sock ) . "\n"; + NOTE: Doing this immediately after Client_SSLify or Server_SSLify will result in "(NONE)" because the SSL handshake + is not done yet. The socket is nonblocking, so you will have to wait a little bit for it to get ready. + apoc@blackhole:~/mygit/perl-poe-sslify/examples$ perl serverclient.pl + got connection from: 127.0.0.1 - commencing Server_SSLify() + SSLified: 127.0.0.1 cipher type: ((NONE)) + Connected to server, commencing Client_SSLify() + SSLified the connection to the server + Connected to SSL server + Input: hola + got input from: 127.0.0.1 cipher type: (AES256-SHA) input: 'hola' + Got Reply: hola + Input: ^C + stopped at serverclient.pl line 126. + =head2 SSLify_GetSocket Returns the actual socket used by the SSLified socket, useful for stuff like getpeername()/getsockname() @@ -476,7 +484,7 @@ Stuffs all of the above functions in @EXPORT_OK so you have to request them directly -head1 SUPPORT +=head1 SUPPORT You can find documentation for this module with the perldoc command. @@ -486,6 +494,10 @@ =over 4 +=item * Search CPAN + +L + =item * AnnoCPAN: Annotated CPAN documentation L @@ -494,13 +506,33 @@ L -=item * RT: CPAN's request tracker +=item * CPAN Forum + +L + +=item * RT: CPAN's Request Tracker L -=item * Search CPAN - -L +=item * CPANTS Kwalitee + +L + +=item * CPAN Testers Results + +L + +=item * CPAN Testers Matrix + +L + +=item * Git Source Code Repository + +This code is currently hosted on github.com under the account "apocalypse". Please feel free to browse it +and pull from it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull +from your repository :) + +L =back @@ -520,22 +552,25 @@ Apocalypse Eapocal@cpan.orgE -=head1 PROPS - Original code is entirely Rocco Caputo ( Creator of POE ) -> I simply packaged up the code into something everyone could use and accepted the burden of maintaining it :) From the PoCo::Client::HTTP code =] - # TODO - This code should probably become a POE::Kernel method, + # This code should probably become a POE::Kernel method, # seeing as it's rather baroque and potentially useful in a number # of places. +ASCENT also helped a lot with the nonblocking mode, without his hard work this +module would still be stuck in the stone age :) + =head1 COPYRIGHT AND LICENSE -Copyright 2009 by Apocalypse/Rocco Caputo +Copyright 2010 by Apocalypse/Rocco Caputo/Dariusz Jackowski This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. +The full text of the license can be found in the LICENSE file included with this module. + =cut diff --git a/mylib/example.crt b/mylib/example.crt new file mode 100644 index 0000000..48aceb6 --- /dev/null +++ b/mylib/example.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIDCCAggCCQCFFIApNMYn+zANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJY +WDETMBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UEBxMJU29tZS1DaXR5MRowGAYD +VQQKExFTb21lLUNvbXBhbnkgTHRkLjAeFw0xMDAzMDkyMzM0NDBaFw0yMzExMTYy +MzM0NDBaMFIxCzAJBgNVBAYTAlhYMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYD +VQQHEwlTb21lLUNpdHkxGjAYBgNVBAoTEVNvbWUtQ29tcGFueSBMdGQuMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy7vONqMnWdBNGEstmvJDH3vmDh/y +ZxgpkJhKNTIEbyo5B+m29mc860A4VNKGmi/c/Z0zx7ETu5GpTZAvVdhr9LWHKh6j +vH4xGfCfCCfyZtFIxEsIawpaRi2AkKRb4386NCDXdomVCiihAEn2VKS13nZaztMu +EwVFg3l5jIj8sHE9UJbTmbCQOJre1brAK/2l0FEfy03oCygYvtTUzYksbLNsNiG7 +LJ/Y8opoKwdcboVzMOg1dnoY6a3J7hpDd6FSTKcpqxNltk3x1fWh+zEd0Pl3YAMF +uW1mIbOIuSuQD9mZqcxDIaAb/yrU5N82zh7Kkba3MCs1B6eaCWPJcDFeFwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4IBAQBDXa1fKk1NF05/9w93q8/QDINXQhlWFZDIr5oB +A0rU2Rezljji92ElZCl/nGfianeCoCjA6+xMY37eUn8OOfJh1e6a45E1sRyXgZZv +tlZmt65K/UlZCYQ8+jEPjP+Ea/iKq3IUN0RKObOxB3QvOucx0ECfqZeiApuhkjZJ +I97dcD3ybwQ2rZcRzIccKQYsfnzLIzUjLlEbvyIOk6jyGKV6lZfmkeyuDbFlBdcG +85Ts5GpXM7lojmdz858PgNEtCEkoSO8LQSdWftsoCxWTEPdMTBPotMN0FgySO5Wr +d91Rn/uL5LFSGD4CV8u94IcS/qLf1IqheGUWPYZ7edwt+zQm +-----END CERTIFICATE----- diff --git a/mylib/example.key b/mylib/example.key new file mode 100644 index 0000000..444a63d --- /dev/null +++ b/mylib/example.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAy7vONqMnWdBNGEstmvJDH3vmDh/yZxgpkJhKNTIEbyo5B+m2 +9mc860A4VNKGmi/c/Z0zx7ETu5GpTZAvVdhr9LWHKh6jvH4xGfCfCCfyZtFIxEsI +awpaRi2AkKRb4386NCDXdomVCiihAEn2VKS13nZaztMuEwVFg3l5jIj8sHE9UJbT +mbCQOJre1brAK/2l0FEfy03oCygYvtTUzYksbLNsNiG7LJ/Y8opoKwdcboVzMOg1 +dnoY6a3J7hpDd6FSTKcpqxNltk3x1fWh+zEd0Pl3YAMFuW1mIbOIuSuQD9mZqcxD +IaAb/yrU5N82zh7Kkba3MCs1B6eaCWPJcDFeFwIDAQABAoIBAQDAWYEZHrMBXePb +4uac2su6xuxuO4VOBob/qHivfpinJ9MOgH8oZlIivdAxbU599mCL5cbEa4/40E5r +F41JXHqYYGbLwn/Ob8oF9qL/PU/j+QXdpgW9odmXVlCCv1quECmwm60xkjXvkGpp +bmsugQ/giBe2G7XtaVW3CpJSt+by4VW+qf8ke3Y8TQHoW+dUPiexb9KFFTvy9i1O +HV1Z3pQ3Y8yH1qb5xzCNYv1/ofjbAMlv1Cf9CHpAnIWlXA7QCb1VktF2eMjv435d +oxhEhYidARWZfxiYRcewt0FfPU7/yDZXgphLSrQLWEJs6bOxDRhA6l/cOcQX/XG1 +t7I5I9JhAoGBAPuweXOTUEfZdrveXM5716/o+qYMZC330LvOqbpJnB5up8cPR9sl +VB1dZqFMYdFTVD9oPKPY726RxptE8ylShhe5LuPRjF3/aYmXlrQROSJJ5uJ5exr9 +mJjXaRp9AW8wTOqDkHbiyb+WrpVL5klYHvgnJrivokzc246tdsvADJ6xAoGBAM85 +EgTQhHT5OFe4bQavROd8khuTTD3iGQQO0cR5Oh8EjbMc3ejp7qUE41I30DoFpn64 +uOiQzxzpOJFGiid+8FoG7k2/K96j0j9JiBNv93Te1OhOvG+2JirrygoM9E3ofmF5 +trDpCVgs5+mAkdJctmpXME5PPmEKo5+b2cK9S8tHAoGAS5s0sLJVEHBUCZV/nYt4 +PGCpQs5AHcruyiwHsm1AV6f4AIUnmb456WRQWy3dyIrWqQGADdwx+K4T0jrBLO2c +WG4Jlugw2V/LbUi7PbZaymEW2XuSroX1nBxBd3KLxsHkqSICeKQh5Mq4ASM+t1Og +Yf0o1Zv1Dk/eKJsVL5mucNECgYEAst3nIMK/4pwQNx+Y9DErf8i18Rl2sN/NigQk +qrudIJL0oMtk/JuYA1axxREqKjsgWLen3A7Kx4DD9Bn4PFlEq+DZp8BA5L9xRnF5 +BJYb+gQxsIft/VsznM7EKWK/KdRp6kd+Gzw7daHlWFdDB1pBlo7FwoKCLI9bZvTG +xWJR3xcCgYEAyHPtp36K2epbFkpCf5pmtWCVk4lRNs6MSE0aJmbqZhRW971V81ev +xa2DZj3KHcXCfDW5Dnjiyx9qf7GS1ts9dfWyKjr6qEwBcGQMXW3C6p9fuFXkxGhe +Yx2KYA48kiY5Rj5wTpixwdq3YWASlZOQs282UEb+cxZHxpT2YUiIJkc= +-----END RSA PRIVATE KEY----- diff --git a/t/apocalypse.t b/t/apocalypse.t index faf266a..199ba25 100644 --- a/t/apocalypse.t +++ b/t/apocalypse.t @@ -8,5 +8,7 @@ } else { # lousy hack for kwalitee require Test::NoWarnings; require Test::Pod; require Test::Pod::Coverage; - is_apocalypse_here(); + is_apocalypse_here( { + deny => qr/^(?:(?:OutdatedPrereq|Dependencie)s|ModuleUsed|Strict|Fixme|Pod_Spelling)$/, + } ); } diff --git a/t/simple.t b/t/simple.t new file mode 100644 index 0000000..ae51f36 --- /dev/null +++ b/t/simple.t @@ -0,0 +1,154 @@ +#!/usr/bin/perl + +# Thanks to ASCENT for this test! + +use strict; use warnings; + +my $numtests; +BEGIN { + $numtests = 22; + + eval "use Test::NoWarnings"; + if ( ! $@ ) { + # increment by one + $numtests++; + + } +} + +use Test::More tests => $numtests; + +use POE; +use POE::Component::Client::TCP; +use POE::Component::Server::TCP; +use POE::Component::SSLify qw/Client_SSLify Server_SSLify SSLify_Options SSLify_GetCipher SSLify_ContextCreate/; +use Net::SSLeay qw/ERROR_WANT_READ ERROR_WANT_WRITE/; +use POSIX qw/F_GETFL F_SETFL O_NONBLOCK EAGAIN EWOULDBLOCK/; + +# TODO rewrite this to use Test::POE::Server::TCP and stuff :) + +my $port; + +POE::Component::Server::TCP->new +( + Alias => 'myserver', + Address => '127.0.0.1', + Port => 0, + + Started => sub + { + use Socket qw/sockaddr_in/; + $port = (sockaddr_in($_[HEAP]->{listener}->getsockname))[0]; + }, + ClientConnected => sub + { + ok(1, 'SERVER: accepted'); + }, + ClientDisconnected => sub + { + ok(1, 'SERVER: client disconnected'); + $_[KERNEL]->post(myserver => 'shutdown'); + }, + ClientPreConnect => sub + { + eval { SSLify_Options('mylib/example.key', 'mylib/example.crt', 'sslv3') }; + eval { SSLify_Options('../mylib/example.key', '../mylib/example.crt', 'sslv3') } if ($@); + ok(!$@, "SERVER: SSLify_Options $@"); + + my $socket = eval { Server_SSLify($_[ARG0]) }; + ok(!$@, "SERVER: Server_SSLify $@"); + ok(1, 'SERVER: SSLify_GetCipher: '. SSLify_GetCipher($socket)); + + my $flags = fcntl($_[ARG0], F_GETFL, 0); + ok($flags & O_NONBLOCK, 'SERVER: SSLified socket is non-blocking?'); + + return ($socket); + }, + ClientInput => sub + { + my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0]; + + ## At this point, connection MUST be encrypted. + my $cipher = SSLify_GetCipher($heap->{client}->get_output_handle); + ok($cipher ne '(NONE)', "SERVER: SSLify_GetCipher: $cipher"); + + if ($request eq 'ping') + { + ok(1, "SERVER: recv: $request"); + $heap->{client}->put("pong"); + } + elsif ($request eq 'ping2') + { + ok(1, "SERVER: recv: $request"); + $heap->{client}->put("pong2"); + } + }, +); + +POE::Component::Client::TCP->new +( + Alias => 'myclient', + RemoteAddress => '127.0.0.1', + RemotePort => $port, + Connected => sub + { + ok(1, 'CLIENT: connected'); + + $_[HEAP]->{server}->put("ping"); + }, + PreConnect => sub + { + my $ctx = eval { SSLify_ContextCreate(undef, undef, 'sslv3') }; + ok(!$@, "CLIENT: SSLify_ContextCreate $@"); + my $socket = eval { Client_SSLify($_[ARG0], undef, undef, $ctx) }; + ok(!$@, "CLIENT: Client_SSLify $@"); + ok(1, 'CLIENT: SSLify_GetCipher: '. SSLify_GetCipher($socket)); + + my $flags = fcntl($_[ARG0], F_GETFL, 0); + ok($flags & O_NONBLOCK, 'CLIENT: SSLified socket is non-blocking?'); + + return ($socket); + }, + ServerInput => sub + { + my ($kernel, $heap, $line) = @_[KERNEL, HEAP, ARG0]; + + ## At this point, connection MUST be encrypted. + my $cipher = SSLify_GetCipher($heap->{server}->get_output_handle); + ok($cipher ne '(NONE)', "CLIENT: SSLify_GetCipher: $cipher"); + + if ($line eq 'pong') + { + ok(1, "CLIENT: recv: $line"); + + # Skip 2 Net::SSLeay::renegotiate() tests on FreeBSD because of + # http://security.freebsd.org/advisories/FreeBSD-SA-09:15.ssl.asc + TODO: { + local $TODO = "Net::SSLeay::renegotiate() does not work on all platforms"; + + ## Force SSL renegotiation + my $ssl = tied(*{$heap->{server}->get_output_handle})->{ssl}; + my $reneg_num = Net::SSLeay::num_renegotiations($ssl); + + ok(1 == Net::SSLeay::renegotiate($ssl), 'CLIENT: SSL renegotiation'); + my $handshake = Net::SSLeay::do_handshake($ssl); + my $err = Net::SSLeay::get_error($ssl, $handshake); + + ## 1 == Successful handshake, ERROR_WANT_(READ|WRITE) == non-blocking. + ok($handshake == 1 || $err == ERROR_WANT_READ || $err == ERROR_WANT_WRITE, 'CLIENT: SSL handshake'); + ok($reneg_num < Net::SSLeay::num_renegotiations($ssl), 'CLIENT: Increased number of negotiations'); + } + + $heap->{server}->put('ping2'); + } + + elsif ($line eq 'pong2') + { + ok(1, "CLIENT: recv: $line"); + $kernel->yield('shutdown'); + } + }, +); + +$poe_kernel->run(); +exit 0;