Android系统中,一个网络类型可以设置多个DNS,Android系统中,最多可以设置三个DNS,可以通过其属性net.dns*查看。DNS在dhcp等流程之后,会通过netd设置到libc,DNS解析是通过libc实现。当解析一个域名时第一个DNS超时,则会使用第二个、第三个DNS解析,解析后的结果会进行缓存。

最近需要在Android framework中增加DNS检测功能,在网络连接后,检测DNS是否有效,并将有效的DNS尽量置前。尽量保证第一个DNS的有效性,加快对新域名的解析速度。

framework需要修改的点有两个:
1)需要增加DNS检测接口;
2)在检测完成后调用DNS设置接口重新设置DNS

DNS检测接口实现

1、在NetworkUtils 工具类函数中增加native接口checkDnsValid

public class NetworkUtils {
......省略/** check dns server */public native static int checkDnsValid(String dns, String host);
......省略    }

2、JNI实现:

frameworks/base/core/jni/android_net_NetUtils.cpp
......省略
static jint android_net_utils_checkDnsValid(JNIEnv* env, jobject clazz, jstring dns, jstring host)
{int result = 0;const char *dnsStr = env->GetStringUTFChars(dns, NULL);const char* hostStr= env->GetStringUTFChars(host, NULL);ALOGD("android_net_utils_checkDnsValid : DNS[%s] HOST[%s] \n",dnsStr,hostStr);    char* hostIp =::ngethost((unsigned char*)hostStr ,1, dnsStr);//T_A  Ipv4 addressif(hostIp != NULL){result = 1;ALOGD("android_net_utils_checkDnsValid : DNS[%s] [%s:%s] \n",dnsStr,hostStr,hostIp);}    env->ReleaseStringUTFChars(dns, dnsStr);env->ReleaseStringUTFChars(host, hostStr);return (jint)result;
}
......省略{ "checkDnsValid", "(Ljava/lang/String;Ljava/lang/String;)I",  (void *)android_net_utils_checkDnsValid },
......省略};

