1 /*
2  * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 4203167
26  *
27  * @summary RMI blocks in HttpAwareServerSocket.accept() if you telnet to it
28  * @author Adrian Colley
29  *
30  * @library ../../../../../java/rmi/testlibrary
31  * @build TestIface TestImpl TestImpl_Stub
32  * @run main/othervm/policy=security.policy/timeout=60 -Dsun.rmi.server.disableIncomingHttp=false BlockAcceptTest
33  */
34 
35 /* This test attempts to stymie the RMI accept loop.  The accept loop in
36  * RMI endlessly accepts a connection, spawns a thread for it, and repeats.
37  * The accept() call can be replaced by a user-supplied library which
38  * might foolishly block indefinitely in its accept() method, which would
39  * prevent RMI from accepting other connections on that socket.
40  *
41  * Unfortunately, HttpAwareServerSocket (default server socket) is/was such
42  * a foolish thing.  It reads 4 bytes to see if they're "POST" before
43  * returning.  The bug fix is to move the HTTP stuff into the mainloop,
44  * which has the side effect of enabling it for non-default socketfactories.
45  *
46  * This test:
47  * 1. Creates an object and exports it.
48  * 2. Connects to the listening RMI port and sends nothing, to hold it up.
49  * 3. Makes a regular call, using HTTP tunnelling.
50  * 4. Fails to deadlock, thereby passing the test.
51  *
52  * Some runtime dependencies I'm trying to eliminate:
53  * 1. We don't know the port number until after exporting the object, but
54  *    have to set it in http.proxyPort somehow.  Hopefully http.proxyPort
55  *    isn't read too soon or this test will fail with a ConnectException.
56  */
57 
58 import java.rmi.*;
59 import java.rmi.server.RMISocketFactory;
60 import java.io.*;
61 import java.net.*;
62 
63 import sun.rmi.transport.proxy.RMIMasterSocketFactory;
64 import sun.rmi.transport.proxy.RMIHttpToPortSocketFactory;
65 
66 public class BlockAcceptTest
67 {
main(String[] args)68     public static void main(String[] args)
69         throws Exception
70     {
71         // Make trouble for ourselves
72         if (System.getSecurityManager() == null)
73             System.setSecurityManager(new RMISecurityManager());
74 
75         // HTTP direct to the server port
76         System.setProperty("http.proxyHost", "127.0.0.1");
77 
78         // Set the socket factory.
79         System.err.println("(installing HTTP-out socket factory)");
80         HttpOutFactory fac = new HttpOutFactory();
81         RMISocketFactory.setSocketFactory(fac);
82 
83         // Create remote object
84         TestImpl impl = new TestImpl();
85 
86         // Export and get which port.
87         System.err.println("(exporting remote object)");
88         TestIface stub = impl.export();
89         try {
90             int port = fac.whichPort();
91 
92             // Sanity
93             if (port == 0)
94                 throw new Error("TEST FAILED: export didn't reserve a port(?)");
95 
96             // Set the HTTP port, at last.
97             System.setProperty("http.proxyPort", port+"");
98 
99             // Now, connect to that port
100             //Thread.sleep(2000);
101             System.err.println("(connecting to listening port on 127.0.0.1:" +
102                                port + ")");
103             Socket DoS = new Socket("127.0.0.1", port);
104             // we hold the connection open until done with the test.
105 
106             // The test itself: make a remote call and see if it's blocked or
107             // if it works
108             //Thread.sleep(2000);
109             System.err.println("(making RMI-through-HTTP call)");
110             System.err.println("(typical test failure deadlocks here)");
111             String result = stub.testCall("dummy load");
112 
113             System.err.println(" => " + result);
114             if (!("OK".equals(result)))
115                 throw new Error("TEST FAILED: result not OK");
116             System.err.println("Test passed.");
117 
118             // Clean up, including writing a byte to that connection just in
119             // case an optimizer thought of optimizing it out of existence
120             try {
121                 DoS.getOutputStream().write(0);
122                 DoS.getOutputStream().close();
123             } catch (Throwable apathy) {
124             }
125 
126         } finally {
127             try {
128                 impl.unexport();
129             } catch (Throwable unmatter) {
130             }
131         }
132 
133         // Should exit here
134     }
135 
136     private static class HttpOutFactory
137         extends RMISocketFactory
138     {
139         private int servport = 0;
140 
createSocket(String h, int p)141         public Socket createSocket(String h, int p)
142             throws IOException
143         {
144             return ((new RMIHttpToPortSocketFactory()).createSocket(h, p));
145         }
146 
147         /** Create a server socket and remember which port it's on.
148          * Aborts if createServerSocket(0) is called twice, because then
149          * it doesn't know whether to remember the first or second port.
150          */
createServerSocket(int p)151         public ServerSocket createServerSocket(int p)
152             throws IOException
153         {
154             ServerSocket ss;
155             ss = (new RMIMasterSocketFactory()).createServerSocket(p);
156             if (p == 0) {
157                 if (servport != 0) {
158                     System.err.println("TEST FAILED: " +
159                                        "Duplicate createServerSocket(0)");
160                     throw new Error("Test aborted (createServerSocket)");
161                 }
162                 servport = ss.getLocalPort();
163             }
164             return (ss);
165         }
166 
167         /** Return which port was reserved by createServerSocket(0).
168          * If the return value was 0, createServerSocket(0) wasn't called.
169          */
whichPort()170         public int whichPort() {
171             return (servport);
172         }
173     } // end class HttpOutFactory
174 }
175