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