Support strict mode private DNS on VPNs that provide Internet.

Currently, strict mode private DNS does not work on VPNs because
NetworkMonitor does not validate VPNs. When a VPN connects, it
immediately transitions to ValidatedState, skipping private DNS
hostname resolution.

This change makes NetworkMonitor perform private DNS hostname
resolution and evaluation even on VPNs.

In order to ensure that the system always immediately switches to
the VPN as soon as it connects, remove the unvalidated penalty
for VPN networks. This ensures that the VPN score is always 101
and the VPN always outscores other networks as soon as it
connects. Previously, it would only outscore other networks
when no-op validation completed.

Bug: 122652057
Test: atest FrameworksNetTests NetworkStackTests
Test: manually ran a VPN with private DNS in strict mode
atest android.net.cts.ConnectivityManagerTest com.android.cts.net.HostsideVpnTests
Change-Id: Iaa78a7edcf23755c89d7b354edbc28d37d74d891
This commit is contained in:
Lorenzo Colitti
2019-03-22 00:28:28 +09:00
parent 14b9e8b6d8
commit 2fca7e3cb3
3 changed files with 67 additions and 8 deletions

View File

@@ -28,6 +28,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
@@ -489,7 +490,7 @@ public class ConnectivityServiceTest {
MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport);
@@ -619,6 +620,10 @@ public class ConnectivityServiceTest {
mNetworkAgent.sendNetworkScore(mScore);
}
public int getScore() {
return mScore;
}
public void explicitlySelected(boolean acceptUnvalidated) {
mNetworkAgent.explicitlySelected(acceptUnvalidated);
}
@@ -1330,6 +1335,8 @@ public class ConnectivityServiceTest {
return TYPE_WIFI;
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
case TRANSPORT_VPN:
return TYPE_VPN;
default:
return TYPE_NONE;
}
@@ -5392,6 +5399,58 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(defaultCallback);
}
@Test
public void testVpnUnvalidated() throws Exception {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(callback);
// Bring up Ethernet.
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
final int uid = Process.myUid();
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
// TODO: this looks like a spurious callback.
callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
callback.assertNoCallback();
assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
vpnNetworkAgent.mNetworkCapabilities));
// Pretend that the VPN network validates.
vpnNetworkAgent.setNetworkValid();
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
callback.assertNoCallback();
vpnNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@Test
public void testVpnSetUnderlyingNetworks() {
final int uid = Process.myUid();