Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43ecf94a authored by Erik Kline's avatar Erik Kline Committed by android-build-merger
Browse files

Merge "Add basic resolution of Private DNS hostname" am: a3bf36f0 am: 4218c522

am: 0b250b61

Change-Id: Iacdad88b49aff3e34afc93acfb8d3eeca9745c43
parents 36ec567b 0b250b61
Loading
Loading
Loading
Loading
+144 −33
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
@@ -400,6 +401,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
     */
    private static final int EVENT_REVALIDATE_NETWORK = 36;

    // Handle changes in Private DNS settings.
    private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;

    private static String eventName(int what) {
        return sMagicDecoderRing.get(what, Integer.toString(what));
    }
@@ -886,6 +890,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mMultinetworkPolicyTracker.start();

        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
        registerPrivateDnsSettingsCallbacks();
    }

    private Tethering makeTethering() {
@@ -946,6 +951,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
                EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
    }

    private void registerPrivateDnsSettingsCallbacks() {
        for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
            mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
        }
    }

    private synchronized int nextNetworkRequestId() {
        return mNextNetworkRequestId++;
    }
@@ -2114,13 +2125,37 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    synchronized (mNetworkForNetId) {
                        nai = mNetworkForNetId.get(msg.arg2);
                    }
                    if (nai != null) {
                        final boolean valid =
                                (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                    if (nai == null) break;

                    final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                    final boolean wasValidated = nai.lastValidated;
                    final boolean wasDefault = isDefaultNetwork(nai);
                        if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
                                (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));

                    final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
                            ? (PrivateDnsConfig) msg.obj : null;
                    final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";

                    final boolean reevaluationRequired;
                    final String logMsg;
                    if (valid) {
                        reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
                        logMsg = (DBG && (privateDnsCfg != null))
                                 ? " with " + privateDnsCfg.toString() : "";
                    } else {
                        reevaluationRequired = false;
                        logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
                                 ? " with redirect to " + redirectUrl : "";
                    }
                    if (DBG) {
                        log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
                    }
                    // If there is a change in Private DNS configuration,
                    // trigger reevaluation of the network to test it.
                    if (reevaluationRequired) {
                        nai.networkMonitor.sendMessage(
                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
                        break;
                    }
                    if (valid != nai.lastValidated) {
                        if (wasDefault) {
                            metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
@@ -2136,7 +2171,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    updateInetCondition(nai);
                    // Let the NetworkAgent know the state of its network
                    Bundle redirectUrlBundle = new Bundle();
                        redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
                    redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
                    nai.asyncChannel.sendMessage(
                            NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                            (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
@@ -2144,7 +2179,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    if (wasValidated && !nai.lastValidated) {
                        handleNetworkUnvalidated(nai);
                    }
                    }
                    break;
                }
                case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
@@ -2183,6 +2217,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    }
                    break;
                }
                case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
                    final NetworkAgentInfo nai;
                    synchronized (mNetworkForNetId) {
                        nai = mNetworkForNetId.get(msg.arg2);
                    }
                    if (nai == null) break;

                    final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
                    final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
                    if (nai.lastValidated && reevaluationRequired) {
                        nai.networkMonitor.sendMessage(
                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
                    }
                    break;
                }
            }
            return true;
        }
@@ -2218,6 +2267,63 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    private void handlePrivateDnsSettingsChanged() {
        final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();

        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
            // Private DNS only ever applies to networks that might provide
            // Internet access and therefore also require validation.
            if (!NetworkMonitor.isValidationRequired(
                    mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
                continue;
            }

            // Notify the NetworkMonitor thread in case it needs to cancel or
            // schedule DNS resolutions. If a DNS resolution is required the
            // result will be sent back to us.
            nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);

            if (!cfg.inStrictMode()) {
                // No strict mode hostname DNS resolution needed, so just update
                // DNS settings directly. In opportunistic and "off" modes this
                // just reprograms netd with the network-supplied DNS servers
                // (and of course the boolean of whether or not to attempt TLS).
                //
                // TODO: Consider code flow parity with strict mode, i.e. having
                // NetworkMonitor relay the PrivateDnsConfig back to us and then
                // performing this call at that time.
                updatePrivateDns(nai, cfg);
            }
        }
    }

    private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
        final boolean reevaluationRequired = true;
        final boolean dontReevaluate = false;

        final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
        updateDnses(nai.linkProperties, null, nai.network.netId);

        if (newCfg == null) {
            if (oldCfg == null) return dontReevaluate;
            return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
        }

        if (oldCfg == null) {
            return newCfg.useTls ? reevaluationRequired : dontReevaluate;
        }

        if (oldCfg.useTls != newCfg.useTls) {
            return reevaluationRequired;
        }

        if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
            return reevaluationRequired;
        }

        return dontReevaluate;
    }

    private void updateLingerState(NetworkAgentInfo nai, long now) {
        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
        // 2. If the network was lingering and there are now requests, unlinger it.
@@ -2352,6 +2458,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                } catch (Exception e) {
                    loge("Exception removing network: " + e);
                }
                mDnsManager.removeNetwork(nai.network);
            }
            synchronized (mNetworkForNetId) {
                mNetIdInUse.delete(nai.network.netId);
@@ -2898,6 +3005,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
                    break;
                }
                case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
                    handlePrivateDnsSettingsChanged();
                    break;
            }
        }
    }
