IO-Async-SSL

 view release on metacpan or  search on metacpan

Build.PL  view on Meta::CPAN

my $build = Module::Build->new(
   module_name => 'IO::Async::SSL',
   requires => {
      'perl' => '5.014',

      'Future' => '0.33',
      'IO::Async::Loop' => '0.66', # Listener bugfixes, on_accept_error semantics
      'IO::Async::Handle' => '0.29',
      'IO::Async::Protocol::Stream' => 0,
      'IO::Async::Stream' => '0.59',
      'IO::Socket::SSL' => '2.003',
   },
   test_requires => {
      'IO::Async::OS' => 0,
      'IO::Async::Test' => '0.68',
      'Test::Identity' => 0,
      'Test::More' => '0.88', # done_testing
   },
   configure_requires => {
      'Module::Build' => '0.4004', # test_requires
   },

Changes  view on Meta::CPAN

         * Spelling fix 'querying' (RT142917)

0.23    2022-05-23
        [BUGFIXES]
         * Force IPv4 in openssl s_client
         * Declare dependency on Test::Identity
         * Pick port dynamically in t/20stream.t

0.22    2018-06-06 12:01:54
        [BUGFIXES]
         * Increase IO::Socket::SSL version dependency to 2.003 due to
           important bugfixes (RT#125220)

0.21    2017-11-01 11:46:00
        [BUGFIXES]
         * Detect early failures of IO::Socket::SSL->SSL_start such as bad
           arguments

0.20    2017/07/11 12:49:13
        [BUGFIXES]
         * Minor fixes to openssl-running unit tests
         * Provide a dhparam.pem override file for testing with socat, as some
           versions object to the default one (thanks ilmari)
         * Rebuild SSL certs to 2048 bits

0.19    2015/10/15 16:44:06
        [BUGFIXES]
         * Catch ->start_SSL throwing errors, rather than expecting it to
           return undef
         * Swallow SSL errors if 'on_ssl_error' handles them

0.18    2015/06/29 23:52:36
        [BUGFIXES]
         * Apply utf8::downgrade() inplace to strings before we ->syswrite
           them, ensuring that IO::Socket::SSL doesn't double-encode UTF-8
           (related to RT98372).

0.17    2015/05/29 19:52:02
        [BUGFIXES]
         * Tell openssl s_server to be -quiet so its verbose junk doesn't
           upset the test script (RT104757)
         * No need to even unpack $buf from @_ in sslwrite when it's not
           passed to the underlying _syswrite (RT103774)

0.16    2015/04/17 20:52:35
        [BUGFIXES]
         * Declare required version of IO::Socket::SSL to get default_ca()
         * Explicitly set $! to EINVAL when unit-test mocking methods fail
         * Declare requirement on IO::Async 0.66 for various bugfixes

0.15    2015/04/04 14:52:23
        [CHANGES]
         * Use IO::Async::Stream reader/writer functions instead of SSLStream
           subclass
         * Use hints provided by IO::Socket::SSL::default_ca (RT96474)

        [BUGFIXES]
         * Remember about EWOULDBLOCK on MSWin32
         * Handle EAGAIN from acceptor (RT102403)
         * Don't invoke on_ssl_error handler for cleartext acceptor failures
           (RT102405)

0.14    2014/03/27 11:24:08
        [BUGFIXES]
         * Avoid relying on strong forward references in Future, by creating

META.json  view on Meta::CPAN

            "Module::Build" : "0.4004"
         }
      },
      "runtime" : {
         "requires" : {
            "Future" : "0.33",
            "IO::Async::Handle" : "0.29",
            "IO::Async::Loop" : "0.66",
            "IO::Async::Protocol::Stream" : "0",
            "IO::Async::Stream" : "0.59",
            "IO::Socket::SSL" : "2.003",
            "perl" : "5.014"
         }
      },
      "test" : {
         "requires" : {
            "IO::Async::OS" : "0",
            "IO::Async::Test" : "0.68",
            "Test::Identity" : "0",
            "Test::More" : "0.88"
         }

META.yml  view on Meta::CPAN

    version: '0.25'
  IO::Async::SSLStream:
    file: lib/IO/Async/SSLStream.pm
    version: '0.25'
requires:
  Future: '0.33'
  IO::Async::Handle: '0.29'
  IO::Async::Loop: '0.66'
  IO::Async::Protocol::Stream: '0'
  IO::Async::Stream: '0.59'
  IO::Socket::SSL: '2.003'
  perl: '5.014'
resources:
  license: http://dev.perl.org/licenses/
version: '0.25'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

README  view on Meta::CPAN

        },
    
        on_resolve_error => sub { print STDERR "Cannot resolve - $_[0]\n"; },
        on_connect_error => sub { print STDERR "Cannot connect\n"; },
        on_ssl_error     => sub { print STDERR "Cannot negotiate SSL - $_[-1]\n"; },
     );

