目前IPV6的大力推广,所以现在越来越多的Android设备都开始支持IPV6了,那么在网络上就是IPV4//IPV6双栈连接
IPV4/IPV6双栈连接那么系统就会得到下发的ipv4 dns服务器和ipv6 dns服务器,DNS服务器地址在dns协议中保存在一个数组中,后面进行DNS请求也是对该数组进行遍历,所以首先进行请求的目标DNS就是保存在数组中的第一个(具体DNS原理可以查看
https://blog.csdn.net/cc0410/article/details/103288483 这篇文章)
而数组中保存的DNS顺序依据是什么呢?依据的是IPV4或者IPV6哪个先连接成功,如果IPV4先连接成功,那么DNS数组中优先存储的就是IPV4下发的DNS服务器,如果是IPV6先连接成功,那么DNS数组中优先存储的就是IPV6下发的DNS服务器
IPV4先连成功:
dns_list[0] = 8.8.8.8
dns_list[1] = 114.114.114.114
dns_list[2] = fe80::1
dns_list[3] = 2000::1
IPV6先连成功:
dns_list[0] = fe80::1
dns_list[1] = 2000::1
dns_list[2] = 8.8.8.8
dns_list[3] = 114.114.114.114
假设我们想先向IPV6 DNS发起请求,但这时是IPV4先连成功,那么Android原生流程就无法满足,所以这个时候我们需要对DNS的保存过程做一个调整:
/** * set dns priority according to sys.network.priority(4 ro 6) */ private Collection<InetAddress> sortDnsesByPriority(Collection<InetAddress> dnses) { Collection<InetAddress> dnsesList = new ArrayList<InetAddress>(); if (dnses == null || (dnses != null && dnses.size() < 2)) { return dnses; } ArrayList<InetAddress> dnsTempListV6 = new ArrayList<InetAddress>(); ArrayList<InetAddress> dnsTempListV4 = new ArrayList<InetAddress>(); for (InetAddress inet : dnses) { if (inet instanceof Inet6Address) { dnsTempListV6.add(inet); } else if (inet instanceof Inet4Address) { dnsTempListV4.add(inet); } else { loge("sortDnsesByPriority failed for InetAddress: " + inet.getHostAddress()); } } if (DNS_HIGH_PRORITY_IPV6.equals( SystemProperties.get("sys.dns.priority", DNS_HIGH_PRORITY_IPV6))) { // dns priority: v6 > v4 dnsesList.addAll(dnsTempListV6); dnsesList.addAll(dnsTempListV4); } else { // dns priority: v6 < v4 dnsesList.addAll(dnsTempListV4); dnsesList.addAll(dnsTempListV6); } return dnsesList; }
上面的代码就是调整DNS顺序的代码,根据需求读取属性sys.dns.priority的值来确定是将ipv6 dns放在前面还是ipv4 dns放在前面
接下来就是在什么地方调用这个函数了:
IPV4和IPV6连接成功后,都会通知ConnectivityService,将获取到的地址,DNS这些设置到系统中,而设置DNS就是调用
handleDnsConfigurationChange,当然新的android版本可能换了接口,但是流程肯定是不变的,我们可以找调用了setDnsServersForInterface这个接口的函数,设置DNS就是靠的setDnsServersForInterface函数
所以我们在setDnsServersForInterface之前把DNS顺序调整好再设置下去就可以了,如下所示:
private void handleDnsConfigurationChange(int netType) { // add default net's dns entries NetworkStateTracker nt = mNetTrackers[netType]; if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { LinkProperties p = nt.getLinkProperties(); if (p == null) return; Collection<InetAddress> dnses = p.getDnses(); // set dns priority for chinamobile dnses = sortDnsesByPriority(dnses); //这里进行DNS排序,排好序之后再走后面设置流程 if (mNetConfigs[netType].isDefault()) { String network = nt.getNetworkInfo().getTypeName(); synchronized (mDnsLock) { boolean isDefaultDns = true; if((mEthernetManager.getWifiEthernetCoexistState() == mEthernetManager.WIFI_ETHERNET_COEXIST_ENABLED) && (mActiveDefaultNetwork != -1) && (mActiveDefaultNetwork != netType) && (mNetConfigs[mActiveDefaultNetwork].priority > mNetConfigs[netType].priority)) isDefaultDns = false; if (isDoubleNetCoexist(netType) && (mActiveDefaultNetwork != -1) && (mNetConfigs[mActiveDefaultNetwork].priority > mNetConfigs[netType].priority)) { loge( "handleDnsConfigurationChange:Coexist for double net,no need to set defaulte DNS"); isDefaultDns = false; } loge("handleDnsConfigurationChange: isDefaultDns = " + isDefaultDns); updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), isDefaultDns); } } else { try { mNetd.setDnsServersForInterface(p.getInterfaceName(), NetworkUtils.makeStrings(dnses), p.getDomains()); } catch (Exception e) { if (DBG) loge("exception setting dns servers: " + e); } // set per-pid dns for attached secondary nets List<Integer> pids = mNetRequestersPids[netType]; for (Integer pid : pids) { try { mNetd.setDnsInterfaceForPid(p.getInterfaceName(), pid); } catch (Exception e) { Slog.e(TAG, "exception setting interface for pid: " + e); } } } flushVmDnsCache(); } else if (nt != null && !nt.getNetworkInfo().isConnected()) { Slog.e(TAG, "handle Disconnect delete dnses"); LinkProperties p = mCurrentLinkProperties[netType]; if (p == null) return; Collection<InetAddress> dnses = p.getDnses(); Slog.e(TAG, "netType(" + netType + ") dnses: " + dnses); if (mNetConfigs[netType].isDefault()) { String network = nt.getNetworkInfo().getTypeName(); synchronized (mDnsLock) { deleteDnsLocked(network, dnses); } } flushVmDnsCache(); } }
有一点需要注意,调整DNS顺序要在系统设置DNS之前,如果在已经保存后,再调整顺序再保存,就无法生效了,dns协议中的DNS顺序不会改变,因为只要设置下去的DNS和保存过的DNS不管顺序上是否有区别,只要是一样的,就不会更改dns协议中保存过的DNS了