核心部分,DNS检测接口ngethost的实现,检测接口参考开源代码并加以优化,类似的功能开源的工具有nslookup等。通过对指定域名,如百度www.baidu.com的解析,来判断DNS是否有效,超时时间为5s。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <utils/Log.h>#define LOG_TAG "NetUtils"#define T_A 1           /*Ipv4 address*/
#define T_NS 2          /*Nameserver*/
#define T_CNAME 5       /*canonical name*/
#define T_SOA 6         /*start of authority zone */
#define T_PTR 12        /*domain name pointer */
#define T_MX 15         /*Mail server*/char * ngethost (unsigned char* , int, const char *);static void reverseIP(char *,char *);
static void removeDotsFromName(unsigned char*,unsigned char*);
static unsigned char* ReadName (unsigned char*,unsigned char*,int*);/*DNS header*/
struct DNS_HEADER
{unsigned short id;     // identification number# if __BYTE_ORDER == __LITTLE_ENDIANunsigned char rd :1;  // recursion desiredunsigned char tc :1;    // truncated messageunsigned char aa :1;    // authoritive answerunsigned char opcode :4; // purpose of messageunsigned char qr :1;     // query/response flagunsigned char rcode :4; // response codeunsigned char cd :1;  // checking disabledunsigned char ad :1;    // authenticated dataunsigned char z :1;    // reserved and unusedunsigned char ra :1;  // recursion available
# endif
# if __BYTE_ORDER == __BIG_ENDIANunsigned char qr :1;unsigned char opcode :4;unsigned char aa :1;unsigned char tc :1;unsigned char rd :1;unsigned char ra :1;unsigned char z :1;unsigned char ad :1;unsigned char cd :1;unsigned char rcode :4;
# endifunsigned short q_count; // number of question entriesunsigned short ans_count; // number of answer entriesunsigned short auth_count; // number of authority entriesunsigned short add_count; // number of resource entries
};struct QUESTION               /*QUESTION DATA*/
{unsigned short qtype;      /*query type:IN,NS,CNAME,SOA,PTR,MX*/unsigned short qclass;     /*query class:IN or CHAOS*/
};#pragma pack(push, 1)
struct R_DATA               /*RESOURCE RECORD DATA*/
{unsigned short type;unsigned short _class;unsigned int ttl;unsigned short data_len;
};
#pragma pack(pop)
struct RES_RECORD           /*RESOURCE RECORD FIELD:AUTHORITATIVE,ANSWER or ADDITIONAL*/
{unsigned char *name;struct R_DATA *resource;unsigned char *rdata;
};typedef struct                /*QUESTION FIELD*/
{unsigned char *name;struct QUESTION *ques;
} QUERY;void reverseIP(char *addr, char *tar )      /*change a.b.c.d to d.c.b.a.in-addr.arpa*/
{int i,j,count_dots=0,pos=0;char buffer[10];for(i=strlen(addr)-1;i>=0;i--){if(addr[i]=='.'){for(j=count_dots-1;j>=0;j--){*(tar+pos)=buffer[j];pos++;}*(tar+pos)='.';pos++;count_dots=0;}else{buffer[count_dots]=addr[i];count_dots++;}}for(j=count_dots-1;j>=0;j--){*(tar+pos)=buffer[j];pos++;}         char *arpa = ".in-addr.arpa";for(i=0;i<14;i++){*(tar+pos) = *arpa;pos++;arpa++;}
}/*perform nslookup*/
char* ngethost(unsigned char *host , int query_type, const char *dns_server)
{unsigned char buf[65536],*qname,*reader;int i , j , stop , s;struct sockaddr_in a,dest;struct timeval timeout;timeout.tv_sec = 5; timeout.tv_usec = 0; struct RES_RECORD answers[50],auth[50],addinfo[50]; struct DNS_HEADER *dns = NULL;struct QUESTION *qinfo = NULL;ALOGD("Resolving %s from dns[%s]\n" , host,dns_server);s = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));    /*set timeout on this socket*/dest.sin_family = AF_INET;dest.sin_port = htons(53);dest.sin_addr.s_addr = inet_addr(dns_server); dns = (struct DNS_HEADER *)&buf;            /*DNS HEADER*/dns->id = (unsigned short) htons(getpid());dns->qr = 0; dns->opcode = 0;              /*standard query*/dns->aa = 0;          dns->tc = 1; dns->rd = 1;                   /*recursion desired*/dns->ra = 0; dns->z = 0;dns->ad = 0;dns->cd = 0;dns->rcode = 0;dns->q_count = htons(1); dns->ans_count = 0;dns->auth_count = 0;dns->add_count = 0;qname =(unsigned char*)&buf[sizeof(struct DNS_HEADER)];                      /*DNS QUESTION NAME.ANY JUNK VALUE WILL DO*/removeDotsFromName(qname , host);qinfo =(struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; /*DNS QUESTION TYPE AND CLASS*/qinfo->qtype = htons( query_type ); qinfo->qclass = htons(1); if( sendto(s,(char*)buf,sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION),0,(struct sockaddr*)&dest,sizeof(dest)) < 0){ALOGD("\nSending Packet to %s faile\n",dns_server);goto fail;}i=sizeof(dest);if(recvfrom (s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest , (socklen_t*)&i ) < 0){goto fail;}dns = (struct DNS_HEADER*) buf;if(dns->ra==0){goto fail;}        if(dns->rcode==0){reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)]; /*THE RESPONSE*/stop=0;for(i=0;i<ntohs(dns->ans_count);i++){answers[i].name=ReadName(reader,buf,&stop);  reader = reader + stop;answers[i].resource = (struct R_DATA*)(reader);reader = reader + sizeof(struct R_DATA);if(ntohs(answers[i].resource->type) == 1)   /*read address*/{answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)answers[i].rdata[j]=reader[j];answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';reader = reader + ntohs(answers[i].resource->data_len);}else                       /*read name*/{answers[i].rdata = ReadName(reader,buf,&stop);reader = reader + stop;}}for(i=0;i<ntohs(dns->auth_count);i++)          {auth[i].name=ReadName(reader,buf,&stop);reader+=stop;auth[i].resource=(struct R_DATA*)(reader);reader+=sizeof(struct R_DATA);if(ntohs(auth[i].resource->type)==1)       /*read address*/{auth[i].rdata = (unsigned char*)malloc(ntohs(auth[i].resource->data_len));for(j=0;j<ntohs(auth[i].resource->data_len);j++)auth[i].rdata[j]=reader[j];auth[i].rdata[ntohs(auth[i].resource->data_len)]='\0';reader+=ntohs(auth[i].resource->data_len);}else                        /*read name*/{auth[i].rdata=ReadName(reader,buf,&stop);reader+=stop;}}for(i=0;i<ntohs(dns->add_count);i++){addinfo[i].name=ReadName(reader,buf,&stop);reader+=stop;addinfo[i].resource=(struct R_DATA*)(reader);reader+=sizeof(struct R_DATA);if(ntohs(addinfo[i].resource->type)==1)                /*read address*/{addinfo[i].rdata = (unsigned char*)malloc(ntohs(addinfo[i].resource->data_len));for(j=0;j<ntohs(addinfo[i].resource->data_len);j++){addinfo[i].rdata[j]=reader[j];}addinfo[i].rdata[ntohs(addinfo[i].resource->data_len)]='\0';reader+=ntohs(addinfo[i].resource->data_len);}else                             /*read name*/{addinfo[i].rdata=ReadName(reader,buf,&stop);reader+=stop;}}for(i=0 ; i < ntohs(dns->ans_count) ; i++){ALOGD("----------ntohs(dns->ans_count[%d])--------------\n",i);if( ntohs(answers[i].resource->type) == T_A) //IPv4 address{ALOGD("---ntohs(answers[%d].resource->type) == T_A--\n",i);long *p;p=(long*)answers[i].rdata;a.sin_addr.s_addr=(*p); ALOGD("has IPv4 address : %s \n",inet_ntoa(a.sin_addr));if(s > 0) close(s);return inet_ntoa(a.sin_addr);}else if(ntohs(answers[i].resource->type)== T_CNAME) ALOGD("has alias name : %s \n",answers[i].rdata);else if(ntohs(answers[i].resource->type)== T_PTR)ALOGD("has domain name :%s \n",answers[i].rdata);}}fail:if(s > 0) close(s);return NULL;
}u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count)
{unsigned char *name;unsigned int p=0,jumped=0,offset;int i , j;*count = 1;name = (unsigned char*)malloc(256);      /*maximum allowed length is 256*/name[0]='\0';while(*reader!=0){if(*reader>=192){offset = (*reader)*256 + *(reader+1) - 49152;reader = buffer + offset - 1;jumped = 1; }elsename[p++]=*reader;reader = reader+1;if(jumped==0)*count = *count + 1;}name[p]='\0';if(jumped==1)*count = *count + 1;for(i=0;i<(int)strlen((const char*)name);i++) {p=name[i];for(j=0;j<(int)p;j++) {name[i]=name[i+1];i=i+1;}name[i]='.';}name[i-1]='\0';return name;
}void removeDotsFromName(unsigned char* dns,unsigned char* host)
{int lock = 0 , i;strcat((char*)host,".");for(i = 0 ; i < strlen((char*)host) ; i++) {if(host[i]=='.') {*dns++ = i-lock;        /*replace the dot with the number of characters after it before the next dot*/for(;lock<i;lock++) *dns++=host[lock];lock++; }}*dns++='\0';
}

