Merge "Add a CTS test for private DNS on VPNs." into qt-dev am: 17311d9af6 am: fe44dd21ba am: 523cc30322

Change-Id: If38d0c7e8b878e76ff4a52fcdd441b002ce9ddf0
This commit is contained in:
Automerger Merge Worker
2019-12-13 01:41:48 +00:00

View File

@@ -20,6 +20,7 @@ import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.*; import static android.system.OsConstants.*;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -33,6 +34,7 @@ import android.net.Proxy;
import android.net.ProxyInfo; import android.net.ProxyInfo;
import android.net.VpnService; import android.net.VpnService;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.Process; import android.os.Process;
import android.os.SystemProperties; import android.os.SystemProperties;
@@ -62,8 +64,11 @@ import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -95,6 +100,13 @@ import java.util.concurrent.TimeUnit;
*/ */
public class VpnTest extends InstrumentationTestCase { public class VpnTest extends InstrumentationTestCase {
// These are neither public nor @TestApi.
// TODO: add them to @TestApi.
private static final String PRIVATE_DNS_MODE_SETTING = "private_dns_mode";
private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier";
public static String TAG = "VpnTest"; public static String TAG = "VpnTest";
public static int TIMEOUT_MS = 3 * 1000; public static int TIMEOUT_MS = 3 * 1000;
public static int SOCKET_TIMEOUT_MS = 100; public static int SOCKET_TIMEOUT_MS = 100;
@@ -112,6 +124,9 @@ public class VpnTest extends InstrumentationTestCase {
final Object mLock = new Object(); final Object mLock = new Object();
final Object mLockShutdown = new Object(); final Object mLockShutdown = new Object();
private String mOldPrivateDnsMode;
private String mOldPrivateDnsSpecifier;
private boolean supportedHardware() { private boolean supportedHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager(); final PackageManager pm = getInstrumentation().getContext().getPackageManager();
return !pm.hasSystemFeature("android.hardware.type.watch"); return !pm.hasSystemFeature("android.hardware.type.watch");
@@ -123,6 +138,7 @@ public class VpnTest extends InstrumentationTestCase {
mNetwork = null; mNetwork = null;
mCallback = null; mCallback = null;
storePrivateDnsSetting();
mDevice = UiDevice.getInstance(getInstrumentation()); mDevice = UiDevice.getInstance(getInstrumentation());
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
@@ -137,6 +153,7 @@ public class VpnTest extends InstrumentationTestCase {
@Override @Override
public void tearDown() throws Exception { public void tearDown() throws Exception {
restorePrivateDnsSetting();
mRemoteSocketFactoryClient.unbind(); mRemoteSocketFactoryClient.unbind();
if (mCallback != null) { if (mCallback != null) {
mCM.unregisterNetworkCallback(mCallback); mCM.unregisterNetworkCallback(mCallback);
@@ -567,6 +584,95 @@ public class VpnTest extends InstrumentationTestCase {
} }
} }
private ContentResolver getContentResolver() {
return getInstrumentation().getContext().getContentResolver();
}
private boolean isPrivateDnsInStrictMode() {
return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(
Settings.Global.getString(getContentResolver(), PRIVATE_DNS_MODE_SETTING));
}
private void storePrivateDnsSetting() {
mOldPrivateDnsMode = Settings.Global.getString(getContentResolver(),
PRIVATE_DNS_MODE_SETTING);
mOldPrivateDnsSpecifier = Settings.Global.getString(getContentResolver(),
PRIVATE_DNS_SPECIFIER_SETTING);
}
private void restorePrivateDnsSetting() {
Settings.Global.putString(getContentResolver(), PRIVATE_DNS_MODE_SETTING,
mOldPrivateDnsMode);
Settings.Global.putString(getContentResolver(), PRIVATE_DNS_SPECIFIER_SETTING,
mOldPrivateDnsSpecifier);
}
// TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
private void expectPrivateDnsHostname(final String hostname) throws Exception {
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
final CountDownLatch latch = new CountDownLatch(1);
final NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
if (network.equals(mNetwork) &&
Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
latch.countDown();
}
}
};
mCM.registerNetworkCallback(request, callback);
try {
assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mCM.unregisterNetworkCallback(callback);
}
}
private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
final ContentResolver cr = getInstrumentation().getContext().getContentResolver();
String privateDnsHostname;
if (strictMode) {
privateDnsHostname = "vpncts-nx.metric.gstatic.com";
Settings.Global.putString(cr, PRIVATE_DNS_SPECIFIER_SETTING, privateDnsHostname);
Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING,
PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
} else {
Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, PRIVATE_DNS_MODE_OPPORTUNISTIC);
privateDnsHostname = null;
}
expectPrivateDnsHostname(privateDnsHostname);
String randomName = "vpncts-" + new Random().nextInt(1000000000) + "-ds.metric.gstatic.com";
if (strictMode) {
// Strict mode private DNS is enabled. DNS lookups should fail, because the private DNS
// server name is invalid.
try {
InetAddress.getByName(randomName);
fail("VPN DNS lookup should fail with private DNS enabled");
} catch (UnknownHostException expected) {
}
} else {
// Strict mode private DNS is disabled. DNS lookup should succeed, because the VPN
// provides no DNS servers, and thus DNS falls through to the default network.
assertNotNull("VPN DNS lookup should succeed with private DNS disabled",
InetAddress.getByName(randomName));
}
}
// Tests that strict mode private DNS is used on VPNs.
private void checkStrictModePrivateDns() throws Exception {
final boolean initialMode = isPrivateDnsInStrictMode();
setAndVerifyPrivateDns(!initialMode);
setAndVerifyPrivateDns(initialMode);
}
public void testDefault() throws Exception { public void testDefault() throws Exception {
if (!supportedHardware()) return; if (!supportedHardware()) return;
// If adb TCP port opened, this test may running by adb over network. // If adb TCP port opened, this test may running by adb over network.
@@ -598,6 +704,9 @@ public class VpnTest extends InstrumentationTestCase {
assertSocketClosed(fd, TEST_HOST); assertSocketClosed(fd, TEST_HOST);
checkTrafficOnVpn(); checkTrafficOnVpn();
checkStrictModePrivateDns();
receiver.unregisterQuietly(); receiver.unregisterQuietly();
} }
@@ -615,6 +724,8 @@ public class VpnTest extends InstrumentationTestCase {
assertSocketClosed(fd, TEST_HOST); assertSocketClosed(fd, TEST_HOST);
checkTrafficOnVpn(); checkTrafficOnVpn();
checkStrictModePrivateDns();
} }
public void testAppDisallowed() throws Exception { public void testAppDisallowed() throws Exception {