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