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:
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user