# HG changeset patch # User Maxim Dounin # Date 1684422432 -10800 # Node ID dbb7561a9441ba7387993d1b3ebe52516eb2c38b # Parent 7681a970f6bd1b21d9735052801f9e24f143f74d Tests: reworked stream SSL tests to use IO::Socket::SSL. Relevant infrastructure is provided in Test::Nginx::Stream. This also ensures that SSL handshake and various read operations are guarded with timeouts. The stream_ssl_verify_client.t test uses IO::Socket::SSL::_get_ssl_object() to access the Net::SSLeay object directly, as it seems to be the only way to obtain CA list with IO::Socket::SSL. While not exactly correct, this seems to be good enough for tests. diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm --- a/lib/Test/Nginx/Stream.pm +++ b/lib/Test/Nginx/Stream.pm @@ -38,17 +38,38 @@ sub new { unshift(@_, "PeerAddr") if @_ == 1; - $self->{_socket} = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => '127.0.0.1', - @_ - ) - or die "Can't connect to nginx: $!\n"; + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + local $SIG{PIPE} = sub { die "sigpipe\n" }; + alarm(8); + + $self->{_socket} = IO::Socket::INET->new( + Proto => "tcp", + PeerAddr => '127.0.0.1', + @_ + ) + or die "Can't connect to nginx: $!\n"; - if ({@_}->{'SSL'}) { - require IO::Socket::SSL; - IO::Socket::SSL->start_SSL($self->{_socket}, @_) - or die $IO::Socket::SSL::SSL_ERROR . "\n"; + if ({@_}->{'SSL'}) { + require IO::Socket::SSL; + IO::Socket::SSL->start_SSL( + $self->{_socket}, + SSL_verify_mode => + IO::Socket::SSL::SSL_VERIFY_NONE(), + @_ + ) + or die $IO::Socket::SSL::SSL_ERROR . "\n"; + + my $s = $self->{_socket}; + log_in("ssl cipher: " . $s->get_cipher()); + log_in("ssl cert: " . $s->peer_certificate('issuer')); + } + + alarm(0); + }; + alarm(0); + if ($@) { + log_in("died: $@"); } $self->{_socket}->autoflush(1); @@ -56,6 +77,11 @@ sub new { return $self; } +sub DESTROY { + my $self = shift; + $self->{_socket}->close(); +} + sub write { my ($self, $message, %extra) = @_; my $s = $self->{_socket}; @@ -135,6 +161,11 @@ sub sockport { return $self->{_socket}->sockport(); } +sub socket { + my ($self) = @_; + $self->{_socket}; +} + ############################################################################### 1; diff --git a/stream_ssl.t b/stream_ssl.t --- a/stream_ssl.t +++ b/stream_ssl.t @@ -19,23 +19,17 @@ BEGIN { use FindBin; chdir($FindBin::Bin use lib 'lib'; use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; - plan(skip_all => 'win32') if $^O eq 'MSWin32'; -my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); +my $t = Test::Nginx->new()->has(qw/stream stream_ssl socket_ssl/) + ->has_daemon('openssl'); $t->plan(5)->write_file_expand('nginx.conf', <<'EOF'); @@ -110,8 +104,6 @@ foreach my $name ('localhost', 'inherits or die "Can't create certificate for $name: $!\n"; } -my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - $t->write_file('password', 'localhost'); $t->write_file('password_many', "wrong$CRLF" . "localhost$CRLF"); $t->write_file('password_stream', 'inherits'); @@ -132,38 +124,30 @@ kill 'INT', $p if $@; ############################################################################### -my ($s, $ssl); - -($s, $ssl) = get_ssl_socket(8443); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); - -($s, $ssl) = get_ssl_socket(8444); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); - -($s, $ssl) = get_ssl_socket(8445); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); +like(get(8443), qr/200 OK/, 'ssl'); +like(get(8444), qr/200 OK/, 'ssl password many'); +like(get(8445), qr/200 OK/, 'ssl password fifo'); # ssl_certificate inheritance -($s, $ssl) = get_ssl_socket(8443); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); - -($s, $ssl) = get_ssl_socket(8446); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); +like(cert(8443), qr/CN=localhost/, 'CN'); +like(cert(8446), qr/CN=inherits/, 'CN inner'); ############################################################################### -sub get_ssl_socket { - my ($port) = @_; +sub get { + my $s = get_socket(@_); + return $s->io("GET / HTTP/1.0$CRLF$CRLF"); +} - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); +sub cert { + my $s = get_socket(@_); + return $s->socket()->dump_peer_certificate(); +} + +sub get_socket { + my ($port) = @_; + return stream(PeerAddr => '127.0.0.1:' . port($port), SSL => 1); } ############################################################################### diff --git a/stream_ssl_certificate.t b/stream_ssl_certificate.t --- a/stream_ssl_certificate.t +++ b/stream_ssl_certificate.t @@ -16,29 +16,16 @@ BEGIN { use FindBin; chdir($FindBin::Bin use lib 'lib'; use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; - -eval { - my $ctx = Net::SSLeay::CTX_new() or die; - my $ssl = Net::SSLeay::new($ctx) or die; - Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die; -}; -plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@; - my $t = Test::Nginx->new() ->has(qw/stream stream_ssl stream_geo stream_return openssl:1.0.2/) + ->has(qw/socket_ssl_sni/) ->has_daemon('openssl') ->write_file_expand('nginx.conf', <<'EOF'); @@ -69,7 +56,7 @@ stream { server { listen 127.0.0.1:8080 ssl; - return $ssl_server_name:$ssl_session_reused; + return $ssl_server_name:$ssl_session_reused:$ssl_protocol; ssl_certificate $one.crt; ssl_certificate_key $one.key; @@ -154,59 +141,63 @@ like(get('password', 8083), qr/password/ # session reuse -my ($s, $ssl) = get('default', 8080); -my $ses = Net::SSLeay::get_session($ssl); +my $s = session('default', 8080); -like(get('default', 8080, $ses), qr/default:r/, 'session reused'); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); + +like(get('default', 8080, $s), qr/default:r/, 'session reused'); TODO: { # ticket key name mismatch prevents session resumption local $TODO = 'not yet' unless $t->has_version('1.23.2'); -like(get('default', 8081, $ses), qr/default:r/, 'session id context match'); +like(get('default', 8081, $s), qr/default:r/, 'session id context match'); } +} -like(get('default', 8082, $ses), qr/default:\./, 'session id context distinct'); +like(get('default', 8082, $s), qr/default:\./, 'session id context distinct'); # errors -Net::SSLeay::ERR_clear_error(); -get_ssl_socket('nx', 8084); -ok(Net::SSLeay::ERR_peek_error(), 'no certificate'); +ok(!get('nx', 8084), 'no certificate'); ############################################################################### sub get { - my ($host, $port, $ctx) = @_; - my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return; - - local $SIG{PIPE} = 'IGNORE'; - - my $r = Net::SSLeay::read($ssl); - Net::SSLeay::shutdown($ssl); - $s->close(); - return $r unless wantarray(); - return ($s, $ssl); + my $s = get_socket(@_) || return; + return $s->read(); } sub cert { - my ($host, $port, $ctx) = @_; - my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return; - Net::SSLeay::dump_peer_certificate($ssl); + my $s = get_socket(@_) || return; + return $s->socket()->dump_peer_certificate(); +} + +sub session { + my $s = get_socket(@_); + $s->read(); + return $s->socket(); } -sub get_ssl_socket { - my ($host, $port, $ses) = @_; +sub get_socket { + my ($host, $port, $ctx) = @_; + return stream( + PeerAddr => '127.0.0.1:' . port($port), + SSL => 1, + SSL_hostname => $host, + SSL_session_cache_size => 100, + SSL_session_key => 1, + SSL_reuse_ctx => $ctx + ); +} - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_tlsext_host_name($ssl, $host); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); +sub test_tls13 { + return get('default', 8080) =~ /TLSv1.3/; } ############################################################################### diff --git a/stream_ssl_conf_command.t b/stream_ssl_conf_command.t --- a/stream_ssl_conf_command.t +++ b/stream_ssl_conf_command.t @@ -16,22 +16,16 @@ BEGIN { use FindBin; chdir($FindBin::Bin use lib 'lib'; use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; - my $t = Test::Nginx->new() ->has(qw/stream stream_ssl stream_return openssl:1.0.2/) + ->has(qw/socket_ssl_reused/) ->has_daemon('openssl'); plan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL'); @@ -92,32 +86,31 @@ foreach my $name ('localhost', 'override ############################################################################### -my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); +my $s; -my ($s, $ssl) = get_ssl_socket(); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=override/, 'Certificate'); +$s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1, + SSL_session_cache_size => 100 +); +$s->read(); + +like($s->socket()->dump_peer_certificate(), qr/CN=override/, 'Certificate'); -my $ses = Net::SSLeay::get_session($ssl); -($s, $ssl) = get_ssl_socket(ses => $ses); -ok(Net::SSLeay::session_reused($ssl), 'SessionTicket'); +$s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1, + SSL_reuse_ctx => $s->socket() +); +ok($s->socket()->get_session_reused(), 'SessionTicket'); -($s, $ssl) = get_ssl_socket(ciphers => - 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'); -is(Net::SSLeay::get_cipher($ssl), +$s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1, + SSL_cipher_list => + 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' +); +is($s->socket()->get_cipher(), 'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference'); ############################################################################### - -sub get_ssl_socket { - my (%extra) = @_; - - my $s = IO::Socket::INET->new('127.0.0.1:' . port(8443)); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $extra{ses}) if $extra{ses}; - Net::SSLeay::set_cipher_list($ssl, $extra{ciphers}) if $extra{ciphers}; - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); -} - -############################################################################### diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t --- a/stream_ssl_session_reuse.t +++ b/stream_ssl_session_reuse.t @@ -19,23 +19,17 @@ BEGIN { use FindBin; chdir($FindBin::Bin use lib 'lib'; use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; +my $t = Test::Nginx->new()->has(qw/stream stream_ssl socket_ssl_sslversion/) + ->has_daemon('openssl')->plan(7); -my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); - -$t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); +$t->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -124,8 +118,6 @@ foreach my $name ('localhost') { or die "Can't create certificate for $name: $!\n"; } -my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - $t->run_daemon(\&http_daemon); $t->run(); @@ -145,6 +137,10 @@ my $ctx = Net::SSLeay::CTX_new() or die( # - only cache off TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); local $TODO = 'no TLSv1.3 sessions in LibreSSL' if $t->has_module('LibreSSL') && test_tls13(); @@ -168,29 +164,30 @@ is(test_reuse(8449), 0, 'cache off not r ############################################################################### sub test_tls13 { - my ($s, $ssl) = get_ssl_socket(8443); - return (Net::SSLeay::version($ssl) > 0x303); + my $s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1 + ); + return ($s->socket()->get_sslversion_int() > 0x303); } sub test_reuse { my ($port) = @_; - my ($s, $ssl) = get_ssl_socket($port); - Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); - Net::SSLeay::read($ssl); - my $ses = Net::SSLeay::get_session($ssl); - ($s, $ssl) = get_ssl_socket($port, $ses); - return Net::SSLeay::session_reused($ssl); -} + + my $s = stream( + PeerAddr => '127.0.0.1:' . port($port), + SSL => 1, + SSL_session_cache_size => 100 + ); + $s->io("GET / HTTP/1.0$CRLF$CRLF"); -sub get_ssl_socket { - my ($port, $ses) = @_; + $s = stream( + PeerAddr => '127.0.0.1:' . port($port), + SSL => 1, + SSL_reuse_ctx => $s->socket() + ); - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); + return $s->socket()->get_session_reused(); } ############################################################################### diff --git a/stream_ssl_variables.t b/stream_ssl_variables.t --- a/stream_ssl_variables.t +++ b/stream_ssl_variables.t @@ -23,22 +23,8 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; - -eval { - my $ctx = Net::SSLeay::CTX_new() or die; - my $ssl = Net::SSLeay::new($ctx) or die; - Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die; -}; -plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@; - -my $t = Test::Nginx->new()->has(qw/stream stream_ssl stream_return/) +my $t = Test::Nginx->new() + ->has(qw/stream stream_ssl stream_return socket_ssl_sni/) ->has_daemon('openssl'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -59,12 +45,12 @@ stream { server { listen 127.0.0.1:8080; - listen 127.0.0.1:8081 ssl; + listen 127.0.0.1:8443 ssl; return $ssl_session_reused:$ssl_session_id:$ssl_cipher:$ssl_protocol; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8444 ssl; return $ssl_server_name; } } @@ -93,21 +79,32 @@ foreach my $name ('localhost') { ############################################################################### -my ($s, $ssl); +my $s; is(stream('127.0.0.1:' . port(8080))->read(), ':::', 'no ssl'); -($s, $ssl) = get_ssl_socket(port(8081)); -like(Net::SSLeay::read($ssl), qr/^\.:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, +$s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1, + SSL_session_cache_size => 100 +); +like($s->read(), qr/^\.:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables'); TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); local $TODO = 'no TLSv1.3 sessions in LibreSSL' if $t->has_module('LibreSSL') && test_tls13(); -my $ses = Net::SSLeay::get_session($ssl); -($s, $ssl) = get_ssl_socket(port(8081), $ses); -like(Net::SSLeay::read($ssl), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, +$s = stream( + PeerAddr => '127.0.0.1:' . port(8443), + SSL => 1, + SSL_reuse_ctx => $s->socket() +); +like($s->read(), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables - session reused'); } @@ -115,36 +112,37 @@ like(Net::SSLeay::read($ssl), qr/^r:(\w{ SKIP: { skip 'no sni', 3 unless $t->has_module('sni'); -($s, $ssl) = get_ssl_socket(port(8082), undef, 'example.com'); -is(Net::SSLeay::ssl_read_all($ssl), 'example.com', 'ssl server name'); +$s = stream( + PeerAddr => '127.0.0.1:' . port(8444), + SSL => 1, + SSL_session_cache_size => 100, + SSL_hostname => 'example.com' +); +is($s->read(), 'example.com', 'ssl server name'); -my $ses = Net::SSLeay::get_session($ssl); -($s, $ssl) = get_ssl_socket(port(8082), $ses, 'example.com'); -is(Net::SSLeay::ssl_read_all($ssl), 'example.com', 'ssl server name - reused'); +$s = stream( + PeerAddr => '127.0.0.1:' . port(8444), + SSL => 1, + SSL_reuse_ctx => $s->socket(), + SSL_hostname => 'example.com' +); +is($s->read(), 'example.com', 'ssl server name - reused'); -($s, $ssl) = get_ssl_socket(port(8082)); -is(Net::SSLeay::ssl_read_all($ssl), '', 'ssl server name empty'); +$s = stream( + PeerAddr => '127.0.0.1:' . port(8444), + SSL => 1 +); +is($s->read(), '', 'ssl server name empty'); } +undef $s; + ############################################################################### sub test_tls13 { - ($s, $ssl) = get_ssl_socket(port(8081)); - Net::SSLeay::read($ssl) =~ /TLSv1.3/; -} - -sub get_ssl_socket { - my ($port, $ses, $name) = @_; - - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_tlsext_host_name($ssl, $name) if defined $name; - Net::SSLeay::set_session($ssl, $ses) if defined $ses; - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); + $s = stream(PeerAddr => '127.0.0.1:' . port(8443), SSL => 1); + $s->read() =~ /TLSv1.3/; } ############################################################################### diff --git a/stream_ssl_verify_client.t b/stream_ssl_verify_client.t --- a/stream_ssl_verify_client.t +++ b/stream_ssl_verify_client.t @@ -24,15 +24,7 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -eval { - require Net::SSLeay; - Net::SSLeay::load_error_strings(); - Net::SSLeay::SSLeay_add_ssl_algorithms(); - Net::SSLeay::randomize(); -}; -plan(skip_all => 'Net::SSLeay not installed') if $@; - -my $t = Test::Nginx->new()->has(qw/stream stream_ssl stream_return/) +my $t = Test::Nginx->new()->has(qw/stream stream_ssl stream_return socket_ssl/) ->has_daemon('openssl'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -154,18 +146,23 @@ sub test_tls13 { sub get { my ($port, $cert) = @_; - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") - or die if $cert; - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); + my $s = stream( + PeerAddr => '127.0.0.1:' . port($port), + SSL => 1, + $cert ? ( + SSL_cert_file => "$d/$cert.crt", + SSL_key_file => "$d/$cert.key" + ) : () + ); - my $buf = Net::SSLeay::read($ssl); - log_in($buf); - return $buf unless wantarray(); + return $s->read() unless wantarray(); + # Note: this uses IO::Socket::SSL::_get_ssl_object() internal method. + # While not exactly correct, it looks like there is no other way to + # obtain CA list with IO::Socket::SSL, and this seems to be good + # enough for tests. + + my $ssl = $s->socket()->_get_ssl_object(); my $list = Net::SSLeay::get_client_CA_list($ssl); my @names; for my $i (0 .. Net::SSLeay::sk_X509_NAME_num($list) - 1) {