(一百九十九)Android Q 学习私人DNS

目录

1.Settings相关类

2.代码搜索

2.1 ConnectivityManager

2.2 DevicePolicyManager

2.3 ConnectivityService

2.4 DnsManager

3.流程梳理

3.1 ConnectivityService

3.2 NetworkMonitor

3.3 ConnectivityService

3.4 netd

4.总结


1.Settings相关类

布局network_and_internet.xml
    <com.android.settings.network.PrivateDnsModeDialogPreference
        android:key="private_dns_settings"
        android:title="@string/select_private_dns_configuration_title"
        android:order="15"
        android:dialogTitle="@string/select_private_dns_configuration_dialog_title"
        android:dialogLayout="@layout/private_dns_mode_dialog"
        android:positiveButtonText="@string/save"
        android:negativeButtonText="@android:string/cancel" />
PrivateDnsModeDialogPreference这是个Android自定义的控件,点击会弹出对话框,对话框是以android:dialogLayout指定的

PrivateDnsPreferenceController是xml中对应控制Preference的summary和显示状态的

private_dns_mode_dialog是对应的对话框

2.代码搜索

私人dns 设置中的菜单其实很简单,关键代码如下

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_POSITIVE) {
            final Context context = getContext();
            if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
                // Only clickable if hostname is valid, so we could save it safely
                Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY,
                        mEditText.getText().toString());
            }

            FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
                    SettingsEnums.ACTION_PRIVATE_DNS_MODE, mMode);
            Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode);
        }
    }

私人dns有三个选择项,分别是关闭、自动和私人dns提供商主机名,即手动输入设置

./strings.xml:245:    <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 提供商主机名"</string>
./strings.xml:246:    <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"输入 DNS 提供商的主机名"</string>
./strings.xml:247:    <string name="private_dns_mode_provider_failure" msgid="231837290365031223">"无法连接"</string>

模式设置其实就是设置到MODE_KEY的SettingsProvider数据库里,而手动输入的dns提供商主机名会放到HOSTNAME_KEY的SettingsProvider数据库里

对应的数据库键值

    @VisibleForTesting
    static final String MODE_KEY = Settings.Global.PRIVATE_DNS_MODE;
    @VisibleForTesting
    static final String HOSTNAME_KEY = Settings.Global.PRIVATE_DNS_SPECIFIER;

看一下哪里获取的

在framework里搜了下主要出现在如下的文件中

./base/services/core/java/com/android/server/connectivity/DnsManager.java

./base/services/core/java/com/android/server/ConnectivityService.java

./base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java

./base/services/core/java/com/android/server/pm/UserRestrictionsUtils.java

./base/core/java/android/app/admin/DevicePolicyManager.java

./base/core/java/android/net/ConnectivityManager.java

2.1 ConnectivityManager

    /**
     * Private DNS Mode values.
     *
     * The "private_dns_mode" global setting stores a String value which is
     * expected to be one of the following.
     */

    /**
     * @hide
     */
    public static final String PRIVATE_DNS_MODE_OFF = "off";
    /**
     * @hide
     */
    public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
    /**
     * @hide
     */
    public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
    /**
     * The default Private DNS mode.
     *
     * This may change from release to release or may become dependent upon
     * the capabilities of the underlying platform.
     *
     * @hide
     */
    public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;

ConnectivityManager中声明了三种模式,分别为关闭、自动和手动,默认为自动

2.2 DevicePolicyManager

    /**
     * Specifies that the Private DNS setting is in an unknown state.
     */
    public static final int PRIVATE_DNS_MODE_UNKNOWN = 0;

    /**
     * Specifies that Private DNS was turned off completely.
     */
    public static final int PRIVATE_DNS_MODE_OFF = 1;

    /**
     * Specifies that the device owner requested opportunistic DNS over TLS
     */
    public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;

    /**
     * Specifies that the device owner configured a specific host to use for Private DNS.
     */
    public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;

