Skip to content

Commit c47e641

Browse files
committed
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.
1 parent debfe2f commit c47e641

File tree

7 files changed

+214
-223
lines changed

7 files changed

+214
-223
lines changed

lib/Test/Nginx/Stream.pm

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,50 @@ sub new {
3838

3939
unshift(@_, "PeerAddr") if @_ == 1;
4040

41-
$self->{_socket} = IO::Socket::INET->new(
42-
Proto => "tcp",
43-
PeerAddr => '127.0.0.1',
44-
@_
45-
)
46-
or die "Can't connect to nginx: $!\n";
47-
48-
if ({@_}->{'SSL'}) {
49-
require IO::Socket::SSL;
50-
IO::Socket::SSL->start_SSL($self->{_socket}, @_)
51-
or die $IO::Socket::SSL::SSL_ERROR . "\n";
41+
eval {
42+
local $SIG{ALRM} = sub { die "timeout\n" };
43+
local $SIG{PIPE} = sub { die "sigpipe\n" };
44+
alarm(8);
45+
46+
$self->{_socket} = IO::Socket::INET->new(
47+
Proto => "tcp",
48+
PeerAddr => '127.0.0.1',
49+
@_
50+
)
51+
or die "Can't connect to nginx: $!\n";
52+
53+
if ({@_}->{'SSL'}) {
54+
require IO::Socket::SSL;
55+
IO::Socket::SSL->start_SSL(
56+
$self->{_socket},
57+
SSL_verify_mode =>
58+
IO::Socket::SSL::SSL_VERIFY_NONE(),
59+
@_
60+
)
61+
or die $IO::Socket::SSL::SSL_ERROR . "\n";
62+
63+
my $s = $self->{_socket};
64+
log_in("ssl cipher: " . $s->get_cipher());
65+
log_in("ssl cert: " . $s->peer_certificate('issuer'));
66+
}
67+
68+
alarm(0);
69+
};
70+
alarm(0);
71+
if ($@) {
72+
log_in("died: $@");
5273
}
5374

5475
$self->{_socket}->autoflush(1);
5576

5677
return $self;
5778
}
5879

80+
sub DESTROY {
81+
my $self = shift;
82+
$self->{_socket}->close();
83+
}
84+
5985
sub write {
6086
my ($self, $message, %extra) = @_;
6187
my $s = $self->{_socket};
@@ -135,6 +161,11 @@ sub sockport {
135161
return $self->{_socket}->sockport();
136162
}
137163

164+
sub socket {
165+
my ($self) = @_;
166+
$self->{_socket};
167+
}
168+
138169
###############################################################################
139170

140171
1;

stream_ssl.t

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,17 @@ BEGIN { use FindBin; chdir($FindBin::Bin); }
1919

2020
use lib 'lib';
2121
use Test::Nginx;
22+
use Test::Nginx::Stream qw/ stream /;
2223

2324
###############################################################################
2425

2526
select STDERR; $| = 1;
2627
select STDOUT; $| = 1;
2728

28-
eval {
29-
require Net::SSLeay;
30-
Net::SSLeay::load_error_strings();
31-
Net::SSLeay::SSLeay_add_ssl_algorithms();
32-
Net::SSLeay::randomize();
33-
};
34-
plan(skip_all => 'Net::SSLeay not installed') if $@;
35-
3629
plan(skip_all => 'win32') if $^O eq 'MSWin32';
3730

38-
my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl');
31+
my $t = Test::Nginx->new()->has(qw/stream stream_ssl socket_ssl/)
32+
->has_daemon('openssl');
3933

4034
$t->plan(5)->write_file_expand('nginx.conf', <<'EOF');
4135
@@ -110,8 +104,6 @@ foreach my $name ('localhost', 'inherits') {
110104
or die "Can't create certificate for $name: $!\n";
111105
}
112106

113-
my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
114-
115107
$t->write_file('password', 'localhost');
116108
$t->write_file('password_many', "wrong$CRLF" . "localhost$CRLF");
117109
$t->write_file('password_stream', 'inherits');
@@ -132,38 +124,30 @@ $t->waitforsocket('127.0.0.1:' . port(8081));
132124

133125
###############################################################################
134126

135-
my ($s, $ssl);
136-
137-
($s, $ssl) = get_ssl_socket(8443);
138-
Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF");
139-
like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl');
140-
141-
($s, $ssl) = get_ssl_socket(8444);
142-
Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF");
143-
like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many');
144-
145-
($s, $ssl) = get_ssl_socket(8445);
146-
Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF");
147-
like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo');
127+
like(get(8443), qr/200 OK/, 'ssl');
128+
like(get(8444), qr/200 OK/, 'ssl password many');
129+
like(get(8445), qr/200 OK/, 'ssl password fifo');
148130

149131
# ssl_certificate inheritance
150132

151-
($s, $ssl) = get_ssl_socket(8443);
152-
like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN');
153-
154-
($s, $ssl) = get_ssl_socket(8446);
155-
like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner');
133+
like(cert(8443), qr/CN=localhost/, 'CN');
134+
like(cert(8446), qr/CN=inherits/, 'CN inner');
156135

157136
###############################################################################
158137

159-
sub get_ssl_socket {
160-
my ($port) = @_;
138+
sub get {
139+
my $s = get_socket(@_);
140+
return $s->io("GET / HTTP/1.0$CRLF$CRLF");
141+
}
161142

162-
my $s = IO::Socket::INET->new('127.0.0.1:' . port($port));
163-
my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
164-
Net::SSLeay::set_fd($ssl, fileno($s));
165-
Net::SSLeay::connect($ssl) or die("ssl connect");
166-
return ($s, $ssl);
143+
sub cert {
144+
my $s = get_socket(@_);
145+
return $s->socket()->dump_peer_certificate();
146+
}
147+
148+
sub get_socket {
149+
my ($port) = @_;
150+
return stream(PeerAddr => '127.0.0.1:' . port($port), SSL => 1);
167151
}
168152

169153
###############################################################################

stream_ssl_certificate.t

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,16 @@ BEGIN { use FindBin; chdir($FindBin::Bin); }
1616

1717
use lib 'lib';
1818
use Test::Nginx;
19+
use Test::Nginx::Stream qw/ stream /;
1920

2021
###############################################################################
2122

2223
select STDERR; $| = 1;
2324
select STDOUT; $| = 1;
2425

25-
eval {
26-
require Net::SSLeay;
27-
Net::SSLeay::load_error_strings();
28-
Net::SSLeay::SSLeay_add_ssl_algorithms();
29-
Net::SSLeay::randomize();
30-
};
31-
plan(skip_all => 'Net::SSLeay not installed') if $@;
32-
33-
eval {
34-
my $ctx = Net::SSLeay::CTX_new() or die;
35-
my $ssl = Net::SSLeay::new($ctx) or die;
36-
Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
37-
};
38-
plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
39-
4026
my $t = Test::Nginx->new()
4127
->has(qw/stream stream_ssl stream_geo stream_return openssl:1.0.2/)
28+
->has(qw/socket_ssl_sni/)
4229
->has_daemon('openssl')
4330
->write_file_expand('nginx.conf', <<'EOF');
4431
@@ -69,7 +56,7 @@ stream {
6956
7057
server {
7158
listen 127.0.0.1:8080 ssl;
72-
return $ssl_server_name:$ssl_session_reused;
59+
return $ssl_server_name:$ssl_session_reused:$ssl_protocol;
7360
7461
ssl_certificate $one.crt;
7562
ssl_certificate_key $one.key;
@@ -154,59 +141,63 @@ like(get('password', 8083), qr/password/, 'ssl_password_file');
154141

155142
# session reuse
156143

157-
my ($s, $ssl) = get('default', 8080);
158-
my $ses = Net::SSLeay::get_session($ssl);
144+
my $s = session('default', 8080);
159145

160-
like(get('default', 8080, $ses), qr/default:r/, 'session reused');
146+
TODO: {
147+
local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'
148+
if $Net::SSLeay::VERSION < 1.88 && test_tls13();
149+
local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'
150+
if $IO::Socket::SSL::VERSION < 2.061 && test_tls13();
151+
152+
like(get('default', 8080, $s), qr/default:r/, 'session reused');
161153

162154
TODO: {
163155
# ticket key name mismatch prevents session resumption
164156
local $TODO = 'not yet' unless $t->has_version('1.23.2');
165157

166-
like(get('default', 8081, $ses), qr/default:r/, 'session id context match');
158+
like(get('default', 8081, $s), qr/default:r/, 'session id context match');
167159

160+
}
168161
}
169162

170-
like(get('default', 8082, $ses), qr/default:\./, 'session id context distinct');
163+
like(get('default', 8082, $s), qr/default:\./, 'session id context distinct');
171164

172165
# errors
173166

174-
Net::SSLeay::ERR_clear_error();
175-
get_ssl_socket('nx', 8084);
176-
ok(Net::SSLeay::ERR_peek_error(), 'no certificate');
167+
ok(!get('nx', 8084), 'no certificate');
177168

178169
###############################################################################
179170

180171
sub get {
181-
my ($host, $port, $ctx) = @_;
182-
my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return;
172+
my $s = get_socket(@_) || return;
173+
return $s->read();
174+
}
183175

184-
local $SIG{PIPE} = 'IGNORE';
176+
sub cert {
177+
my $s = get_socket(@_) || return;
178+
return $s->socket()->dump_peer_certificate();
179+
}
185180

186-
my $r = Net::SSLeay::read($ssl);
187-
Net::SSLeay::shutdown($ssl);
188-
$s->close();
189-
return $r unless wantarray();
190-
return ($s, $ssl);
181+
sub session {
182+
my $s = get_socket(@_);
183+
$s->read();
184+
return $s->socket();
191185
}
192186

193-
sub cert {
187+
sub get_socket {
194188
my ($host, $port, $ctx) = @_;
195-
my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return;
196-
Net::SSLeay::dump_peer_certificate($ssl);
189+
return stream(
190+
PeerAddr => '127.0.0.1:' . port($port),
191+
SSL => 1,
192+
SSL_hostname => $host,
193+
SSL_session_cache_size => 100,
194+
SSL_session_key => 1,
195+
SSL_reuse_ctx => $ctx
196+
);
197197
}
198198

199-
sub get_ssl_socket {
200-
my ($host, $port, $ses) = @_;
201-
202-
my $s = IO::Socket::INET->new('127.0.0.1:' . port($port));
203-
my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
204-
my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
205-
Net::SSLeay::set_tlsext_host_name($ssl, $host);
206-
Net::SSLeay::set_session($ssl, $ses) if defined $ses;
207-
Net::SSLeay::set_fd($ssl, fileno($s));
208-
Net::SSLeay::connect($ssl) or die("ssl connect");
209-
return ($s, $ssl);
199+
sub test_tls13 {
200+
return get('default', 8080) =~ /TLSv1.3/;
210201
}
211202

212203
###############################################################################

stream_ssl_conf_command.t

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,16 @@ BEGIN { use FindBin; chdir($FindBin::Bin); }
1616

1717
use lib 'lib';
1818
use Test::Nginx;
19+
use Test::Nginx::Stream qw/ stream /;
1920

2021
###############################################################################
2122

2223
select STDERR; $| = 1;
2324
select STDOUT; $| = 1;
2425

25-
eval {
26-
require Net::SSLeay;
27-
Net::SSLeay::load_error_strings();
28-
Net::SSLeay::SSLeay_add_ssl_algorithms();
29-
Net::SSLeay::randomize();
30-
};
31-
plan(skip_all => 'Net::SSLeay not installed') if $@;
32-
3326
my $t = Test::Nginx->new()
3427
->has(qw/stream stream_ssl stream_return openssl:1.0.2/)
28+
->has(qw/socket_ssl_reused/)
3529
->has_daemon('openssl');
3630

3731
plan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL');
@@ -92,32 +86,31 @@ $t->run()->plan(3);
9286

9387
###############################################################################
9488

95-
my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
96-
97-
my ($s, $ssl) = get_ssl_socket();
98-
like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=override/, 'Certificate');
99-
100-
my $ses = Net::SSLeay::get_session($ssl);
101-
($s, $ssl) = get_ssl_socket(ses => $ses);
102-
ok(Net::SSLeay::session_reused($ssl), 'SessionTicket');
103-
104-
($s, $ssl) = get_ssl_socket(ciphers =>
105-
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384');
106-
is(Net::SSLeay::get_cipher($ssl),
89+
my $s;
90+
91+
$s = stream(
92+
PeerAddr => '127.0.0.1:' . port(8443),
93+
SSL => 1,
94+
SSL_session_cache_size => 100
95+
);
96+
$s->read();
97+
98+
like($s->socket()->dump_peer_certificate(), qr/CN=override/, 'Certificate');
99+
100+
$s = stream(
101+
PeerAddr => '127.0.0.1:' . port(8443),
102+
SSL => 1,
103+
SSL_reuse_ctx => $s->socket()
104+
);
105+
ok($s->socket()->get_session_reused(), 'SessionTicket');
106+
107+
$s = stream(
108+
PeerAddr => '127.0.0.1:' . port(8443),
109+
SSL => 1,
110+
SSL_cipher_list =>
111+
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
112+
);
113+
is($s->socket()->get_cipher(),
107114
'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference');
108115

109116
###############################################################################
110-
111-
sub get_ssl_socket {
112-
my (%extra) = @_;
113-
114-
my $s = IO::Socket::INET->new('127.0.0.1:' . port(8443));
115-
my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
116-
Net::SSLeay::set_session($ssl, $extra{ses}) if $extra{ses};
117-
Net::SSLeay::set_cipher_list($ssl, $extra{ciphers}) if $extra{ciphers};
118-
Net::SSLeay::set_fd($ssl, fileno($s));
119-
Net::SSLeay::connect($ssl) or die("ssl connect");
120-
return ($s, $ssl);
121-
}
122-
123-
###############################################################################

0 commit comments

Comments
 (0)