diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index 10942191d..8cb1060e4 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -2,35 +2,34 @@ require_relative 'utils' require_relative 'ut_eof' -if defined?(OpenSSL::SSL) +return unless defined?(OpenSSL::SSL) -module OpenSSL::SSLPairM - def setup +module OpenSSL::SSLPair + def ssl_pair svr_dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") ee_exts = [ ["keyUsage", "keyEncipherment,digitalSignature", true], ] - @svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa-1") - @svr_cert = issue_cert(svr_dn, @svr_key, 1, ee_exts, nil, nil) - end + svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa-1") + svr_cert = issue_cert(svr_dn, svr_key, 1, ee_exts, nil, nil) - def ssl_pair host = "127.0.0.1" - tcps = create_tcp_server(host, 0) - port = tcps.connect_address.ip_port + svr = TCPServer.new(host, 0) + svr.setsockopt(:TCP, :NODELAY, 1) + port = svr.connect_address.ip_port + tcps = nil th = Thread.new { + tcps = svr.accept sctx = OpenSSL::SSL::SSLContext.new - sctx.cert = @svr_cert - sctx.key = @svr_key - sctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION - ssls = OpenSSL::SSL::SSLServer.new(tcps, sctx) - ns = ssls.accept - ssls.close - ns + sctx.add_certificate(svr_cert, svr_key) + ssl = OpenSSL::SSL::SSLSocket.new(tcps, sctx) + ssl.accept + ssl } - tcpc = create_tcp_client(host, port) + tcpc = TCPSocket.new(host, port) + tcpc.setsockopt(:TCP, :NODELAY, 1) c = OpenSSL::SSL::SSLSocket.new(tcpc) c.connect s = th.value @@ -39,57 +38,7 @@ def ssl_pair ensure tcpc&.close tcps&.close - s&.close - end -end - -module OpenSSL::SSLPair - include OpenSSL::SSLPairM - - def create_tcp_server(host, port) - TCPServer.new(host, port) - end - - def create_tcp_client(host, port) - TCPSocket.new(host, port) - end -end - -module OpenSSL::SSLPairLowlevelSocket - include OpenSSL::SSLPairM - - def create_tcp_server(host, port) - Addrinfo.tcp(host, port).listen - end - - def create_tcp_client(host, port) - Addrinfo.tcp(host, port).connect - end -end - -module OpenSSL::TestEOF1M - def open_file(content) - ssl_pair { |s1, s2| - begin - th = Thread.new { s2 << content; s2.close } - yield s1 - ensure - th&.join - end - } - end -end - -module OpenSSL::TestEOF2M - def open_file(content) - ssl_pair { |s1, s2| - begin - th = Thread.new { s1 << content; s1.close } - yield s2 - ensure - th&.join - end - } + svr&.close end end @@ -97,28 +46,36 @@ module OpenSSL::TestPairM def test_getc ssl_pair {|s1, s2| s1 << "a" + s1.close assert_equal(?a, s2.getc) + assert_nil(s2.getc) } end def test_getbyte ssl_pair {|s1, s2| s1 << "a" + s1.close assert_equal(97, s2.getbyte) + assert_nil(s2.getbyte) } end - def test_readbyte + def test_readchar ssl_pair {|s1, s2| s1 << "b" - assert_equal(98, s2.readbyte) + s1.close + assert_equal("b", s2.readchar) + assert_raise(EOFError) { s2.readchar } } end - def test_readbyte_eof + def test_readbyte ssl_pair {|s1, s2| - s2.close - assert_raise(EOFError) { s1.readbyte } + s1 << "b" + s1.close + assert_equal(98, s2.readbyte) + assert_raise(EOFError) { s2.readbyte } } end @@ -216,6 +173,25 @@ def test_multibyte_read_write } end + def test_sysread_and_syswrite + ssl_pair {|s1, s2| + str = "x" * 100 + "\n" + s1.syswrite(str) + newstr = s2.sysread(str.bytesize) + assert_equal(str, newstr) + + buf = String.new + s1.syswrite(str) + assert_same(buf, s2.sysread(str.size, buf)) + assert_equal(str, buf) + + obj = Object.new + obj.define_singleton_method(:to_str) { str } + s1.syswrite(obj) + assert_equal(str, s2.sysread(str.bytesize)) + } + end + def test_read_nonblock ssl_pair {|s1, s2| err = nil @@ -393,151 +369,33 @@ def test_write_multiple_arguments } end - def test_partial_tls_record_read_nonblock + def test_copy_stream ssl_pair { |s1, s2| - # the beginning of a TLS record - s1.io.write("\x17") - # should raise a IO::WaitReadable since a full TLS record is not available - # for reading - assert_raise(IO::WaitReadable) { s2.read_nonblock(1) } + IO.pipe do |r, w| + str = "hello world\n" + w.write(str) + IO.copy_stream(r, s1, str.bytesize) + IO.copy_stream(s2, w, str.bytesize) + assert_equal(str, r.read(str.bytesize)) + end } end - def tcp_pair - host = "127.0.0.1" - serv = TCPServer.new(host, 0) - port = serv.connect_address.ip_port - sock1 = TCPSocket.new(host, port) - sock2 = serv.accept - serv.close - [sock1, sock2] - ensure - serv.close if serv && !serv.closed? - end - - def test_connect_accept_nonblock_no_exception - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.cert = @svr_cert - ctx2.key = @svr_key - - sock1, sock2 = tcp_pair - - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - accepted = s2.accept_nonblock(exception: false) - assert_equal :wait_readable, accepted - - ctx1 = OpenSSL::SSL::SSLContext.new - s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) - th = Thread.new do - rets = [] - begin - rv = s1.connect_nonblock(exception: false) - rets << rv - case rv - when :wait_writable - IO.select(nil, [s1], nil, 5) - when :wait_readable - IO.select([s1], nil, nil, 5) - end - end until rv == s1 - rets - end - - until th.join(0.01) - accepted = s2.accept_nonblock(exception: false) - assert_include([s2, :wait_readable, :wait_writable ], accepted) - end - - rets = th.value - assert_instance_of Array, rets - rets.each do |rv| - assert_include([s1, :wait_readable, :wait_writable ], rv) - end - ensure - th.join if th - s1.close if s1 - s2.close if s2 - sock1.close if sock1 - sock2.close if sock2 - accepted.close if accepted.respond_to?(:close) - end - - def test_connect_accept_nonblock - ctx = OpenSSL::SSL::SSLContext.new - ctx.cert = @svr_cert - ctx.key = @svr_key - - sock1, sock2 = tcp_pair - - th = Thread.new { - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx) - 5.times { - begin - break s2.accept_nonblock - rescue IO::WaitReadable - IO.select([s2], nil, nil, 1) - rescue IO::WaitWritable - IO.select(nil, [s2], nil, 1) - end - sleep 0.2 - } - } - - s1 = OpenSSL::SSL::SSLSocket.new(sock1) - 5.times { - begin - break s1.connect_nonblock - rescue IO::WaitReadable - IO.select([s1], nil, nil, 1) - rescue IO::WaitWritable - IO.select(nil, [s1], nil, 1) - end - sleep 0.2 + def test_close_write + ssl_pair { |s1, s2| + message = "abc"*1024 + s1.write(message) + s1.close_write + assert_equal(message, s2.read) + s2.write(message) + s2.close_write + assert_equal(message, s1.read) } - - s2 = th.value - - s1.print "a\ndef" - assert_equal("a\n", s2.gets) - ensure - sock1&.close - sock2&.close - th&.join end end -class OpenSSL::TestEOF1 < OpenSSL::TestCase - include OpenSSL::TestEOF +class OpenSSL::TestSSLPair < OpenSSL::TestCase include OpenSSL::SSLPair - include OpenSSL::TestEOF1M -end - -class OpenSSL::TestEOF1LowlevelSocket < OpenSSL::TestCase - include OpenSSL::TestEOF - include OpenSSL::SSLPairLowlevelSocket - include OpenSSL::TestEOF1M -end - -class OpenSSL::TestEOF2 < OpenSSL::TestCase - include OpenSSL::TestEOF - include OpenSSL::SSLPair - include OpenSSL::TestEOF2M -end - -class OpenSSL::TestEOF2LowlevelSocket < OpenSSL::TestCase - include OpenSSL::TestEOF - include OpenSSL::SSLPairLowlevelSocket - include OpenSSL::TestEOF2M -end - -class OpenSSL::TestPair < OpenSSL::TestCase - include OpenSSL::SSLPair - include OpenSSL::TestPairM -end - -class OpenSSL::TestPairLowlevelSocket < OpenSSL::TestCase - include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestPairM -end - + include OpenSSL::TestEOF end diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 914b80623..a252a0e64 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -75,24 +75,23 @@ def test_ctx_options_config end def test_ssl_with_server_cert - ctx_proc = -> ctx { - ctx.cert = @svr_cert - ctx.key = @svr_key - ctx.extra_chain_cert = [@ca_cert] - } - server_proc = -> (ctx, ssl) { + sctx = OpenSSL::SSL::SSLContext.new + sctx.cert = @svr_cert + sctx.key = @svr_key + sctx.extra_chain_cert = [@ca_cert] + + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx).accept assert_equal @svr_cert.to_der, ssl.cert.to_der assert_equal nil, ssl.peer_cert - - readwrite_loop(ctx, ssl) - } - start_server(ctx_proc: ctx_proc, server_proc: server_proc) { |port| + readwrite_loop(ssl) + end + start_server_proc(server_proc) { |port| begin sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect - assert_equal sock, ssl.io assert_equal nil, ssl.cert assert_equal @svr_cert.to_der, ssl.peer_cert.to_der @@ -108,6 +107,98 @@ def test_ssl_with_server_cert } end + def test_connect_accept_nonblock_no_exception + server_proc = proc do |sock| + s2 = OpenSSL::SSL::SSLSocket.new(sock, make_server_context) + accepted = s2.accept_nonblock(exception: false) + assert_equal(:wait_readable, accepted) + loop do + rv = s2.accept_nonblock(exception: false) + case rv + when :wait_readable + IO.select([s2], nil, nil, 1) + when :wait_writable + IO.select(nil, [s2], nil, 1) + else + assert_same(s2, rv) + break + end + end + assert_equal("abc\n", s2.gets) + s2.puts("abc") + s2.close + end + start_server_proc(server_proc) do |port| + sock = TCPSocket.new("127.0.0.1", port) + s1 = OpenSSL::SSL::SSLSocket.new(sock) + loop do + rv = s1.connect_nonblock(exception: false) + case rv + when :wait_readable + IO.select([s1], nil, nil, 1) + when :wait_writable + IO.select(nil, [s1], nil, 1) + else + assert_same(s1, rv) + break + end + end + s1.puts("abc") + assert_equal("abc\n", s1.gets) + ensure + sock&.close + end + end + + def test_connect_accept_nonblock + server_proc = proc do |sock| + s2 = OpenSSL::SSL::SSLSocket.new(sock, make_server_context) + rv = 5.times { + begin + break s2.accept_nonblock + rescue IO::WaitReadable + IO.select([s2], nil, nil, 1) + rescue IO::WaitWritable + IO.select(nil, [s2], nil, 1) + end + } + assert_same(s2, rv) + assert_equal("a\n", s2.gets) + s2.puts("b") + end + start_server_proc(server_proc) do |port| + sock = TCPSocket.new("127.0.0.1", port) + s1 = OpenSSL::SSL::SSLSocket.new(sock) + rv = 5.times { + begin + break s1.connect_nonblock + rescue IO::WaitReadable + IO.select([s1], nil, nil, 1) + rescue IO::WaitWritable + IO.select(nil, [s1], nil, 1) + end + } + assert_same(s1, rv) + s1.print "a\ndef" + assert_equal("b\n", s1.gets) + ensure + sock&.close + end + end + + def test_low_level_socket + start_server do |port| + sock = Socket.tcp("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.connect + ssl.puts("abc") + assert_equal("abc\n", ssl.gets) + ensure + ssl&.close + sock&.close + end + end + def test_socket_open start_server { |port| begin @@ -158,37 +249,11 @@ def test_socket_open_with_local_address_port_context } end - def test_socket_close_write - server_proc = proc do |ctx, ssl| - message = ssl.read - ssl.write(message) - ssl.close_write - ensure - ssl.close - end - - start_server(server_proc: server_proc) do |port| - ctx = OpenSSL::SSL::SSLContext.new - ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx) - ssl.sync_close = true - ssl.connect - - message = "abc"*1024 - ssl.write message - ssl.close_write - assert_equal message, ssl.read - ensure - ssl&.close - end - end - def test_add_certificate - ctx_proc = -> ctx { - # Unset values set by start_server - ctx.cert = ctx.key = ctx.extra_chain_cert = nil - ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA + + start_server(sctx) do |port| server_connect(port) { |ssl| assert_equal @svr_cert.subject, ssl.peer_cert.subject assert_equal [@svr_cert.subject, @ca_cert.subject], @@ -215,13 +280,11 @@ def test_add_certificate_multiple_certs ecdsa_dn = OpenSSL::X509::Name.parse_rfc2253("CN=localhost2") ecdsa_cert = issue_cert(ecdsa_dn, ecdsa_key, 456, exts, ca2_cert, ca2_key) - ctx_proc = -> ctx { - # Unset values set by start_server - ctx.cert = ctx.key = ctx.extra_chain_cert = nil - ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA - ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert]) - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA + sctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert]) + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = :TLS1_2 # TODO: We need this to force certificate type ctx.ciphers = "aECDSA" @@ -253,14 +316,12 @@ def test_extra_chain_cert_auto_chain # AWS-LC enables SSL_MODE_NO_AUTO_CHAIN by default unless aws_lc? - ctx_proc = -> ctx { - # Sanity check: start_server won't set extra_chain_cert - assert_nil ctx.extra_chain_cert - ctx.cert_store = OpenSSL::X509::Store.new.tap { |store| - store.add_cert(@ca_cert) - } + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key) # no extra certs + sctx.cert_store = OpenSSL::X509::Store.new.tap { |store| + store.add_cert(@ca_cert) } - start_server(ctx_proc: ctx_proc) { |port| + start_server(sctx) { |port| server_connect(port) { |ssl| ssl.puts "abc"; assert_equal "abc\n", ssl.gets assert_equal @svr_cert.to_der, ssl.peer_cert.to_der @@ -270,27 +331,6 @@ def test_extra_chain_cert_auto_chain end end - def test_sysread_and_syswrite - start_server { |port| - server_connect(port) { |ssl| - str = +("x" * 100 + "\n") - ssl.syswrite(str) - newstr = ssl.sysread(str.bytesize) - assert_equal(str, newstr) - - buf = String.new - ssl.syswrite(str) - assert_same buf, ssl.sysread(str.size, buf) - assert_equal(str, buf) - - obj = Object.new - obj.define_singleton_method(:to_str) { str } - ssl.syswrite(obj) - assert_equal(str, ssl.sysread(str.bytesize)) - } - } - end - def test_read_with_timeout omit "does not support timeout" unless IO.method_defined?(:timeout) @@ -314,30 +354,30 @@ def test_read_with_timeout end end - def test_getbyte - start_server { |port| - server_connect(port) { |ssl| - str = +("x" * 100 + "\n") - ssl.syswrite(str) - newstr = str.bytesize.times.map { |i| - ssl.getbyte - }.pack("C*") - assert_equal(str, newstr) - } - } - end + def test_partial_tls_record_read_nonblock + written = Thread::Queue.new + read = Thread::Queue.new + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, make_server_context).accept + str = ssl.gets + ssl.puts(str) - def test_readbyte - start_server { |port| - server_connect(port) { |ssl| - str = +("x" * 100 + "\n") - ssl.syswrite(str) - newstr = str.bytesize.times.map { |i| - ssl.readbyte - }.pack("C*") - assert_equal(str, newstr) - } - } + # the beginning of a TLS record + ssl.io.write("\x17") + written << :written + read.pop + end + start_server_proc(server_proc) do |port| + server_connect(port) do |ssl| + ssl.puts("abc") + assert_equal("abc\n", ssl.gets) + written.pop + # should raise a IO::WaitReadable since a full TLS record is not available + # for reading + assert_raise(IO::WaitReadable) { ssl.send(:sysread_nonblock, 1) } + read << :done + end + end end def test_sync_close @@ -383,20 +423,6 @@ def test_sync_close_initialize_opt end end - def test_copy_stream - start_server do |port| - server_connect(port) do |ssl| - IO.pipe do |r, w| - str = "hello world\n" - w.write(str) - IO.copy_stream(r, ssl, str.bytesize) - IO.copy_stream(ssl, w, str.bytesize) - assert_equal str, r.read(str.bytesize) - end - end - end - end - def test_verify_mode_default ctx = OpenSSL::SSL::SSLContext.new assert_equal OpenSSL::SSL::VERIFY_NONE, ctx.verify_mode @@ -436,36 +462,44 @@ def test_verify_mode_server_cert def test_verify_mode_client_cert_required # Optional, client certificate not supplied - vflag = OpenSSL::SSL::VERIFY_PEER - accept_proc = -> ssl { + sctx = make_server_context + sctx.verify_mode = OpenSSL::SSL::VERIFY_PEER + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx).accept assert_equal nil, ssl.peer_cert - } - start_server(verify_mode: vflag, accept_proc: accept_proc) { |port| + readwrite_loop(ssl) + end + start_server_proc(server_proc) { |port| assert_nothing_raised { server_connect(port) { |ssl| ssl.puts("abc"); ssl.gets } } } # Required, client certificate not supplied - vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - start_server(verify_mode: vflag, ignore_listener_error: true) { |port| - assert_handshake_error { + sctx = make_server_context + sctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + start_server(sctx, ignore_listener_error: true) { |port| + # TLS 1.3 alert: certificate_required(116) + assert_raise_with_message(OpenSSL::SSL::SSLError, /alert number 116/) { server_connect(port) { |ssl| ssl.puts("abc"); ssl.gets } } } end def test_client_auth_success - vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - ctx_proc = proc { |ctx| - store = OpenSSL::X509::Store.new - store.add_cert(@ca_cert) - store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT - ctx.cert_store = store - # LibreSSL doesn't support client_cert_cb in TLS 1.3 - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl? - } - start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |port| + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + + sctx = make_server_context + sctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + sctx.cert_store = store + # LibreSSL doesn't support client_cert_cb in TLS 1.3 + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl? + + start_server(sctx) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.key = @cli_key ctx.cert = @cli_cert @@ -491,8 +525,11 @@ def test_client_auth_success end def test_client_cert_cb_ignore_error - vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - start_server(verify_mode: vflag, ignore_listener_error: true) do |port| + sctx = make_server_context + sctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + + start_server(sctx, ignore_listener_error: true) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.client_cert_cb = -> ssl { raise "exception in client_cert_cb must be suppressed" @@ -500,7 +537,7 @@ def test_client_cert_cb_ignore_error # 1. Exception in client_cert_cb is suppressed # 2. No client certificate will be sent to the server # 3. SSL_VERIFY_FAIL_IF_NO_PEER_CERT causes the handshake to fail - assert_handshake_error { + assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets } } end @@ -509,16 +546,16 @@ def test_client_cert_cb_ignore_error def test_client_ca pend "LibreSSL doesn't support certificate_authorities" if libressl? - ctx_proc = Proc.new do |ctx| - store = OpenSSL::X509::Store.new - store.add_cert(@ca_cert) - store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT - ctx.cert_store = store - ctx.client_ca = [@ca_cert] - end + sctx = make_server_context + sctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + sctx.cert_store = store + sctx.client_ca = [@ca_cert] - vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |port| + start_server(sctx) { |port| ctx = OpenSSL::SSL::SSLContext.new client_ca_from_server = nil ctx.client_cert_cb = Proc.new do |sslconn| @@ -680,13 +717,14 @@ def test_finished_messages client_finished = nil client_peer_finished = nil - start_server(accept_proc: proc { |server| - server_finished = server.finished_message - server_peer_finished = server.peer_finished_message - }) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE - server_connect(port, ctx) { |ssl| + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, make_server_context).accept + server_finished = ssl.finished_message + server_peer_finished = ssl.peer_finished_message + readwrite_loop(ssl) + end + start_server_proc(server_proc) { |port| + server_connect(port) { |ssl| ssl.puts "abc"; ssl.gets client_finished = ssl.finished_message @@ -717,14 +755,13 @@ def test_post_connect_check_with_anon_ciphers omit_on_fips omit "AWS-LC does not support DHE ciphersuites" if aws_lc? - ctx_proc = -> ctx { - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - ctx.ciphers = "aNULL" - ctx.tmp_dh = Fixtures.pkey("dh-1") - ctx.security_level = 0 - } + sctx = OpenSSL::SSL::SSLContext.new + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + sctx.ciphers = "aNULL" + sctx.tmp_dh = Fixtures.pkey("dh-1") + sctx.security_level = 0 - start_server(ctx_proc: ctx_proc) { |port| + start_server(sctx) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION ctx.ciphers = "aNULL" @@ -991,14 +1028,6 @@ def create_null_byte_SAN_certificate(critical = false) cert end - def socketpair - if defined? UNIXSocket - UNIXSocket.pair - else - Socket.pair(Socket::AF_INET, Socket::SOCK_STREAM, 0) - end - end - def test_keylog_cb omit "Keylog callback is not supported" if libressl? @@ -1050,19 +1079,18 @@ def test_tlsext_hostname fooctx.cert = @cli_cert fooctx.key = @cli_key - ctx_proc = proc { |ctx| - ctx.servername_cb = proc { |ssl, servername| - case servername - when "foo.example.com" - fooctx - when "bar.example.com" - nil - else - raise "unreachable" - end - } + sctx = make_server_context + sctx.servername_cb = proc { |ssl, servername| + case servername + when "foo.example.com" + fooctx + when "bar.example.com" + nil + else + raise "unreachable" + end } - start_server(ctx_proc: ctx_proc) do |port| + start_server(sctx) do |port| sock = TCPSocket.new("127.0.0.1", port) begin ssl = OpenSSL::SSL::SSLSocket.new(sock) @@ -1093,84 +1121,54 @@ def test_tlsext_hostname end def test_servername_cb_exception - sock1, sock2 = socketpair - - t = Thread.new { - s1 = OpenSSL::SSL::SSLSocket.new(sock1) - s1.hostname = "localhost" + server_proc = proc do |sock| + sctx = make_server_context + sctx.servername_cb = lambda { |args| raise RuntimeError, "foo" } + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx) + assert_raise_with_message(RuntimeError, "foo") { ssl.accept } + end + start_server_proc(server_proc) do |port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.hostname = "example.org" assert_raise_with_message(OpenSSL::SSL::SSLError, /unrecognized.name/i) { - s1.connect + ssl.connect } - } - - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.servername_cb = lambda { |args| raise RuntimeError, "foo" } - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - assert_raise_with_message(RuntimeError, "foo") { s2.accept } - assert t.join - ensure - sock1.close - sock2.close - t.kill.join + ensure + sock&.close + end end def test_servername_cb_raises_an_exception_on_unknown_objects - sock1, sock2 = socketpair - - t = Thread.new { - s1 = OpenSSL::SSL::SSLSocket.new(sock1) - s1.hostname = "localhost" - assert_raise(OpenSSL::SSL::SSLError) { s1.connect } - } - - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.servername_cb = lambda { |args| Object.new } - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - assert_raise(ArgumentError) { s2.accept } - assert t.join - ensure - sock1.close - sock2.close - t.kill.join - end - - def test_accept_errors_include_peeraddr - context = OpenSSL::SSL::SSLContext.new - context.cert = @svr_cert - context.key = @svr_key - - server = TCPServer.new("127.0.0.1", 0) - port = server.connect_address.ip_port - - ssl_server = OpenSSL::SSL::SSLServer.new(server, context) - - t = Thread.new do - assert_raise_with_message(OpenSSL::SSL::SSLError, /peeraddr=127\.0\.0\.1/) do - ssl_server.accept - end + server_proc = proc do |sock| + sctx = make_server_context + sctx.servername_cb = lambda { |args| Object.new } + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx) + assert_raise(ArgumentError) { ssl.accept } + end + start_server_proc(server_proc) do |port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.hostname = "example.org" + assert_raise_with_message(OpenSSL::SSL::SSLError, /unrecognized.name/i) { + ssl.connect + } + ensure + sock&.close end - - sock = TCPSocket.new("127.0.0.1", port) - sock << "\x00" * 1024 - - assert t.join - ensure - sock&.close - server.close end def test_verify_hostname_on_connect - ctx_proc = proc { |ctx| - exts = [ - ["keyUsage", "keyEncipherment,digitalSignature", true], - ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \ - "DNS:c*.example.com,DNS:d.*.example.com"], - ] - ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) - ctx.key = @svr_key - } + exts = [ + ["keyUsage", "keyEncipherment,digitalSignature", true], + ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \ + "DNS:c*.example.com,DNS:d.*.example.com"], + ] + cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(cert, @svr_key) - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + start_server(sctx, ignore_listener_error: true) do |port| ctx = OpenSSL::SSL::SSLContext.new assert_equal false, ctx.verify_hostname ctx.verify_hostname = true @@ -1206,16 +1204,15 @@ def test_verify_hostname_on_connect end def test_verify_hostname_failure_error_code - ctx_proc = proc { |ctx| - exts = [ - ["keyUsage", "keyEncipherment,digitalSignature", true], - ["subjectAltName", "DNS:a.example.com"], - ] - ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) - ctx.key = @svr_key - } + exts = [ + ["keyUsage", "keyEncipherment,digitalSignature", true], + ["subjectAltName", "DNS:a.example.com"], + ] + cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(cert, @svr_key) - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + start_server(sctx, ignore_listener_error: true) do |port| verify_callback_ok = verify_callback_err = nil ctx = OpenSSL::SSL::SSLContext.new @@ -1242,6 +1239,27 @@ def test_verify_hostname_failure_error_code end end + def test_connect_systemcallerror + # SSL_connect() should fail with SSL_ERROR_SYSCALL and errno should be + # kept intact from the underlying recv(2)/send(2). + pend "AWS-LC does not preserve errno on SSL_ERROR_SYSCALL" if aws_lc? + + server_proc = proc do |sock| + sock.setsockopt(:SOCKET, :LINGER, [1, 0].pack("ii")) + sock.read(1) + sock.close + end + start_server_proc(server_proc) do |port| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + assert_raise(Errno::ECONNRESET, Errno::EPIPE) { + ssl.connect + } + ensure + sock&.close + end + end + def test_connect_certificate_verify_failed_exception_message start_server(ignore_listener_error: true) { |port| ctx = OpenSSL::SSL::SSLContext.new @@ -1251,12 +1269,13 @@ def test_connect_certificate_verify_failed_exception_message } } - ctx_proc = proc { |ctx| - now = Time.now - ctx.cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key, - not_before: now - 7200, not_after: now - 3600) - } - start_server(ignore_listener_error: true, ctx_proc: ctx_proc) { |port| + now = Time.now + cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key, + not_before: now - 7200, not_after: now - 3600) + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(cert, @svr_key) + + start_server(sctx, ignore_listener_error: true) { |port| store = OpenSSL::X509::Store.new store.add_cert(@ca_cert) ctx = OpenSSL::SSL::SSLContext.new @@ -1267,6 +1286,16 @@ def test_connect_certificate_verify_failed_exception_message } end + def test_connect_exception_message_include_peeraddr + start_server(ignore_listener_error: true) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER + assert_raise_with_message(OpenSSL::SSL::SSLError, /peeraddr=127\.0\.0\.1/) do + server_connect(port, ctx) { } + end + end + end + def check_supported_protocol_versions possible_versions = [ OpenSSL::SSL::SSL3_VERSION, @@ -1275,16 +1304,16 @@ def check_supported_protocol_versions OpenSSL::SSL::TLS1_2_VERSION, OpenSSL::SSL::TLS1_3_VERSION, ] - supported = [] - ctx_proc = proc { |ctx| - # The default security level is 1 in OpenSSL <= 3.1, 2 in OpenSSL >= 3.2 - # In OpenSSL >= 3.0, TLS 1.1 or older is disabled at level 1 - ctx.security_level = 0 - # Explicitly reset them to avoid influenced by OPENSSL_CONF - ctx.min_version = ctx.max_version = nil - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + + sctx = make_server_context + # The default security level is 1 in OpenSSL <= 3.1, 2 in OpenSSL >= 3.2 + # In OpenSSL >= 3.0, TLS 1.1 or older is disabled at level 1 + sctx.security_level = 0 + # Explicitly reset them to avoid influenced by OPENSSL_CONF + sctx.min_version = sctx.max_version = nil + + start_server(sctx, ignore_listener_error: true) do |port| possible_versions.each do |ver| ctx = OpenSSL::SSL::SSLContext.new ctx.security_level = 0 @@ -1293,7 +1322,7 @@ def check_supported_protocol_versions ssl.puts "abc"; assert_equal "abc\n", ssl.gets } supported << ver - rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET + rescue OpenSSL::SSL::SSLError end end @@ -1306,20 +1335,19 @@ def check_supported_protocol_versions def test_set_params_min_version supported = check_supported_protocol_versions - store = OpenSSL::X509::Store.new - store.add_cert(@ca_cert) + return unless supported.include?(OpenSSL::SSL::SSL3_VERSION) - if supported.include?(OpenSSL::SSL::SSL3_VERSION) - # SSLContext#set_params properly disables SSL 3.0 by default - ctx_proc = proc { |ctx| - ctx.min_version = ctx.max_version = OpenSSL::SSL::SSL3_VERSION - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.set_params(cert_store: store, verify_hostname: false) - assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) } - } - end + # SSLContext#set_params properly disables SSL 3.0 by default + sctx = make_server_context + sctx.min_version = sctx.max_version = OpenSSL::SSL::SSL3_VERSION + + start_server(sctx, ignore_listener_error: true) { |port| + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params(cert_store: store, verify_hostname: false) + assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) } + } end def test_minmax_version @@ -1338,11 +1366,11 @@ def test_minmax_version # Server enables a single version supported.each do |ver| - ctx_proc = proc { |ctx| - ctx.security_level = 0 - ctx.min_version = ctx.max_version = ver - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.security_level = 0 + sctx.min_version = sctx.max_version = ver + + start_server(sctx, ignore_listener_error: true) { |port| supported.each do |cver| # Client enables a single version ctx1 = OpenSSL::SSL::SSLContext.new @@ -1391,11 +1419,11 @@ def test_minmax_version # Server sets min_version (earliest is disabled) sver = supported[1] - ctx_proc = proc { |ctx| - ctx.security_level = 0 - ctx.min_version = sver - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.security_level = 0 + sctx.min_version = sver + + start_server(sctx, ignore_listener_error: true) { |port| supported.each do |cver| # Client sets min_version ctx1 = OpenSSL::SSL::SSLContext.new @@ -1425,12 +1453,12 @@ def test_minmax_version # Server sets max_version (latest is disabled) sver = supported[-2] - ctx_proc = proc { |ctx| - ctx.security_level = 0 - ctx.min_version = 0 - ctx.max_version = sver - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.security_level = 0 + sctx.min_version = 0 + sctx.max_version = sver + + start_server(sctx, ignore_listener_error: true) { |port| supported.each do |cver| # Client sets min_version ctx1 = OpenSSL::SSL::SSLContext.new @@ -1520,10 +1548,9 @@ def test_respect_system_default_min EOF f.close - ctx_proc = proc { |ctx| - ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + sctx = make_server_context + sctx.min_version = sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + start_server(sctx, ignore_listener_error: true) do |port| assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;") sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i) ctx = OpenSSL::SSL::SSLContext.new @@ -1536,10 +1563,9 @@ def test_respect_system_default_min end; end - ctx_proc = proc { |ctx| - ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + sctx = make_server_context + sctx.min_version = sctx.max_version = OpenSSL::SSL::TLS1_3_VERSION + start_server(sctx, ignore_listener_error: true) do |port| assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;") sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i) ctx = OpenSSL::SSL::SSLContext.new @@ -1566,12 +1592,11 @@ def test_options_disable_versions end # Server disables TLS 1.2 and earlier - ctx_proc = proc { |ctx| - ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | - OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 | - OpenSSL::SSL::OP_NO_TLSv1_2 - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | + OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 | + OpenSSL::SSL::OP_NO_TLSv1_2 + start_server(sctx, ignore_listener_error: true) { |port| # Client only supports TLS 1.2 ctx1 = OpenSSL::SSL::SSLContext.new ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION @@ -1584,10 +1609,9 @@ def test_options_disable_versions } # Server only supports TLS 1.2 - ctx_proc = proc { |ctx| - ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.min_version = sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + start_server(sctx, ignore_listener_error: true) { |port| # Client doesn't support TLS 1.2 ctx1 = OpenSSL::SSL::SSLContext.new ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_2 @@ -1613,9 +1637,13 @@ def test_ssl_methods_constant def test_renegotiation_cb num_handshakes = 0 - renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 } - ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb } - start_server(ctx_proc: ctx_proc) { |port| + sctx = make_server_context + sctx.renegotiation_cb = -> ssl { + num_handshakes += 1 + assert_kind_of(OpenSSL::SSL::SSLSocket, ssl) + } + + start_server(sctx) { |port| server_connect(port) { |ssl| assert_equal(1, num_handshakes) ssl.puts "abc"; assert_equal "abc\n", ssl.gets @@ -1625,13 +1653,10 @@ def test_renegotiation_cb def test_alpn_protocol_selection_ary advertised = ["http/1.1", "spdy/2"] - ctx_proc = Proc.new { |ctx| - ctx.alpn_select_cb = -> (protocols) { - protocols.first - } - ctx.alpn_protocols = advertised - } - start_server(ctx_proc: ctx_proc) { |port| + sctx = make_server_context + sctx.alpn_select_cb = -> protocols { protocols.first } + + start_server(sctx) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.alpn_protocols = advertised server_connect(port, ctx) { |ssl| @@ -1642,38 +1667,30 @@ def test_alpn_protocol_selection_ary end def test_alpn_protocol_selection_cancel - sock1, sock2 = socketpair - - ctx1 = OpenSSL::SSL::SSLContext.new - ctx1.cert = @svr_cert - ctx1.key = @svr_key - ctx1.alpn_select_cb = -> (protocols) { nil } - ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) - - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.alpn_protocols = ["http/1.1"] - ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - - t = Thread.new { - ssl2.connect_nonblock(exception: false) - } - assert_raise_with_message(TypeError, /nil/) { ssl1.accept } - t.join - ensure - sock1&.close - sock2&.close - ssl1&.close - ssl2&.close - t&.kill - t&.join + server_proc = proc do |sock| + sctx = make_server_context + sctx.alpn_select_cb = -> (protocols) { nil } + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx) + assert_raise_with_message(TypeError, /nil/) { ssl.accept } + end + start_server_proc(server_proc) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.alpn_protocols = ["http/1.1"] + # no_application_protocol alert + assert_raise_with_message(OpenSSL::SSL::SSLError, /alert number 120/) { + server_connect(port, ctx) { } + } + end end def test_npn_protocol_selection_ary return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) advertised = ["http/1.1", "spdy/2"] - ctx_proc = proc { |ctx| ctx.npn_protocols = advertised } - start_server(ctx_proc: ctx_proc) { |port| + sctx = make_server_context + sctx.npn_protocols = advertised + + start_server(sctx) { |port| selector = lambda { |which| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = :TLS1_2 @@ -1695,8 +1712,10 @@ def advertised.each yield "http/1.1" yield "spdy/2" end - ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised } - start_server(ctx_proc: ctx_proc) { |port| + sctx = make_server_context + sctx.npn_protocols = advertised + + start_server(sctx) { |port| selector = lambda { |selected, which| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = :TLS1_2 @@ -1713,8 +1732,10 @@ def advertised.each def test_npn_protocol_selection_cancel return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) - ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.npn_protocols = ["http/1.1"] + + start_server(sctx, ignore_listener_error: true) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = :TLS1_2 ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new } @@ -1735,8 +1756,10 @@ def test_npn_advertised_protocol_too_long def test_npn_selected_protocol_too_long return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) - ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + sctx = make_server_context + sctx.npn_protocols = ["http/1.1"] + + start_server(sctx, ignore_listener_error: true) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = :TLS1_2 ctx.npn_select_cb = -> (protocols) { "a" * 256 } @@ -1744,13 +1767,14 @@ def test_npn_selected_protocol_too_long } end - def readwrite_loop_safe(ctx, ssl) - readwrite_loop(ctx, ssl) - rescue OpenSSL::SSL::SSLError - end - def test_close_after_socket_close - start_server(server_proc: method(:readwrite_loop_safe)) { |port| + # The client closes the TCP socket without SSLSocket#stop + sctx = make_server_context + if defined?(OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF) + sctx.options |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF + end + + start_server(sctx) { |port| sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect @@ -1776,11 +1800,10 @@ def test_get_ephemeral_key omit_on_fips # kRSA - ctx_proc1 = proc { |ctx| - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - ctx.ciphers = "kRSA" - } - start_server(ctx_proc: ctx_proc1, ignore_listener_error: true) do |port| + sctx = make_server_context + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + sctx.ciphers = "kRSA" + start_server(sctx, ignore_listener_error: true) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION ctx.ciphers = "kRSA" @@ -1809,10 +1832,9 @@ def test_get_ephemeral_key end # ECDHE - ctx_proc3 = proc { |ctx| - ctx.groups = "P-256" - } - start_server(ctx_proc: ctx_proc3) do |port| + sctx = make_server_context + sctx.groups = "P-256" + start_server(sctx) do |port| server_connect(port) { |ssl| assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key ssl.puts "abc"; assert_equal "abc\n", ssl.gets @@ -1836,12 +1858,11 @@ def test_fallback_scsv server_connect(port, ctx) end - ctx_proc = proc { |ctx| - ctx.security_level = 0 - ctx.min_version = 0 - ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.security_level = 0 + sctx.min_version = 0 + sctx.max_version = OpenSSL::SSL::TLS1_1_VERSION + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.enable_fallback_scsv ctx.security_level = 0 @@ -1856,8 +1877,7 @@ def test_fallback_scsv # Here is not OK # TLS1.2 is supported, fallback to TLS1.1 (downgrade attack) and signaling the fallback # Server support better, so refuse the connection - sock1, sock2 = socketpair - begin + server_proc = proc do |sock| # This test is for the downgrade protection mechanism of TLS1.2. # This is why ctx1 bounds max_version == TLS1.2. # Otherwise, this test fails when using openssl 1.1.1 (or later) that supports TLS1.3. @@ -1866,27 +1886,21 @@ def test_fallback_scsv ctx1.security_level = 0 ctx1.min_version = 0 ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION - s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) - + s1 = OpenSSL::SSL::SSLSocket.new(sock, ctx1) + # AWS-LC has slightly different error messages in all-caps. + assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate.fallback/i) { + s1.accept + } + end + start_server_proc(server_proc) do |port| ctx2 = OpenSSL::SSL::SSLContext.new ctx2.enable_fallback_scsv ctx2.security_level = 0 ctx2.min_version = 0 ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - # AWS-LC has slightly different error messages in all-caps. - t = Thread.new { - assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback|INAPPROPRIATE_FALLBACK/) { - s2.connect - } + assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate.fallback/i) { + server_connect(port, ctx2) { } } - assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback|INAPPROPRIATE_FALLBACK/) { - s1.accept - } - t.join - ensure - sock1.close - sock2.close end end @@ -1897,15 +1911,16 @@ def test_tmp_dh_callback dh = Fixtures.pkey("dh-1") called = false - ctx_proc = -> ctx { - ctx.max_version = :TLS1_2 - ctx.ciphers = "DH:!NULL" - ctx.tmp_dh_callback = ->(*args) { - called = true - dh - } + + sctx = make_server_context + sctx.max_version = :TLS1_2 + sctx.ciphers = "DH:!NULL" + sctx.tmp_dh_callback = ->(*args) { + called = true + dh } - start_server(ctx_proc: ctx_proc) do |port| + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.groups = "P-256" # Exclude RFC 7919 groups server_connect(port, ctx) { |ssl| @@ -2005,13 +2020,11 @@ def test_sigalgs ecdsa_key = Fixtures.pkey("p256") ecdsa_cert = issue_cert(@svr, ecdsa_key, 10, svr_exts, @ca_cert, @ca_key) - ctx_proc = -> ctx { - # Unset values set by start_server - ctx.cert = ctx.key = ctx.extra_chain_cert = nil - ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA - ctx.add_certificate(ecdsa_cert, ecdsa_key, [@ca_cert]) # ECDSA - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA + sctx.add_certificate(ecdsa_cert, ecdsa_key, [@ca_cert]) # ECDSA + + start_server(sctx) do |port| ctx1 = OpenSSL::SSL::SSLContext.new ctx1.sigalgs = "rsa_pss_rsae_sha256" server_connect(port, ctx1) { |ssl| @@ -2048,18 +2061,19 @@ def test_client_sigalgs ecdsa_key = Fixtures.pkey("p256") ecdsa_cert = issue_cert(@cli, ecdsa_key, 10, cli_exts, @ca_cert, @ca_key) - ctx_proc = -> ctx { - store = OpenSSL::X509::Store.new - store.add_cert(@ca_cert) - store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT - ctx.cert_store = store - ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - ctx.client_sigalgs = "ECDSA+SHA256" - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + sctx = make_server_context + sctx.cert_store = store + sctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + sctx.client_sigalgs = "ECDSA+SHA256" + + start_server(sctx, ignore_listener_error: true) do |port| ctx1 = OpenSSL::SSL::SSLContext.new ctx1.add_certificate(@cli_cert, @cli_key) # RSA - assert_handshake_error { + assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx1) { |ssl| ssl.puts("abc"); ssl.gets } @@ -2078,13 +2092,14 @@ def test_get_sigalg # SSL_get0_peer_signature_name() not supported return unless openssl?(3, 5, 0) - server_proc = -> (ctx, ssl) { + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, make_server_context).accept assert_equal('rsa_pss_rsae_sha256', ssl.sigalg) assert_nil(ssl.peer_sigalg) - readwrite_loop(ctx, ssl) - } - start_server(server_proc: server_proc) do |port| + readwrite_loop(ssl) + end + start_server_proc(server_proc) do |port| cli_ctx = OpenSSL::SSL::SSLContext.new server_connect(port, cli_ctx) do |ssl| assert_nil(ssl.sigalg) @@ -2106,20 +2121,18 @@ def test_pqc_sigalg digest: nil) rsa = Fixtures.pkey("rsa-1") rsa_cert = issue_cert(@svr, rsa, 61, [], @ca_cert, @ca_key) - ctx_proc = -> ctx { - # Unset values set by start_server - ctx.cert = ctx.key = ctx.extra_chain_cert = nil - ctx.sigalgs = "rsa_pss_rsae_sha256:mldsa65" - ctx.add_certificate(mldsa_cert, mldsa) - ctx.add_certificate(rsa_cert, rsa) - } - server_proc = -> (ctx, ssl) { - assert_equal('mldsa65', ssl.sigalg) + sctx = OpenSSL::SSL::SSLContext.new + sctx.sigalgs = "rsa_pss_rsae_sha256:mldsa65" + sctx.add_certificate(mldsa_cert, mldsa) + sctx.add_certificate(rsa_cert, rsa) - readwrite_loop(ctx, ssl) - } - start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx).accept + assert_equal('mldsa65', ssl.sigalg) + readwrite_loop(ssl) + end + start_server_proc(server_proc) do |port| ctx = OpenSSL::SSL::SSLContext.new # Set signature algorithm because while OpenSSL may use ML-DSA by # default, the system OpenSSL configuration affects the used signature @@ -2131,12 +2144,12 @@ def test_pqc_sigalg } end - server_proc = -> (ctx, ssl) { + server_proc = proc do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx).accept assert_equal('rsa_pss_rsae_sha256', ssl.sigalg) - - readwrite_loop(ctx, ssl) - } - start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| + readwrite_loop(ssl) + end + start_server_proc(server_proc) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.sigalgs = 'rsa_pss_rsae_sha256' server_connect(port, ctx) { |ssl| @@ -2149,12 +2162,12 @@ def test_pqc_sigalg def test_connect_works_when_setting_dh_callback_to_nil omit "AWS-LC does not support DHE ciphersuites" if aws_lc? - ctx_proc = -> ctx { - ctx.max_version = :TLS1_2 - ctx.ciphers = "DH:!NULL" # use DH - ctx.tmp_dh_callback = nil - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.max_version = :TLS1_2 + sctx.ciphers = "DH:!NULL" # use DH + sctx.tmp_dh_callback = nil + + start_server(sctx) do |port| assert_nothing_raised { server_connect(port) { } } end end @@ -2165,12 +2178,12 @@ def test_tmp_dh omit "AWS-LC does not support DHE ciphersuites" if aws_lc? dh = Fixtures.pkey("dh-1") - ctx_proc = -> ctx { - ctx.max_version = :TLS1_2 - ctx.ciphers = "DH:!NULL" # use DH - ctx.tmp_dh = dh - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.max_version = :TLS1_2 + sctx.ciphers = "DH:!NULL" # use DH + sctx.tmp_dh = dh + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.groups = "P-256" # Exclude RFC 7919 groups server_connect(port, ctx) { |ssl| @@ -2180,13 +2193,13 @@ def test_tmp_dh end def test_set_groups_tls12 - ctx_proc = -> ctx { - # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3 - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - ctx.ciphers = "kEECDH" - ctx.groups = "P-384:P-521" - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3 + sctx = make_server_context + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + sctx.ciphers = "kEECDH" + sctx.groups = "P-384:P-521" + + start_server(sctx, ignore_listener_error: true) do |port| # Test 1: Client=P-256:P-384, Server=P-384:P-521 --> P-384 ctx = OpenSSL::SSL::SSLContext.new ctx.groups = "P-256:P-384" @@ -2224,11 +2237,11 @@ def test_set_groups_tls12 end def test_set_groups_tls13 - ctx_proc = -> ctx { - # Assume TLS 1.3 is enabled and chosen by default - ctx.groups = "P-384:P-521" - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + # Assume TLS 1.3 is enabled and chosen by default + sctx = make_server_context + sctx.groups = "P-384:P-521" + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.groups = "P-256:P-384" # disable P-521 @@ -2251,10 +2264,10 @@ def test_pqc_group 'SecP256r1MLKEM768', 'SecP384r1MLKEM1024' ].each do |group| - ctx_proc = -> ctx { - ctx.groups = group - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.groups = group + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.groups = group server_connect(port, ctx) { |ssl| @@ -2303,15 +2316,12 @@ def test_security_level def test_dup ctx = OpenSSL::SSL::SSLContext.new - sock1, sock2 = socketpair - ssl = OpenSSL::SSL::SSLSocket.new(sock1, ctx) + Socket.open(:INET, :STREAM) { |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - assert_raise(NoMethodError) { ctx.dup } - assert_raise(NoMethodError) { ssl.dup } - ensure - ssl.close if ssl - sock1.close - sock2.close + assert_raise(NoMethodError) { ctx.dup } + assert_raise(NoMethodError) { ssl.dup } + } end def test_freeze_calls_setup @@ -2327,17 +2337,14 @@ def test_freeze_calls_setup end def test_fileno - ctx = OpenSSL::SSL::SSLContext.new - sock1, sock2 = socketpair - - socket = OpenSSL::SSL::SSLSocket.new(sock1) - server = OpenSSL::SSL::SSLServer.new(sock2, ctx) + Socket.open(:INET, :STREAM) { |sock| + ctx = OpenSSL::SSL::SSLContext.new + ssl = OpenSSL::SSL::SSLSocket.new(sock) + server = OpenSSL::SSL::SSLServer.new(sock, ctx) - assert_equal socket.fileno, socket.to_io.fileno - assert_equal server.fileno, server.to_io.fileno - ensure - sock1.close - sock2.close + assert_equal(sock.fileno, ssl.fileno) + assert_equal(sock.fileno, server.fileno) + } end def test_export_keying_material @@ -2412,14 +2419,6 @@ def server_connect(port, ctx = nil) sock.close end end - - def assert_handshake_error - # different OpenSSL versions react differently when facing a SSL/TLS version - # that has been marked as forbidden, therefore any of these may be raised - assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::EPIPE) { - yield - } - end end end diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb index 37874ca27..ab6379385 100644 --- a/test/openssl/test_ssl_session.rb +++ b/test/openssl/test_ssl_session.rb @@ -5,10 +5,10 @@ class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase def test_session - ctx_proc = proc { |ctx| - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + + start_server(sctx) do |port| server_connect_with_session(port, nil, nil) { |ssl| session = ssl.session assert(session == OpenSSL::SSL::Session.new(session.to_pem)) @@ -120,14 +120,15 @@ def test_resumption } } - ctx_proc = proc { |ctx| - ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET - # Disable server-side session cache which is enabled by default - ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF - # Session tickets must be retrieved via ctx.session_new_cb in TLS 1.3 in AWS-LC. - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl? || aws_lc? - } - start_server(ctx_proc: ctx_proc) do |port| + sctx = make_server_context + sctx.options &= ~OpenSSL::SSL::OP_NO_TICKET + # Disable server-side session cache which is enabled by default + sctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF + # Session tickets must be retrieved via ctx.session_new_cb in TLS 1.3 in + # AWS-LC. + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl? || aws_lc? + + start_server(sctx) do |port| sess1 = server_connect_with_session(port, nil, nil) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal false, ssl.session_reused? @@ -147,14 +148,16 @@ def test_resumption end def test_server_session_cache - ctx_proc = Proc.new do |ctx| - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - ctx.options |= OpenSSL::SSL::OP_NO_TICKET - end + sctx = make_server_context + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + sctx.options |= OpenSSL::SSL::OP_NO_TICKET connections = nil saved_session = nil - server_proc = Proc.new do |ctx, ssl| + + server_proc = Proc.new do |sock| + ssl = OpenSSL::SSL::SSLSocket.new(sock, sctx).accept + ctx = ssl.context stats = ctx.session_cache_stats case connections @@ -194,10 +197,9 @@ def test_server_session_cache assert_equal true, ctx.session_add(saved_session.dup) end - readwrite_loop(ctx, ssl) + readwrite_loop(ssl) end - - start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| + start_server_proc(server_proc) do |port| first_session = nil 10.times do |i| connections = i @@ -285,11 +287,12 @@ def test_ctx_client_session_cb_tls13 def test_ctx_client_session_cb_tls13_exception omit "LibreSSL does not call session_new_cb in TLS 1.3" if libressl? - server_proc = lambda do |ctx, ssl| - readwrite_loop(ctx, ssl) - rescue SystemCallError, OpenSSL::SSL::SSLError + sctx = make_server_context + if defined?(OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF) + sctx.options |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF end - start_server(server_proc: server_proc) do |port| + + start_server(sctx) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.min_version = :TLS1_3 ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT @@ -308,42 +311,42 @@ def test_ctx_client_session_cb_tls13_exception def test_ctx_server_session_cb connections = nil called = {} + cctx = OpenSSL::SSL::SSLContext.new cctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - sctx = nil - ctx_proc = Proc.new { |ctx| - sctx = ctx - ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION - ctx.options |= OpenSSL::SSL::OP_NO_TICKET - - # get_cb is called whenever a client proposed to resume a session but - # the session could not be found in the internal session cache. - last_server_session = nil - ctx.session_get_cb = lambda { |ary| - _sess, data = ary - called[:get] = data - - if connections == 2 - last_server_session.dup - else - nil - end - } - ctx.session_new_cb = lambda { |ary| - _sock, sess = ary - called[:new] = sess - last_server_session = sess - } - - if TEST_SESSION_REMOVE_CB - ctx.session_remove_cb = lambda { |ary| - _ctx, sess = ary - called[:remove] = sess - } + sctx = make_server_context + sctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + sctx.options |= OpenSSL::SSL::OP_NO_TICKET + + # get_cb is called whenever a client proposed to resume a session but + # the session could not be found in the internal session cache. + last_server_session = nil + sctx.session_get_cb = lambda { |ary| + _sess, data = ary + called[:get] = data + + if connections == 2 + last_server_session.dup + else + nil end } - start_server(ctx_proc: ctx_proc) do |port| + + sctx.session_new_cb = lambda { |ary| + _sock, sess = ary + called[:new] = sess + last_server_session = sess + } + + if TEST_SESSION_REMOVE_CB + sctx.session_remove_cb = lambda { |ary| + _ctx, sess = ary + called[:remove] = sess + } + end + + start_server(sctx) do |port| connections = 0 sess0 = server_connect_with_session(port, cctx, nil) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets diff --git a/test/openssl/ut_eof.rb b/test/openssl/ut_eof.rb index 06aa632a6..807bbd015 100644 --- a/test/openssl/ut_eof.rb +++ b/test/openssl/ut_eof.rb @@ -4,6 +4,17 @@ if defined?(OpenSSL) module OpenSSL::TestEOF + def open_file(content) + ssl_pair { |s1, s2| + begin + th = Thread.new { s2 << content; s2.close } + yield s1 + ensure + th&.join + end + } + end + def test_getbyte_eof open_file("") {|f| assert_nil f.getbyte } end diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index 7e6fe8b16..7dd3a719d 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -190,93 +190,87 @@ def setup @server = nil end - def readwrite_loop(ctx, ssl) + def readwrite_loop(ssl) while line = ssl.gets ssl.write(line) end + ssl.close end - def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, - ctx_proc: nil, server_proc: method(:readwrite_loop), - accept_proc: proc{}, - ignore_listener_error: false, &block) - IO.pipe {|stop_pipe_r, stop_pipe_w| - ctx = OpenSSL::SSL::SSLContext.new - ctx.cert = @svr_cert - ctx.key = @svr_key - ctx.verify_mode = verify_mode - ctx_proc.call(ctx) if ctx_proc - - Socket.do_not_reverse_lookup = true + def make_server_context + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key) + sctx + end + + def start_server_proc(server_proc, &block) + IO.pipe do |stop_pipe_r, stop_pipe_w| tcps = TCPServer.new("127.0.0.1", 0) + tcps.setsockopt(:TCP, :NODELAY, 1) port = tcps.connect_address.ip_port - ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) - threads = [] - begin - server_thread = Thread.new do - Thread.current.report_on_exception = false - - begin - loop do - begin - readable, = IO.select([ssls, stop_pipe_r]) - break if readable.include? stop_pipe_r - ssl = ssls.accept - accept_proc.call(ssl) - rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL, - Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET - retry if ignore_listener_error - raise - end - - th = Thread.new do - Thread.current.report_on_exception = false - - begin - server_proc.call(ctx, ssl) - ensure - ssl.close - end - true - end - threads << th - end - ensure - tcps.close - end - end + sockets = [] + server_thread = Thread.new do + Thread.current.report_on_exception = false + + loop do + readable, = IO.select([tcps, stop_pipe_r]) + break if readable.include? stop_pipe_r + sockets << sock = tcps.accept - client_thread = Thread.new do - Thread.current.report_on_exception = false + th = Thread.new do + Thread.current.report_on_exception = false - begin - block.call(port) - ensure - # Stop accepting new connection - stop_pipe_w.close - server_thread.join + server_proc.call(sock) end + threads << th end - threads.unshift client_thread ensure - # Terminate existing connections. If a thread did 'pend', re-raise it. - pend = nil - threads.each { |th| - begin - timeout = EnvUtil.apply_timeout_scale(30) - th.join(timeout) or - th.raise(RuntimeError, "[start_server] thread did not exit in #{timeout} secs") - rescue Test::Unit::PendedError - pend = $! - rescue Exception - end - } - raise pend if pend - assert_join_threads(threads) + tcps.close + end + + client_thread = Thread.new do + Thread.current.report_on_exception = false + + block.call(port) + ensure + # Stop accepting new connection + stop_pipe_w.close + server_thread.join + end + threads.unshift client_thread + ensure + # Terminate existing connections. If a thread did 'pend', re-raise it. + pend = nil + threads.each { |th| + begin + timeout = EnvUtil.apply_timeout_scale(30) + th.join(timeout) or + th.raise(RuntimeError, "[start_server] thread did not exit in #{timeout} secs") + rescue Test::Unit::PendedError + pend = $! + rescue Exception + end + } + sockets.each(&:close) + raise pend if pend + assert_join_threads(threads) + end + end + + def start_server(ctx = make_server_context, ignore_listener_error: false, &block) + server_proc = -> sock { + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + begin + ssl.accept + rescue OpenSSL::SSL::SSLError, Errno::ECONNABORTED, Errno::ECONNRESET + next if ignore_listener_error + raise end + readwrite_loop(ssl) } + start_server_proc(server_proc, &block) end end