DevicePolicyManager中定义了类似的

    /**
     * The selected mode has been set successfully. If the mode is
     * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} then it implies the supplied host is valid
     * and reachable.
     */
    public static final int PRIVATE_DNS_SET_NO_ERROR = 0;

    /**
     * If the {@code privateDnsHost} provided was of a valid hostname but that host was found
     * to not support DNS-over-TLS.
     */
    public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1;

    /**
     * General failure to set the Private DNS mode, not due to one of the reasons listed above.
     */
    public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2;

还有几种状态

2.3 ConnectivityService

        // restore private DNS settings to default mode (opportunistic)
        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
            Settings.Global.putString(mContext.getContentResolver(),
                    Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC);
        }

CS是恢复网络设置的时候会将dns的模式重新设置为自动

2.4 DnsManager

    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);
    }

    private static String getPrivateDnsMode(ContentResolver cr) {
        String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
        if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
        return mode;
    }

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

看起来是主动获取settings设置的dns模式,将其封装到一个叫做PrivateDnsConfig的类里面去

public 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;
    }

    /**
     * Indicates whether this is a strict mode private DNS configuration.
     */
    public boolean inStrictMode() {
        return useTls && !TextUtils.isEmpty(hostname);
    }

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

    /**
     * Create a stable AIDL-compatible parcel from the current instance.
     */
    public PrivateDnsConfigParcel toParcel() {
        final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel();
        parcel.hostname = hostname;
        parcel.ips = toParcelableArray(
                Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class);

        return parcel;
    }

    /**
     * Build a configuration from a stable AIDL-compatible parcel.
     */
    public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) {
        InetAddress[] ips = new InetAddress[parcel.ips.length];
        ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress)
                .toArray(ips);
        return new PrivateDnsConfig(parcel.hostname, ips);
    }
}

注释里将私人dns的流程说了很清楚

/**
 * Encapsulate the management of DNS settings for networks.
 *
 * This class it NOT designed for concurrent access. Furthermore, all non-static
 * methods MUST be called from ConnectivityService's thread.
 *
 * [ Private DNS ]
 * The code handling Private DNS is spread across several components, but this
 * seems like the least bad place to collect all the observations.
 *
 * Private DNS handling and updating occurs in response to several different
 * events. Each is described here with its corresponding intended handling.
 *
 * [A] Event: A new network comes up.
 * Mechanics:
 *     [1] ConnectivityService gets notifications from NetworkAgents.
 *     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
 *         into CONNECTED state, the Private DNS configuration is retrieved,
 *         programmed, and strict mode hostname resolution (if applicable) is
 *         enqueued in NetworkAgent's NetworkMonitor, via a call to
 *         handlePerNetworkPrivateDnsConfig().
 *     [3] Re-resolution of strict mode hostnames that fail to return any
 *         IP addresses happens inside NetworkMonitor; it sends itself a
 *         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
 *         schedule.
 *     [4] Successfully resolved hostnames are sent to ConnectivityService
 *         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
 *         IP addresses are programmed into netd via:
 *
 *             updatePrivateDns() -> updateDnses()
 *
 *         both of which make calls into DnsManager.
 *     [5] Upon a successful hostname resolution NetworkMonitor initiates a
 *         validation attempt in the form of a lookup for a one-time hostname
 *         that uses Private DNS.
 *
 * [B] Event: Private DNS settings are changed.
 * Mechanics:
 *     [1] ConnectivityService gets notifications from its SettingsObserver.
 *     [2] handlePrivateDnsSettingsChanged() is called, which calls
 *         handlePerNetworkPrivateDnsConfig() and the process proceeds
 *         as if from A.3 above.
 *
 * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
 * Mechanics:
 *     [1] NetworkMonitor is notified and initiates a reevaluation, which
 *         always bypasses Private DNS.
 *     [2] Once completed, NetworkMonitor checks if strict mode is in operation
 *         and if so enqueues another evaluation of Private DNS, as if from
 *         step A.5 above.
 *
 * @hide
 */

3.流程梳理

跟着上面注释理一下流程

3.1 ConnectivityService

