1 /*
2  * Copyright (c) 2013, 2014, 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 7100957
26  * @summary Java doesn't correctly handle the SOCKS protocol when used over IPv6.
27  * @run testng SocksIPv6Test
28  */
29 
30 package test.java.net.Socks;
31 
32 import java.io.BufferedReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.io.OutputStreamWriter;
37 import java.net.Authenticator;
38 import java.net.InetSocketAddress;
39 import java.net.URL;
40 import java.net.Proxy;
41 import java.lang.Override;
42 import java.net.InetAddress;
43 import java.net.Inet6Address;
44 import java.net.ServerSocket;
45 import java.net.SocketException;
46 import java.net.NetworkInterface;
47 import java.net.UnknownHostException;
48 import java.util.Collections;
49 import java.util.List;
50 
51 import libcore.net.NetworkSecurityPolicy;
52 
53 import com.google.mockwebserver.MockResponse;
54 import com.google.mockwebserver.MockWebServer;
55 import org.testng.annotations.AfterClass;
56 import org.testng.annotations.BeforeClass;
57 import org.testng.annotations.Test;
58 
59 import static org.testng.Assert.*;
60 
61 public class SocksIPv6Test {
62 
63     // Android-added: value set before this test execution.
64     private NetworkSecurityPolicy oldNetworkSecurityPolicy;
65     // Android-changed: com.sun.net.httpserver. is not available on Android.
66     // private HttpServer server;
67     private MockWebServer server;
68     private SocksServer socks;
69     private String response = "Hello.";
70     private static boolean shouldRun = false;
71 
72     @BeforeClass
setUp()73     public void setUp() throws Exception {
74         shouldRun = ensureInet6AddressFamily() && ensureIPv6OnLoopback();
75 
76         // Android-added: cleartext communication might not be allowed.
77         oldNetworkSecurityPolicy = NetworkSecurityPolicy.getInstance();
78         NetworkSecurityPolicy.setInstance(new NetworkSecurityPolicy.DefaultNetworkSecurityPolicy());
79 
80         // Android-changed: use MockWebServer instead of HttpServer.
81         /*
82         server = HttpServer.create(new InetSocketAddress(0), 0);
83         server.createContext("/", ex -> {
84             ex.sendResponseHeaders(200, response.length());
85             try (BufferedWriter writer = new BufferedWriter(
86                     new OutputStreamWriter(ex.getResponseBody(), "UTF-8"))) {
87                 writer.write(response);
88             }
89             ex.close();
90         });
91         server.start();
92         */
93         server = new MockWebServer();
94         server.enqueue(new MockResponse().setResponseCode(200).setBody(response));
95         server.play();
96 
97         socks = new SocksServer(0, false);
98         socks.addUser("user", "pass");
99         socks.start();
100 
101         Authenticator.setDefault(new Authenticator() {
102             @Override
103             protected java.net.PasswordAuthentication getPasswordAuthentication() {
104                 return new java.net.PasswordAuthentication(
105                         "user", "pass".toCharArray());
106             }
107         });
108     }
109 
ensureIPv6OnLoopback()110     private boolean ensureIPv6OnLoopback() throws Exception {
111         boolean ipv6 = false;
112 
113         List<NetworkInterface> nics = Collections.list(NetworkInterface.getNetworkInterfaces());
114         for (NetworkInterface nic : nics) {
115             if (!nic.isLoopback()) {
116                 continue;
117             }
118             List<InetAddress> addrs = Collections.list(nic.getInetAddresses());
119             for (InetAddress addr : addrs) {
120                 if (addr instanceof Inet6Address) {
121                     ipv6 = true;
122                     break;
123                 }
124             }
125         }
126         if (!ipv6)
127             System.out.println("IPv6 is not enabled on loopback. Skipping test suite.");
128         return ipv6;
129     }
130 
ensureInet6AddressFamily()131     private boolean ensureInet6AddressFamily() throws IOException {
132         try (ServerSocket s = new ServerSocket()) {
133             s.bind(new InetSocketAddress("::1", 0));
134             return true;
135         } catch (SocketException e) {
136             System.out.println("Inet 6 address family is not available. Skipping test suite.");
137         }
138         return false;
139     }
140 
141     @Test(groups = "unit")
testSocksOverIPv6()142     public void testSocksOverIPv6() throws Exception {
143         if (!shouldRun) return;
144 
145         Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("::1",
146                 socks.getPort()));
147         // Android-changed: different server implementation is used.
148         // URL url = new URL("http://[::1]:" + server.getAddress().getPort());
149         URL url = new URL("http://[::1]:" + server.getPort());
150         java.net.URLConnection conn = url.openConnection(proxy);
151         String actual = "";
152         try (BufferedReader reader = new BufferedReader(
153                 new InputStreamReader(conn.getInputStream()))) {
154             actual = reader.readLine();
155         }
156         assertEquals(actual, response);
157     }
158 
159     @Test(groups = "unit")
testSocksOverIPv6Hostname()160     public void testSocksOverIPv6Hostname() throws Exception {
161         if (!shouldRun) return;
162 
163         String ipv6Hostname = InetAddress.getByName("::1").getHostName();
164         String ipv4Hostname = InetAddress.getByName("127.0.0.1").getHostName();
165 
166         if (ipv6Hostname.equals(InetAddress.getByName("::1").getHostAddress())) {
167             System.out.println("Unable to get the hostname of the IPv6 loopback "
168                     + "address. Skipping test case.");
169             return;
170         }
171 
172         if (ipv6Hostname.equals(ipv4Hostname)) {
173             System.out.println("IPv6 and IPv4 loopback addresses map to the"
174                     + " same hostname. Skipping test case.");
175             return;
176         }
177 
178         Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(ipv6Hostname,
179                 socks.getPort()));
180         // Android-changed: different server implementation is used.
181         // URL url = new URL("http://" + ipv6Hostname + ":" + server.getAddress().getPort());
182         URL url = new URL("http://" + ipv6Hostname + ":" + server.getPort()); // server.getAddress().getPort());
183         java.net.URLConnection conn = url.openConnection(proxy);
184         String actual = "";
185         try (BufferedReader reader = new BufferedReader(
186                 new InputStreamReader(conn.getInputStream()))) {
187             actual = reader.readLine();
188         }
189         assertEquals(actual, response);
190     }
191 
192     @AfterClass
tearDown()193     public void tearDown() {
194         // Android-changed: different server implementation is used. Also restore old
195         // NetworkSecurityPolicy.
196         /*
197         if (server != null) {
198             server.stop(1);
199         }
200         */
201         if (oldNetworkSecurityPolicy != null) {
202             NetworkSecurityPolicy.setInstance(oldNetworkSecurityPolicy);
203         }
204         if (socks != null) {
205             socks.terminate();
206         }
207     }
208 
209     // Android-added: shutdown MockWebServer.
210     @AfterClass
shutdownServer()211     public void shutdownServer() throws Exception {
212         if (server != null) {
213             server.shutdown();
214         }
215     }
216 }
217