DNS检测流程

在网络连接后,framework层在ConnectivityService中处理所有类型的网络连接,handleDnsConfigurationChange函数则是对DNS设置的地方。可以在此进行DNS检测,也可以在handleConnect中进行。
例如在handleConnect中增加,
1)如果当前网络类型设置的DNS个数>=2,则进行DNS检测。
2)联网后1s,在后台调用NetworkUtils.checkDnsValid(value,“www.baidu.com”);检测设置的各个DNS有效性。
3)如果第一个有效的DNS index 满足0<index<dnses.size,即说明需要重置DNS了。将第一个有效的DNS置于最前。

// TODO: add for check dnsNetworkStateTracker nt = mNetTrackers[newNetType];if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()){final LinkProperties p = nt.getLinkProperties();if (p == null) return;final Collection<InetAddress> dnses = p.getDnses();if(dnses.size() < 2) return;Runnable dnsCheckPorcess = new Runnable(){ public void run() { synchronized(this){try {Collection<InetAddress> dnsesVaild = new ArrayList();int vaildIndex = 0;for (InetAddress dns : dnses) {String value = dns.getHostAddress();int ret = NetworkUtils.checkDnsValid(value,"www.baidu.com");Slog.d("NetUtils","checking " + value  + ">>>result:" + ret);if(ret==1) {dnsesVaild.add(dns);break;}vaildIndex++;}if(vaildIndex == 0/*first dns is vaild*/||vaildIndex >= dnses.size()/*no vaild dns*/){Slog.d("NetUtils","no need reset dns");return;}for (InetAddress dns : dnses) {if(!dnsesVaild.contains(dns)){dnsesVaild.add(dns);}}Slog.d("NetUtils",">>>>>>>>>>>>>>>>>>>>>>>>>>>>reset dns");mNetd.setDnsServersForInterface(p.getInterfaceName(),NetworkUtils.makeStrings(dnsesVaild), p.getDomains());} catch (Exception e) {Slog.e("NetUtils","exception checkDnsValid: " + e);}}} };mHandler.postDelayed(dnsCheckPorcess,1000);}

DNS检测重置

