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