DESCRIPTION

    This module extends existing IO::Async classes with extra methods to
    allow the use of SSL or TLS-based connections using IO::Socket::SSL. It
    does not directly provide any methods or functions of its own.

    Primarily, it provides SSL_connect and SSL_listen, which yield
    IO::Socket::SSL-upgraded socket handles or IO::Async::Stream instances,
    and two forms of SSL_upgrade to upgrade an existing TCP connection to
    use SSL.

    As an additional convenience, if the SSL_verify_mode and SSL_ca_*
    options are omitted, the module will attempt to provide them by
    querying the result of IO::Socket::SSL's default_ca function.
    Otherwise, the module will print a warning and set SSL_VERIFY_NONE
    instead.

LOOP METHODS

    The following extra methods are added to IO::Async::Loop.

 SSL_upgrade

       ( $stream or $socket ) = $loop->SSL_upgrade( %params )->get;

README  view on Meta::CPAN

      result from the future.

      If a plain socket handle is passed, that will be returned from the
      future instead.

    SSL_server => BOOL

      If true, indicates this is the server side of the connection.

    In addition, any parameter whose name starts SSL_ will be passed to the
    IO::Socket::SSL constructor.

    The following legacy callback arguments are also supported, in case the
    returned future is not used:

    on_upgraded => CODE

      A continuation that is invoked when the socket has been successfully
      upgraded to SSL. It will be passed an instance of an IO::Socket::SSL,
      which will have appropriate SSL-compatible reader/writer functions
      attached.

       $on_upgraded->( $sslsocket )

    on_error => CODE

      A continuation that is invoked if IO::Socket::SSL detects an error
      while negotiating the upgrade.

       $on_error->( $! )

 SSL_connect

       $stream = $loop->SSL_connect( %params )->get;

    This method performs a non-blocking connection to a given address or
    set of addresses, upgrades the socket to SSL, then yields a
    IO::Async::Stream object when the SSL handshake is complete.

    It takes all the same arguments as IO::Async::Loop::connect(). Any
    argument whose name starts SSL_ will be passed on to the
    IO::Socket::SSL constructor rather than the Loop's connect method. It
    is not required to pass the socktype option, as SSL implies this will
    be stream.

    This method can also upgrade an existing IO::Async::Stream or subclass
    instance given as the handle argument, by setting the reader and writer
    functions.

 SSL_connect (void)

       $loop->SSL_connect( %params,

README  view on Meta::CPAN

          on_stream => sub { ... },
       );

    When not returning a future, this method also supports the on_connected
    and on_stream continuations.

    In addition, the following arguments are then required:

    on_ssl_error => CODE

      A continuation that is invoked if IO::Socket::SSL detects an
      SSL-based error once the actual stream socket is connected.

    If the on_connected continuation is used, the socket handle it yields
    will be a IO::Socket::SSL, which must be wrapped in
    IO::Async::SSLStream to be used by IO::Async. The on_stream
    continuation will already yield such an instance.

 SSL_listen

       $loop->SSL_listen( %params )->get;

    This method sets up a listening socket using the addresses given, and
    will invoke the callback each time a new connection is accepted on the
    socket and the SSL handshake has been completed. This can be either the
    on_accept or on_stream continuation; on_socket is not supported.

    It takes all the same arguments as IO::Async::Loop::listen(). Any
    argument whose name starts SSL_ will be passed on to the
    IO::Socket::SSL constructor rather than the Loop's listen method. It is
    not required to pass the socktype option, as SSL implies this will be
    stream.

    In addition, the following arguments are rquired:

    on_ssl_error => CODE

      A continuation that is invoked if IO::Socket::SSL detects an
      SSL-based error once the actual stream socket is connected.

    The underlying IO::Socket::SSL socket will also require the server key
    and certificate for a server-mode socket. See its documentation for
    more details.

    If the on_accept continuation is used, the socket handle it yields will
    be a IO::Socket::SSL, which must be wrapped in IO::Async::SSLStream to
    be used by IO::Async. The on_stream continuation will already yield
    such an instance.

