ruby/test/openssl/test_pair.rb

507 строки
12 KiB
Ruby

# frozen_string_literal: true
require_relative 'utils'
require_relative 'ut_eof'
if defined?(OpenSSL::SSL)
module OpenSSL::SSLPairM
def setup
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
def ssl_pair
host = "127.0.0.1"
tcps = create_tcp_server(host, 0)
port = tcps.connect_address.ip_port
th = Thread.new {
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
}
tcpc = create_tcp_client(host, port)
c = OpenSSL::SSL::SSLSocket.new(tcpc)
c.connect
s = th.value
yield c, s
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
}
end
end
module OpenSSL::TestPairM
def test_getc
ssl_pair {|s1, s2|
s1 << "a"
assert_equal(?a, s2.getc)
}
end
def test_gets
ssl_pair {|s1, s2|
s1 << "abc\n\n$def123ghi"
s1.close
ret = s2.gets
assert_equal Encoding::BINARY, ret.encoding
assert_equal "abc\n", ret
assert_equal "\n$", s2.gets("$")
assert_equal "def123", s2.gets(/\d+/)
assert_equal "ghi", s2.gets
assert_equal nil, s2.gets
}
end
def test_gets_eof_limit
ssl_pair {|s1, s2|
s1.write("hello")
s1.close # trigger EOF
assert_match "hello", s2.gets("\n", 6), "[ruby-core:70149] [Bug #11400]"
}
end
def test_readpartial
ssl_pair {|s1, s2|
s2.write "a\nbcd"
assert_equal("a\n", s1.gets)
result = String.new
result << s1.readpartial(10) until result.length == 3
assert_equal("bcd", result)
s2.write "efg"
result = String.new
result << s1.readpartial(10) until result.length == 3
assert_equal("efg", result)
s2.close
assert_raise(EOFError) { s1.readpartial(10) }
assert_raise(EOFError) { s1.readpartial(10) }
assert_equal("", s1.readpartial(0))
}
end
def test_readall
ssl_pair {|s1, s2|
s2.close
assert_equal("", s1.read)
}
end
def test_readline
ssl_pair {|s1, s2|
s2.close
assert_raise(EOFError) { s1.readline }
}
end
def test_puts_empty
ssl_pair {|s1, s2|
s1.puts
s1.close
assert_equal("\n", s2.read)
}
end
def test_multibyte_read_write
# German a umlaut
auml = [%w{ C3 A4 }.join('')].pack('H*')
auml.force_encoding(Encoding::UTF_8)
bsize = auml.bytesize
ssl_pair { |s1, s2|
assert_equal bsize, s1.write(auml)
read = s2.read(bsize)
assert_equal Encoding::ASCII_8BIT, read.encoding
assert_equal bsize, read.bytesize
assert_equal auml, read.force_encoding(Encoding::UTF_8)
s1.puts(auml)
read = s2.gets
assert_equal Encoding::ASCII_8BIT, read.encoding
assert_equal bsize + 1, read.bytesize
assert_equal auml + "\n", read.force_encoding(Encoding::UTF_8)
}
end
def test_read_nonblock
ssl_pair {|s1, s2|
err = nil
assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) {
begin
s2.read_nonblock(10)
ensure
err = $!
end
}
assert_kind_of(IO::WaitReadable, err)
s1.write "abc\ndef\n"
IO.select([s2])
assert_equal("ab", s2.read_nonblock(2))
assert_equal("c\n", s2.gets)
ret = nil
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
assert_equal("def\n", ret)
s1.close
IO.select([s2])
assert_raise(EOFError) { s2.read_nonblock(10) }
}
end
def test_read_nonblock_no_exception
ssl_pair {|s1, s2|
assert_equal :wait_readable, s2.read_nonblock(10, exception: false)
s1.write "abc\ndef\n"
IO.select([s2])
assert_equal("ab", s2.read_nonblock(2, exception: false))
assert_equal("c\n", s2.gets)
ret = nil
assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
assert_equal("def\n", ret)
s1.close
IO.select([s2])
assert_equal(nil, s2.read_nonblock(10, exception: false))
}
end
def test_read_with_outbuf
ssl_pair { |s1, s2|
s1.write("abc\n")
buf = String.new
ret = s2.read(2, buf)
assert_same ret, buf
assert_equal "ab", ret
buf = +"garbage"
ret = s2.read(2, buf)
assert_same ret, buf
assert_equal "c\n", ret
buf = +"garbage"
assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
assert_equal "", buf
s1.close
buf = +"garbage"
assert_equal nil, s2.read(100, buf)
assert_equal "", buf
}
end
def test_write_nonblock
ssl_pair {|s1, s2|
assert_equal 3, s1.write_nonblock("foo")
assert_equal "foo", s2.read(3)
data = "x" * 16384
written = 0
while true
begin
written += s1.write_nonblock(data)
rescue IO::WaitWritable, IO::WaitReadable
break
end
end
assert written > 0
assert_equal written, s2.read(written).bytesize
}
end
def test_write_nonblock_no_exceptions
ssl_pair {|s1, s2|
assert_equal 3, s1.write_nonblock("foo", exception: false)
assert_equal "foo", s2.read(3)
data = "x" * 16384
written = 0
while true
case ret = s1.write_nonblock(data, exception: false)
when :wait_readable, :wait_writable
break
else
written += ret
end
end
assert written > 0
assert_equal written, s2.read(written).bytesize
}
end
def test_write_nonblock_with_buffered_data
ssl_pair {|s1, s2|
s1.write "foo"
s1.write_nonblock("bar")
s1.write "baz"
s1.close
assert_equal("foobarbaz", s2.read)
}
end
def test_write_nonblock_with_buffered_data_no_exceptions
ssl_pair {|s1, s2|
s1.write "foo"
s1.write_nonblock("bar", exception: false)
s1.write "baz"
s1.close
assert_equal("foobarbaz", s2.read)
}
end
def test_write_nonblock_retry
ssl_pair {|s1, s2|
# fill up a socket so we hit EAGAIN
written = String.new
n = 0
buf = 'a' * 4099
case ret = s1.write_nonblock(buf, exception: false)
when :wait_readable then break
when :wait_writable then break
when Integer
written << buf
n += ret
exp = buf.bytesize
if ret != exp
buf = buf.byteslice(ret, exp - ret)
end
end while true
assert_kind_of Symbol, ret
# make more space for subsequent write:
readed = s2.read(n)
assert_equal written, readed
# this fails if SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER is missing:
buf2 = Marshal.load(Marshal.dump(buf))
assert_kind_of Integer, s1.write_nonblock(buf2, exception: false)
}
end
def test_write_zero
ssl_pair {|s1, s2|
assert_equal 0, s2.write_nonblock('', exception: false)
assert_kind_of Symbol, s1.read_nonblock(1, exception: false)
assert_equal 0, s2.syswrite('')
assert_kind_of Symbol, s1.read_nonblock(1, exception: false)
assert_equal 0, s2.write('')
assert_kind_of Symbol, s1.read_nonblock(1, exception: false)
}
end
def test_write_multiple_arguments
ssl_pair {|s1, s2|
str1 = "foo"; str2 = "bar"
assert_equal 6, s1.write(str1, str2)
s1.close
assert_equal "foobar", s2.read
}
end
def test_partial_tls_record_read_nonblock
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) }
}
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
}
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
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
end