diff --git a/Changes b/Changes index 909b0a4..2348909 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,10 @@ We now load certificate files via CTX_use_certificate_chain_file(), thanks Zephaniah E. Loss-Cutler-Hull OpenSSL docs suggest it - http://www.openssl.org/docs/ssl/SSL_CTX_use_certificate.html#NOTES PLEASE yell at me if you need the old functionality - the docs suggest this is the "better" way to do it... + + Add the ability to pass a subref to call on connection/negotiation success, thanks Zephaniah E. Loss-Cutler-Hull + NOTE: This will not work if you do a renegotiation or any other zany SSL stuff! + Add the SSLify_GetStatus function to get the status of the connection 1.003 Released: 2011-02-28 15:52:24 UTC diff --git a/lib/POE/Component/SSLify/ClientHandle.pm b/lib/POE/Component/SSLify/ClientHandle.pm index 04165ab..f762748 100644 --- a/lib/POE/Component/SSLify/ClientHandle.pm +++ b/lib/POE/Component/SSLify/ClientHandle.pm @@ -29,7 +29,7 @@ # 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 $res = Net::SSLeay::connect( $ssl ) or die_if_ssl_error( 'ssl connect' ); -#warn "Net::SSLeay::connect(TIEHANDLE) -> $res"; + my $self = bless { 'ssl' => $ssl, 'ctx' => $ctx, diff --git a/lib/POE/Component/SSLify/ServerHandle.pm b/lib/POE/Component/SSLify/ServerHandle.pm index db10e89..463a54b 100644 --- a/lib/POE/Component/SSLify/ServerHandle.pm +++ b/lib/POE/Component/SSLify/ServerHandle.pm @@ -20,7 +20,7 @@ # 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 $res = Net::SSLeay::accept( $ssl ) and die_if_ssl_error( 'ssl accept' ); -#warn "Net::SSLeay::accept(TIEHANDLE) -> $res"; + my $self = bless { 'ssl' => $ssl, 'ctx' => $ctx, @@ -35,29 +35,20 @@ sub _check_status { my $self = shift; - my $method = shift; # Okay, is negotiation done? # http://www.openssl.org/docs/ssl/SSL_connect.html#RETURN_VALUES - if ( $self->{'status'} == -1 ) { - # client or server? - my $res; - if ( exists $self->{'client'} ) { - $res = Net::SSLeay::connect( $self->{'ssl'} ); -# warn "Net::SSLeay::connect($method) -> $res"; - } else { - $res = Net::SSLeay::accept( $self->{'ssl'} ); -# warn "Net::SSLeay::accept($method) -> $res"; - } - - if ( $res == 0 ) { - # TODO error? - } elsif ( $res == 1 ) { - $self->{'status'} = 1; - - # call the hook function for successful connect - $self->{'on_connect'}->( $self ) if defined $self->{'on_connect'}; - } + if ( exists $self->{'client'} ) { + $self->{'status'} = Net::SSLeay::connect( $self->{'ssl'} ); + } else { + $self->{'status'} = Net::SSLeay::accept( $self->{'ssl'} ); + } + + if ( $self->{'status'} == 0 ) { + # TODO error? + } elsif ( $self->{'status'} == 1 ) { + # call the hook function for successful connect + $self->{'on_connect'}->( $self->{'orig_socket'} ) if defined $self->{'on_connect'}; } } @@ -70,7 +61,7 @@ my( $buf, $len, $offset ) = \( @_ ); # Check connection status - $self->_check_status( 'READ' ); + $self->_check_status if $self->{'status'} == -1; # If we have no offset, replace the buffer with some input if ( ! defined $$offset ) { @@ -110,7 +101,7 @@ my( $self, $buf, $len, $offset ) = @_; # Check connection status - $self->_check_status( 'WRITE' ); + $self->_check_status if $self->{'status'} == -1; # If we have nothing to offset, then start from the beginning if ( ! defined $offset ) { diff --git a/lib/POE/Component/SSLify.pm b/lib/POE/Component/SSLify.pm index fead931..0de98d4 100644 --- a/lib/POE/Component/SSLify.pm +++ b/lib/POE/Component/SSLify.pm @@ -30,7 +30,10 @@ # Do the exporting magic... require Exporter; our @ISA = qw( Exporter ); -our @EXPORT_OK = qw( Client_SSLify Server_SSLify SSLify_Options SSLify_GetCTX SSLify_GetCipher SSLify_GetSocket SSLify_GetSSL SSLify_ContextCreate ); +our @EXPORT_OK = qw( + Client_SSLify Server_SSLify + SSLify_Options SSLify_GetCTX SSLify_GetCipher SSLify_GetSocket SSLify_GetSSL SSLify_ContextCreate SSLify_GetStatus +); # Bring in some socket-related stuff use Symbol qw( gensym ); @@ -38,6 +41,10 @@ # we need IO 1.24 for it's win32 fixes but it includes IO::Handle 1.27_02 which is dev... # unfortunately we have to jump to IO 1.25 which includes IO::Handle 1.28... argh! use IO::Handle 1.28; + +# Use Scalar::Util's weaken() for the connref stuff +use Scalar::Util qw( weaken ); +use Task::Weaken 1.03; # to make sure it actually works! # The server-side CTX stuff my $ctx = undef; @@ -54,7 +61,7 @@ $socket = Client_SSLify( $socket ); # the default $socket = Client_SSLify( $socket, $version, $options ); # sets more options for the context $socket = Client_SSLify( $socket, undef, undef, $ctx ); # pass in a custom context - $socket = Client_SSLify( $socket, sub { print "CONNECTED" } ); # call your connection function + $socket = Client_SSLify( $socket, sub { warn "CONNECTED" } ); # call your connection function If $ctx is defined, SSLify will ignore other args. If $ctx isn't defined, SSLify will create it from the $version + $options parameters. @@ -115,6 +122,12 @@ my $newsock = gensym(); tie( *$newsock, 'POE::Component::SSLify::ClientHandle', $socket, $version, $options, $ctx, $connref ) or die "Unable to tie to our subclass: $!"; + # argh, store the newsock in the tied class to use for connref + if ( defined $connref ) { + tied( *$newsock )->{'orig_socket'} = $newsock; + weaken( tied( *$newsock )->{'orig_socket'} ); + } + # All done! return $newsock; } @@ -125,9 +138,9 @@ to call when connection/negotiation is done. my $socket = shift; # get the socket from somewhere - $socket = Server_SSLify( $socket ); + $socket = Server_SSLify( $socket ); # the default $socket = Server_SSLify( $socket, $ctx ); # use your custom context - $socket = Server_SSLify( $socket, sub { print "CONNECTED" } ); # call your connection function + $socket = Server_SSLify( $socket, sub { warn "CONNECTED" } ); # call your connection function NOTE: SSLify_Options must be set first! @@ -177,6 +190,12 @@ # Now, we create the new socket and bind it to our subclass of Net::SSLeay::Handle my $newsock = gensym(); tie( *$newsock, 'POE::Component::SSLify::ServerHandle', $socket, ( $custom_ctx || $ctx ), $connref ) or die "Unable to tie to our subclass: $!"; + + # argh, store the newsock in the tied class to use for connref + if ( defined $connref ) { + tied( *$newsock )->{'orig_socket'} = $newsock; + weaken( tied( *$newsock )->{'orig_socket'} ); + } # All done! return $newsock; @@ -372,6 +391,20 @@ return tied( *$sock )->{'ssl'}; } +=func SSLify_GetStatus + + Returns the status of the SSL negotiation/handshake/connection. + + -1 == still in negotiation stage + 0 == internal SSL error, connection will be dead + 1 == negotiation successful +=cut + +sub SSLify_GetStatus { + my $sock = shift; + return tied( *$sock )->{'status'}; +} + 1; =pod diff --git a/t/4_hooks.t b/t/4_hooks.t deleted file mode 100644 index 429e544..0000000 --- a/t/4_hooks.t +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/perl - -# Thanks to ASCENT for this test! - -use strict; use warnings; - -my $numtests; -BEGIN { - $numtests = 14; - -# disabled for testing now... -# eval "use Test::NoWarnings"; -# if ( ! $@ ) { -# # increment by one -# $numtests++; -# -# } -} - -use Test::More tests => $numtests; - -use POE 1.267; -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 SSLify_GetSocket SSLify_GetSSL/; - -# 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], sub { - pass( "Got connect hook for server" ); - } ) }; - ok(!$@, "SERVER: Server_SSLify $@"); - ok(1, 'SERVER: SSLify_GetCipher: '. SSLify_GetCipher($socket)); - - # We pray that IO::Handle is sane... - ok( SSLify_GetSocket( $socket )->blocking == 0, 'SERVER: SSLified socket is non-blocking?'); - - return ($socket); - }, - ClientInput => sub - { - my ($kernel, $heap, $line) = @_[KERNEL, HEAP, ARG0]; - - die "Unknown line from CLIENT: $line"; - }, - ClientError => sub - { - # Thanks to H. Merijn Brand for spotting this FAIL in 5.12.0! - # The default PoCo::Server::TCP handler will throw a warning, which causes Test::NoWarnings to FAIL :( - my ($syscall, $errno, $error) = @_[ ARG0..ARG2 ]; - - # TODO are there other "errors" that is harmless? - $error = "Normal disconnection" unless $error; - my $msg = "Got SERVER $syscall error $errno: $error"; - unless ( $syscall eq 'read' and $errno == 0 ) { - fail( $msg ); - } else { - diag( $msg ) if $ENV{TEST_VERBOSE}; - } - }, -); - -POE::Component::Client::TCP->new -( - Alias => 'myclient', - RemoteAddress => '127.0.0.1', - RemotePort => $port, - Connected => sub - { - ok(1, 'CLIENT: connected'); - }, - PreConnect => sub - { - my $ctx = eval { SSLify_ContextCreate(undef, undef, 'sslv3') }; - ok(!$@, "CLIENT: SSLify_ContextCreate $@"); - my $socket = eval { Client_SSLify($_[ARG0], undef, undef, $ctx, sub { - pass( "Got connect hook for client" ); - $poe_kernel->post( 'myclient' => 'shutdown' ); - }) }; - ok(!$@, "CLIENT: Client_SSLify $@"); - ok(1, 'CLIENT: SSLify_GetCipher: '. SSLify_GetCipher($socket)); - - # We pray that IO::Handle is sane... - ok( SSLify_GetSocket( $socket )->blocking == 0, 'CLIENT: SSLified socket is non-blocking?'); - - return ($socket); - }, - ServerInput => sub - { - my ($kernel, $heap, $line) = @_[KERNEL, HEAP, ARG0]; - - $kernel->yield('shutdown'); - }, - ServerError => sub - { - # Thanks to H. Merijn Brand for spotting this FAIL in 5.12.0! - # The default PoCo::Client::TCP handler will throw a warning, which causes Test::NoWarnings to FAIL :( - my ($syscall, $errno, $error) = @_[ ARG0..ARG2 ]; - - # TODO are there other "errors" that is harmless? - $error = "Normal disconnection" unless $error; - my $msg = "Got CLIENT $syscall error $errno: $error"; - unless ( $syscall eq 'read' and $errno == 0 ) { - fail( $msg ); - } else { - diag( $msg ) if $ENV{TEST_VERBOSE}; - } - }, -); - -$poe_kernel->run(); - -pass( 'shut down sanely' ); - -exit 0;