12f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin/*
22f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Copyright (C) 2015 The Android Open Source Project
32f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
42f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Licensed under the Apache License, Version 2.0 (the "License");
52f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * you may not use this file except in compliance with the License.
62f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * You may obtain a copy of the License at
72f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
82f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *      http://www.apache.org/licenses/LICENSE-2.0
92f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Unless required by applicable law or agreed to in writing, software
112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * distributed under the License is distributed on an "AS IS" BASIS,
122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * See the License for the specific language governing permissions and
142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * limitations under the License.
152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin */
162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinpackage libcore.net;
182f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport junit.framework.TestCase;
202f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport libcore.io.IoUtils;
212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.io.Closeable;
222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.io.IOException;
232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.JarURLConnection;
242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.ServerSocket;
252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.Socket;
262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.URL;
272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.Arrays;
28b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubakerimport java.util.HashMap;
29b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubakerimport java.util.Map;
302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.Callable;
31c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fullerimport java.util.concurrent.ExecutorService;
32c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fullerimport java.util.concurrent.Executors;
332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.Future;
342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.TimeUnit;
352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.TimeoutException;
362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.ErrorManager;
372f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.Level;
382f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.LogRecord;
392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.SocketHandler;
402f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
412f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinpublic class NetworkSecurityPolicyTest extends TestCase {
422f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
437a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    private NetworkSecurityPolicy mOriginalPolicy;
442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    @Override
462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    protected void setUp() throws Exception {
472f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        super.setUp();
487a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        mOriginalPolicy = NetworkSecurityPolicy.getInstance();
492f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
502f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
512f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    @Override
522f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    protected void tearDown() throws Exception {
532f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try {
547a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            NetworkSecurityPolicy.setInstance(mOriginalPolicy);
552f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        } finally {
562f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            super.tearDown();
572f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
582f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
592f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
602f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicySetterAndGetter() {
617a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
627a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
632f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
647a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
657a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(true, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
662f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
677a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
687a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
692f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
707a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
717a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(true, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
722f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
732f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
74b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker    public void testHostnameAwareCleartextTrafficPolicySetterAndGetter() {
75b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
76b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false,
77b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
78b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
79b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
80b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(true,
81b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
82b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
83b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        TestNetworkSecurityPolicy policy = new TestNetworkSecurityPolicy(false);
84b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        policy.addHostMapping("localhost", true);
85b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        policy.addHostMapping("example.com", false);
86b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(policy);
87b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
88b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(true,
89b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
90b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false,
91b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("example.com"));
92b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
93b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker    }
94b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
955a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    public void testCleartextTrafficPolicyWithHttpURLConnection() throws Exception {
965a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
977a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
985a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
995a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
1005a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1015a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                url.openConnection().getContent();
1025a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1035a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1045a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1055a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertDataTransmittedByClient();
1065a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1075a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1085a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1095a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // that URLConnection.openConnection or getContent fail with an IOException.
1107a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1115a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1125a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
1135a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1145a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                url.openConnection().getContent();
1155a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1165a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1175a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1185a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertNoDataTransmittedByClient();
1195a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1205a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    }
1215a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithFtpURLConnection() throws Exception {
1232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1247a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
1262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
1282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                url.openConnection().getContent();
1302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
1342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1372f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // that URLConnection.openConnection or getContent fail with an IOException.
1387a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1402f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
1412f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1422f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                url.openConnection().getContent();
1432f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
1472f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1482f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
1492f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1505a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    public void testCleartextTrafficPolicyWithJarHttpURLConnection() throws Exception {
1515a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1527a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1535a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1545a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
1555a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1565a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1575a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1585a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1595a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1605a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertDataTransmittedByClient();
1615a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1625a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1635a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1645a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // that JarURLConnection.openConnection or getManifest fail with an IOException.
1657a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1665a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1675a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
1685a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1695a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1705a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1715a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1725a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1735a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertNoDataTransmittedByClient();
1745a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1755a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    }
1765a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1772f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithJarFtpURLConnection() throws Exception {
1782f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1797a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1802f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
1812f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1822f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
1832f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1842f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1852f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1862f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1872f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1882f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
1892f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1902f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1912f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1922f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // that JarURLConnection.openConnection or getManifest fail with an IOException.
1937a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1942f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1952f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
1962f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1972f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1982f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1992f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
2002f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
2012f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
2022f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2032f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
2042f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2052f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exception {
2062f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
2077a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
2082f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
2092f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            SocketHandler logger = new SocketHandler("localhost", server.getPort());
2102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            MockErrorManager mockErrorManager = new MockErrorManager();
2112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.setErrorManager(mockErrorManager);
2122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.setLevel(Level.ALL);
2132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            LogRecord record = new LogRecord(Level.INFO, "A log record");
2142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            assertTrue(logger.isLoggable(record));
2152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.publish(record);
2162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            assertNull(mockErrorManager.getMostRecentException());
2172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
218c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            logger.close();
2192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2202f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted.
2227a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
2232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
2242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
2252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                new SocketHandler("localhost", server.getPort());
2262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
2272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
2282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
2292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
2302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
2322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    /**
2342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     * Server socket which listens on a local port and captures the first chunk of data transmitted
2352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     * by the client.
2362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     */
2372f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    private static class CapturingServerSocket implements Closeable {
2382f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final ServerSocket mSocket;
2392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final int mPort;
240c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller        private final ExecutorService executor;
241c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller        private final Future<byte[]> mFirstChunkReceivedFuture;
2422f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2432f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        /**
2442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * Constructs a new socket listening on a local port.
2452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         */
2462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public CapturingServerSocket() throws IOException {
2472f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            this(null);
2482f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2492f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2502f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        /**
2512f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * Constructs a new socket listening on a local port, which sends the provided reply as
2522f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * soon as a client connects to it.
2532f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         */
2542f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public CapturingServerSocket(final byte[] replyOnConnect) throws IOException {
2552f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mSocket = new ServerSocket(0);
2562f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mPort = mSocket.getLocalPort();
257c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            Callable<byte[]> callable = () -> {
258c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                try (Socket client = mSocket.accept()) {
259c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    // Reply (if requested)
260c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    if (replyOnConnect != null) {
261c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                        client.getOutputStream().write(replyOnConnect);
262c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                        client.getOutputStream().flush();
2632f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                    }
264c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller
265c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    // Read request
266c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    byte[] buf = new byte[64 * 1024];
267c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    int chunkSize = client.getInputStream().read(buf);
268c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    if (chunkSize == -1) {
269c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                        // Connection closed without any data received
270c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                        return new byte[0];
271c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    }
272c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    // Received some data
273c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    return Arrays.copyOf(buf, chunkSize);
274c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                } finally {
275c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                    IoUtils.closeQuietly(mSocket);
2762f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                }
277c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            };
278c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            executor = Executors.newSingleThreadExecutor();
279c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            mFirstChunkReceivedFuture = executor.submit(callable);
2802f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2812f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2822f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public int getPort() {
2832f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            return mPort;
2842f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2852f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2862f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public Future<byte[]> getFirstReceivedChunkFuture() {
2872f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            return mFirstChunkReceivedFuture;
2882f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2892f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2902f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        @Override
2912f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public void close() {
2922f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            IoUtils.closeQuietly(mSocket);
293c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            executor.shutdown();
2942f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2952f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2962f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private void assertDataTransmittedByClient()
2972f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                throws Exception {
298c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller            byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(4, TimeUnit.SECONDS);
2992f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) {
3002f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail("Client did not transmit any data to server");
3012f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3022f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3032f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3042f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private void assertNoDataTransmittedByClient()
3052f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                throws Exception {
3062f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            byte[] firstChunkFromClient;
3072f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
308c0c30eb6f377b1eefd57d1bfd908546ca92b527cNeil Fuller                firstChunkFromClient = getFirstReceivedChunkFuture().get(4, TimeUnit.SECONDS);
3092f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (TimeoutException expected) {
3102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                return;
3112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            if ((firstChunkFromClient != null) && (firstChunkFromClient.length > 0)) {
3132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail("Client transmitted " + firstChunkFromClient.length+ " bytes: "
3142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        + new String(firstChunkFromClient, "US-ASCII"));
3152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
3182f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    private static class MockErrorManager extends ErrorManager {
3202f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private Exception mMostRecentException;
3212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public Exception getMostRecentException() {
3232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            synchronized (this) {
3242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                return mMostRecentException;
3252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        @Override
3292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public void error(String message, Exception exception, int errorCode) {
3302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            synchronized (this) {
3312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                mMostRecentException = exception;
3322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
3357a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
3367a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    private static class TestNetworkSecurityPolicy extends NetworkSecurityPolicy {
3377a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        private final boolean mCleartextTrafficPermitted;
338b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        private final Map<String, Boolean> mHostMap = new HashMap<String, Boolean>();
3397a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
3407a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        public TestNetworkSecurityPolicy(boolean cleartextTrafficPermitted) {
3417a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            mCleartextTrafficPermitted = cleartextTrafficPermitted;
3427a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        }
3437a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
344b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        public void addHostMapping(String hostname, boolean isCleartextTrafficPermitted) {
345b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            mHostMap.put(hostname, isCleartextTrafficPermitted);
346b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        }
347b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
3487a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        @Override
3497a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        public boolean isCleartextTrafficPermitted() {
3507a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            return mCleartextTrafficPermitted;
3517a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        }
352b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
353b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        @Override
354b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        public boolean isCleartextTrafficPermitted(String hostname) {
355b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            if (mHostMap.containsKey(hostname)) {
356b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                return mHostMap.get(hostname);
357b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            }
358b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
359b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            return isCleartextTrafficPermitted();
360b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        }
36188e73140702b45b792f91edddfe41ca3e9310c04Chad Brubaker
36288e73140702b45b792f91edddfe41ca3e9310c04Chad Brubaker        @Override
36388e73140702b45b792f91edddfe41ca3e9310c04Chad Brubaker        public boolean isCertificateTransparencyVerificationRequired(String hostname) {
36488e73140702b45b792f91edddfe41ca3e9310c04Chad Brubaker            return false;
36588e73140702b45b792f91edddfe41ca3e9310c04Chad Brubaker        }
3667a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    }
3672f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin}
368