@@ -4587,11 +4697,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
        final NetworkAgentInfo defaultNai = getDefaultNetwork();
        final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);

        Collection<InetAddress> dnses = newLp.getDnsServers();
        if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
        if (DBG) {
            final Collection<InetAddress> dnses = newLp.getDnsServers();
            log("Setting DNS servers for network " + netId + " to " + dnses);
        }
        try {
            mDnsManager.setDnsConfigurationForNetwork(
                    netId, dnses, newLp.getDomains(), isDefaultNetwork);
            mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
        } catch (Exception e) {
            loge("Exception in setDnsConfigurationForNetwork: " + e);
        }
+0 −12
Original line number Diff line number Diff line
@@ -21,9 +21,6 @@ import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
@@ -1957,15 +1954,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
        }
    }

    private static boolean shouldUseTls(ContentResolver cr) {
        String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE);
        if (TextUtils.isEmpty(privateDns)) {
            privateDns = PRIVATE_DNS_DEFAULT_MODE;
        }
        return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
               privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
    }

    @Override
    public void addVpnUidRanges(int netId, UidRange[] ranges) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+141 −44
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.connectivity;

import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
@@ -29,19 +30,32 @@ import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
import android.net.Uri;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.system.GaiException;
import android.system.OsConstants;
import android.system.StructAddrinfo;
import android.text.TextUtils;
import android.util.Slog;

import com.android.server.connectivity.MockableSystemProperties;

import libcore.io.Libcore;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.StringJoiner;


