1 /*
2  * Copyright (C) 2018 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 com.android.server.connectivity;
18 
19 import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
20 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
21 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
22 import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
23 import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
24 import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
25 import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
26 import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
27 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
28 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
29 
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.net.IDnsResolver;
34 import android.net.LinkProperties;
35 import android.net.Network;
36 import android.net.NetworkUtils;
37 import android.net.ResolverParamsParcel;
38 import android.net.Uri;
39 import android.net.shared.PrivateDnsConfig;
40 import android.os.Binder;
41 import android.os.RemoteException;
42 import android.os.ServiceSpecificException;
43 import android.os.UserHandle;
44 import android.provider.Settings;
45 import android.text.TextUtils;
46 import android.util.Pair;
47 import android.util.Slog;
48 
49 import java.net.InetAddress;
50 import java.util.Arrays;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.Iterator;
56 import java.util.Map;
57 import java.util.Set;
58 import java.util.stream.Collectors;
59 
60 
61 /**
62  * Encapsulate the management of DNS settings for networks.
63  *
64  * This class it NOT designed for concurrent access. Furthermore, all non-static
65  * methods MUST be called from ConnectivityService's thread.
66  *
67  * [ Private DNS ]
68  * The code handling Private DNS is spread across several components, but this
69  * seems like the least bad place to collect all the observations.
70  *
71  * Private DNS handling and updating occurs in response to several different
72  * events. Each is described here with its corresponding intended handling.
73  *
74  * [A] Event: A new network comes up.
75  * Mechanics:
76  *     [1] ConnectivityService gets notifications from NetworkAgents.
77  *     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
78  *         into CONNECTED state, the Private DNS configuration is retrieved,
79  *         programmed, and strict mode hostname resolution (if applicable) is
80  *         enqueued in NetworkAgent's NetworkMonitor, via a call to
81  *         handlePerNetworkPrivateDnsConfig().
82  *     [3] Re-resolution of strict mode hostnames that fail to return any
83  *         IP addresses happens inside NetworkMonitor; it sends itself a
84  *         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
85  *         schedule.
86  *     [4] Successfully resolved hostnames are sent to ConnectivityService
87  *         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
88  *         IP addresses are programmed into netd via:
89  *
90  *             updatePrivateDns() -> updateDnses()
91  *
92  *         both of which make calls into DnsManager.
93  *     [5] Upon a successful hostname resolution NetworkMonitor initiates a
94  *         validation attempt in the form of a lookup for a one-time hostname
95  *         that uses Private DNS.
96  *
97  * [B] Event: Private DNS settings are changed.
98  * Mechanics:
99  *     [1] ConnectivityService gets notifications from its SettingsObserver.
100  *     [2] handlePrivateDnsSettingsChanged() is called, which calls
101  *         handlePerNetworkPrivateDnsConfig() and the process proceeds
102  *         as if from A.3 above.
103  *
104  * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
105  * Mechanics:
106  *     [1] NetworkMonitor is notified and initiates a reevaluation, which
107  *         always bypasses Private DNS.
108  *     [2] Once completed, NetworkMonitor checks if strict mode is in operation
109  *         and if so enqueues another evaluation of Private DNS, as if from
110  *         step A.5 above.
111  *
112  * @hide
113  */
114 public class DnsManager {
115     private static final String TAG = DnsManager.class.getSimpleName();
116     private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig();
117 
118     /* Defaults for resolver parameters. */
119     private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
120     private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
121     private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
122     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
123 
getPrivateDnsConfig(ContentResolver cr)124     public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
125         final String mode = getPrivateDnsMode(cr);
126 
127         final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
128 
129         if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
130             final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
131             return new PrivateDnsConfig(specifier, null);
132         }
133 
134         return new PrivateDnsConfig(useTls);
135     }
136 
getPrivateDnsSettingsUris()137     public static Uri[] getPrivateDnsSettingsUris() {
138         return new Uri[]{
139             Settings.Global.getUriFor(PRIVATE_DNS_DEFAULT_MODE),
140             Settings.Global.getUriFor(PRIVATE_DNS_MODE),
141             Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
142         };
143     }
144 
145     public static class PrivateDnsValidationUpdate {
146         final public int netId;
147         final public InetAddress ipAddress;
148         final public String hostname;
149         final public boolean validated;
150 
PrivateDnsValidationUpdate(int netId, InetAddress ipAddress, String hostname, boolean validated)151         public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
152                 String hostname, boolean validated) {
153             this.netId = netId;
154             this.ipAddress = ipAddress;
155             this.hostname = hostname;
156             this.validated = validated;
157         }
158     }
159 
160     private static class PrivateDnsValidationStatuses {
161         enum ValidationStatus {
162             IN_PROGRESS,
163             FAILED,
164             SUCCEEDED
165         }
166 
167         // Validation statuses of <hostname, ipAddress> pairs for a single netId
168         // Caution : not thread-safe. As mentioned in the top file comment, all
169         // methods of this class must only be called on ConnectivityService's thread.
170         private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap;
171 
PrivateDnsValidationStatuses()172         private PrivateDnsValidationStatuses() {
173             mValidationMap = new HashMap<>();
174         }
175 
hasValidatedServer()176         private boolean hasValidatedServer() {
177             for (ValidationStatus status : mValidationMap.values()) {
178                 if (status == ValidationStatus.SUCCEEDED) {
179                     return true;
180                 }
181             }
182             return false;
183         }
184 
updateTrackedDnses(String[] ipAddresses, String hostname)185         private void updateTrackedDnses(String[] ipAddresses, String hostname) {
186             Set<Pair<String, InetAddress>> latestDnses = new HashSet<>();
187             for (String ipAddress : ipAddresses) {
188                 try {
189                     latestDnses.add(new Pair(hostname,
190                             InetAddress.parseNumericAddress(ipAddress)));
191                 } catch (IllegalArgumentException e) {}
192             }
193             // Remove <hostname, ipAddress> pairs that should not be tracked.
194             for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it =
195                     mValidationMap.entrySet().iterator(); it.hasNext(); ) {
196                 Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next();
197                 if (!latestDnses.contains(entry.getKey())) {
198                     it.remove();
199                 }
200             }
201             // Add new <hostname, ipAddress> pairs that should be tracked.
202             for (Pair<String, InetAddress> p : latestDnses) {
203                 if (!mValidationMap.containsKey(p)) {
204                     mValidationMap.put(p, ValidationStatus.IN_PROGRESS);
205                 }
206             }
207         }
208 
updateStatus(PrivateDnsValidationUpdate update)209         private void updateStatus(PrivateDnsValidationUpdate update) {
210             Pair<String, InetAddress> p = new Pair(update.hostname,
211                     update.ipAddress);
212             if (!mValidationMap.containsKey(p)) {
213                 return;
214             }
215             if (update.validated) {
216                 mValidationMap.put(p, ValidationStatus.SUCCEEDED);
217             } else {
218                 mValidationMap.put(p, ValidationStatus.FAILED);
219             }
220         }
221 
fillInValidatedPrivateDns(LinkProperties lp)222         private LinkProperties fillInValidatedPrivateDns(LinkProperties lp) {
223             lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST);
224             mValidationMap.forEach((key, value) -> {
225                     if (value == ValidationStatus.SUCCEEDED) {
226                         lp.addValidatedPrivateDnsServer(key.second);
227                     }
228                 });
229             return lp;
230         }
231     }
232 
233     private final Context mContext;
234     private final ContentResolver mContentResolver;
235     private final IDnsResolver mDnsResolver;
236     private final MockableSystemProperties mSystemProperties;
237     // TODO: Replace these Maps with SparseArrays.
238     private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
239     private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
240 
241     private int mNumDnsEntries;
242     private int mSampleValidity;
243     private int mSuccessThreshold;
244     private int mMinSamples;
245     private int mMaxSamples;
246     private String mPrivateDnsMode;
247     private String mPrivateDnsSpecifier;
248 
DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp)249     public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) {
250         mContext = ctx;
251         mContentResolver = mContext.getContentResolver();
252         mDnsResolver = dnsResolver;
253         mSystemProperties = sp;
254         mPrivateDnsMap = new HashMap<>();
255         mPrivateDnsValidationMap = new HashMap<>();
256 
257         // TODO: Create and register ContentObservers to track every setting
258         // used herein, posting messages to respond to changes.
259     }
260 
getPrivateDnsConfig()261     public PrivateDnsConfig getPrivateDnsConfig() {
262         return getPrivateDnsConfig(mContentResolver);
263     }
264 
removeNetwork(Network network)265     public void removeNetwork(Network network) {
266         mPrivateDnsMap.remove(network.netId);
267         mPrivateDnsValidationMap.remove(network.netId);
268     }
269 
updatePrivateDns(Network network, PrivateDnsConfig cfg)270     public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
271         Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
272         return (cfg != null)
273                 ? mPrivateDnsMap.put(network.netId, cfg)
274                 : mPrivateDnsMap.remove(network.netId);
275     }
276 
updatePrivateDnsStatus(int netId, LinkProperties lp)277     public void updatePrivateDnsStatus(int netId, LinkProperties lp) {
278         // Use the PrivateDnsConfig data pushed to this class instance
279         // from ConnectivityService.
280         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
281                 PRIVATE_DNS_OFF);
282 
283         final boolean useTls = privateDnsCfg.useTls;
284         final PrivateDnsValidationStatuses statuses =
285                 useTls ? mPrivateDnsValidationMap.get(netId) : null;
286         final boolean validated = (null != statuses) && statuses.hasValidatedServer();
287         final boolean strictMode = privateDnsCfg.inStrictMode();
288         final String tlsHostname = strictMode ? privateDnsCfg.hostname : null;
289         final boolean usingPrivateDns = strictMode || validated;
290 
291         lp.setUsePrivateDns(usingPrivateDns);
292         lp.setPrivateDnsServerName(tlsHostname);
293         if (usingPrivateDns && null != statuses) {
294             statuses.fillInValidatedPrivateDns(lp);
295         } else {
296             lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST);
297         }
298     }
299 
updatePrivateDnsValidation(PrivateDnsValidationUpdate update)300     public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) {
301         final PrivateDnsValidationStatuses statuses =
302                 mPrivateDnsValidationMap.get(update.netId);
303         if (statuses == null) return;
304         statuses.updateStatus(update);
305     }
306 
setDnsConfigurationForNetwork( int netId, LinkProperties lp, boolean isDefaultNetwork)307     public void setDnsConfigurationForNetwork(
308             int netId, LinkProperties lp, boolean isDefaultNetwork) {
309 
310         updateParametersSettings();
311         final ResolverParamsParcel paramsParcel = new ResolverParamsParcel();
312 
313         // We only use the PrivateDnsConfig data pushed to this class instance
314         // from ConnectivityService because it works in coordination with
315         // NetworkMonitor to decide which networks need validation and runs the
316         // blocking calls to resolve Private DNS strict mode hostnames.
317         //
318         // At this time we do not attempt to enable Private DNS on non-Internet
319         // networks like IMS.
320         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
321                 PRIVATE_DNS_OFF);
322 
323         final boolean useTls = privateDnsCfg.useTls;
324         final boolean strictMode = privateDnsCfg.inStrictMode();
325         paramsParcel.netId = netId;
326         paramsParcel.sampleValiditySeconds = mSampleValidity;
327         paramsParcel.successThreshold = mSuccessThreshold;
328         paramsParcel.minSamples = mMinSamples;
329         paramsParcel.maxSamples = mMaxSamples;
330         paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers());
331         paramsParcel.domains = getDomainStrings(lp.getDomains());
332         paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
333         paramsParcel.tlsServers =
334                 strictMode ? NetworkUtils.makeStrings(
335                         Arrays.stream(privateDnsCfg.ips)
336                               .filter((ip) -> lp.isReachable(ip))
337                               .collect(Collectors.toList()))
338                 : useTls ? paramsParcel.servers  // Opportunistic
339                 : new String[0];            // Off
340         paramsParcel.tlsFingerprints = new String[0];
341         // Prepare to track the validation status of the DNS servers in the
342         // resolver config when private DNS is in opportunistic or strict mode.
343         if (useTls) {
344             if (!mPrivateDnsValidationMap.containsKey(netId)) {
345                 mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
346             }
347             mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers,
348                     paramsParcel.tlsName);
349         } else {
350             mPrivateDnsValidationMap.remove(netId);
351         }
352 
353         Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
354                 + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
355                 Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
356                 paramsParcel.successThreshold, paramsParcel.minSamples,
357                 paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
358                 paramsParcel.retryCount, paramsParcel.tlsName,
359                 Arrays.toString(paramsParcel.tlsServers)));
360 
361         try {
362             mDnsResolver.setResolverConfiguration(paramsParcel);
363         } catch (RemoteException | ServiceSpecificException e) {
364             Slog.e(TAG, "Error setting DNS configuration: " + e);
365             return;
366         }
367 
368         // TODO: netd should listen on [::1]:53 and proxy queries to the current
369         // default network, and we should just set net.dns1 to ::1, not least
370         // because applications attempting to use net.dns resolvers will bypass
371         // the privacy protections of things like DNS-over-TLS.
372         if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
373         flushVmDnsCache();
374     }
375 
setDefaultDnsSystemProperties(Collection<InetAddress> dnses)376     public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
377         int last = 0;
378         for (InetAddress dns : dnses) {
379             ++last;
380             setNetDnsProperty(last, dns.getHostAddress());
381         }
382         for (int i = last + 1; i <= mNumDnsEntries; ++i) {
383             setNetDnsProperty(i, "");
384         }
385         mNumDnsEntries = last;
386     }
387 
flushVmDnsCache()388     private void flushVmDnsCache() {
389         /*
390          * Tell the VMs to toss their DNS caches
391          */
392         final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
393         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
394         /*
395          * Connectivity events can happen before boot has completed ...
396          */
397         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
398         final long ident = Binder.clearCallingIdentity();
399         try {
400             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
401         } finally {
402             Binder.restoreCallingIdentity(ident);
403         }
404     }
405 
updateParametersSettings()406     private void updateParametersSettings() {
407         mSampleValidity = getIntSetting(
408                 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
409                 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
410         if (mSampleValidity < 0 || mSampleValidity > 65535) {
411             Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
412                     DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
413             mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
414         }
415 
416         mSuccessThreshold = getIntSetting(
417                 DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
418                 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
419         if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
420             Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
421                     DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
422             mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
423         }
424 
425         mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
426         mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
427         if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
428             Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
429                     "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
430                     DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
431             mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
432             mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
433         }
434     }
435 
getIntSetting(String which, int dflt)436     private int getIntSetting(String which, int dflt) {
437         return Settings.Global.getInt(mContentResolver, which, dflt);
438     }
439 
setNetDnsProperty(int which, String value)440     private void setNetDnsProperty(int which, String value) {
441         final String key = "net.dns" + which;
442         // Log and forget errors setting unsupported properties.
443         try {
444             mSystemProperties.set(key, value);
445         } catch (Exception e) {
446             Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
447         }
448     }
449 
getPrivateDnsMode(ContentResolver cr)450     private static String getPrivateDnsMode(ContentResolver cr) {
451         String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
452         if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
453         if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
454         return mode;
455     }
456 
getStringSetting(ContentResolver cr, String which)457     private static String getStringSetting(ContentResolver cr, String which) {
458         return Settings.Global.getString(cr, which);
459     }
460 
getDomainStrings(String domains)461     private static String[] getDomainStrings(String domains) {
462         return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
463     }
464 }
465