重置参考handleDnsConfigurationChange代码实现即可,framework层对DNS的操作,均是通过netd设置到底层,详细了解需要参考framework层NetworkManagementService和netd实现代码。
重置如下
mNetd.setDnsServersForInterface(p.getInterfaceName(),NetworkUtils.makeStrings(dnsesVaild), p.getDomains());
但这里没有清除DNS缓存。

验证方法

WiFi联网后,手动设置DNS,第一个DNS随便填一个不可用的IP,第二个DNS填8.8.8.8,尝试ping一个新的域名,看是否能很快反馈结果,确实如此。
同时抓打印,抓包,看日志是否符合代码流程,同时看网络包第一个使用的DNS是8.8.8.8

Android framework 增加DNS检测功能相关推荐

  1. android framework增加新的系统服务

    [android]Framework新增系统服务 分类: android 2014-04-24 17:21  638人阅读  评论(0)  收藏  举报 在android源码中增加一项系统服务,如在a ...

  2. Android Framework增加API 报错 Missing nullability on parameter

    MissingNullability: android.os.cipher.Xxxx#init(int, Key, int) parameter #1:Missing nullability on p ...

  3. [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注解支持

    以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3709957.html 之前写过一篇博客,是使用Fragment来实现T ...

  4. 初识Android framework层

    Android系统的构成如下,从上到下依次是 Application应用层 Framework框架层 LIbrary系统库层 Linux内核层 关于Framework层: Android的Framew ...

  5. Android 11.0 framework 增加音量+音量-键唤醒屏幕的功能

    目录 1.概述 2.framework 增加音量+音量-键唤醒屏幕的功能的核心代码

  6. android内存泄漏原因分析,Android Studio3.6的内存泄漏检测功能 VS LeakCanary

    2020年2月,谷歌发布了Android Studio 3.6版.它包括一个新的"内存泄漏检测"功能.这是否意味着我们不再需要流行的内存泄漏检测库"Leak Canary ...

  7. android关闭人脸检测功能,【Android知识】录像预览模式下打开人脸检测

    开发的一款高通平台Android 9.0基于Camera2实现的预览界面通过侧键实现拍照,录像等功能的应用中,预览界面默认是采用的原生的VideoModule.java界面实现,测试发现设置中开启人脸 ...

  8. 乐鑫esp8266学习rtos3.0笔记第6篇:esp8266-12模块基于rtos3.1版本ota功能远程空中升级固件,官网之上增加dns域名解析!(附带demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. Esp8266之 搭建开发环境,开始一个"hello ...

  9. 三星Android Pie软件,至少在AndroidPie版本上的三星GalaxyS9现在具有缺陷检测功能

    昨天,我们获得了在 Qualcomm Snapdragon Samsung Galaxy S9 + 上运行的最新版本的Samsung Experience,即Samsung Experience 10 ...

最新文章

  1. 一种新型鱼眼图像轮廓提取算法
  2. 把sqlserver中存储过程改写到oracle中
  3. 嵌入式笔录(6)单管收音机电路分析
  4. python批量查询数据库_Python + MySQL 批量查询百度收录
  5. 管理者的困境:放权或者崩溃
  6. leveldb资料整理
  7. 计算机x线影像ppt,计算机X线摄影ppt课件
  8. 交互(python 版)
  9. Matlab学习记录 1
  10. Feign自定义编程配置
  11. 关注细节但不陷入细节
  12. mysql 高级查询总结_MySQL高级查询
  13. oracle报表工具查询数据太慢优化方案,页面优化和sql优化
  14. AJAX请求中payload和formdata两种方式
  15. Value-Decomposition Networks For Cooperative Multi-Agent Learning(VDN)
  16. mp3太大怎么压缩变小?
  17. request.getParameter、request.getParameterValues、request.getParameterMap用法详解
  18. 下载C语言标准库源码
  19. js原生下载excel(xlxs及xls格式)、word、png(图片格式)方法
  20. everedit选择_EverEdit

热门文章

  1. LGP (Local Gradient Patterns)特征匹配算法
  2. 北京联通2013年夏天大规模促销IPTV 与光纤宽带提速同步推进
  3. python 微信for Mac 自动发送当前热搜和天气
  4. Spring cloud系统架构的淘宝客之一
  5. STM32Cube安装固件库出现 invalid zip file or missing expected pdsc file within pack root directory
  6. iPhone数据恢复软件:Omni Recover for Mac
  7. 股票交易数据接口获取股票基础信息数据的过程
  8. 2022-2027年中国红薯淀粉行业市场调研及未来发展趋势预测报告
  9. 【三】3D匹配Matching之可变形曲面匹配Deformable Surface——serialize_deformable_surface_model()算子
  10. AutoCAD入门基本操作