*     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
*         into CONNECTED state, the Private DNS configuration is retrieved,
*         programmed, and strict mode hostname resolution (if applicable) is
*         enqueued in NetworkAgent's NetworkMonitor, via a call to
*         handlePerNetworkPrivateDnsConfig().
    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
        final NetworkInfo.State state = newInfo.getState();
        NetworkInfo oldInfo = null;
        final int oldScore = networkAgent.getCurrentScore();
        synchronized (networkAgent) {
            oldInfo = networkAgent.networkInfo;
            networkAgent.networkInfo = newInfo;
        }
        notifyLockdownVpn(networkAgent);

        if (DBG) {
            log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
                    (oldInfo == null ? "null" : oldInfo.getState()) +
                    " to " + state);
        }

        if (!networkAgent.created
                && (state == NetworkInfo.State.CONNECTED
                || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {

            // A network that has just connected has zero requests and is thus a foreground network.
            networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);

            if (!createNativeNetwork(networkAgent)) return;
            networkAgent.created = true;
        }

        if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
            networkAgent.everConnected = true;

            if (networkAgent.linkProperties == null) {
                Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
            }

            // NetworkCapabilities need to be set before sending the private DNS config to
            // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
            synchronized (networkAgent) {
                networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
            }
            handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());

然后调用handlePerNetworkPrivateDnsConfig

    private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
        // Private DNS only ever applies to networks that might provide
        // Internet access and therefore also require validation.
        if (!networkRequiresPrivateDnsValidation(nai)) return;

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

        // With Private DNS bypass support, we can proceed to update the
        // Private DNS config immediately, even if we're in strict mode
        // and have not yet resolved the provider name into a set of IPs.
        updatePrivateDns(nai, cfg);
    }

3.2 NetworkMonitor

NetworkMonitorManager

    public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
        final long token = Binder.clearCallingIdentity();
        try {
            mNetworkMonitor.notifyPrivateDnsChanged(config);
            return true;
        } catch (RemoteException e) {
            log("Error in notifyPrivateDnsChanged", e);
            return false;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

NetworkStackService

        @Override
        public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
            checkNetworkStackCallingPermission();
            mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config));
        }

NetworkMonitor

    /**
     * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
     * @param newCfg The new private DNS configuration.
     */
    public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
        // Cancel any outstanding resolutions.
        removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
        // Send the update to the proper thread.
        sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
    }

发送一个私人DNS配置改变的message

    /**
     * ConnectivityService notifies NetworkMonitor of settings changes to
     * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
     * strict mode, then an event is sent back to ConnectivityService with the
     * result of the resolution attempt.
     *
     * A separate message is used to trigger (re)evaluation of the Private DNS
     * configuration, so that the message can be handled as needed in different
     * states, including being ignored until after an ongoing captive portal
     * validation phase is completed.
     */
    private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = 13;

然后看下哪个状态会来处理

                case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
                    final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
                    if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) {
                        // No DNS resolution required.
                        //
                        // We don't force any validation in opportunistic mode
                        // here. Opportunistic mode nameservers are validated
                        // separately within netd.
                        //
                        // Reset Private DNS settings state.
                        mPrivateDnsProviderHostname = "";
                        break;
                    }

                    mPrivateDnsProviderHostname = cfg.hostname;

                    // DNS resolutions via Private DNS strict mode block for a
                    // few seconds (~4.2) checking for any IP addresses to
                    // arrive and validate. Initiating a (re)evaluation now
                    // should not significantly alter the validation outcome.
                    //
                    // No matter what: enqueue a validation request; one of
                    // three things can happen with this request:
                    //     [1] ignored (EvaluatingState or CaptivePortalState)
                    //     [2] transition to EvaluatingPrivateDnsState
                    //         (DefaultState and ValidatedState)
                    //     [3] handled (EvaluatingPrivateDnsState)
                    //
                    // The Private DNS configuration to be evaluated will:
                    //     [1] be skipped (not in strict mode), or
                    //     [2] validate (huzzah), or
                    //     [3] encounter some problem (invalid hostname,
                    //         no resolved IP addresses, IPs unreachable,
                    //         port 853 unreachable, port 853 is not running a
                    //         DNS-over-TLS server, et cetera).
                    sendMessage(CMD_EVALUATE_PRIVATE_DNS);
                    break;
                }

