blob: 36f5a6c354f27502f838b38bd482948f681e7d69 [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 Kline1742fe12017-12-13 19:40:49 +090037import android.os.Binder;
38import android.os.INetworkManagementService;
39import android.os.Handler;
40import android.os.UserHandle;
41import android.provider.Settings;
Erik Klinea24d4592018-01-11 21:07:29 +090042import android.system.GaiException;
43import android.system.OsConstants;
44import android.system.StructAddrinfo;
Erik Kline1742fe12017-12-13 19:40:49 +090045import android.text.TextUtils;
46import android.util.Slog;
47
48import com.android.server.connectivity.MockableSystemProperties;
49
Erik Klinea24d4592018-01-11 21:07:29 +090050import libcore.io.Libcore;
51
Erik Kline1742fe12017-12-13 19:40:49 +090052import java.net.InetAddress;
Erik Klinea24d4592018-01-11 21:07:29 +090053import java.util.Arrays;
Erik Kline1742fe12017-12-13 19:40:49 +090054import java.util.Collection;
Erik Klinea24d4592018-01-11 21:07:29 +090055import java.util.HashMap;
56import java.util.Map;
57import java.util.stream.Collectors;
58import java.util.StringJoiner;
Erik Kline1742fe12017-12-13 19:40:49 +090059
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 * @hide
68 */
69public class DnsManager {
70 private static final String TAG = DnsManager.class.getSimpleName();
71
72 /* Defaults for resolver parameters. */
73 private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
74 private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
75 private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
76 private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
77
Erik Klinea24d4592018-01-11 21:07:29 +090078 public static class PrivateDnsConfig {
79 public final boolean useTls;
80 public final String hostname;
81 public final InetAddress[] ips;
82
83 public PrivateDnsConfig() {
84 this(false);
85 }
86
87 public PrivateDnsConfig(boolean useTls) {
88 this.useTls = useTls;
89 this.hostname = "";
90 this.ips = new InetAddress[0];
91 }
92
93 public PrivateDnsConfig(String hostname, InetAddress[] ips) {
94 this.useTls = !TextUtils.isEmpty(hostname);
95 this.hostname = useTls ? hostname : "";
96 this.ips = (ips != null) ? ips : new InetAddress[0];
97 }
98
99 public PrivateDnsConfig(PrivateDnsConfig cfg) {
100 useTls = cfg.useTls;
101 hostname = cfg.hostname;
102 ips = cfg.ips;
103 }
104
105 public boolean inStrictMode() {
106 return useTls && !TextUtils.isEmpty(hostname);
107 }
108
109 public String toString() {
110 return PrivateDnsConfig.class.getSimpleName() +
111 "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
112 }
113 }
114
115 public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
116 final String mode = getPrivateDnsMode(cr);
117
118 final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
119
120 if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
121 final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
122 return new PrivateDnsConfig(specifier, null);
123 }
124
125 return new PrivateDnsConfig(useTls);
126 }
127
128 public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
129 final StructAddrinfo hints = new StructAddrinfo();
130 // Unnecessary, but expressly no AI_ADDRCONFIG.
131 hints.ai_flags = 0;
132 // Fetch all IP addresses at once to minimize re-resolution.
133 hints.ai_family = OsConstants.AF_UNSPEC;
134 hints.ai_socktype = OsConstants.SOCK_DGRAM;
135
136 try {
137 final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
138 if (ips != null && ips.length > 0) {
139 return new PrivateDnsConfig(name, ips);
140 }
141 } catch (GaiException ignored) {}
142
143 return null;
144 }
145
146 public static Uri[] getPrivateDnsSettingsUris() {
147 final Uri[] uris = new Uri[2];
148 uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
149 uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
150 return uris;
151 }
152
Erik Kline1742fe12017-12-13 19:40:49 +0900153 private final Context mContext;
154 private final ContentResolver mContentResolver;
155 private final INetworkManagementService mNMS;
156 private final MockableSystemProperties mSystemProperties;
Erik Klinea24d4592018-01-11 21:07:29 +0900157 private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
Erik Kline1742fe12017-12-13 19:40:49 +0900158
159 private int mNumDnsEntries;
160 private int mSampleValidity;
161 private int mSuccessThreshold;
162 private int mMinSamples;
163 private int mMaxSamples;
164 private String mPrivateDnsMode;
165 private String mPrivateDnsSpecifier;
166
167 public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
168 mContext = ctx;
169 mContentResolver = mContext.getContentResolver();
170 mNMS = nms;
171 mSystemProperties = sp;
Erik Klinea24d4592018-01-11 21:07:29 +0900172 mPrivateDnsMap = new HashMap<>();
Erik Kline1742fe12017-12-13 19:40:49 +0900173
174 // TODO: Create and register ContentObservers to track every setting
175 // used herein, posting messages to respond to changes.
176 }
177
Erik Klinea24d4592018-01-11 21:07:29 +0900178 public PrivateDnsConfig getPrivateDnsConfig() {
179 return getPrivateDnsConfig(mContentResolver);
180 }
181
182 public void removeNetwork(Network network) {
183 mPrivateDnsMap.remove(network.netId);
184 }
185
186 public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
187 Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
188 return (cfg != null)
189 ? mPrivateDnsMap.put(network.netId, cfg)
Andreas Gampeba061332018-03-05 17:23:07 -0800190 : mPrivateDnsMap.remove(network.netId);
Erik Kline1742fe12017-12-13 19:40:49 +0900191 }
192
193 public void setDnsConfigurationForNetwork(
Erik Klinea24d4592018-01-11 21:07:29 +0900194 int netId, LinkProperties lp, boolean isDefaultNetwork) {
Erik Kline117e7f32018-03-04 21:01:01 +0900195 final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
196 final String[] domainStrs = getDomainStrings(lp.getDomains());
197
198 updateParametersSettings();
199 final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
200
Erik Klinea24d4592018-01-11 21:07:29 +0900201 // We only use the PrivateDnsConfig data pushed to this class instance
202 // from ConnectivityService because it works in coordination with
203 // NetworkMonitor to decide which networks need validation and runs the
204 // blocking calls to resolve Private DNS strict mode hostnames.
205 //
206 // At this time we do attempt to enable Private DNS on non-Internet
207 // networks like IMS.
208 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
Erik Kline1742fe12017-12-13 19:40:49 +0900209
Erik Klinea24d4592018-01-11 21:07:29 +0900210 final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
211 final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
212 final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
Erik Kline117e7f32018-03-04 21:01:01 +0900213 final String[] tlsServers =
214 strictMode ? NetworkUtils.makeStrings(
215 Arrays.stream(privateDnsCfg.ips)
216 .filter((ip) -> lp.isReachable(ip))
217 .collect(Collectors.toList()))
218 : useTls ? assignedServers // Opportunistic
219 : new String[0]; // Off
Erik Klinea24d4592018-01-11 21:07:29 +0900220
221 Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
Erik Kline117e7f32018-03-04 21:01:01 +0900222 netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
223 Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
Erik Kline1742fe12017-12-13 19:40:49 +0900224 try {
225 mNMS.setDnsConfigurationForNetwork(
Erik Kline117e7f32018-03-04 21:01:01 +0900226 netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
Erik Kline1742fe12017-12-13 19:40:49 +0900227 } catch (Exception e) {
228 Slog.e(TAG, "Error setting DNS configuration: " + e);
229 return;
230 }
231
232 // TODO: netd should listen on [::1]:53 and proxy queries to the current
233 // default network, and we should just set net.dns1 to ::1, not least
234 // because applications attempting to use net.dns resolvers will bypass
235 // the privacy protections of things like DNS-over-TLS.
Erik Klinea24d4592018-01-11 21:07:29 +0900236 if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
Erik Kline1742fe12017-12-13 19:40:49 +0900237 flushVmDnsCache();
238 }
239
240 public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
241 int last = 0;
242 for (InetAddress dns : dnses) {
243 ++last;
244 setNetDnsProperty(last, dns.getHostAddress());
245 }
246 for (int i = last + 1; i <= mNumDnsEntries; ++i) {
247 setNetDnsProperty(i, "");
248 }
249 mNumDnsEntries = last;
250 }
251
252 private void flushVmDnsCache() {
253 /*
254 * Tell the VMs to toss their DNS caches
255 */
256 final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
257 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
258 /*
259 * Connectivity events can happen before boot has completed ...
260 */
261 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
262 final long ident = Binder.clearCallingIdentity();
263 try {
264 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
265 } finally {
266 Binder.restoreCallingIdentity(ident);
267 }
268 }
269
Erik Kline1742fe12017-12-13 19:40:49 +0900270 private void updateParametersSettings() {
271 mSampleValidity = getIntSetting(
272 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
273 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
274 if (mSampleValidity < 0 || mSampleValidity > 65535) {
275 Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
276 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
277 mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
278 }
279
280 mSuccessThreshold = getIntSetting(
281 DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
282 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
283 if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
284 Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
285 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
286 mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
287 }
288
289 mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
290 mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
291 if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
292 Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
293 "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
294 DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
295 mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
296 mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
297 }
298 }
299
Erik Kline1742fe12017-12-13 19:40:49 +0900300 private int getIntSetting(String which, int dflt) {
301 return Settings.Global.getInt(mContentResolver, which, dflt);
302 }
303
304 private void setNetDnsProperty(int which, String value) {
305 final String key = "net.dns" + which;
306 // Log and forget errors setting unsupported properties.
307 try {
308 mSystemProperties.set(key, value);
309 } catch (Exception e) {
310 Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
311 }
312 }
313
Erik Klinea24d4592018-01-11 21:07:29 +0900314 private static String getPrivateDnsMode(ContentResolver cr) {
315 final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
316 return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
317 }
318
319 private static String getStringSetting(ContentResolver cr, String which) {
320 return Settings.Global.getString(cr, which);
321 }
322
323 private static String[] getDomainStrings(String domains) {
324 return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
Erik Kline1742fe12017-12-13 19:40:49 +0900325 }
326}