blob: 2a361a02d7d20e2e5d512dc3e1bff32b821debe9 [file] [log] [blame]
Erik Kline1742fe12017-12-13 19:40:49 +09001/*
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;
Erik Klinea24d4592018-01-11 21:07:29 +090020import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
Erik Kline1742fe12017-12-13 19:40:49 +090021import 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;
Erik Klinea24d4592018-01-11 21:07:29 +090033import android.net.LinkProperties;
34import android.net.Network;
Erik Kline1742fe12017-12-13 19:40:49 +090035import android.net.NetworkUtils;
Erik Klinea24d4592018-01-11 21:07:29 +090036import android.net.Uri;
Erik Klinec43d2f52018-03-21 07:18:33 -070037import android.net.dns.ResolvUtil;
Erik Kline1742fe12017-12-13 19:40:49 +090038import 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;
Erik Klinec43d2f52018-03-21 07:18:33 -070049import java.net.UnknownHostException;
Erik Klinea24d4592018-01-11 21:07:29 +090050import java.util.Arrays;
Erik Kline1742fe12017-12-13 19:40:49 +090051import java.util.Collection;
Erik Klinea24d4592018-01-11 21:07:29 +090052import java.util.HashMap;
53import java.util.Map;
54import java.util.stream.Collectors;
55import java.util.StringJoiner;
Erik Kline1742fe12017-12-13 19:40:49 +090056
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 *
Erik Kline79c6d052018-03-21 07:18:33 -070064 * [ 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 *
Erik Kline1742fe12017-12-13 19:40:49 +0900109 * @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
Erik Klinea24d4592018-01-11 21:07:29 +0900120 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) {
Erik Klinea24d4592018-01-11 21:07:29 +0900171 try {
Erik Klinec43d2f52018-03-21 07:18:33 -0700172 final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(network, name);
173 return new PrivateDnsConfig(name, ips);
174 } catch (UnknownHostException uhe) {
175 return new PrivateDnsConfig(name, null);
176 }
Erik Klinea24d4592018-01-11 21:07:29 +0900177 }
178
179 public static Uri[] getPrivateDnsSettingsUris() {
Erik Klinec43d2f52018-03-21 07:18:33 -0700180 return new Uri[]{
181 Settings.Global.getUriFor(PRIVATE_DNS_MODE),
182 Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
183 };
Erik Klinea24d4592018-01-11 21:07:29 +0900184 }
185
Erik Kline1742fe12017-12-13 19:40:49 +0900186 private final Context mContext;
187 private final ContentResolver mContentResolver;
188 private final INetworkManagementService mNMS;
189 private final MockableSystemProperties mSystemProperties;
Erik Klinea24d4592018-01-11 21:07:29 +0900190 private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
Erik Kline1742fe12017-12-13 19:40:49 +0900191
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;
Erik Klinea24d4592018-01-11 21:07:29 +0900205 mPrivateDnsMap = new HashMap<>();
Erik Kline1742fe12017-12-13 19:40:49 +0900206
207 // TODO: Create and register ContentObservers to track every setting
208 // used herein, posting messages to respond to changes.
209 }
210
Erik Klinea24d4592018-01-11 21:07:29 +0900211 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)
Andreas Gampeba061332018-03-05 17:23:07 -0800223 : mPrivateDnsMap.remove(network.netId);
Erik Kline1742fe12017-12-13 19:40:49 +0900224 }
225
226 public void setDnsConfigurationForNetwork(
Erik Klinea24d4592018-01-11 21:07:29 +0900227 int netId, LinkProperties lp, boolean isDefaultNetwork) {
Erik Kline117e7f32018-03-04 21:01:01 +0900228 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
Erik Klinea24d4592018-01-11 21:07:29 +0900234 // 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 //
Erik Klinec43d2f52018-03-21 07:18:33 -0700239 // At this time we do not attempt to enable Private DNS on non-Internet
Erik Klinea24d4592018-01-11 21:07:29 +0900240 // networks like IMS.
241 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
Erik Kline1742fe12017-12-13 19:40:49 +0900242
Erik Klinea24d4592018-01-11 21:07:29 +0900243 final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
244 final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
245 final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
Erik Kline117e7f32018-03-04 21:01:01 +0900246 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
Erik Klinea24d4592018-01-11 21:07:29 +0900253
254 Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
Erik Kline117e7f32018-03-04 21:01:01 +0900255 netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
256 Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
Erik Kline1742fe12017-12-13 19:40:49 +0900257 try {
258 mNMS.setDnsConfigurationForNetwork(
Erik Kline117e7f32018-03-04 21:01:01 +0900259 netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
Erik Kline1742fe12017-12-13 19:40:49 +0900260 } 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.
Erik Klinea24d4592018-01-11 21:07:29 +0900269 if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
Erik Kline1742fe12017-12-13 19:40:49 +0900270 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
Erik Kline1742fe12017-12-13 19:40:49 +0900303 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
Erik Kline1742fe12017-12-13 19:40:49 +0900333 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
Erik Klinea24d4592018-01-11 21:07:29 +0900347 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(" ");
Erik Kline1742fe12017-12-13 19:40:49 +0900358 }
359}