package IO::Stream::MatrixSSL::Client;
use 5.010001;
use warnings;
use strict;
use utf8;
use Carp;
our $VERSION = 'v2.0.2';
use IO::Stream::const;
use IO::Stream::MatrixSSL::const;
use Crypt::MatrixSSL3 qw( :all );
use Scalar::Util qw( weaken );
use parent qw( -norequire IO::Stream::MatrixSSL );
sub new {
my ($class, $opt) = @_;
my $self = bless {
crt => undef, # filename(s) with client's certificate(s)
key => undef, # filename with client's private key
pass => undef, # password to decrypt private key
trusted_CA => $Crypt::MatrixSSL3::CA_CERTIFICATES, # filename(s) with trusted root CA cert(s)
cb => undef, # callback for validating certificate
%{$opt // {}},
out_buf => q{}, # modified on: OUT
out_pos => undef, # modified on: OUT
out_bytes => 0, # modified on: OUT
in_buf => q{}, # modified on: IN
in_bytes => 0, # modified on: IN
ip => undef, # modified on: RESOLVED
is_eof => undef, # modified on: EOF
# TODO Make this field public and add feature 'restore session'.
# _ssl_session=> undef, # MatrixSSL 'sessionId' object
_ssl => undef, # MatrixSSL 'session' object
_ssl_keys => undef, # MatrixSSL 'keys' object
_handshaked => 0, # flag, will be true after handshake
_want_write => 0, # flag, will be true if write() was called before handshake
_want_close => 0, # flag, will be true after generating MATRIXSSL_REQUEST_CLOSE
_closed => 0, # flag, will be true after sending MATRIXSSL_REQUEST_CLOSE
_t => undef,
_cb_t => undef,
}, $class;
weaken(my $this = $self);
$self->{_cb_t} = sub { $this && $this->T() };
my $cb = !$self->{cb} ? undef : sub {
$this ? $this->{cb}->($this, @_) : CERTVALIDATOR_INTERNAL_ERROR
};
# Initialize SSL.
# TODO OPTIMIZATION Cache {_ssl_keys}.
$self->{_ssl_keys} = Crypt::MatrixSSL3::Keys->new();
my $rc = $self->{_ssl_keys}->load_rsa(
$self->{crt}, $self->{key}, $self->{pass}, $self->{trusted_CA}
);
croak 'ssl error: '.get_ssl_error($rc) if $rc != PS_SUCCESS;
# TODO OPTIMIZATION Use {_ssl_session}.
# TODO Add feature: let user provide @cipherSuites.
# TODO Add feature: let user provide $expectedName.
$self->{_ssl} = Crypt::MatrixSSL3::Client->new(
$self->{_ssl_keys}, undef, undef, $cb, undef, undef, undef
);
$rc = $self->{_ssl}->sent_data( $self->{_ssl}->get_outdata($self->{out_buf}) );
croak 'ssl error: '.get_ssl_error($rc) if $rc != PS_SUCCESS;
return $self;
}
sub PREPARE {
my ($self, $fh, $host, $port) = @_;
if (!defined $host) { # ... else timer will be set on CONNECTED
$self->{_t} = EV::timer(TOHANDSHAKE, 0, $self->{_cb_t});
}
$self->{_slave}->PREPARE($fh, $host, $port);
$self->{_slave}->WRITE(); # output 'client hello'
return;
}
1;