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