如果处于严格模式会发送评估私人DNS的消息,即手动设置了dns

    /**
     * Indicates whether this is a strict mode private DNS configuration.
     */
    public boolean inStrictMode() {
        return useTls && !TextUtils.isEmpty(hostname);
    }

当发送CMD_EVALUATE_PRIVATE_DNS的时候会导致重新校验

    // Being in the ValidatedState State indicates a Network is:
    // - Successfully validated, or
    // - Wanted "as is" by the user, or
    // - Does not satisfy the default NetworkRequest and so validation has been skipped.
    private class ValidatedState extends State {
...

        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
...
                case CMD_EVALUATE_PRIVATE_DNS:
                    transitionTo(mEvaluatingPrivateDnsState);

看下EvaluatingPrivateDnsState的处理

*     [3] Re-resolution of strict mode hostnames that fail to return any
*         IP addresses happens inside NetworkMonitor; it sends itself a
*         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
*         schedule.
    private class EvaluatingPrivateDnsState extends State {
        private int mPrivateDnsReevalDelayMs;
        private PrivateDnsConfig mPrivateDnsConfig;

        @Override
        public void enter() {
            mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
            mPrivateDnsConfig = null;
            sendMessage(CMD_EVALUATE_PRIVATE_DNS);
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_EVALUATE_PRIVATE_DNS:
                    if (inStrictMode()) {
                        if (!isStrictModeHostnameResolved()) {
                            resolveStrictModeHostname();

                            if (isStrictModeHostnameResolved()) {
                                notifyPrivateDnsConfigResolved();
                            } else {
                                handlePrivateDnsEvaluationFailure();
                                break;
                            }
                        }

                        // Look up a one-time hostname, to bypass caching.
                        //
                        // Note that this will race with ConnectivityService
                        // code programming the DNS-over-TLS server IP addresses
                        // into netd (if invoked, above). If netd doesn't know
                        // the IP addresses yet, or if the connections to the IP
                        // addresses haven't yet been validated, netd will block
                        // for up to a few seconds before failing the lookup.
                        if (!sendPrivateDnsProbe()) {
                            handlePrivateDnsEvaluationFailure();
                            break;
                        }
                    }

                    // All good!
                    transitionTo(mValidatedState);
                    break;

然后sendPrivateDnsProbe

        private boolean sendPrivateDnsProbe() {
            // q.v. system/netd/server/dns/DnsTlsTransport.cpp
            final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
            final String host = UUID.randomUUID().toString().substring(0, 8)
                    + oneTimeHostnameSuffix;
            final Stopwatch watch = new Stopwatch().start();
            boolean success = false;
            long time;
            try {
                final InetAddress[] ips = mNetwork.getAllByName(host);
                time = watch.stop();
                final String strIps = Arrays.toString(ips);
                success = (ips != null && ips.length > 0);
                validationLog(PROBE_PRIVDNS, host, String.format("%dms: %s", time, strIps));
            } catch (UnknownHostException uhe) {
                time = watch.stop();
                validationLog(PROBE_PRIVDNS, host,
                        String.format("%dms - Error: %s", time, uhe.getMessage()));
            }
            logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
            mEvaluationState.noteProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS, success);
            return success;
        }

3.3 ConnectivityService

NetworkMonitor通过callback再调用到CS的接口

        private void notifyPrivateDnsConfigResolved() {
            try {
                mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel());
            } catch (RemoteException e) {
                Log.e(TAG, "Error sending private DNS config resolved notification", e);
            }
        }
*     [4] Successfully resolved hostnames are sent to ConnectivityService
*         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
*         IP addresses are programmed into netd via:
*
*             updatePrivateDns() -> updateDnses()
*
*         both of which make calls into DnsManager.

看下notifyPrivateDnsConfigResolved

        @Override
        public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) {
            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED,
                    0, mNetId, PrivateDnsConfig.fromParcel(config)));
        }

