1 /*
  2  * Copyright (c) 2015, 2024, 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 /*
 25  * @test
 26  * @bug 8067105
 27  * @library /test/lib
 28  * @summary Socket returned by ServerSocket.accept() is inherited by child process on Windows
 29  * @author Chris Hegarty
 30  */
 31 
 32 import java.io.*;
 33 import java.net.*;
 34 import java.nio.channels.ServerSocketChannel;
 35 import java.util.ArrayList;
 36 import java.util.Arrays;
 37 import java.util.Collections;
 38 import java.util.List;
 39 import java.util.concurrent.TimeUnit;
 40 import java.util.function.Supplier;
 41 import jdk.test.lib.net.IPSupport;
 42 import jdk.test.lib.process.ProcessTools;
 43 
 44 public class AcceptInheritHandle {
 45 
 46     enum ServerSocketProducer {
 47         JAVA_NET(() -> {
 48             try {
 49                 return new ServerSocket();
 50             } catch(IOException x) {
 51                 throw new UncheckedIOException(x);
 52             }
 53         }),
 54         NIO_CHANNELS(() -> {
 55             try {
 56                 return ServerSocketChannel.open().socket();
 57             } catch (IOException x) {
 58                 throw new UncheckedIOException(x);
 59             }
 60         });
 61 
 62         final Supplier<ServerSocket> supplier;
 63         ServerSocketProducer(Supplier<ServerSocket> supplier) {
 64             this.supplier = supplier;
 65         }
 66         Supplier<ServerSocket> supplier () { return supplier; }
 67     }
 68 
 69     static final String JAVA = System.getProperty("java.home")
 70         + File.separator + "bin" + File.separator + "java";
 71 
 72     static final String CLASSPATH = System.getProperty("java.class.path");
 73 
 74     public static void main(String[] args) throws Exception {
 75         if (args.length == 1)
 76             server(ServerSocketProducer.valueOf(args[0]));
 77         else
 78             mainEntry();
 79     }
 80 
 81     static void mainEntry() throws Exception {
 82         testJavaNetServerSocket();
 83         testNioServerSocketChannel();
 84     }
 85 
 86     static void testJavaNetServerSocket() throws Exception {
 87         test(ServerSocketProducer.JAVA_NET);
 88         if (IPSupport.hasIPv4()) {
 89             test(ServerSocketProducer.JAVA_NET, "-Djava.net.preferIPv4Stack=true");
 90         }
 91     }
 92     static void testNioServerSocketChannel() throws Exception {
 93         test(ServerSocketProducer.NIO_CHANNELS);
 94     }
 95 
 96     static void test(ServerSocketProducer ssp, String... jvmArgs) throws Exception {
 97         System.out.println("\nStarting test for " + ssp.name());
 98 
 99         List<String> commands = new ArrayList<>();
100         Collections.addAll(commands, jvmArgs);
101         commands.add("AcceptInheritHandle");
102         commands.add(ssp.name());
103 
104         System.out.println("Executing: "+ commands);
105         ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(commands);
106         pb.redirectError(ProcessBuilder.Redirect.INHERIT);
107         Process serverProcess = pb.start();
108         DataInputStream dis = new DataInputStream(serverProcess.getInputStream());
109 
110         int port = dis.readInt();
111         System.out.println("Server process listening on " + port + ", connecting...");
112 
113         String address;
114         if (Arrays.stream(jvmArgs).anyMatch("-Djava.net.preferIPv4Stack=true"::equals)) {
115             address = "127.0.0.1";
116         } else {
117             InetAddress loopback = InetAddress.getLoopbackAddress();
118             address = loopback.getHostAddress();
119         }
120         Socket socket = new Socket(address, port);
121         String s = dis.readUTF();
122         System.out.println("Server process said " + s);
123 
124         serverProcess.destroy();
125         serverProcess.waitFor(30, TimeUnit.SECONDS);
126         System.out.println("serverProcess exitCode:" + serverProcess.exitValue());
127 
128         try {
129             socket.setSoTimeout(10 * 1000);
130             socket.getInputStream().read();
131         } catch (SocketTimeoutException x) {
132             // failed
133             throw new RuntimeException("Failed: should get reset, not " + x);
134         } catch (SocketException x) {
135             System.out.println("Expected:" + x);
136         }
137     }
138 
139     static void server(ServerSocketProducer producer) throws Exception {
140         try (ServerSocket ss = producer.supplier().get()) {
141             InetAddress loopback = InetAddress.getLoopbackAddress();
142             ss.bind(new InetSocketAddress(loopback, 0));
143             int port = ss.getLocalPort();
144             DataOutputStream dos = new DataOutputStream(System.out);
145             dos.writeInt(port);
146             dos.flush();
147 
148             ss.accept();  // do not close
149 
150             Runtime.getRuntime().exec("sleep 20");
151             Thread.sleep(3 * 1000);
152 
153             dos.writeUTF("kill me!");
154             dos.flush();
155             Thread.sleep(30 * 1000);
156         }
157     }
158 }