From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/test/socket/test_unix.rb | 666 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 jni/ruby/test/socket/test_unix.rb (limited to 'jni/ruby/test/socket/test_unix.rb') diff --git a/jni/ruby/test/socket/test_unix.rb b/jni/ruby/test/socket/test_unix.rb new file mode 100644 index 0000000..866c839 --- /dev/null +++ b/jni/ruby/test/socket/test_unix.rb @@ -0,0 +1,666 @@ +begin + require "socket" +rescue LoadError +end + +require "test/unit" +require "tempfile" +require "timeout" +require "tmpdir" +require "thread" +require "io/nonblock" + +class TestSocket_UNIXSocket < Test::Unit::TestCase + def test_fd_passing + r1, w = IO.pipe + s1, s2 = UNIXSocket.pair + begin + s1.send_io(nil) + rescue NotImplementedError + assert_raise(NotImplementedError) { s2.recv_io } + rescue TypeError + s1.send_io(r1) + r2 = s2.recv_io + assert_equal(r1.stat.ino, r2.stat.ino) + assert_not_equal(r1.fileno, r2.fileno) + assert(r2.close_on_exec?) + w.syswrite "a" + assert_equal("a", r2.sysread(10)) + ensure + s1.close + s2.close + w.close + r1.close + r2.close if r2 && !r2.closed? + end + end + + def test_fd_passing_n + io_ary = [] + return if !defined?(Socket::SCM_RIGHTS) + io_ary.concat IO.pipe + io_ary.concat IO.pipe + io_ary.concat IO.pipe + send_io_ary = [] + io_ary.each {|io| + send_io_ary << io + UNIXSocket.pair {|s1, s2| + begin + ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, + send_io_ary.map {|io2| io2.fileno }.pack("i!*")]) + rescue NotImplementedError + return + end + assert_equal(1, ret) + ret = s2.recvmsg(:scm_rights=>true) + _, _, _, *ctls = ret + recv_io_ary = [] + begin + ctls.each {|ctl| + next if ctl.level != Socket::SOL_SOCKET || ctl.type != Socket::SCM_RIGHTS + recv_io_ary.concat ctl.unix_rights + } + assert_equal(send_io_ary.length, recv_io_ary.length) + send_io_ary.length.times {|i| + assert_not_equal(send_io_ary[i].fileno, recv_io_ary[i].fileno) + assert(File.identical?(send_io_ary[i], recv_io_ary[i])) + assert(recv_io_ary[i].close_on_exec?) + } + ensure + recv_io_ary.each {|io2| io2.close if !io2.closed? } + end + } + } + ensure + io_ary.each {|io| io.close if !io.closed? } + end + + def test_fd_passing_n2 + io_ary = [] + return if !defined?(Socket::SCM_RIGHTS) + return if !defined?(Socket::AncillaryData) + io_ary.concat IO.pipe + io_ary.concat IO.pipe + io_ary.concat IO.pipe + send_io_ary = [] + io_ary.each {|io| + send_io_ary << io + UNIXSocket.pair {|s1, s2| + begin + ancdata = Socket::AncillaryData.unix_rights(*send_io_ary) + ret = s1.sendmsg("\0", 0, nil, ancdata) + rescue NotImplementedError + return + end + assert_equal(1, ret) + ret = s2.recvmsg(:scm_rights=>true) + _, _, _, *ctls = ret + recv_io_ary = [] + begin + ctls.each {|ctl| + next if ctl.level != Socket::SOL_SOCKET || ctl.type != Socket::SCM_RIGHTS + recv_io_ary.concat ctl.unix_rights + } + assert_equal(send_io_ary.length, recv_io_ary.length) + send_io_ary.length.times {|i| + assert_not_equal(send_io_ary[i].fileno, recv_io_ary[i].fileno) + assert(File.identical?(send_io_ary[i], recv_io_ary[i])) + assert(recv_io_ary[i].close_on_exec?) + } + ensure + recv_io_ary.each {|io2| io2.close if !io2.closed? } + end + } + } + ensure + io_ary.each {|io| io.close if !io.closed? } + end + + def test_fd_passing_race_condition + r1, w = IO.pipe + s1, s2 = UNIXSocket.pair + s1.nonblock = s2.nonblock = true + lock = Mutex.new + nr = 0 + x = 2 + y = 1000 + begin + s1.send_io(nil) + rescue NotImplementedError + assert_raise(NotImplementedError) { s2.recv_io } + rescue TypeError + thrs = x.times.map do + Thread.new do + y.times do + s2.recv_io.close + lock.synchronize { nr += 1 } + end + true + end + end + (x * y).times { s1.send_io r1 } + assert_equal([true]*x, thrs.map { |t| t.value }) + assert_equal x * y, nr + ensure + s1.close + s2.close + w.close + r1.close + end + end + + def test_sendmsg + return if !defined?(Socket::SCM_RIGHTS) + IO.pipe {|r1, w| + UNIXSocket.pair {|s1, s2| + begin + ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, [r1.fileno].pack("i!")]) + rescue NotImplementedError + return + end + assert_equal(1, ret) + r2 = s2.recv_io + begin + assert(File.identical?(r1, r2)) + assert(r2.close_on_exec?) + ensure + r2.close + end + } + } + end + + def test_sendmsg_ancillarydata_int + return if !defined?(Socket::SCM_RIGHTS) + return if !defined?(Socket::AncillaryData) + IO.pipe {|r1, w| + UNIXSocket.pair {|s1, s2| + begin + ad = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, r1.fileno) + ret = s1.sendmsg("\0", 0, nil, ad) + rescue NotImplementedError + return + end + assert_equal(1, ret) + r2 = s2.recv_io + begin + assert(File.identical?(r1, r2)) + ensure + r2.close + end + } + } + end + + def test_sendmsg_ancillarydata_unix_rights + return if !defined?(Socket::SCM_RIGHTS) + return if !defined?(Socket::AncillaryData) + IO.pipe {|r1, w| + UNIXSocket.pair {|s1, s2| + begin + ad = Socket::AncillaryData.unix_rights(r1) + ret = s1.sendmsg("\0", 0, nil, ad) + rescue NotImplementedError + return + end + assert_equal(1, ret) + r2 = s2.recv_io + begin + assert(File.identical?(r1, r2)) + ensure + r2.close + end + } + } + end + + def test_recvmsg + return if !defined?(Socket::SCM_RIGHTS) + return if !defined?(Socket::AncillaryData) + IO.pipe {|r1, w| + UNIXSocket.pair {|s1, s2| + s1.send_io(r1) + ret = s2.recvmsg(:scm_rights=>true) + data, srcaddr, flags, *ctls = ret + assert_equal("\0", data) + if flags == nil + # struct msghdr is 4.3BSD style (msg_accrights field). + assert_instance_of(Array, ctls) + assert_equal(0, ctls.length) + else + # struct msghdr is POSIX/4.4BSD style (msg_control field). + assert_equal(0, flags & (Socket::MSG_TRUNC|Socket::MSG_CTRUNC)) + assert_instance_of(Addrinfo, srcaddr) + assert_instance_of(Array, ctls) + assert_equal(1, ctls.length) + ctl = ctls[0] + assert_instance_of(Socket::AncillaryData, ctl) + assert_equal(Socket::SOL_SOCKET, ctl.level) + assert_equal(Socket::SCM_RIGHTS, ctl.type) + assert_instance_of(String, ctl.data) + ios = ctl.unix_rights + assert_equal(1, ios.length) + r2 = ios[0] + begin + assert(File.identical?(r1, r2)) + assert(r2.close_on_exec?) + ensure + r2.close + end + end + } + } + end + + def bound_unix_socket(klass) + tmpfile = Tempfile.new("s") + path = tmpfile.path + tmpfile.close(true) + io = klass.new(path) + yield io, path + ensure + io.close + File.unlink path if path && File.socket?(path) + end + + def test_addr + bound_unix_socket(UNIXServer) {|serv, path| + UNIXSocket.open(path) {|c| + s = serv.accept + begin + assert_equal(["AF_UNIX", path], c.peeraddr) + assert_equal(["AF_UNIX", ""], c.addr) + assert_equal(["AF_UNIX", ""], s.peeraddr) + assert_equal(["AF_UNIX", path], s.addr) + assert_equal(path, s.path) + assert_equal("", c.path) + ensure + s.close + end + } + } + end + + def test_cloexec + bound_unix_socket(UNIXServer) {|serv, path| + UNIXSocket.open(path) {|c| + s = serv.accept + begin + assert(serv.close_on_exec?) + assert(c.close_on_exec?) + assert(s.close_on_exec?) + ensure + s.close + end + } + } + end + + def test_noname_path + s1, s2 = UNIXSocket.pair + assert_equal("", s1.path) + assert_equal("", s2.path) + ensure + s1.close + s2.close + end + + def test_noname_addr + s1, s2 = UNIXSocket.pair + assert_equal(["AF_UNIX", ""], s1.addr) + assert_equal(["AF_UNIX", ""], s2.addr) + ensure + s1.close + s2.close + end + + def test_noname_peeraddr + s1, s2 = UNIXSocket.pair + assert_equal(["AF_UNIX", ""], s1.peeraddr) + assert_equal(["AF_UNIX", ""], s2.peeraddr) + ensure + s1.close + s2.close + end + + def test_noname_unpack_sockaddr_un + s1, s2 = UNIXSocket.pair + n = nil + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != "" + ensure + s1.close + s2.close + end + + def test_noname_recvfrom + s1, s2 = UNIXSocket.pair + s2.write("a") + assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10)) + ensure + s1.close + s2.close + end + + def test_noname_recv_nonblock + s1, s2 = UNIXSocket.pair + s2.write("a") + IO.select [s1] + assert_equal("a", s1.recv_nonblock(10)) + ensure + s1.close + s2.close + end + + def test_too_long_path + assert_raise(ArgumentError) { Socket.sockaddr_un("a" * 3000) } + assert_raise(ArgumentError) { UNIXServer.new("a" * 3000) } + end + + def test_abstract_namespace + return if /linux/ !~ RUBY_PLATFORM + addr = Socket.pack_sockaddr_un("\0foo") + assert_match(/\0foo\z/, addr) + assert_equal("\0foo", Socket.unpack_sockaddr_un(addr)) + end + + def test_dgram_pair + s1, s2 = UNIXSocket.pair(Socket::SOCK_DGRAM) + begin + s1.recv_nonblock(10) + fail + rescue => e + assert(IO::EAGAINWaitReadable === e) + assert(IO::WaitReadable === e) + end + s2.send("", 0) + s2.send("haha", 0) + s2.send("", 0) + s2.send("", 0) + assert_equal("", s1.recv(10)) + assert_equal("haha", s1.recv(10)) + assert_equal("", s1.recv(10)) + assert_equal("", s1.recv(10)) + assert_raise(IO::EAGAINWaitReadable) { s1.recv_nonblock(10) } + ensure + s1.close if s1 + s2.close if s2 + end + + def test_dgram_pair_sendrecvmsg_errno_set + s1, s2 = to_close = UNIXSocket.pair(Socket::SOCK_DGRAM) + pipe = IO.pipe + to_close.concat(pipe) + set_errno = lambda do + begin + pipe[0].read_nonblock(1) + fail + rescue => e + assert(IO::EAGAINWaitReadable === e) + end + end + Timeout.timeout(10) do + set_errno.call + assert_equal(2, s1.sendmsg("HI")) + set_errno.call + assert_equal("HI", s2.recvmsg[0]) + end + ensure + to_close.each(&:close) if to_close + end + + def test_epipe # [ruby-dev:34619] + UNIXSocket.pair {|s1, s2| + s1.shutdown(Socket::SHUT_WR) + assert_raise(Errno::EPIPE) { s1.write "a" } + assert_equal(nil, s2.read(1)) + s2.write "a" + assert_equal("a", s1.read(1)) + } + end + + def test_socket_pair_with_block + pair = nil + ret = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s1, s2| + pair = [s1, s2] + :return_value + } + assert_equal(:return_value, ret) + assert_kind_of(Socket, pair[0]) + assert_kind_of(Socket, pair[1]) + end + + def test_unix_socket_pair_with_block + pair = nil + UNIXSocket.pair {|s1, s2| + pair = [s1, s2] + } + assert_kind_of(UNIXSocket, pair[0]) + assert_kind_of(UNIXSocket, pair[1]) + end + + def test_unix_socket_pair_close_on_exec + UNIXSocket.pair {|s1, s2| + assert(s1.close_on_exec?) + assert(s2.close_on_exec?) + } + end + + def test_initialize + Dir.mktmpdir {|d| + Socket.open(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s| + s.bind(Socket.pack_sockaddr_un("#{d}/s1")) + addr = s.getsockname + assert_nothing_raised { Socket.unpack_sockaddr_un(addr) } + assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(addr) } + } + Socket.open("AF_UNIX", "SOCK_STREAM", 0) {|s| + s.bind(Socket.pack_sockaddr_un("#{d}/s2")) + addr = s.getsockname + assert_nothing_raised { Socket.unpack_sockaddr_un(addr) } + assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(addr) } + } + } + end + + def test_unix_server_socket + Dir.mktmpdir {|d| + path = "#{d}/sock" + s0 = nil + Socket.unix_server_socket(path) {|s| + assert_equal(path, s.local_address.unix_path) + assert(File.socket?(path)) + s0 = s + } + assert(s0.closed?) + assert_raise(Errno::ENOENT) { File.stat path } + } + end + + def test_getcred_ucred + return if /linux/ !~ RUBY_PLATFORM + Dir.mktmpdir {|d| + sockpath = "#{d}/sock" + Socket.unix_server_socket(sockpath) {|serv| + Socket.unix(sockpath) {|c| + s, = serv.accept + begin + cred = s.getsockopt(:SOCKET, :PEERCRED) + inspect = cred.inspect + assert_match(/ pid=#{$$} /, inspect) + assert_match(/ euid=#{Process.euid} /, inspect) + assert_match(/ egid=#{Process.egid} /, inspect) + assert_match(/ \(ucred\)/, inspect) + ensure + s.close + end + } + } + } + end + + def test_getcred_xucred + return if /freebsd|darwin/ !~ RUBY_PLATFORM + Dir.mktmpdir {|d| + sockpath = "#{d}/sock" + serv = Socket.unix_server_socket(sockpath) + Socket.unix(sockpath) + s, = serv.accept + cred = s.getsockopt(0, Socket::LOCAL_PEERCRED) + inspect = cred.inspect + assert_match(/ euid=#{Process.euid} /, inspect) + assert_match(/ \(xucred\)/, inspect) + } + end + + def test_sendcred_ucred + return if /linux/ !~ RUBY_PLATFORM + Dir.mktmpdir {|d| + sockpath = "#{d}/sock" + Socket.unix_server_socket(sockpath) {|serv| + Socket.unix(sockpath) {|c| + s, = serv.accept + begin + s.setsockopt(:SOCKET, :PASSCRED, 1) + c.print "a" + msg, _, _, cred = s.recvmsg + inspect = cred.inspect + assert_equal("a", msg) + assert_match(/ pid=#{$$} /, inspect) + assert_match(/ uid=#{Process.uid} /, inspect) + assert_match(/ gid=#{Process.gid} /, inspect) + assert_match(/ \(ucred\)/, inspect) + ensure + s.close + end + } + } + } + end + + def test_sendcred_sockcred + return if /netbsd|freebsd/ !~ RUBY_PLATFORM + Dir.mktmpdir {|d| + sockpath = "#{d}/sock" + serv = Socket.unix_server_socket(sockpath) + c = Socket.unix(sockpath) + s, = serv.accept + s.setsockopt(0, Socket::LOCAL_CREDS, 1) + c.print "a" + msg, _, _, cred = s.recvmsg + assert_equal("a", msg) + inspect = cred.inspect + assert_match(/ uid=#{Process.uid} /, inspect) + assert_match(/ euid=#{Process.euid} /, inspect) + assert_match(/ gid=#{Process.gid} /, inspect) + assert_match(/ egid=#{Process.egid} /, inspect) + assert_match(/ \(sockcred\)/, inspect) + } + end + + def test_sendcred_cmsgcred + return if /freebsd/ !~ RUBY_PLATFORM + Dir.mktmpdir {|d| + sockpath = "#{d}/sock" + serv = Socket.unix_server_socket(sockpath) + c = Socket.unix(sockpath) + s, = serv.accept + c.sendmsg("a", 0, nil, [:SOCKET, Socket::SCM_CREDS, ""]) + msg, _, _, cred = s.recvmsg + assert_equal("a", msg) + inspect = cred.inspect + assert_match(/ pid=#{$$} /, inspect) + assert_match(/ uid=#{Process.uid} /, inspect) + assert_match(/ euid=#{Process.euid} /, inspect) + assert_match(/ gid=#{Process.gid} /, inspect) + assert_match(/ \(cmsgcred\)/, inspect) + } + end + + def test_getpeereid + Dir.mktmpdir {|d| + path = "#{d}/sock" + Socket.unix_server_socket(path) {|serv| + Socket.unix(path) {|c| + s, = serv.accept + begin + assert_equal([Process.euid, Process.egid], c.getpeereid) + assert_equal([Process.euid, Process.egid], s.getpeereid) + rescue NotImplementedError + ensure + s.close + end + } + } + } + end + + def test_abstract_unix_server + return if /linux/ !~ RUBY_PLATFORM + name = "\0ruby-test_unix" + s0 = nil + UNIXServer.open(name) {|s| + assert_equal(name, s.local_address.unix_path) + s0 = s + UNIXSocket.open(name) {|c| + sock = s.accept + begin + assert_equal(name, c.remote_address.unix_path) + ensure + sock.close + end + } + } + assert(s0.closed?) + end + + def test_abstract_unix_socket_econnrefused + return if /linux/ !~ RUBY_PLATFORM + name = "\0ruby-test_unix" + assert_raise(Errno::ECONNREFUSED) do + UNIXSocket.open(name) {} + end + end + + def test_abstract_unix_server_socket + return if /linux/ !~ RUBY_PLATFORM + name = "\0ruby-test_unix" + s0 = nil + Socket.unix_server_socket(name) {|s| + assert_equal(name, s.local_address.unix_path) + s0 = s + Socket.unix(name) {|c| + sock, = s.accept + begin + assert_equal(name, c.remote_address.unix_path) + ensure + sock.close + end + } + } + assert(s0.closed?) + end + + def test_autobind + return if /linux/ !~ RUBY_PLATFORM + s0 = nil + Socket.unix_server_socket("") {|s| + name = s.local_address.unix_path + assert_match(/\A\0[0-9a-f]{5}\z/, name) + s0 = s + Socket.unix(name) {|c| + sock, = s.accept + begin + assert_equal(name, c.remote_address.unix_path) + ensure + sock.close + end + } + } + assert(s0.closed?) + end + +end if defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM -- cgit v1.2.3