/* * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* @test * @bug 4203167 * * @summary RMI blocks in HttpAwareServerSocket.accept() if you telnet to it * @author Adrian Colley * * @library ../../../../../java/rmi/testlibrary * @build TestIface TestImpl TestImpl_Stub * @run main/othervm/policy=security.policy/timeout=60 BlockAcceptTest */ /* This test attempts to stymie the RMI accept loop. The accept loop in * RMI endlessly accepts a connection, spawns a thread for it, and repeats. * The accept() call can be replaced by a user-supplied library which * might foolishly block indefinitely in its accept() method, which would * prevent RMI from accepting other connections on that socket. * * Unfortunately, HttpAwareServerSocket (default server socket) is/was such * a foolish thing. It reads 4 bytes to see if they're "POST" before * returning. The bug fix is to move the HTTP stuff into the mainloop, * which has the side effect of enabling it for non-default socketfactories. * * This test: * 1. Creates an object and exports it. * 2. Connects to the listening RMI port and sends nothing, to hold it up. * 3. Makes a regular call, using HTTP tunnelling. * 4. Fails to deadlock, thereby passing the test. * * Some runtime dependencies I'm trying to eliminate: * 1. We don't know the port number until after exporting the object, but * have to set it in http.proxyPort somehow. Hopefully http.proxyPort * isn't read too soon or this test will fail with a ConnectException. */ import java.rmi.*; import java.rmi.server.RMISocketFactory; import java.io.*; import java.net.*; import sun.rmi.transport.proxy.RMIMasterSocketFactory; import sun.rmi.transport.proxy.RMIHttpToPortSocketFactory; public class BlockAcceptTest { public static void main(String[] args) throws Exception { // Make trouble for ourselves if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager()); // HTTP direct to the server port System.setProperty("http.proxyHost", "127.0.0.1"); // Set the socket factory. System.err.println("(installing HTTP-out socket factory)"); HttpOutFactory fac = new HttpOutFactory(); RMISocketFactory.setSocketFactory(fac); // Create remote object TestImpl impl = new TestImpl(); // Export and get which port. System.err.println("(exporting remote object)"); TestIface stub = impl.export(); try { int port = fac.whichPort(); // Sanity if (port == 0) throw new Error("TEST FAILED: export didn't reserve a port(?)"); // Set the HTTP port, at last. System.setProperty("http.proxyPort", port+""); // Now, connect to that port //Thread.sleep(2000); System.err.println("(connecting to listening port on 127.0.0.1:" + port + ")"); Socket DoS = new Socket("127.0.0.1", port); // we hold the connection open until done with the test. // The test itself: make a remote call and see if it's blocked or // if it works //Thread.sleep(2000); System.err.println("(making RMI-through-HTTP call)"); System.err.println("(typical test failure deadlocks here)"); String result = stub.testCall("dummy load"); System.err.println(" => " + result); if (!("OK".equals(result))) throw new Error("TEST FAILED: result not OK"); System.err.println("Test passed."); // Clean up, including writing a byte to that connection just in // case an optimizer thought of optimizing it out of existence try { DoS.getOutputStream().write(0); DoS.getOutputStream().close(); } catch (Throwable apathy) { } } finally { try { impl.unexport(); } catch (Throwable unmatter) { } } // Should exit here } private static class HttpOutFactory extends RMISocketFactory { private int servport = 0; public Socket createSocket(String h, int p) throws IOException { return ((new RMIHttpToPortSocketFactory()).createSocket(h, p)); } /** Create a server socket and remember which port it's on. * Aborts if createServerSocket(0) is called twice, because then * it doesn't know whether to remember the first or second port. */ public ServerSocket createServerSocket(int p) throws IOException { ServerSocket ss; ss = (new RMIMasterSocketFactory()).createServerSocket(p); if (p == 0) { if (servport != 0) { System.err.println("TEST FAILED: " + "Duplicate createServerSocket(0)"); throw new Error("Test aborted (createServerSocket)"); } servport = ss.getLocalPort(); } return (ss); } /** Return which port was reserved by createServerSocket(0). * If the return value was 0, createServerSocket(0) wasn't called. */ public int whichPort() { return (servport); } } // end class HttpOutFactory }