albus123 has asked for the wisdom of the Perl Monks concerning the following question:

I have recently started using SSL with LWP and it works great! I am currently going through the code to get a basic understanding of where (and how) the SSL handshake happens. After enabling debugs in SSL.pm, I figured that connect_SSL() is the relevant function call and the SSL handshake seems to be 'complete' after the following code snippet:

for my $dummy (1) { #DEBUG( 'calling ssleay::connect' ); my $rv = Net::SSLeay::connect($ssl); $DEBUG>=3 && DEBUG("Net::SSLeay::connect -> $rv" ); if ( $rv < 0 ) { unless ( $self->_set_rw_error( $ssl,$rv )) { $self->error("SSL connect attempt failed with unknown +error"); delete ${*$self}{'_SSL_opening'}; ${*$self}{'_SSL_opened'} = -1; $DEBUG>=1 && DEBUG( "fatal SSL error: $SSL_ERROR" ); return $self->fatal_ssl_error(); } $DEBUG>=2 && DEBUG('ssl handshake in progress' ); # connect failed because handshake needs to be completed # if socket was non-blocking or no timeout was given retur +n with this error return if ! defined($timeout); # wait until socket is readable or writable my $rv; if ( $timeout>0 ) { my $vec = ''; vec($vec,$self->fileno,1) = 1; $DEBUG>=2 && DEBUG( "waiting for fd to become ready: $ +SSL_ERROR" ); $rv = $SSL_ERROR == SSL_WANT_READ ? select( $vec,undef,u +ndef,$timeout) : $SSL_ERROR == SSL_WANT_WRITE ? select( undef,$vec, +undef,$timeout) : undef; } else { $DEBUG>=2 && DEBUG("handshake failed because no more t +ime" ); $! = ETIMEDOUT } if ( ! $rv ) { $DEBUG>=2 && DEBUG("handshake failed because socket di +d not became ready" ); # failed because of timeout, return $! ||= ETIMEDOUT; delete ${*$self}{'_SSL_opening'}; ${*$self}{'_SSL_opened'} = -1; $self->blocking(1); # was blocking before return } # socket is ready, try non-blocking connect again after re +computing timeout $DEBUG>=2 && DEBUG("socket ready, retrying connect" ); my $now = time(); $timeout -= $now - $start; $start = $now; redo; } elsif ( $rv == 0 ) { delete ${*$self}{'_SSL_opening'}; $DEBUG>=2 && DEBUG("connection failed - connect returned 0 +" ); $self->error("SSL connect attempt failed because of handsh +ake problems" ); ${*$self}{'_SSL_opened'} = -1; return $self->fatal_ssl_error(); } }

I can see the cipher list being set before this 'for' loop. But I do not understand the code in this loop. Why does SSLeay::connect() fail and why is a READ performed on the socket? From debugs, I could see that this loop is executing more than once. After the READ is successful, there is a comment 'socket is ready, try non-blocking connect again after recomputing timeout'. Does this mean that there is some data on the socket (client/server cipher list?) and will be 'processed'? This example here http://search.cpan.org/~sampo/Net_SSLeay.pm-1.25/SSLeay.pm calls this function only once, which doesn't clarify things. Can someone please help me understand this.

IO::Socket::SSL version: 1.84

Net::SSLeay version: 1.21

Thanks & Regards,

Replies are listed 'Best First'.
Re: Query regarding connect_SSL (IO::Socket::SSL.pm)
by Athanasius (Archbishop) on Aug 08, 2015 at 09:46 UTC

    Hello albus123,

    According to the comments within the loop itself, the call to Net::SSLeay::connect may fail because:

    1. the connection failed or an unknown error occurred (in which case: the loop exits via return $self->fatal_ssl_error();)
    2. the handshake is incomplete and either the socket is non-blocking or no timeout was specified (in which case: the loop exits via return; which returns either the empty list, undef, or nothing, depending on the context in which the subroutine was called)
    3. the handshake timed out (result as in 2)
    4. the socket was not ready, either for reading or writing (in which case the timeout value is adjusted and the loop repeats via redo)

    There are both read and write operations performed on the socket because the SSL handshake involves a back-and-forth exchange between the client and the server. See Transport_Layer_Security#TLS_handshake.

    See redo for the loop control. I have no idea what the statements containing ${*$self} are supposed to do :-( but they are only a part of the error-handling mechanism.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks for the detailed response. That clarified my questions. Sincere apologies for the delayed response.