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_PROVIDER_HOSTNAME; 22 import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES; 23 import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES; 24 import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS; 25 import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT; 26 import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; 27 import static android.provider.Settings.Global.PRIVATE_DNS_MODE; 28 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; 29 30 import android.annotation.NonNull; 31 import android.content.ContentResolver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.net.IDnsResolver; 35 import android.net.LinkProperties; 36 import android.net.Network; 37 import android.net.NetworkUtils; 38 import android.net.ResolverOptionsParcel; 39 import android.net.ResolverParamsParcel; 40 import android.net.Uri; 41 import android.net.shared.PrivateDnsConfig; 42 import android.os.Binder; 43 import android.os.RemoteException; 44 import android.os.ServiceSpecificException; 45 import android.os.UserHandle; 46 import android.provider.Settings; 47 import android.text.TextUtils; 48 import android.util.Pair; 49 import android.util.Slog; 50 51 import java.net.InetAddress; 52 import java.util.Arrays; 53 import java.util.Collection; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.HashSet; 57 import java.util.Iterator; 58 import java.util.Map; 59 import java.util.Set; 60 import java.util.concurrent.ConcurrentHashMap; 61 import java.util.stream.Collectors; 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. However, an exceptional 69 * case is getPrivateDnsConfig(Network) which is exclusively for 70 * ConnectivityService#dumpNetworkDiagnostics() on a random binder thread. 71 * 72 * [ Private DNS ] 73 * The code handling Private DNS is spread across several components, but this 74 * seems like the least bad place to collect all the observations. 75 * 76 * Private DNS handling and updating occurs in response to several different 77 * events. Each is described here with its corresponding intended handling. 78 * 79 * [A] Event: A new network comes up. 80 * Mechanics: 81 * [1] ConnectivityService gets notifications from NetworkAgents. 82 * [2] in updateNetworkInfo(), the first time the NetworkAgent goes into 83 * into CONNECTED state, the Private DNS configuration is retrieved, 84 * programmed, and strict mode hostname resolution (if applicable) is 85 * enqueued in NetworkAgent's NetworkMonitor, via a call to 86 * handlePerNetworkPrivateDnsConfig(). 87 * [3] Re-resolution of strict mode hostnames that fail to return any 88 * IP addresses happens inside NetworkMonitor; it sends itself a 89 * delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff 90 * schedule. 91 * [4] Successfully resolved hostnames are sent to ConnectivityService 92 * inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved 93 * IP addresses are programmed into netd via: 94 * 95 * updatePrivateDns() -> updateDnses() 96 * 97 * both of which make calls into DnsManager. 98 * [5] Upon a successful hostname resolution NetworkMonitor initiates a 99 * validation attempt in the form of a lookup for a one-time hostname 100 * that uses Private DNS. 101 * 102 * [B] Event: Private DNS settings are changed. 103 * Mechanics: 104 * [1] ConnectivityService gets notifications from its SettingsObserver. 105 * [2] handlePrivateDnsSettingsChanged() is called, which calls 106 * handlePerNetworkPrivateDnsConfig() and the process proceeds 107 * as if from A.3 above. 108 * 109 * [C] Event: An application calls ConnectivityManager#reportBadNetwork(). 110 * Mechanics: 111 * [1] NetworkMonitor is notified and initiates a reevaluation, which 112 * always bypasses Private DNS. 113 * [2] Once completed, NetworkMonitor checks if strict mode is in operation 114 * and if so enqueues another evaluation of Private DNS, as if from 115 * step A.5 above. 116 * 117 * @hide 118 */ 119 public class DnsManager { 120 private static final String TAG = DnsManager.class.getSimpleName(); 121 private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig(); 122 123 /* Defaults for resolver parameters. */ 124 private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800; 125 private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25; 126 private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; 127 private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; 128 getPrivateDnsConfig(ContentResolver cr)129 public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { 130 final String mode = getPrivateDnsMode(cr); 131 132 final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode); 133 134 if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) { 135 final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER); 136 return new PrivateDnsConfig(specifier, null); 137 } 138 139 return new PrivateDnsConfig(useTls); 140 } 141 getPrivateDnsSettingsUris()142 public static Uri[] getPrivateDnsSettingsUris() { 143 return new Uri[]{ 144 Settings.Global.getUriFor(PRIVATE_DNS_DEFAULT_MODE), 145 Settings.Global.getUriFor(PRIVATE_DNS_MODE), 146 Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER), 147 }; 148 } 149 150 public static class PrivateDnsValidationUpdate { 151 final public int netId; 152 final public InetAddress ipAddress; 153 final public String hostname; 154 final public boolean validated; 155 PrivateDnsValidationUpdate(int netId, InetAddress ipAddress, String hostname, boolean validated)156 public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress, 157 String hostname, boolean validated) { 158 this.netId = netId; 159 this.ipAddress = ipAddress; 160 this.hostname = hostname; 161 this.validated = validated; 162 } 163 } 164 165 private static class PrivateDnsValidationStatuses { 166 enum ValidationStatus { 167 IN_PROGRESS, 168 FAILED, 169 SUCCEEDED 170 } 171 172 // Validation statuses of <hostname, ipAddress> pairs for a single netId 173 // Caution : not thread-safe. As mentioned in the top file comment, all 174 // methods of this class must only be called on ConnectivityService's thread. 175 private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap; 176 PrivateDnsValidationStatuses()177 private PrivateDnsValidationStatuses() { 178 mValidationMap = new HashMap<>(); 179 } 180 hasValidatedServer()181 private boolean hasValidatedServer() { 182 for (ValidationStatus status : mValidationMap.values()) { 183 if (status == ValidationStatus.SUCCEEDED) { 184 return true; 185 } 186 } 187 return false; 188 } 189 updateTrackedDnses(String[] ipAddresses, String hostname)190 private void updateTrackedDnses(String[] ipAddresses, String hostname) { 191 Set<Pair<String, InetAddress>> latestDnses = new HashSet<>(); 192 for (String ipAddress : ipAddresses) { 193 try { 194 latestDnses.add(new Pair(hostname, 195 InetAddress.parseNumericAddress(ipAddress))); 196 } catch (IllegalArgumentException e) {} 197 } 198 // Remove <hostname, ipAddress> pairs that should not be tracked. 199 for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it = 200 mValidationMap.entrySet().iterator(); it.hasNext(); ) { 201 Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next(); 202 if (!latestDnses.contains(entry.getKey())) { 203 it.remove(); 204 } 205 } 206 // Add new <hostname, ipAddress> pairs that should be tracked. 207 for (Pair<String, InetAddress> p : latestDnses) { 208 if (!mValidationMap.containsKey(p)) { 209 mValidationMap.put(p, ValidationStatus.IN_PROGRESS); 210 } 211 } 212 } 213 updateStatus(PrivateDnsValidationUpdate update)214 private void updateStatus(PrivateDnsValidationUpdate update) { 215 Pair<String, InetAddress> p = new Pair(update.hostname, 216 update.ipAddress); 217 if (!mValidationMap.containsKey(p)) { 218 return; 219 } 220 if (update.validated) { 221 mValidationMap.put(p, ValidationStatus.SUCCEEDED); 222 } else { 223 mValidationMap.put(p, ValidationStatus.FAILED); 224 } 225 } 226 fillInValidatedPrivateDns(LinkProperties lp)227 private LinkProperties fillInValidatedPrivateDns(LinkProperties lp) { 228 lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST); 229 mValidationMap.forEach((key, value) -> { 230 if (value == ValidationStatus.SUCCEEDED) { 231 lp.addValidatedPrivateDnsServer(key.second); 232 } 233 }); 234 return lp; 235 } 236 } 237 238 private final Context mContext; 239 private final ContentResolver mContentResolver; 240 private final IDnsResolver mDnsResolver; 241 private final MockableSystemProperties mSystemProperties; 242 private final ConcurrentHashMap<Integer, PrivateDnsConfig> mPrivateDnsMap; 243 // TODO: Replace the Map with SparseArrays. 244 private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap; 245 private final Map<Integer, LinkProperties> mLinkPropertiesMap; 246 private final Map<Integer, int[]> mTransportsMap; 247 248 private int mNumDnsEntries; 249 private int mSampleValidity; 250 private int mSuccessThreshold; 251 private int mMinSamples; 252 private int mMaxSamples; 253 DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp)254 public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) { 255 mContext = ctx; 256 mContentResolver = mContext.getContentResolver(); 257 mDnsResolver = dnsResolver; 258 mSystemProperties = sp; 259 mPrivateDnsMap = new ConcurrentHashMap<>(); 260 mPrivateDnsValidationMap = new HashMap<>(); 261 mLinkPropertiesMap = new HashMap<>(); 262 mTransportsMap = new HashMap<>(); 263 264 // TODO: Create and register ContentObservers to track every setting 265 // used herein, posting messages to respond to changes. 266 } 267 getPrivateDnsConfig()268 public PrivateDnsConfig getPrivateDnsConfig() { 269 return getPrivateDnsConfig(mContentResolver); 270 } 271 removeNetwork(Network network)272 public void removeNetwork(Network network) { 273 mPrivateDnsMap.remove(network.netId); 274 mPrivateDnsValidationMap.remove(network.netId); 275 mTransportsMap.remove(network.netId); 276 mLinkPropertiesMap.remove(network.netId); 277 } 278 279 // This is exclusively called by ConnectivityService#dumpNetworkDiagnostics() which 280 // is not on the ConnectivityService handler thread. getPrivateDnsConfig(@onNull Network network)281 public PrivateDnsConfig getPrivateDnsConfig(@NonNull Network network) { 282 return mPrivateDnsMap.getOrDefault(network.netId, PRIVATE_DNS_OFF); 283 } 284 updatePrivateDns(Network network, PrivateDnsConfig cfg)285 public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) { 286 Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")"); 287 return (cfg != null) 288 ? mPrivateDnsMap.put(network.netId, cfg) 289 : mPrivateDnsMap.remove(network.netId); 290 } 291 updatePrivateDnsStatus(int netId, LinkProperties lp)292 public void updatePrivateDnsStatus(int netId, LinkProperties lp) { 293 // Use the PrivateDnsConfig data pushed to this class instance 294 // from ConnectivityService. 295 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId, 296 PRIVATE_DNS_OFF); 297 298 final boolean useTls = privateDnsCfg.useTls; 299 final PrivateDnsValidationStatuses statuses = 300 useTls ? mPrivateDnsValidationMap.get(netId) : null; 301 final boolean validated = (null != statuses) && statuses.hasValidatedServer(); 302 final boolean strictMode = privateDnsCfg.inStrictMode(); 303 final String tlsHostname = strictMode ? privateDnsCfg.hostname : null; 304 final boolean usingPrivateDns = strictMode || validated; 305 306 lp.setUsePrivateDns(usingPrivateDns); 307 lp.setPrivateDnsServerName(tlsHostname); 308 if (usingPrivateDns && null != statuses) { 309 statuses.fillInValidatedPrivateDns(lp); 310 } else { 311 lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST); 312 } 313 } 314 updatePrivateDnsValidation(PrivateDnsValidationUpdate update)315 public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) { 316 final PrivateDnsValidationStatuses statuses = 317 mPrivateDnsValidationMap.get(update.netId); 318 if (statuses == null) return; 319 statuses.updateStatus(update); 320 } 321 322 /** 323 * When creating a new network or transport types are changed in a specific network, 324 * transport types are always saved to a hashMap before update dns config. 325 * When destroying network, the specific network will be removed from the hashMap. 326 * The hashMap is always accessed on the same thread. 327 */ updateTransportsForNetwork(int netId, @NonNull int[] transportTypes)328 public void updateTransportsForNetwork(int netId, @NonNull int[] transportTypes) { 329 mTransportsMap.put(netId, transportTypes); 330 sendDnsConfigurationForNetwork(netId); 331 } 332 333 /** 334 * When {@link LinkProperties} are changed in a specific network, they are 335 * always saved to a hashMap before update dns config. 336 * When destroying network, the specific network will be removed from the hashMap. 337 * The hashMap is always accessed on the same thread. 338 */ noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp)339 public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) { 340 mLinkPropertiesMap.put(netId, lp); 341 sendDnsConfigurationForNetwork(netId); 342 } 343 344 /** 345 * Send dns configuration parameters to resolver for a given network. 346 */ sendDnsConfigurationForNetwork(int netId)347 public void sendDnsConfigurationForNetwork(int netId) { 348 final LinkProperties lp = mLinkPropertiesMap.get(netId); 349 final int[] transportTypes = mTransportsMap.get(netId); 350 if (lp == null || transportTypes == null) return; 351 updateParametersSettings(); 352 final ResolverParamsParcel paramsParcel = new ResolverParamsParcel(); 353 354 // We only use the PrivateDnsConfig data pushed to this class instance 355 // from ConnectivityService because it works in coordination with 356 // NetworkMonitor to decide which networks need validation and runs the 357 // blocking calls to resolve Private DNS strict mode hostnames. 358 // 359 // At this time we do not attempt to enable Private DNS on non-Internet 360 // networks like IMS. 361 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId, 362 PRIVATE_DNS_OFF); 363 final boolean useTls = privateDnsCfg.useTls; 364 final boolean strictMode = privateDnsCfg.inStrictMode(); 365 366 paramsParcel.netId = netId; 367 paramsParcel.sampleValiditySeconds = mSampleValidity; 368 paramsParcel.successThreshold = mSuccessThreshold; 369 paramsParcel.minSamples = mMinSamples; 370 paramsParcel.maxSamples = mMaxSamples; 371 paramsParcel.servers = 372 NetworkUtils.makeStrings(lp.getDnsServers()); 373 paramsParcel.domains = getDomainStrings(lp.getDomains()); 374 paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : ""; 375 paramsParcel.tlsServers = 376 strictMode ? NetworkUtils.makeStrings( 377 Arrays.stream(privateDnsCfg.ips) 378 .filter((ip) -> lp.isReachable(ip)) 379 .collect(Collectors.toList())) 380 : useTls ? paramsParcel.servers // Opportunistic 381 : new String[0]; // Off 382 paramsParcel.resolverOptions = new ResolverOptionsParcel(); 383 paramsParcel.transportTypes = transportTypes; 384 // Prepare to track the validation status of the DNS servers in the 385 // resolver config when private DNS is in opportunistic or strict mode. 386 if (useTls) { 387 if (!mPrivateDnsValidationMap.containsKey(netId)) { 388 mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses()); 389 } 390 mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers, 391 paramsParcel.tlsName); 392 } else { 393 mPrivateDnsValidationMap.remove(netId); 394 } 395 396 Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " 397 + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers), 398 Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds, 399 paramsParcel.successThreshold, paramsParcel.minSamples, 400 paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec, 401 paramsParcel.retryCount, paramsParcel.tlsName, 402 Arrays.toString(paramsParcel.tlsServers))); 403 404 try { 405 mDnsResolver.setResolverConfiguration(paramsParcel); 406 } catch (RemoteException | ServiceSpecificException e) { 407 Slog.e(TAG, "Error setting DNS configuration: " + e); 408 return; 409 } 410 } 411 setDefaultDnsSystemProperties(Collection<InetAddress> dnses)412 public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) { 413 int last = 0; 414 for (InetAddress dns : dnses) { 415 ++last; 416 setNetDnsProperty(last, dns.getHostAddress()); 417 } 418 for (int i = last + 1; i <= mNumDnsEntries; ++i) { 419 setNetDnsProperty(i, ""); 420 } 421 mNumDnsEntries = last; 422 } 423 424 /** 425 * Flush DNS caches and events work before boot has completed. 426 */ flushVmDnsCache()427 public 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