继续看下消息处理

    /**
     * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS
     * config was resolved.
     * obj = PrivateDnsConfig
     * arg2 = netid
     */
    public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;

                case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                    if (nai == null) break;

                    updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
                    break;
                }

    private void updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
        mDnsManager.updatePrivateDns(nai.network, newCfg);
        updateDnses(nai.linkProperties, null, nai.network.netId);
    }
    private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
        if (oldLp != null && newLp.isIdenticalDnses(oldLp)) {
            return;  // no updating necessary
        }

        final NetworkAgentInfo defaultNai = getDefaultNetwork();
        final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);

        if (DBG) {
            final Collection<InetAddress> dnses = newLp.getDnsServers();
            log("Setting DNS servers for network " + netId + " to " + dnses);
        }
        try {
            mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
        } catch (Exception e) {
            loge("Exception in setDnsConfigurationForNetwork: " + e);
        }
    }

DnsManager

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

    public void setDnsConfigurationForNetwork(
            int netId, LinkProperties lp, boolean isDefaultNetwork) {

        updateParametersSettings();
        final ResolverParamsParcel paramsParcel = new ResolverParamsParcel();

        // 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 not attempt to enable Private DNS on non-Internet
        // networks like IMS.
        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                PRIVATE_DNS_OFF);

        final boolean useTls = privateDnsCfg.useTls;
        final boolean strictMode = privateDnsCfg.inStrictMode();
        paramsParcel.netId = netId;
        paramsParcel.sampleValiditySeconds = mSampleValidity;
        paramsParcel.successThreshold = mSuccessThreshold;
        paramsParcel.minSamples = mMinSamples;
        paramsParcel.maxSamples = mMaxSamples;
        paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers());
        paramsParcel.domains = getDomainStrings(lp.getDomains());
        paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
        paramsParcel.tlsServers =
                strictMode ? NetworkUtils.makeStrings(
                        Arrays.stream(privateDnsCfg.ips)
                              .filter((ip) -> lp.isReachable(ip))
                              .collect(Collectors.toList()))
                : useTls ? paramsParcel.servers  // Opportunistic
                : new String[0];            // Off
        paramsParcel.tlsFingerprints = new String[0];
        // Prepare to track the validation status of the DNS servers in the
        // resolver config when private DNS is in opportunistic or strict mode.
        if (useTls) {
            if (!mPrivateDnsValidationMap.containsKey(netId)) {
                mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
            }
            mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers,
                    paramsParcel.tlsName);
        } else {
            mPrivateDnsValidationMap.remove(netId);
        }

        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
                + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
                Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
                paramsParcel.successThreshold, paramsParcel.minSamples,
                paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
                paramsParcel.retryCount, paramsParcel.tlsName,
                Arrays.toString(paramsParcel.tlsServers)));

        try {
            mDnsResolver.setResolverConfiguration(paramsParcel);
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Error setting DNS configuration: " + e);
            return;
        }

        // TODO: netd should listen on [::1]:53 and proxy queries to the current
        // 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(lp.getDnsServers());
        flushVmDnsCache();
    }

3.4 netd

