DnsManager.java revision 79c6d0590ed178c4995d05cf2fb49662b0055686
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
17package com.android.server.connectivity;
18
19import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
20import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
21import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
22import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
23import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
24import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
25import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
26import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
27import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
28import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
29
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.net.LinkProperties;
34import android.net.Network;
35import android.net.NetworkUtils;
36import android.net.Uri;
37import android.net.dns.ResolvUtil;
38import android.os.Binder;
39import android.os.INetworkManagementService;
40import android.os.Handler;
41import android.os.UserHandle;
42import android.provider.Settings;
43import android.text.TextUtils;
44import android.util.Slog;
45
46import com.android.server.connectivity.MockableSystemProperties;
47
48import java.net.InetAddress;
49import java.net.UnknownHostException;
50import java.util.Arrays;
51import java.util.Collection;
52import java.util.HashMap;
53import java.util.Map;
54import java.util.stream.Collectors;
55import java.util.StringJoiner;
56
57
58/**
59 * Encapsulate the management of DNS settings for networks.
60 *
61 * This class it NOT designed for concurrent access. Furthermore, all non-static
62 * methods MUST be called from ConnectivityService's thread.
63 *
64 * [ Private DNS ]
65 * The code handling Private DNS is spread across several components, but this
66 * seems like the least bad place to collect all the observations.
67 *
68 * Private DNS handling and updating occurs in response to several different
69 * events. Each is described here with its corresponding intended handling.
70 *
71 * [A] Event: A new network comes up.
72 * Mechanics:
73 *     [1] ConnectivityService gets notifications from NetworkAgents.
74 *     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
75 *         into CONNECTED state, the Private DNS configuration is retrieved,
76 *         programmed, and strict mode hostname resolution (if applicable) is
77 *         enqueued in NetworkAgent's NetworkMonitor, via a call to
78 *         handlePerNetworkPrivateDnsConfig().
79 *     [3] Re-resolution of strict mode hostnames that fail to return any
80 *         IP addresses happens inside NetworkMonitor; it sends itself a
81 *         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
82 *         schedule.
83 *     [4] Successfully resolved hostnames are sent to ConnectivityService
84 *         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
85 *         IP addresses are programmed into netd via:
86 *
87 *             updatePrivateDns() -> updateDnses()
88 *
89 *         both of which make calls into DnsManager.
90 *     [5] Upon a successful hostname resolution NetworkMonitor initiates a
91 *         validation attempt in the form of a lookup for a one-time hostname
92 *         that uses Private DNS.
93 *
94 * [B] Event: Private DNS settings are changed.
95 * Mechanics:
96 *     [1] ConnectivityService gets notifications from its SettingsObserver.
97 *     [2] handlePrivateDnsSettingsChanged() is called, which calls
98 *         handlePerNetworkPrivateDnsConfig() and the process proceeds
99 *         as if from A.3 above.
100 *
101 * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
102 * Mechanics:
103 *     [1] NetworkMonitor is notified and initiates a reevaluation, which
104 *         always bypasses Private DNS.
105 *     [2] Once completed, NetworkMonitor checks if strict mode is in operation
106 *         and if so enqueues another evaluation of Private DNS, as if from
107 *         step A.5 above.
108 *
109 * @hide
110 */
111public class DnsManager {
112    private static final String TAG = DnsManager.class.getSimpleName();
113
114    /* Defaults for resolver parameters. */
115    private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
116    private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
117    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
118    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
119
120    public static class PrivateDnsConfig {
121        public final boolean useTls;
122        public final String hostname;
123        public final InetAddress[] ips;
124
125        public PrivateDnsConfig() {
126            this(false);
127        }
128
129        public PrivateDnsConfig(boolean useTls) {
130            this.useTls = useTls;
131            this.hostname = "";
132            this.ips = new InetAddress[0];
133        }
134
135        public PrivateDnsConfig(String hostname, InetAddress[] ips) {
136            this.useTls = !TextUtils.isEmpty(hostname);
137            this.hostname = useTls ? hostname : "";
138            this.ips = (ips != null) ? ips : new InetAddress[0];
139        }
140
141        public PrivateDnsConfig(PrivateDnsConfig cfg) {
142            useTls = cfg.useTls;
143            hostname = cfg.hostname;
144            ips = cfg.ips;
145        }
146
147        public boolean inStrictMode() {
148            return useTls && !TextUtils.isEmpty(hostname);
149        }
150
151        public String toString() {
152            return PrivateDnsConfig.class.getSimpleName() +
153                    "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
154        }
155    }
156
157    public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
158        final String mode = getPrivateDnsMode(cr);
159
160        final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
161
162        if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
163            final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
164            return new PrivateDnsConfig(specifier, null);
165        }
166
167        return new PrivateDnsConfig(useTls);
168    }
169
170    public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
171        try {
172            final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(network, name);
173            return new PrivateDnsConfig(name, ips);
174        } catch (UnknownHostException uhe) {
175            return new PrivateDnsConfig(name, null);
176        }
177    }
178
179    public static Uri[] getPrivateDnsSettingsUris() {
180        return new Uri[]{
181            Settings.Global.getUriFor(PRIVATE_DNS_MODE),
182            Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
183        };
184    }
185
186    private final Context mContext;
187    private final ContentResolver mContentResolver;
188    private final INetworkManagementService mNMS;
189    private final MockableSystemProperties mSystemProperties;
190    private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
191
192    private int mNumDnsEntries;
193    private int mSampleValidity;
194    private int mSuccessThreshold;
195    private int mMinSamples;
196    private int mMaxSamples;
197    private String mPrivateDnsMode;
198    private String mPrivateDnsSpecifier;
199
200    public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
201        mContext = ctx;
202        mContentResolver = mContext.getContentResolver();
203        mNMS = nms;
204        mSystemProperties = sp;
205        mPrivateDnsMap = new HashMap<>();
206
207        // TODO: Create and register ContentObservers to track every setting
208        // used herein, posting messages to respond to changes.
209    }
210
211    public PrivateDnsConfig getPrivateDnsConfig() {
212        return getPrivateDnsConfig(mContentResolver);
213    }
214
215    public void removeNetwork(Network network) {
216        mPrivateDnsMap.remove(network.netId);
217    }
218
219    public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
220        Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
221        return (cfg != null)
222                ? mPrivateDnsMap.put(network.netId, cfg)
223                : mPrivateDnsMap.remove(network.netId);
224    }
225
226    public void setDnsConfigurationForNetwork(
227            int netId, LinkProperties lp, boolean isDefaultNetwork) {
228        final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
229        final String[] domainStrs = getDomainStrings(lp.getDomains());
230
231        updateParametersSettings();
232        final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
233
234        // We only use the PrivateDnsConfig data pushed to this class instance
235        // from ConnectivityService because it works in coordination with
236        // NetworkMonitor to decide which networks need validation and runs the
237        // blocking calls to resolve Private DNS strict mode hostnames.
238        //
239        // At this time we do not attempt to enable Private DNS on non-Internet
240        // networks like IMS.
241        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
242
243        final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
244        final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
245        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
246        final String[] tlsServers =
247                strictMode ? NetworkUtils.makeStrings(
248                        Arrays.stream(privateDnsCfg.ips)
249                              .filter((ip) -> lp.isReachable(ip))
250                              .collect(Collectors.toList()))
251                : useTls ? assignedServers  // Opportunistic
252                : new String[0];            // Off
253
254        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
255                netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
256                Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
257        try {
258            mNMS.setDnsConfigurationForNetwork(
259                    netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
260        } catch (Exception e) {
261            Slog.e(TAG, "Error setting DNS configuration: " + e);
262            return;
263        }
264
265        // TODO: netd should listen on [::1]:53 and proxy queries to the current
266        // default network, and we should just set net.dns1 to ::1, not least
267        // because applications attempting to use net.dns resolvers will bypass
268        // the privacy protections of things like DNS-over-TLS.
269        if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
270        flushVmDnsCache();
271    }
272
273    public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
274        int last = 0;
275        for (InetAddress dns : dnses) {
276            ++last;
277            setNetDnsProperty(last, dns.getHostAddress());
278        }
279        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
280            setNetDnsProperty(i, "");
281        }
282        mNumDnsEntries = last;
283    }
284
285    private void flushVmDnsCache() {
286        /*
287         * Tell the VMs to toss their DNS caches
288         */
289        final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
290        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
291        /*
292         * Connectivity events can happen before boot has completed ...
293         */
294        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
295        final long ident = Binder.clearCallingIdentity();
296        try {
297            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
298        } finally {
299            Binder.restoreCallingIdentity(ident);
300        }
301    }
302
303    private void updateParametersSettings() {
304        mSampleValidity = getIntSetting(
305                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
306                DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
307        if (mSampleValidity < 0 || mSampleValidity > 65535) {
308            Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
309                    DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
310            mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
311        }
312
313        mSuccessThreshold = getIntSetting(
314                DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
315                DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
316        if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
317            Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
318                    DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
319            mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
320        }
321
322        mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
323        mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
324        if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
325            Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
326                    "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
327                    DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
328            mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
329            mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
330        }
331    }
332
333    private int getIntSetting(String which, int dflt) {
334        return Settings.Global.getInt(mContentResolver, which, dflt);
335    }
336
337    private void setNetDnsProperty(int which, String value) {
338        final String key = "net.dns" + which;
339        // Log and forget errors setting unsupported properties.
340        try {
341            mSystemProperties.set(key, value);
342        } catch (Exception e) {
343            Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
344        }
345    }
346
347    private static String getPrivateDnsMode(ContentResolver cr) {
348        final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
349        return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
350    }
351
352    private static String getStringSetting(ContentResolver cr, String which) {
353        return Settings.Global.getString(cr, which);
354    }
355
356    private static String[] getDomainStrings(String domains) {
357        return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
358    }
359}
360