STREAM PROTOCOL METHODS

    The following extra methods are added to IO::Async::Protocol::Stream.

 SSL_upgrade

       $protocol->SSL_upgrade( %params )->get;

lib/IO/Async/SSL.pm  view on Meta::CPAN


package IO::Async::SSL 0.25;

use v5.14;
use warnings;

use Carp;

use POSIX qw( EAGAIN EWOULDBLOCK );

use IO::Socket::SSL 2.003 qw( $SSL_ERROR SSL_WANT_READ SSL_WANT_WRITE ); # default_ca
   # require >= 2.003 for bugfixes - see RT#125220

use Future 0.33; # ->catch_with_f
use IO::Async::Handle 0.29;
use IO::Async::Loop 0.61; # new Listen API

=head1 NAME

C<IO::Async::SSL> - use SSL/TLS with L<IO::Async>

lib/IO/Async/SSL.pm  view on Meta::CPAN

    },

    on_resolve_error => sub { print STDERR "Cannot resolve - $_[0]\n"; },
    on_connect_error => sub { print STDERR "Cannot connect\n"; },
    on_ssl_error     => sub { print STDERR "Cannot negotiate SSL - $_[-1]\n"; },
 );

=head1 DESCRIPTION

This module extends existing L<IO::Async> classes with extra methods to allow
the use of SSL or TLS-based connections using L<IO::Socket::SSL>. It does not
directly provide any methods or functions of its own.

Primarily, it provides C<SSL_connect> and C<SSL_listen>, which yield
C<IO::Socket::SSL>-upgraded socket handles or L<IO::Async::Stream>
instances, and two forms of C<SSL_upgrade> to upgrade an existing TCP
connection to use SSL.

As an additional convenience, if the C<SSL_verify_mode> and C<SSL_ca_*>
options are omitted, the module will attempt to provide them by querying the
result of L<IO::Socket::SSL>'s C<default_ca> function. Otherwise, the module
will print a warning and set C<SSL_VERIFY_NONE> instead.

=cut

my %SSL_ca_args = IO::Socket::SSL::default_ca();

sub _SSL_args
{
   my %args = @_;

   # SSL clients (i.e. non-server) require a verify mode
   if( !$args{SSL_server} and !defined $args{SSL_verify_mode} and
       !defined $args{SSL_ca_file} and !defined $args{SSL_ca_path} ) {
      unless( %SSL_ca_args ) {
         carp "Unable to set SSL_VERIFY_PEER because IO::Socket::SSL::default_ca() gives nothing";
         $SSL_ca_args{SSL_verify_mode} = IO::Socket::SSL::SSL_VERIFY_NONE();
      }

      %args = ( %SSL_ca_args, %args );
   }

   return %args;
}