./resolv/DnsResolverService.h:44:    ::ndk::ScopedAStatus setResolverConfiguration(
./resolv/ResolverController.cpp:198:int ResolverController::setResolverConfiguration(
 

198 int ResolverController::setResolverConfiguration(
199         const ResolverParamsParcel& resolverParams,
200         const std::set<std::vector<uint8_t>>& tlsFingerprints) {
201     using aidl::android::net::IDnsResolver;
202 
203     // At private DNS validation time, we only know the netId, so we have to guess/compute the
204     // corresponding socket mark.
205     Fwmark fwmark;
206     fwmark.netId = resolverParams.netId;
207     fwmark.explicitlySelected = true;
208     fwmark.protectedFromVpn = true;
209     fwmark.permission = PERMISSION_SYSTEM;
210 
211     // Allow at most MAXNS private DNS servers in a network to prevent too many broken servers.
212     std::vector<std::string> tlsServers = resolverParams.tlsServers;
213     if (tlsServers.size() > MAXNS) {
214         tlsServers.resize(MAXNS);
215     }
216     const int err = gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers,
217                                                  resolverParams.tlsName, tlsFingerprints);
218     if (err != 0) {
219         return err;
220     }
221 
222     // Convert network-assigned server list to bionic's format.
223     const size_t serverCount = std::min<size_t>(MAXNS, resolverParams.servers.size());
224     std::vector<const char*> server_ptrs;
225     for (size_t i = 0; i < serverCount; ++i) {
226         server_ptrs.push_back(resolverParams.servers[i].c_str());
227     }
228 
229     std::string domains_str = android::base::Join(resolverParams.domains, " ");
230 
231     // TODO: Change resolv_set_nameservers_for_net() to use ResolverParamsParcel directly.
232     res_params res_params = {};
233     res_params.sample_validity = resolverParams.sampleValiditySeconds;
234     res_params.success_threshold = resolverParams.successThreshold;
235     res_params.min_samples = resolverParams.minSamples;
236     res_params.max_samples = resolverParams.maxSamples;
237     res_params.base_timeout_msec = resolverParams.baseTimeoutMsec;
238     res_params.retry_count = resolverParams.retryCount;
239 
240     LOG(VERBOSE) << "setDnsServers netId = " << resolverParams.netId
241                  << ", numservers = " << resolverParams.domains.size();
242 
243     return -resolv_set_nameservers_for_net(resolverParams.netId, server_ptrs.data(),
244                                            server_ptrs.size(), domains_str.c_str(), &res_params);
245 }

后面就不看了,应该不大看得懂了

4.总结

对应于DnsManager里的注释

/**
 * Encapsulate the management of DNS settings for networks.
 *
 * This class it NOT designed for concurrent access. Furthermore, all non-static
 * methods MUST be called from ConnectivityService's thread.
 *
 * [ Private DNS ]
 * The code handling Private DNS is spread across several components, but this
 * seems like the least bad place to collect all the observations.
 *
 * Private DNS handling and updating occurs in response to several different
 * events. Each is described here with its corresponding intended handling.
 *
 * [A] Event: A new network comes up.
 * Mechanics:
 *     [1] ConnectivityService gets notifications from NetworkAgents.
 *     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
 *         into CONNECTED state, the Private DNS configuration is retrieved,
 *         programmed, and strict mode hostname resolution (if applicable) is
 *         enqueued in NetworkAgent's NetworkMonitor, via a call to
 *         handlePerNetworkPrivateDnsConfig().
 *     [3] Re-resolution of strict mode hostnames that fail to return any
 *         IP addresses happens inside NetworkMonitor; it sends itself a
 *         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
 *         schedule.
 *     [4] Successfully resolved hostnames are sent to ConnectivityService
 *         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
 *         IP addresses are programmed into netd via:
 *
 *             updatePrivateDns() -> updateDnses()
 *
 *         both of which make calls into DnsManager.
 *     [5] Upon a successful hostname resolution NetworkMonitor initiates a
 *         validation attempt in the form of a lookup for a one-time hostname
 *         that uses Private DNS.
 *
 * [B] Event: Private DNS settings are changed.
 * Mechanics:
 *     [1] ConnectivityService gets notifications from its SettingsObserver.
 *     [2] handlePrivateDnsSettingsChanged() is called, which calls
 *         handlePerNetworkPrivateDnsConfig() and the process proceeds
 *         as if from A.3 above.
 *
 * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
 * Mechanics:
 *     [1] NetworkMonitor is notified and initiates a reevaluation, which
 *         always bypasses Private DNS.
 *     [2] Once completed, NetworkMonitor checks if strict mode is in operation
 *         and if so enqueues another evaluation of Private DNS, as if from
 *         step A.5 above.
 *
 * @hide
 */
发布了198 篇原创文章 · 获赞 65 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/103795224
Q A
q