IO-Async-SSL
view release on metacpan or search on metacpan
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
},
* 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
"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"
}
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'
},
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;
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,
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 )