sub sslread
{

lib/IO/Async/SSL.pm  view on Meta::CPAN

If a plain socket handle is passed, that will be returned from the future
instead.

=item SSL_server => BOOL

If true, indicates this is the server side of the connection.

=back

In addition, any parameter whose name starts C<SSL_> will be passed to the
C<IO::Socket::SSL> constructor.

The following legacy callback arguments are also supported, in case the
returned future is not used:

=over 8

=item on_upgraded => CODE

A continuation that is invoked when the socket has been successfully upgraded
to SSL. It will be passed an instance of an C<IO::Socket::SSL>, which will
have appropriate SSL-compatible reader/writer functions attached.

 $on_upgraded->( $sslsocket )

=item on_error => CODE

A continuation that is invoked if C<IO::Socket::SSL> detects an error while
negotiating the upgrade.

 $on_error->( $! )

=back

=cut

sub IO::Async::Loop::SSL_upgrade
{

lib/IO/Async/SSL.pm  view on Meta::CPAN

      my $on_error    = delete $params{on_error}    or defined wantarray
         or croak "Expected 'on_error' or to return a Future";

      $f->on_done( $on_upgraded ) if $on_upgraded;
      $f->on_fail( $on_error    ) if $on_error;
   }

   my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;

   eval {
      $socket = IO::Socket::SSL->start_SSL( $socket, _SSL_args
         SSL_startHandshake => 0,

         # Required to make IO::Socket::SSL not ->close before we have a chance to remove it from the loop
         SSL_error_trap => sub { },

         %ssl_params,
      ) or die IO::Socket::SSL->errstr;
   } or do {
      chomp( my $e = $@ );
      return $f->fail( $e, "ssl" );
   };

   my $ready_method = $ssl_params{SSL_server} ? "accept_SSL" : "connect_SSL";

   my $ready = sub {
      my ( $self ) = @_;
      if( $socket->$ready_method ) {

lib/IO/Async/SSL.pm  view on Meta::CPAN

               reader => \&sslread,
               writer => \&sslwrite,
            );
         }

         $f->done( $stream || $socket );
         return;
      }

      if( $! != EAGAIN and $! != EWOULDBLOCK ) {
         my $errstr = IO::Socket::SSL::errstr();
         $loop->remove( $self );
         $f->fail( $errstr, "ssl" );
         return;
      }

      $self->want_readready ( $SSL_ERROR == SSL_WANT_READ );
      $self->want_writeready( $SSL_ERROR == SSL_WANT_WRITE );
   };

   # We're going to steal the IO handle from $stream, so we'll have to

lib/IO/Async/SSL.pm  view on Meta::CPAN


=head2 SSL_connect

   $stream = $loop->SSL_connect( %params )->get;

This method performs a non-blocking connection to a given address or set of
addresses, upgrades the socket to SSL, then yields a C<IO::Async::Stream>
object when the SSL handshake is complete.

It takes all the same arguments as C<IO::Async::Loop::connect()>. Any argument
whose name starts C<SSL_> will be passed on to the L<IO::Socket::SSL>
constructor rather than the Loop's C<connect> method. It is not required to
pass the C<socktype> option, as SSL implies this will be C<stream>.

This method can also upgrade an existing C<IO::Async::Stream> or subclass
instance given as the C<handle> argument, by setting the C<reader> and
C<writer> functions.

