diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ea91395bbd..200b643808 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3330,6 +3330,8 @@ public class ConnectivityService extends IConnectivityManager.Stub getNetworkPermission(networkAgent.networkCapabilities)); } mDnsResolver.createNetworkCache(networkAgent.network.netId); + mDnsManager.updateTransportsForNetwork(networkAgent.network.netId, + networkAgent.networkCapabilities.getTransportTypes()); return true; } catch (RemoteException | ServiceSpecificException e) { loge("Error creating network " + networkAgent.network.netId + ": " @@ -6079,7 +6081,13 @@ public class ConnectivityService extends IConnectivityManager.Stub log("Setting DNS servers for network " + netId + " to " + dnses); } try { - mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork); + mDnsManager.noteDnsServersForNetwork(netId, newLp); + // TODO: netd should listen on [::1]:53 and proxy queries to the current + // default network, and we should just set net.dns1 to ::1, not least + // because applications attempting to use net.dns resolvers will bypass + // the privacy protections of things like DNS-over-TLS. + if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers()); + mDnsManager.flushVmDnsCache(); } catch (Exception e) { loge("Exception in setDnsConfigurationForNetwork: " + e); } @@ -6277,6 +6285,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // bubble those changes through. updateAllVpnsCapabilities(); } + + if (!newNc.equalsTransportTypes(prevNc)) { + mDnsManager.updateTransportsForNetwork(nai.network.netId, newNc.getTransportTypes()); + } } /** diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index 5250a771d7..506c8e3919 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -27,6 +27,7 @@ import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; +import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -34,6 +35,7 @@ import android.net.IDnsResolver; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkUtils; +import android.net.ResolverOptionsParcel; import android.net.ResolverParamsParcel; import android.net.Uri; import android.net.shared.PrivateDnsConfig; @@ -237,6 +239,8 @@ public class DnsManager { // TODO: Replace these Maps with SparseArrays. private final Map mPrivateDnsMap; private final Map mPrivateDnsValidationMap; + private final Map mLinkPropertiesMap; + private final Map mTransportsMap; private int mNumDnsEntries; private int mSampleValidity; @@ -253,6 +257,8 @@ public class DnsManager { mSystemProperties = sp; mPrivateDnsMap = new HashMap<>(); mPrivateDnsValidationMap = new HashMap<>(); + mLinkPropertiesMap = new HashMap<>(); + mTransportsMap = new HashMap<>(); // TODO: Create and register ContentObservers to track every setting // used herein, posting messages to respond to changes. @@ -265,6 +271,8 @@ public class DnsManager { public void removeNetwork(Network network) { mPrivateDnsMap.remove(network.netId); mPrivateDnsValidationMap.remove(network.netId); + mTransportsMap.remove(network.netId); + mLinkPropertiesMap.remove(network.netId); } public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) { @@ -304,9 +312,35 @@ public class DnsManager { statuses.updateStatus(update); } - public void setDnsConfigurationForNetwork( - int netId, LinkProperties lp, boolean isDefaultNetwork) { + /** + * When creating a new network or transport types are changed in a specific network, + * transport types are always saved to a hashMap before update dns config. + * When destroying network, the specific network will be removed from the hashMap. + * The hashMap is always accessed on the same thread. + */ + public void updateTransportsForNetwork(int netId, @NonNull int[] transportTypes) { + mTransportsMap.put(netId, transportTypes); + sendDnsConfigurationForNetwork(netId); + } + /** + * When {@link LinkProperties} are changed in a specific network, they are + * always saved to a hashMap before update dns config. + * When destroying network, the specific network will be removed from the hashMap. + * The hashMap is always accessed on the same thread. + */ + public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) { + mLinkPropertiesMap.put(netId, lp); + sendDnsConfigurationForNetwork(netId); + } + + /** + * Send dns configuration parameters to resolver for a given network. + */ + public void sendDnsConfigurationForNetwork(int netId) { + final LinkProperties lp = mLinkPropertiesMap.get(netId); + final int[] transportTypes = mTransportsMap.get(netId); + if (lp == null || transportTypes == null) return; updateParametersSettings(); final ResolverParamsParcel paramsParcel = new ResolverParamsParcel(); @@ -319,15 +353,16 @@ public class DnsManager { // networks like IMS. final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId, PRIVATE_DNS_OFF); - final boolean useTls = privateDnsCfg.useTls; final boolean strictMode = privateDnsCfg.inStrictMode(); + paramsParcel.netId = netId; paramsParcel.sampleValiditySeconds = mSampleValidity; paramsParcel.successThreshold = mSuccessThreshold; paramsParcel.minSamples = mMinSamples; paramsParcel.maxSamples = mMaxSamples; - paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers()); + paramsParcel.servers = + NetworkUtils.makeStrings(lp.getDnsServers()); paramsParcel.domains = getDomainStrings(lp.getDomains()); paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : ""; paramsParcel.tlsServers = @@ -337,6 +372,8 @@ public class DnsManager { .collect(Collectors.toList())) : useTls ? paramsParcel.servers // Opportunistic : new String[0]; // Off + paramsParcel.resolverOptions = new ResolverOptionsParcel(); + paramsParcel.transportTypes = transportTypes; // Prepare to track the validation status of the DNS servers in the // resolver config when private DNS is in opportunistic or strict mode. if (useTls) { @@ -349,7 +386,7 @@ public class DnsManager { mPrivateDnsValidationMap.remove(netId); } - Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " + Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers), Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds, paramsParcel.successThreshold, paramsParcel.minSamples, @@ -363,13 +400,6 @@ public class DnsManager { Slog.e(TAG, "Error setting DNS configuration: " + e); return; } - - // TODO: netd should listen on [::1]:53 and proxy queries to the current - // default network, and we should just set net.dns1 to ::1, not least - // because applications attempting to use net.dns resolvers will bypass - // the privacy protections of things like DNS-over-TLS. - if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers()); - flushVmDnsCache(); } public void setDefaultDnsSystemProperties(Collection dnses) { @@ -384,7 +414,10 @@ public class DnsManager { mNumDnsEntries = last; } - private void flushVmDnsCache() { + /** + * Flush DNS caches and events work before boot has completed. + */ + public void flushVmDnsCache() { /* * Tell the VMs to toss their DNS caches */ diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 912a27f08f..b864e37f9e 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -4907,6 +4907,29 @@ public class ConnectivityServiceTest { reset(mMockDnsResolver); } + @Test + public void testDnsConfigurationTransTypesPushed() throws Exception { + // Clear any interactions that occur as a result of CS starting up. + reset(mMockDnsResolver); + + final NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mWiFiNetworkAgent.getNetwork().netId)); + verify(mMockDnsResolver, times(2)).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + final ResolverParamsParcel resolverParams = mResolverParamsParcelCaptor.getValue(); + assertContainsExactly(resolverParams.transportTypes, TRANSPORT_WIFI); + reset(mMockDnsResolver); + } + @Test public void testPrivateDnsNotification() throws Exception { NetworkRequest request = new NetworkRequest.Builder() diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 8fa0ab979a..a392ae3f13 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -18,22 +18,34 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; +import static com.android.testutils.MiscAssertsKt.assertContainsExactly; +import static com.android.testutils.MiscAssertsKt.assertContainsStringsExactly; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.content.Context; import android.net.IDnsResolver; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; +import android.net.ResolverOptionsParcel; +import android.net.ResolverParamsParcel; import android.net.RouteInfo; import android.net.shared.PrivateDnsConfig; import android.provider.Settings; @@ -47,6 +59,7 @@ import com.android.internal.util.test.FakeSettingsProvider; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -66,8 +79,11 @@ public class DnsManagerTest { static final int TEST_NETID = 100; static final int TEST_NETID_ALTERNATE = 101; static final int TEST_NETID_UNTRACKED = 102; - final boolean IS_DEFAULT = true; - final boolean NOT_DEFAULT = false; + static final int TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800; + static final int TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25; + static final int TEST_DEFAULT_MIN_SAMPLES = 8; + static final int TEST_DEFAULT_MAX_SAMPLES = 64; + static final int[] TEST_TRANSPORT_TYPES = {TRANSPORT_WIFI, TRANSPORT_VPN}; DnsManager mDnsManager; MockContentResolver mContentResolver; @@ -76,6 +92,35 @@ public class DnsManagerTest { @Mock IDnsResolver mMockDnsResolver; @Mock MockableSystemProperties mSystemProperties; + private void assertResolverOptionsEquals( + @NonNull ResolverOptionsParcel actual, + @NonNull ResolverOptionsParcel expected) { + assertEquals(actual.hosts, expected.hosts); + assertEquals(actual.tcMode, expected.tcMode); + assertFieldCountEquals(2, ResolverOptionsParcel.class); + } + + private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual, + @NonNull ResolverParamsParcel expected) { + assertEquals(actual.netId, expected.netId); + assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds); + assertEquals(actual.successThreshold, expected.successThreshold); + assertEquals(actual.minSamples, expected.minSamples); + assertEquals(actual.maxSamples, expected.maxSamples); + assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec); + assertEquals(actual.retryCount, expected.retryCount); + assertContainsStringsExactly(actual.servers, expected.servers); + assertContainsStringsExactly(actual.domains, expected.domains); + assertEquals(actual.tlsName, expected.tlsName); + assertContainsStringsExactly(actual.tlsServers, expected.tlsServers); + assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints); + assertEquals(actual.caCertificate, expected.caCertificate); + assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs); + assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions); + assertContainsExactly(actual.transportTypes, expected.transportTypes); + assertFieldCountEquals(16, ResolverParamsParcel.class); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -103,8 +148,13 @@ public class DnsManagerTest { lp.addDnsServer(InetAddress.getByName("4.4.4.4")); // Send a validation event that is tracked on the alternate netId - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT); - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); + mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp); + mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE, InetAddress.parseNumericAddress("4.4.4.4"), "", true)); @@ -135,7 +185,10 @@ public class DnsManagerTest { InetAddress.parseNumericAddress("6.6.6.6"), InetAddress.parseNumericAddress("2001:db8:66:66::1") })); - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); fixedLp = new LinkProperties(lp); mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); assertTrue(fixedLp.isPrivateDnsActive()); @@ -168,7 +221,10 @@ public class DnsManagerTest { // be tracked. LinkProperties lp = new LinkProperties(); lp.addDnsServer(InetAddress.getByName("3.3.3.3")); - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, InetAddress.parseNumericAddress("3.3.3.3"), "", true)); @@ -179,7 +235,10 @@ public class DnsManagerTest { // Validation event has untracked netId mDnsManager.updatePrivateDns(new Network(TEST_NETID), mDnsManager.getPrivateDnsConfig()); - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED, InetAddress.parseNumericAddress("3.3.3.3"), "", true)); @@ -225,7 +284,10 @@ public class DnsManagerTest { Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF); mDnsManager.updatePrivateDns(new Network(TEST_NETID), mDnsManager.getPrivateDnsConfig()); - mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, InetAddress.parseNumericAddress("3.3.3.3"), "", true)); @@ -258,4 +320,38 @@ public class DnsManagerTest { assertEquals("strictmode.com", cfgStrict.hostname); assertEquals(new InetAddress[0], cfgStrict.ips); } + + @Test + public void testSendDnsConfiguration() throws Exception { + reset(mMockDnsResolver); + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + mDnsManager.getPrivateDnsConfig()); + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(TEST_IFACENAME); + lp.addDnsServer(InetAddress.getByName("3.3.3.3")); + lp.addDnsServer(InetAddress.getByName("4.4.4.4")); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); + mDnsManager.flushVmDnsCache(); + + final ArgumentCaptor resolverParamsParcelCaptor = + ArgumentCaptor.forClass(ResolverParamsParcel.class); + verify(mMockDnsResolver, times(1)).setResolverConfiguration( + resolverParamsParcelCaptor.capture()); + final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue(); + final ResolverParamsParcel expectedParams = new ResolverParamsParcel(); + expectedParams.netId = TEST_NETID; + expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS; + expectedParams.successThreshold = TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT; + expectedParams.minSamples = TEST_DEFAULT_MIN_SAMPLES; + expectedParams.maxSamples = TEST_DEFAULT_MAX_SAMPLES; + expectedParams.servers = new String[]{"3.3.3.3", "4.4.4.4"}; + expectedParams.domains = new String[]{}; + expectedParams.tlsName = ""; + expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"}; + expectedParams.transportTypes = TEST_TRANSPORT_TYPES; + expectedParams.resolverOptions = new ResolverOptionsParcel(); + assertResolverParamsEquals(actualParams, expectedParams); + } }