Home | History | Annotate | Download | only in cts
      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