/**
@@ -61,10 +75,86 @@ public class DnsManager {
    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;

    public static class PrivateDnsConfig {
        public final boolean useTls;
        public final String hostname;
        public final InetAddress[] ips;

        public PrivateDnsConfig() {
            this(false);
        }

        public PrivateDnsConfig(boolean useTls) {
            this.useTls = useTls;
            this.hostname = "";
            this.ips = new InetAddress[0];
        }

        public PrivateDnsConfig(String hostname, InetAddress[] ips) {
            this.useTls = !TextUtils.isEmpty(hostname);
            this.hostname = useTls ? hostname : "";
            this.ips = (ips != null) ? ips : new InetAddress[0];
        }

        public PrivateDnsConfig(PrivateDnsConfig cfg) {
            useTls = cfg.useTls;
            hostname = cfg.hostname;
            ips = cfg.ips;
        }

        public boolean inStrictMode() {
            return useTls && !TextUtils.isEmpty(hostname);
        }

        public String toString() {
            return PrivateDnsConfig.class.getSimpleName() +
                    "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
        }
    }

    public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
        final String mode = getPrivateDnsMode(cr);

        final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);

        if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
            final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
            return new PrivateDnsConfig(specifier, null);
        }

        return new PrivateDnsConfig(useTls);
    }

    public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
        final StructAddrinfo hints = new StructAddrinfo();
        // Unnecessary, but expressly no AI_ADDRCONFIG.
        hints.ai_flags = 0;
        // Fetch all IP addresses at once to minimize re-resolution.
        hints.ai_family = OsConstants.AF_UNSPEC;
        hints.ai_socktype = OsConstants.SOCK_DGRAM;

        try {
            final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
            if (ips != null && ips.length > 0) {
                return new PrivateDnsConfig(name, ips);
            }
        } catch (GaiException ignored) {}

        return null;
    }

    public static Uri[] getPrivateDnsSettingsUris() {
        final Uri[] uris = new Uri[2];
        uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
        uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
        return uris;
    }

    private final Context mContext;
    private final ContentResolver mContentResolver;
    private final INetworkManagementService mNMS;
    private final MockableSystemProperties mSystemProperties;
    private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;

    private int mNumDnsEntries;
    private int mSampleValidity;
@@ -79,44 +169,55 @@ public class DnsManager {
        mContentResolver = mContext.getContentResolver();
        mNMS = nms;
        mSystemProperties = sp;
        mPrivateDnsMap = new HashMap<>();

        // TODO: Create and register ContentObservers to track every setting
        // used herein, posting messages to respond to changes.
    }

    public boolean isPrivateDnsInStrictMode() {
        return !TextUtils.isEmpty(mPrivateDnsMode) &&
               mPrivateDnsMode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) &&
               !TextUtils.isEmpty(mPrivateDnsSpecifier);
    public PrivateDnsConfig getPrivateDnsConfig() {
        return getPrivateDnsConfig(mContentResolver);
    }

    public void removeNetwork(Network network) {
        mPrivateDnsMap.remove(network.netId);
    }

    public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
        Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
        return (cfg != null)
                ? mPrivateDnsMap.put(network.netId, cfg)
                : mPrivateDnsMap.remove(network);
    }

    public void setDnsConfigurationForNetwork(
            int netId, Collection<InetAddress> servers, String domains, boolean isDefaultNetwork) {
        updateParametersSettings();
        updatePrivateDnsSettings();
            int netId, LinkProperties lp, boolean isDefaultNetwork) {
        // We only use the PrivateDnsConfig data pushed to this class instance
        // from ConnectivityService because it works in coordination with
        // NetworkMonitor to decide which networks need validation and runs the
        // blocking calls to resolve Private DNS strict mode hostnames.
        //
        // At this time we do attempt to enable Private DNS on non-Internet
        // networks like IMS.
        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);

        final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
        final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";

        final String[] serverStrs = NetworkUtils.makeStrings(
                strictMode ? Arrays.stream(privateDnsCfg.ips)
                                   .filter((ip) -> lp.isReachable(ip))
                                   .collect(Collectors.toList())
                           : lp.getDnsServers());
        final String[] domainStrs = getDomainStrings(lp.getDomains());

        final String[] serverStrs = NetworkUtils.makeStrings(servers);
        final String[] domainStrs = (domains == null) ? new String[0] : domains.split(" ");
        updateParametersSettings();
        final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
        final boolean useTls = shouldUseTls(mPrivateDnsMode);
        // TODO: Populate tlsHostname once it's decided how the hostname's IP
        // addresses will be resolved:
        //
        //     [1] network-provided DNS servers are included here with the
        //         hostname and netd will use the network-provided servers to
        //         resolve the hostname and fix up its internal structures, or
        //
        //     [2] network-provided DNS servers are included here without the
        //         hostname, the ConnectivityService layer resolves the given
        //         hostname, and then reconfigures netd with this information.
        //
        // In practice, there will always be a need for ConnectivityService or
        // the captive portal app to use the network-provided services to make
        // some queries. This argues in favor of [1], in concert with another
        // mechanism, perhaps setting a high bit in the netid, to indicate
        // via existing DNS APIs which set of servers (network-provided or
        // non-network-provided private DNS) should be queried.
        final String tlsHostname = "";

        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
                netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs),
                Arrays.toString(params), useTls, tlsHostname));
        try {
            mNMS.setDnsConfigurationForNetwork(
                    netId, serverStrs, domainStrs, params, useTls, tlsHostname);
@@ -129,7 +230,7 @@ public class DnsManager {
        // default network, and we should just set net.dns1 to ::1, not least
        // because applications attempting to use net.dns resolvers will bypass
        // the privacy protections of things like DNS-over-TLS.
        if (isDefaultNetwork) setDefaultDnsSystemProperties(servers);
        if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
        flushVmDnsCache();
    }

@@ -163,11 +264,6 @@ public class DnsManager {
        }
    }

    private void updatePrivateDnsSettings() {
        mPrivateDnsMode = getStringSetting(PRIVATE_DNS_MODE);
        mPrivateDnsSpecifier = getStringSetting(PRIVATE_DNS_SPECIFIER);
    }

    private void updateParametersSettings() {
        mSampleValidity = getIntSetting(
                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
@@ -198,10 +294,6 @@ public class DnsManager {
        }
    }

    private String getStringSetting(String which) {
        return Settings.Global.getString(mContentResolver, which);
    }

    private int getIntSetting(String which, int dflt) {
        return Settings.Global.getInt(mContentResolver, which, dflt);
    }
@@ -216,11 +308,16 @@ public class DnsManager {
        }
    }

    private static boolean shouldUseTls(String mode) {
        if (TextUtils.isEmpty(mode)) {
            mode = PRIVATE_DNS_DEFAULT_MODE;
    private static String getPrivateDnsMode(ContentResolver cr) {
        final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
        return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
    }
        return mode.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
               mode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);

    private static String getStringSetting(ContentResolver cr, String which) {
        return Settings.Global.getString(cr, which);
    }

    private static String[] getDomainStrings(String domains) {
        return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
    }
}
+77 −3

File changed.

Preview size limit exceeded, changes collapsed.