1 /*
2  * Copyright (c) 2019, 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 import com.sun.jdi.Bootstrap;
25 import com.sun.jdi.VirtualMachine;
26 import com.sun.jdi.connect.AttachingConnector;
27 import com.sun.jdi.connect.Connector;
28 import com.sun.jdi.connect.IllegalConnectorArgumentsException;
29 import lib.jdb.Debuggee;
30 
31 import java.io.IOException;
32 import java.net.Inet4Address;
33 import java.net.Inet6Address;
34 import java.net.InetAddress;
35 import java.util.Iterator;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.Map;
39 
40 /*
41  * @test
42  * @bug 8184770
43  * @summary Tests that JDWP agent honors jdk net properties
44  * @library /test/lib
45  *
46  * @build HelloWorld JdwpNetProps
47  * @run main/othervm JdwpNetProps
48  */
49 public class JdwpNetProps {
50 
main(String[] args)51     public static void main(String[] args) throws Exception {
52         InetAddress addrs[] = InetAddress.getAllByName("localhost");
53         InetAddress ipv4Address = null;
54         InetAddress ipv6Address = null;
55         for (int i =  0; i < addrs.length; i++) {
56             if (addrs[i] instanceof Inet4Address) {
57                 ipv4Address = addrs[i];
58             } else if (addrs[i] instanceof Inet6Address) {
59                 ipv6Address = addrs[i];
60             }
61         }
62 
63         if (ipv4Address != null) {
64             new ListenTest("localhost", ipv4Address)
65                     .preferIPv4Stack(true)
66                     .run(TestResult.Success);
67             new ListenTest("localhost", ipv4Address)
68                     .preferIPv4Stack(false)
69                     .run(TestResult.Success);
70             if (ipv6Address != null) {
71                 // - only IPv4, so connection prom IPv6 should fail
72                 new ListenTest("localhost", ipv6Address)
73                         .preferIPv4Stack(true)
74                         .preferIPv6Addresses(true)
75                         .run(TestResult.AttachFailed);
76                 // - listen on IPv4
77                 new ListenTest("localhost", ipv6Address)
78                         .preferIPv6Addresses(false)
79                         .run(TestResult.AttachFailed);
80                 // - listen on IPv6
81                 new ListenTest("localhost", ipv6Address)
82                         .preferIPv6Addresses(true)
83                         .run(TestResult.Success);
84             }
85         } else {
86             // IPv6-only system - expected to fail on IPv4 address
87             new ListenTest("localhost", ipv6Address)
88                     .preferIPv4Stack(true)
89                     .run(TestResult.ListenFailed);
90         }
91     }
92 
93     private enum TestResult {
94         Success,
95         ListenFailed,
96         AttachFailed
97     }
98 
99     private static class ListenTest {
100         private final String listenAddress;
101         private final InetAddress connectAddress;
102         private Boolean preferIPv4Stack;
103         private Boolean preferIPv6Addresses;
ListenTest(String listenAddress, InetAddress connectAddress)104         public ListenTest(String listenAddress, InetAddress connectAddress) {
105             this.listenAddress = listenAddress;
106             this.connectAddress = connectAddress;
107         }
preferIPv4Stack(Boolean value)108         public ListenTest preferIPv4Stack(Boolean value) {
109             preferIPv4Stack = value;
110             return this;
111         }
preferIPv6Addresses(Boolean value)112         public ListenTest preferIPv6Addresses(Boolean value) {
113             preferIPv6Addresses = value;
114             return this;
115         }
116 
run(TestResult expectedResult)117         public void run(TestResult expectedResult) throws Exception {
118             List<String> options = new LinkedList<>();
119             if (preferIPv4Stack != null) {
120                 options.add("-Djava.net.preferIPv4Stack=" + preferIPv4Stack.toString());
121             }
122             if (preferIPv6Addresses != null) {
123                 options.add("-Djava.net.preferIPv6Addresses=" + preferIPv6Addresses.toString());
124             }
125             log("Starting listening debuggee at " + listenAddress
126                     + (expectedResult == TestResult.ListenFailed ? ": expected to fail" : ""));
127             Exception error = null;
128             try (Debuggee debuggee = Debuggee.launcher("HelloWorld")
129                     .setAddress(listenAddress + ":0")
130                     .addOptions(options).launch()) {
131                 log("Debuggee is listening on " + listenAddress + ":" + debuggee.getAddress());
132                 log("Connecting from " + connectAddress.getHostAddress()
133                         + ", expected: " + (expectedResult == TestResult.Success ? "Success" : "Failure"));
134                 try {
135                     VirtualMachine vm = attach(connectAddress.getHostAddress(), debuggee.getAddress());
136                     vm.dispose();
137                     if (expectedResult == TestResult.Success) {
138                         log("Attached successfully (as expected)");
139                     } else {
140                         error = new RuntimeException("ERROR: attached successfully");
141                     }
142                 } catch (Exception ex) {
143                     if (expectedResult == TestResult.AttachFailed) {
144                         log("Attach failed (as expected)");
145                     } else {
146                         error = new RuntimeException("ERROR: failed to attach", ex);
147                     }
148                 }
149             } catch (Exception ex) {
150                 if (expectedResult == TestResult.ListenFailed) {
151                     log("Listen failed (as expected)");
152                 } else {
153                     error = new RuntimeException("ERROR: listen failed", ex);
154                 }
155             }
156             if (error != null) {
157                 throw error;
158             }
159         }
160     }
161 
162     private static String ATTACH_CONNECTOR = "com.sun.jdi.SocketAttach";
163     // cache socket attaching connector
164     private static AttachingConnector attachingConnector;
165 
attach(String address, String port)166     private static VirtualMachine attach(String address, String port) throws IOException {
167         if (attachingConnector == null) {
168             attachingConnector = (AttachingConnector)getConnector(ATTACH_CONNECTOR);
169         }
170         Map<String, Connector.Argument> args = attachingConnector.defaultArguments();
171         setConnectorArg(args, "hostname", address);
172         setConnectorArg(args, "port", port);
173         try {
174             return attachingConnector.attach(args);
175         } catch (IllegalConnectorArgumentsException e) {
176             // unexpected.. wrap in RuntimeException
177             throw new RuntimeException(e);
178         }
179     }
180 
getConnector(String name)181     private static Connector getConnector(String name) {
182         List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
183         for (Iterator<Connector> iter = connectors.iterator(); iter.hasNext(); ) {
184             Connector connector = iter.next();
185             if (connector.name().equalsIgnoreCase(name)) {
186                 return connector;
187             }
188         }
189         throw new IllegalArgumentException("Connector " + name + " not found");
190     }
191 
setConnectorArg(Map<String, Connector.Argument> args, String name, String value)192     private static void setConnectorArg(Map<String, Connector.Argument> args, String name, String value) {
193         Connector.Argument arg = args.get(name);
194         if (arg == null) {
195             throw new IllegalArgumentException("Argument " + name + " is not defined");
196         }
197         arg.setValue(value);
198     }
199 
log(Object o)200     private static void log(Object o) {
201         System.out.println(String.valueOf(o));
202     }
203 
204 }
205