Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 1 | #!/usr/bin/perl |
| 2 | |
| 3 | # (C) Sergey Kandaurov |
| 4 | # (C) Nginx, Inc. |
| 5 | |
| 6 | # Tests for stream_ssl_preread module. |
| 7 | |
| 8 | ############################################################################### |
| 9 | |
| 10 | use warnings; |
| 11 | use strict; |
| 12 | |
| 13 | use Test::More; |
| 14 | |
| 15 | BEGIN { use FindBin; chdir($FindBin::Bin); } |
| 16 | |
| 17 | use lib 'lib'; |
| 18 | use Test::Nginx; |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 19 | use Test::Nginx::Stream qw/ stream /; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 20 | |
| 21 | ############################################################################### |
| 22 | |
| 23 | select STDERR; $| = 1; |
| 24 | select STDOUT; $| = 1; |
| 25 | |
| 26 | my $t = Test::Nginx->new()->has(qw/stream stream_map stream_ssl_preread/) |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 27 | ->has(qw/stream_ssl stream_return/)->has_daemon('openssl') |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 28 | ->write_file_expand('nginx.conf', <<'EOF'); |
| 29 | |
| 30 | %%TEST_GLOBALS%% |
| 31 | |
| 32 | daemon off; |
| 33 | |
| 34 | events { |
| 35 | } |
| 36 | |
| 37 | stream { |
| 38 | log_format status $status; |
| 39 | |
| 40 | map $ssl_preread_server_name $name { |
| 41 | "" 127.0.0.1:8093; |
| 42 | default $ssl_preread_server_name; |
| 43 | } |
| 44 | |
| 45 | upstream foo { |
| 46 | server 127.0.0.1:8091; |
| 47 | } |
| 48 | |
| 49 | upstream bar { |
| 50 | server 127.0.0.1:8092; |
| 51 | } |
| 52 | |
Andrey Zelenkov | 939a5af | 2017-07-26 16:42:48 +0300 | [diff] [blame] | 53 | upstream next { |
| 54 | server 127.0.0.1:8094; |
| 55 | server 127.0.0.1:8080; |
| 56 | } |
| 57 | |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 58 | ssl_preread on; |
| 59 | |
| 60 | server { |
| 61 | listen 127.0.0.1:8080; |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 62 | return $name; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | server { |
| 66 | listen 127.0.0.1:8081; |
| 67 | proxy_pass $name; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 68 | } |
| 69 | |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 70 | server { |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 71 | listen 127.0.0.1:8082; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 72 | proxy_pass $name; |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 73 | ssl_preread off; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | server { |
| 77 | listen 127.0.0.1:8083; |
| 78 | proxy_pass $name; |
| 79 | |
| 80 | preread_timeout 2s; |
| 81 | preread_buffer_size 42; |
| 82 | |
| 83 | access_log %%TESTDIR%%/status.log status; |
| 84 | } |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 85 | |
Andrey Zelenkov | 939a5af | 2017-07-26 16:42:48 +0300 | [diff] [blame] | 86 | server { |
| 87 | listen 127.0.0.1:8084; |
| 88 | proxy_pass next; |
| 89 | |
| 90 | proxy_connect_timeout 2s; |
| 91 | preread_buffer_size 8; |
| 92 | } |
| 93 | |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 94 | ssl_certificate_key localhost.key; |
| 95 | ssl_certificate localhost.crt; |
| 96 | |
| 97 | server { |
| 98 | listen 127.0.0.1:8091 ssl; |
| 99 | listen 127.0.0.1:8092 ssl; |
| 100 | listen 127.0.0.1:8093 ssl; |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 101 | ssl_preread off; |
| 102 | return $server_port; |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | |
| 106 | EOF |
| 107 | |
| 108 | eval { require IO::Socket::SSL; die if $IO::Socket::SSL::VERSION < 1.56; }; |
| 109 | plan(skip_all => 'IO::Socket::SSL version >= 1.56 required') if $@; |
| 110 | |
| 111 | eval { |
| 112 | if (IO::Socket::SSL->can('can_client_sni')) { |
| 113 | IO::Socket::SSL->can_client_sni() or die; |
| 114 | } |
| 115 | }; |
| 116 | plan(skip_all => 'IO::Socket::SSL with OpenSSL SNI support required') if $@; |
| 117 | |
| 118 | eval { |
| 119 | my $ctx = Net::SSLeay::CTX_new() or die; |
| 120 | my $ssl = Net::SSLeay::new($ctx) or die; |
| 121 | Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die; |
| 122 | }; |
| 123 | plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@; |
| 124 | |
Sergey Kandaurov | 0a53982 | 2018-03-30 18:08:23 +0300 | [diff] [blame] | 125 | $t->plan(13); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 126 | |
| 127 | $t->write_file('openssl.conf', <<EOF); |
| 128 | [ req ] |
Sergey Kandaurov | 571b1a5 | 2019-07-09 13:37:55 +0300 | [diff] [blame] | 129 | default_bits = 2048 |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 130 | encrypt_key = no |
| 131 | distinguished_name = req_distinguished_name |
| 132 | [ req_distinguished_name ] |
| 133 | EOF |
| 134 | |
| 135 | my $d = $t->testdir(); |
| 136 | |
| 137 | foreach my $name ('localhost') { |
| 138 | system('openssl req -x509 -new ' |
Sergey Kandaurov | 2225e6e | 2017-09-20 14:46:51 +0300 | [diff] [blame] | 139 | . "-config $d/openssl.conf -subj /CN=$name/ " |
| 140 | . "-out $d/$name.crt -keyout $d/$name.key " |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 141 | . ">>$d/openssl.out 2>&1") == 0 |
| 142 | or die "Can't create certificate for $name: $!\n"; |
| 143 | } |
| 144 | |
| 145 | $t->run(); |
| 146 | |
| 147 | ############################################################################### |
| 148 | |
Andrey Zelenkov | 939a5af | 2017-07-26 16:42:48 +0300 | [diff] [blame] | 149 | my ($p1, $p2, $p3, $p4) = (port(8091), port(8092), port(8093), port(8084)); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 150 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 151 | is(get_ssl('foo', 8081), $p1, 'sni'); |
| 152 | is(get_ssl('foo', 8081), $p1, 'sni again'); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 153 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 154 | is(get_ssl('bar', 8081), $p2, 'sni 2'); |
| 155 | is(get_ssl('bar', 8081), $p2, 'sni 2 again'); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 156 | |
| 157 | # fallback to an empty value for some reason |
| 158 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 159 | is(get_ssl('', 8081), $p3, 'no sni'); |
| 160 | is(get_ssl('foo', 8082), $p3, 'preread off'); |
| 161 | is(get_ssl('foo', 8083), undef, 'preread buffer full'); |
Andrey Zelenkov | 426d12f | 2017-10-26 16:36:13 +0300 | [diff] [blame] | 162 | is(stream('127.0.0.1:' . port(8080))->io('x' x 1000), "127.0.0.1:$p3", |
| 163 | 'not a handshake'); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 164 | |
Sergey Kandaurov | 38bbbe3 | 2017-10-21 01:07:48 +0300 | [diff] [blame] | 165 | # ticket #1317 |
Andrey Zelenkov | 939a5af | 2017-07-26 16:42:48 +0300 | [diff] [blame] | 166 | |
| 167 | is(stream("127.0.0.1:$p4")->io('x' x 16), "127.0.0.1:$p3", |
| 168 | 'pending buffers on next upstream'); |
| 169 | |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 170 | # no junk in variable due to short ClientHello length value |
| 171 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 172 | is(get_short(), "127.0.0.1:$p3", 'short client hello'); |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 173 | |
| 174 | # allow record with older SSL version, such as 3.0 |
| 175 | |
| 176 | is(get_oldver(), 'foo', 'older version in ssl record'); |
| 177 | |
Sergey Kandaurov | 0a53982 | 2018-03-30 18:08:23 +0300 | [diff] [blame] | 178 | # SNI "foo|f" fragmented across TLS records |
| 179 | |
| 180 | is(get_frag(), 'foof', 'handshake fragment split on SNI'); |
| 181 | |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 182 | $t->stop(); |
| 183 | |
| 184 | is($t->read_file('status.log'), "400\n", 'preread buffer full - log'); |
| 185 | |
| 186 | ############################################################################### |
| 187 | |
Sergey Kandaurov | 0a53982 | 2018-03-30 18:08:23 +0300 | [diff] [blame] | 188 | sub get_frag { |
| 189 | my $r = pack("N*", 0x16030100, 0x3b010000, 0x380303ac, |
| 190 | 0x8c8678a0, 0xaa1e7eed, 0x3644eed6, 0xc3bd2c69, |
| 191 | 0x7bc7deda, 0x249db0e3, 0x0c339eba, 0xa80b7600, |
| 192 | 0x00020000, 0x0100000d, 0x00000009, 0x00070000, |
| 193 | 0x04666f6f, 0x16030100); |
| 194 | $r .= pack("n", 0x0166); |
| 195 | |
| 196 | http($r); |
| 197 | } |
| 198 | |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 199 | sub get_short { |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 200 | my $r = pack("N*", 0x16030100, 0x38010000, 0x330303eb); |
| 201 | $r .= pack("N*", 0x6357cdba, 0xa6b8d853, 0xf1f6ac0f); |
| 202 | $r .= pack("N*", 0xdf03178c, 0x0ae41824, 0xe7643682); |
| 203 | $r .= pack("N*", 0x3c1b273f, 0xbfde4b00, 0x00000000); |
| 204 | $r .= pack("CN3", 0x0c, 0x00000008, 0x00060000, 0x03666f6f); |
| 205 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 206 | http($r); |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | sub get_oldver { |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 210 | my $r = pack("N*", 0x16030000, 0x38010000, 0x340303eb); |
| 211 | $r .= pack("N*", 0x6357cdba, 0xa6b8d853, 0xf1f6ac0f); |
| 212 | $r .= pack("N*", 0xdf03178c, 0x0ae41824, 0xe7643682); |
| 213 | $r .= pack("N*", 0x3c1b273f, 0xbfde4b00, 0x00000000); |
| 214 | $r .= pack("CN3", 0x0c, 0x00000008, 0x00060000, 0x03666f6f); |
| 215 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 216 | http($r); |
Sergey Kandaurov | 207349f | 2016-12-19 14:38:56 +0300 | [diff] [blame] | 217 | } |
| 218 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 219 | sub get_ssl { |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 220 | my ($host, $port) = @_; |
Sergey Kandaurov | 92fa916 | 2017-08-03 17:29:54 +0300 | [diff] [blame] | 221 | my $s = stream('127.0.0.1:' . port($port)); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 222 | |
| 223 | eval { |
| 224 | local $SIG{ALRM} = sub { die "timeout\n" }; |
| 225 | local $SIG{PIPE} = sub { die "sigpipe\n" }; |
Sergey Kandaurov | 4aa9b91 | 2018-12-24 14:24:51 +0300 | [diff] [blame] | 226 | alarm(8); |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 227 | IO::Socket::SSL->start_SSL($s->{_socket}, |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 228 | SSL_hostname => $host, |
| 229 | SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), |
| 230 | SSL_error_trap => sub { die $_[1] } |
| 231 | ); |
| 232 | alarm(0); |
| 233 | }; |
| 234 | alarm(0); |
| 235 | |
| 236 | if ($@) { |
| 237 | log_in("died: $@"); |
| 238 | return undef; |
| 239 | } |
| 240 | |
Sergey Kandaurov | 391e3a4 | 2017-07-31 14:24:38 +0300 | [diff] [blame] | 241 | return $s->read(); |
Sergey Kandaurov | 2b62d0f | 2016-09-15 14:06:24 +0300 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | ############################################################################### |