DnsManager.java revision ba0613358a100e3f5731ffc9d56c2f104f20a20d
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.os.Binder; 38import android.os.INetworkManagementService; 39import android.os.Handler; 40import android.os.UserHandle; 41import android.provider.Settings; 42import android.system.GaiException; 43import android.system.OsConstants; 44import android.system.StructAddrinfo; 45import android.text.TextUtils; 46import android.util.Slog; 47 48import com.android.server.connectivity.MockableSystemProperties; 49 50import libcore.io.Libcore; 51 52import java.net.InetAddress; 53import java.util.Arrays; 54import java.util.Collection; 55import java.util.HashMap; 56import java.util.Map; 57import java.util.stream.Collectors; 58import java.util.StringJoiner; 59 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 78 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 153 private final Context mContext; 154 private final ContentResolver mContentResolver; 155 private final INetworkManagementService mNMS; 156 private final MockableSystemProperties mSystemProperties; 157 private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap; 158 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; 172 mPrivateDnsMap = new HashMap<>(); 173 174 // TODO: Create and register ContentObservers to track every setting 175 // used herein, posting messages to respond to changes. 176 } 177 178 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) 190 : mPrivateDnsMap.remove(network.netId); 191 } 192 193 public void setDnsConfigurationForNetwork( 194 int netId, LinkProperties lp, boolean isDefaultNetwork) { 195 // We only use the PrivateDnsConfig data pushed to this class instance 196 // from ConnectivityService because it works in coordination with 197 // NetworkMonitor to decide which networks need validation and runs the 198 // blocking calls to resolve Private DNS strict mode hostnames. 199 // 200 // At this time we do attempt to enable Private DNS on non-Internet 201 // networks like IMS. 202 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId); 203 204 final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls; 205 final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode(); 206 final String tlsHostname = strictMode ? privateDnsCfg.hostname : ""; 207 208 final String[] serverStrs = NetworkUtils.makeStrings( 209 strictMode ? Arrays.stream(privateDnsCfg.ips) 210 .filter((ip) -> lp.isReachable(ip)) 211 .collect(Collectors.toList()) 212 : lp.getDnsServers()); 213 final String[] domainStrs = getDomainStrings(lp.getDomains()); 214 215 updateParametersSettings(); 216 final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples }; 217 218 Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)", 219 netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs), 220 Arrays.toString(params), useTls, tlsHostname)); 221 try { 222 mNMS.setDnsConfigurationForNetwork( 223 netId, serverStrs, domainStrs, params, useTls, tlsHostname); 224 } catch (Exception e) { 225 Slog.e(TAG, "Error setting DNS configuration: " + e); 226 return; 227 } 228 229 // TODO: netd should listen on [::1]:53 and proxy queries to the current 230 // default network, and we should just set net.dns1 to ::1, not least 231 // because applications attempting to use net.dns resolvers will bypass 232 // the privacy protections of things like DNS-over-TLS. 233 if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers()); 234 flushVmDnsCache(); 235 } 236 237 public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) { 238 int last = 0; 239 for (InetAddress dns : dnses) { 240 ++last; 241 setNetDnsProperty(last, dns.getHostAddress()); 242 } 243 for (int i = last + 1; i <= mNumDnsEntries; ++i) { 244 setNetDnsProperty(i, ""); 245 } 246 mNumDnsEntries = last; 247 } 248 249 private void flushVmDnsCache() { 250 /* 251 * Tell the VMs to toss their DNS caches 252 */ 253 final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE); 254 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 255 /* 256 * Connectivity events can happen before boot has completed ... 257 */ 258 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 259 final long ident = Binder.clearCallingIdentity(); 260 try { 261 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 262 } finally { 263 Binder.restoreCallingIdentity(ident); 264 } 265 } 266 267 private void updateParametersSettings() { 268 mSampleValidity = getIntSetting( 269 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, 270 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); 271 if (mSampleValidity < 0 || mSampleValidity > 65535) { 272 Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" + 273 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); 274 mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS; 275 } 276 277 mSuccessThreshold = getIntSetting( 278 DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, 279 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); 280 if (mSuccessThreshold < 0 || mSuccessThreshold > 100) { 281 Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" + 282 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); 283 mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT; 284 } 285 286 mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); 287 mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); 288 if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) { 289 Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples + 290 "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " + 291 DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")"); 292 mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES; 293 mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES; 294 } 295 } 296 297 private int getIntSetting(String which, int dflt) { 298 return Settings.Global.getInt(mContentResolver, which, dflt); 299 } 300 301 private void setNetDnsProperty(int which, String value) { 302 final String key = "net.dns" + which; 303 // Log and forget errors setting unsupported properties. 304 try { 305 mSystemProperties.set(key, value); 306 } catch (Exception e) { 307 Slog.e(TAG, "Error setting unsupported net.dns property: ", e); 308 } 309 } 310 311 private static String getPrivateDnsMode(ContentResolver cr) { 312 final String mode = getStringSetting(cr, PRIVATE_DNS_MODE); 313 return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE; 314 } 315 316 private static String getStringSetting(ContentResolver cr, String which) { 317 return Settings.Global.getString(cr, which); 318 } 319 320 private static String[] getDomainStrings(String domains) { 321 return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" "); 322 } 323} 324