=head2 SSL_connect (void)

   $loop->SSL_connect( %params,

lib/IO/Async/SSL.pm  view on Meta::CPAN


When not returning a future, this method also supports the C<on_connected> and
C<on_stream> continuations.

In addition, the following arguments are then required:

=over 8

=item on_ssl_error => CODE

A continuation that is invoked if C<IO::Socket::SSL> detects an SSL-based
error once the actual stream socket is connected.

=back

If the C<on_connected> continuation is used, the socket handle it yields will
be a C<IO::Socket::SSL>, which must be wrapped in C<IO::Async::SSLStream> to
be used by C<IO::Async>. The C<on_stream> continuation will already yield such
an instance.

=cut

sub IO::Async::Loop::SSL_connect
{
   my $loop = shift;
   my %params = @_;

lib/IO/Async/SSL.pm  view on Meta::CPAN

=head2 SSL_listen

   $loop->SSL_listen( %params )->get;

This method sets up a listening socket using the addresses given, and will
invoke the callback each time a new connection is accepted on the socket and
the SSL handshake has been completed. This can be either the C<on_accept> or
C<on_stream> continuation; C<on_socket> is not supported.

It takes all the same arguments as C<IO::Async::Loop::listen()>. Any argument
whose name starts C<SSL_> will be passed on to the L<IO::Socket::SSL>
constructor rather than the Loop's C<listen> method. It is not required to
pass the C<socktype> option, as SSL implies this will be C<stream>.

In addition, the following arguments are rquired:

=over 8

=item on_ssl_error => CODE

A continuation that is invoked if C<IO::Socket::SSL> detects an SSL-based
error once the actual stream socket is connected.

=back

The underlying L<IO::Socket::SSL> socket will also require the server key and
certificate for a server-mode socket. See its documentation for more details.

If the C<on_accept> continuation is used, the socket handle it yields will be
a C<IO::Socket::SSL>, which must be wrapped in C<IO::Async::SSLStream> to be
used by C<IO::Async>. The C<on_stream> continuation will already yield such an
instance.

=cut

sub IO::Async::Loop::SSL_listen
{
   my $loop = shift;
   my %params = @_;

t/04readwrite.t  view on Meta::CPAN

testing_loop( $loop );

my $listen_sock = IO::Socket::INET->new(
   LocalHost => "localhost",
   LocalPort => 0,
   Listen => 1,
) or die "Cannot listen - $@";

# Mass cheating here
no warnings 'redefine';
*IO::Socket::SSL::connect_SSL = sub {
   return 1;
};

my $f = $loop->SSL_connect(
   addr => { family => "inet", ip => $listen_sock->sockhost, port => $listen_sock->sockport },
);

wait_for { $f->is_ready };

my $stream = $f->get;

t/04readwrite.t  view on Meta::CPAN


# A micro mocking framework
{
   my @EXPECT;
   sub expect
   {
      my ( $method, $args, $result, $return ) = @_;
      push @EXPECT, [ $method, $args, $result, $return ];
   }

   *IO::Socket::SSL::sysread = sub {
      my ( $fh, undef, $len, $offset ) = @_;
      @EXPECT or
         fail( "Expected no more calls, got sysread" ), $! = Errno::EINVAL, return undef;

      my $e = shift @EXPECT;
      $e->[0] eq "sysread" or
         fail( "Expected $e->[0], got sysread" ), $! = Errno::EINVAL, return undef;

      pass( "Got sysread" );

      if( $e->[2] eq "return" ) {
         $_[1] = $e->[3];
         return length $e->[3];
      }
      elsif( $e->[2] eq "err" ) {
         $! = Errno::EAGAIN;
         $IO::Socket::SSL::SSL_ERROR = $e->[3];
         return undef;
      }
   };

   *IO::Socket::SSL::syswrite = sub {
      my ( $fh, $buff, $len ) = @_;
      @EXPECT or
         fail( "Expected no more calls, got syswrite" ), $! = Errno::EINVAL, return undef;

      my $e = shift @EXPECT;
      $e->[0] eq "syswrite" or
         fail( "Expected $e->[0], got syswrite" ), $! = Errno::EINVAL, return undef;

      pass( "Got syswrite" );

      is( $e->[1][0], $buff, 'Data for syswrite' );

      if( $e->[2] eq "return" ) {
         return $len;
      }
      elsif( $e->[2] eq "err" ) {
         $! = Errno::EAGAIN;
         $IO::Socket::SSL::SSL_ERROR = $e->[3];
         return undef;
      }
   };
}

# read-wants-read
{
   # Make serversock readready
   $server_sock->syswrite( "1" );

t/04readwrite.t  view on Meta::CPAN


   $read = "";
   CORE::sysread( $stream->read_handle, my $dummy, 8192 );
}

# read-wants-write
{
   # Make serversock readready
   $server_sock->syswrite( "2" );

   expect sysread => [], err => IO::Socket::SSL::SSL_WANT_WRITE;

   wait_for { $stream->want_writeready };

   pass( '$stream->want_writeready' );
   CORE::sysread( $stream->read_handle, my $dummy, 8192 );

   expect sysread => [], return => "late data";

   wait_for { length $read };

t/04readwrite.t  view on Meta::CPAN

}

# write-wants-read
{
   my $flushed;
   $stream->write( "late out data", on_flush => sub { $flushed++ } );

   # more cheating
   $stream->want_readready( 0 );

   expect syswrite => [ "late out data" ], err => IO::Socket::SSL::SSL_WANT_READ;

   wait_for { $stream->want_readready };

   pass( '$stream->want_readready' );

   expect sysread  => [], err => 0;
   expect syswrite => [ "late out data" ], return =>;

   $server_sock->syswrite( "4" );



( run in 0.434 second using v1.01-cache-2.11-cpan-fd5d4e115d8 )