[ruby/openssl] ssl: refactor check_supported_protocol_versions

As reported in <https://github.com/ruby/ruby/pull/12823>,
check_supported_protocol_versions is unstable and occasionally fails
with Errno::ECONNABORTED during SSLSocket#connect on Windows.

When the server-side SSLContext specifies an unsupported SSL/TLS
protocol version, start_server accepts a TCP connection but closes it
without reading ClientHello, as SSLSocket#accept immediately raises an
exception. With Winsock, this can cause the client-side
SSLSocket#connect to raise Errno::ECONNABORTED.

While the simplest fix is to add rescue Errno::ECONNABORTED, this method
can be simplified. Instead, let's set up a server that accepts all
protocol versions and test client connections with different settings.

https://github.com/ruby/openssl/commit/aa7f03e18f
This commit is contained in:
Kazuki Yamaguchi 2025-02-27 22:56:33 +09:00 committed by git
parent c0f3dcf795
commit d4b8da66ca

View File

@ -1244,32 +1244,28 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
OpenSSL::SSL::TLS1_1_VERSION, OpenSSL::SSL::TLS1_1_VERSION,
OpenSSL::SSL::TLS1_2_VERSION, OpenSSL::SSL::TLS1_2_VERSION,
OpenSSL::SSL::TLS1_3_VERSION, OpenSSL::SSL::TLS1_3_VERSION,
].compact ]
# Prepare for testing & do sanity check
supported = [] supported = []
possible_versions.each do |ver| ctx_proc = proc { |ctx|
catch(:unsupported) { # Explicitly reset them to avoid influenced by OPENSSL_CONF
ctx_proc = proc { |ctx| ctx.min_version = ctx.max_version = nil
begin }
ctx.min_version = ctx.max_version = ver start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
rescue ArgumentError, OpenSSL::SSL::SSLError possible_versions.each do |ver|
throw :unsupported ctx = OpenSSL::SSL::SSLContext.new
end ctx.min_version = ctx.max_version = ver
server_connect(port, ctx) { |ssl|
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
} }
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| supported << ver
begin rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
server_connect(port) { |ssl| end
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
else
supported << ver
end
end
}
end end
assert_not_empty supported
# Sanity check: in our test suite we assume these are always supported
assert_include(supported, OpenSSL::SSL::TLS1_2_VERSION)
assert_include(supported, OpenSSL::SSL::TLS1_3_VERSION)
supported supported
end end