1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.cts; 18 19 import java.io.File; 20 import java.io.FileNotFoundException; 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.Scanner; 24 import java.util.regex.Pattern; 25 26 import junit.framework.AssertionFailedError; 27 import junit.framework.TestCase; 28 29 public class ListeningPortsTest extends TestCase { 30 31 /** Address patterns used to check whether we're checking the right column in /proc/net. */ 32 private static final List<String> ADDRESS_PATTERNS = new ArrayList<String>(2); 33 34 static { 35 ADDRESS_PATTERNS.add("[0-9A-F]{8}:[0-9A-F]{4}"); 36 ADDRESS_PATTERNS.add("[0-9A-F]{32}:[0-9A-F]{4}"); 37 } 38 39 /** Ports that are allowed to be listening on the emulator. */ 40 private static final List<String> EXCEPTION_PATTERNS = new ArrayList<String>(6); 41 42 static { 43 // IPv4 exceptions 44 EXCEPTION_PATTERNS.add("00000000:15B3"); // 0.0.0.0:5555 - emulator port 45 EXCEPTION_PATTERNS.add("0F02000A:15B3"); // 10.0.2.15:5555 - net forwarding for emulator 46 EXCEPTION_PATTERNS.add("[0-9A-F]{6}7F:[0-9A-F]{4}"); // IPv4 Loopback 47 48 // IPv6 exceptions 49 EXCEPTION_PATTERNS.add("[0]{25}1[0]{6}:[0-9A-F]{4}"); // IPv6 Loopback 50 EXCEPTION_PATTERNS.add("[0]{16}[0]{4}[0]{4}[0-9A-F]{6}7F:[0-9A-F]{4}"); // IPv4-6 Conversion 51 EXCEPTION_PATTERNS.add("[0]{16}[F]{4}[0]{4}[0-9A-F]{6}7F:[0-9A-F]{4}"); // IPv4-6 Conversion 52 } 53 54 public void testNoListeningTcpPorts() { 55 assertNoListeningPorts("/proc/net/tcp", true); 56 } 57 58 public void testNoListeningTcp6Ports() { 59 assertNoListeningPorts("/proc/net/tcp6", true); 60 } 61 62 public void testNoListeningUdpPorts() throws Exception { 63 assertNoListeningUdpPorts("/proc/net/udp"); 64 } 65 66 public void testNoListeningUdp6Ports() throws Exception { 67 assertNoListeningUdpPorts("/proc/net/udp6"); 68 } 69 70 private static final int RETRIES_MAX = 6; 71 72 /** 73 * UDP tests can be flaky due to DNS lookups. Compensate. 74 */ 75 private static void assertNoListeningUdpPorts(String procFilePath) throws Exception { 76 for (int i = 0; i < RETRIES_MAX; i++) { 77 try { 78 assertNoListeningPorts(procFilePath, false); 79 return; 80 } catch (ListeningPortsAssertionError e) { 81 if (i == RETRIES_MAX - 1) { 82 throw e; 83 } 84 Thread.sleep(2 * 1000 * i); 85 } 86 } 87 throw new IllegalStateException("unreachable"); 88 } 89 90 private static void assertNoListeningPorts(String procFilePath, boolean isTcp) { 91 92 /* 93 * Sample output of "cat /proc/net/tcp" on emulator: 94 * 95 * sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid ... 96 * 0: 0100007F:13AD 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 ... 97 * 1: 00000000:15B3 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 ... 98 * 2: 0F02000A:15B3 0202000A:CE8A 01 00000000:00000000 00:00000000 00000000 0 ... 99 * 100 */ 101 102 File procFile = new File(procFilePath); 103 Scanner scanner = null; 104 try { 105 scanner = new Scanner(procFile); 106 while (scanner.hasNextLine()) { 107 String line = scanner.nextLine().trim(); 108 109 // Skip column headers 110 if (line.startsWith("sl")) { 111 continue; 112 } 113 114 String[] fields = line.split("\\s+"); 115 final int expectedNumColumns = 12; 116 assertTrue(procFilePath + " should have at least " + expectedNumColumns 117 + " columns of output " + fields, fields.length >= expectedNumColumns); 118 119 String localAddress = fields[1]; 120 String state = fields[3]; 121 String uid = fields[7]; 122 123 assertTrue(procFilePath + " should have an IP address in the second column", 124 isAddress(localAddress)); 125 126 String localIp = localAddress.split(":")[0]; 127 int localPort = Integer.parseInt(localAddress.split(":")[1], 16); 128 129 if (!isException(localAddress) && isPortListening(state, isTcp)) { 130 throw new ListeningPortsAssertionError( 131 "Found port listening on addr=" + localIp + ", port=" 132 + localPort + ", UID=" + uid + " in " + procFilePath); 133 } 134 } 135 } catch (FileNotFoundException notFound) { 136 fail("Could not open file " + procFilePath + " to check for listening ports."); 137 } finally { 138 if (scanner != null) { 139 scanner.close(); 140 } 141 } 142 } 143 144 private static boolean isAddress(String localAddress) { 145 return isPatternMatch(ADDRESS_PATTERNS, localAddress); 146 } 147 148 private static boolean isException(String localAddress) { 149 return isPatternMatch(EXCEPTION_PATTERNS, localAddress); 150 } 151 152 private static boolean isPatternMatch(List<String> patterns, String input) { 153 for (String pattern : patterns) { 154 if (Pattern.matches(pattern, input)) { 155 return true; 156 } 157 } 158 return false; 159 } 160 161 private static boolean isPortListening(String state, boolean isTcp) { 162 // 0A = TCP_LISTEN from include/net/tcp_states.h 163 String listeningState = isTcp ? "0A" : "07"; 164 return listeningState.equals(state); 165 } 166 167 private static class ListeningPortsAssertionError extends AssertionFailedError { 168 private ListeningPortsAssertionError(String msg) { 169 super(msg); 170 } 171 } 172 } 173