DnsManager.java revision 1742fe1309b9b1d73a15b40829a9ce2e651d21fd
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_OPPORTUNISTIC;
21import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
22import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
23import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
24import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
25import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
26import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
27import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
28
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.Intent;
32import android.net.NetworkUtils;
33import android.os.Binder;
34import android.os.INetworkManagementService;
35import android.os.Handler;
36import android.os.UserHandle;
37import android.provider.Settings;
38import android.text.TextUtils;
39import android.util.Slog;
40
41import com.android.server.connectivity.MockableSystemProperties;
42
43import java.net.InetAddress;
44import java.util.Collection;
45
46
47/**
48 * Encapsulate the management of DNS settings for networks.
49 *
50 * This class it NOT designed for concurrent access. Furthermore, all non-static
51 * methods MUST be called from ConnectivityService's thread.
52 *
53 * @hide
54 */
55public class DnsManager {
56    private static final String TAG = DnsManager.class.getSimpleName();
57
58    /* Defaults for resolver parameters. */
59    private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
60    private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
61    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
62    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
63
64    private final Context mContext;
65    private final ContentResolver mContentResolver;
66    private final INetworkManagementService mNMS;
67    private final MockableSystemProperties mSystemProperties;
68
69    private int mNumDnsEntries;
70    private int mSampleValidity;
71    private int mSuccessThreshold;
72    private int mMinSamples;
73    private int mMaxSamples;
74    private String mPrivateDnsMode;
75    private String mPrivateDnsSpecifier;
76
77    public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
78        mContext = ctx;
79        mContentResolver = mContext.getContentResolver();
80        mNMS = nms;
81        mSystemProperties = sp;
82
83        // TODO: Create and register ContentObservers to track every setting
84        // used herein, posting messages to respond to changes.
85    }
86
87    public boolean isPrivateDnsInStrictMode() {
88        return !TextUtils.isEmpty(mPrivateDnsMode) &&
89               mPrivateDnsMode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) &&
90               !TextUtils.isEmpty(mPrivateDnsSpecifier);
91    }
92
93    public void setDnsConfigurationForNetwork(
94            int netId, Collection<InetAddress> servers, String domains, boolean isDefaultNetwork) {
95        updateParametersSettings();
96        updatePrivateDnsSettings();
97
98        final String[] serverStrs = NetworkUtils.makeStrings(servers);
99        final String[] domainStrs = (domains == null) ? new String[0] : domains.split(" ");
100        final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
101        final boolean useTls = shouldUseTls(mPrivateDnsMode);
102        // TODO: Populate tlsHostname once it's decided how the hostname's IP
103        // addresses will be resolved:
104        //
105        //     [1] network-provided DNS servers are included here with the
106        //         hostname and netd will use the network-provided servers to
107        //         resolve the hostname and fix up its internal structures, or
108        //
109        //     [2] network-provided DNS servers are included here without the
110        //         hostname, the ConnectivityService layer resolves the given
111        //         hostname, and then reconfigures netd with this information.
112        //
113        // In practice, there will always be a need for ConnectivityService or
114        // the captive portal app to use the network-provided services to make
115        // some queries. This argues in favor of [1], in concert with another
116        // mechanism, perhaps setting a high bit in the netid, to indicate
117        // via existing DNS APIs which set of servers (network-provided or
118        // non-network-provided private DNS) should be queried.
119        final String tlsHostname = "";
120        try {
121            mNMS.setDnsConfigurationForNetwork(
122                    netId, serverStrs, domainStrs, params, useTls, tlsHostname);
123        } catch (Exception e) {
124            Slog.e(TAG, "Error setting DNS configuration: " + e);
125            return;
126        }
127
128        // TODO: netd should listen on [::1]:53 and proxy queries to the current
129        // default network, and we should just set net.dns1 to ::1, not least
130        // because applications attempting to use net.dns resolvers will bypass
131        // the privacy protections of things like DNS-over-TLS.
132        if (isDefaultNetwork) setDefaultDnsSystemProperties(servers);
133        flushVmDnsCache();
134    }
135
136    public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
137        int last = 0;
138        for (InetAddress dns : dnses) {
139            ++last;
140            setNetDnsProperty(last, dns.getHostAddress());
141        }
142        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
143            setNetDnsProperty(i, "");
144        }
145        mNumDnsEntries = last;
146    }
147
148    private void flushVmDnsCache() {
149        /*
150         * Tell the VMs to toss their DNS caches
151         */
152        final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
153        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
154        /*
155         * Connectivity events can happen before boot has completed ...
156         */
157        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
158        final long ident = Binder.clearCallingIdentity();
159        try {
160            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
161        } finally {
162            Binder.restoreCallingIdentity(ident);
163        }
164    }
165
166    private void updatePrivateDnsSettings() {
167        mPrivateDnsMode = getStringSetting(PRIVATE_DNS_MODE);
168        mPrivateDnsSpecifier = getStringSetting(PRIVATE_DNS_SPECIFIER);
169    }
170
171    private void updateParametersSettings() {
172        mSampleValidity = getIntSetting(
173                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
174                DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
175        if (mSampleValidity < 0 || mSampleValidity > 65535) {
176            Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
177                    DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
178            mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
179        }
180
181        mSuccessThreshold = getIntSetting(
182                DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
183                DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
184        if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
185            Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
186                    DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
187            mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
188        }
189
190        mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
191        mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
192        if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
193            Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
194                    "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
195                    DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
196            mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
197            mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
198        }
199    }
200
201    private String getStringSetting(String which) {
202        return Settings.Global.getString(mContentResolver, which);
203    }
204
205    private int getIntSetting(String which, int dflt) {
206        return Settings.Global.getInt(mContentResolver, which, dflt);
207    }
208
209    private void setNetDnsProperty(int which, String value) {
210        final String key = "net.dns" + which;
211        // Log and forget errors setting unsupported properties.
212        try {
213            mSystemProperties.set(key, value);
214        } catch (Exception e) {
215            Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
216        }
217    }
218
219    private static boolean shouldUseTls(String mode) {
220        if (TextUtils.isEmpty(mode)) {
221            mode = PRIVATE_DNS_DEFAULT_MODE;
222        }
223        return mode.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
224               mode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
225    }
226}
227