Android framework 增加DNS检测功能
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检测功能相关推荐
- android framework增加新的系统服务
[android]Framework新增系统服务 分类: android 2014-04-24 17:21 638人阅读 评论(0) 收藏 举报 在android源码中增加一项系统服务,如在a ...
- Android Framework增加API 报错 Missing nullability on parameter
MissingNullability: android.os.cipher.Xxxx#init(int, Key, int) parameter #1:Missing nullability on p ...
- [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注解支持
以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3709957.html 之前写过一篇博客,是使用Fragment来实现T ...
- 初识Android framework层
Android系统的构成如下,从上到下依次是 Application应用层 Framework框架层 LIbrary系统库层 Linux内核层 关于Framework层: Android的Framew ...
- Android 11.0 framework 增加音量+音量-键唤醒屏幕的功能
目录 1.概述 2.framework 增加音量+音量-键唤醒屏幕的功能的核心代码
- android内存泄漏原因分析,Android Studio3.6的内存泄漏检测功能 VS LeakCanary
2020年2月,谷歌发布了Android Studio 3.6版.它包括一个新的"内存泄漏检测"功能.这是否意味着我们不再需要流行的内存泄漏检测库"Leak Canary ...
- android关闭人脸检测功能,【Android知识】录像预览模式下打开人脸检测
开发的一款高通平台Android 9.0基于Camera2实现的预览界面通过侧键实现拍照,录像等功能的应用中,预览界面默认是采用的原生的VideoModule.java界面实现,测试发现设置中开启人脸 ...
- 乐鑫esp8266学习rtos3.0笔记第6篇:esp8266-12模块基于rtos3.1版本ota功能远程空中升级固件,官网之上增加dns域名解析!(附带demo)
本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. Esp8266之 搭建开发环境,开始一个"hello ...
- 三星Android Pie软件,至少在AndroidPie版本上的三星GalaxyS9现在具有缺陷检测功能
昨天,我们获得了在 Qualcomm Snapdragon Samsung Galaxy S9 + 上运行的最新版本的Samsung Experience,即Samsung Experience 10 ...
最新文章
- 一种新型鱼眼图像轮廓提取算法
- 把sqlserver中存储过程改写到oracle中
- 嵌入式笔录(6)单管收音机电路分析
- python批量查询数据库_Python + MySQL 批量查询百度收录
- 管理者的困境:放权或者崩溃
- leveldb资料整理
- 计算机x线影像ppt,计算机X线摄影ppt课件
- 交互(python 版)
- Matlab学习记录 1
- Feign自定义编程配置
- 关注细节但不陷入细节
- mysql 高级查询总结_MySQL高级查询
- oracle报表工具查询数据太慢优化方案,页面优化和sql优化
- AJAX请求中payload和formdata两种方式
- Value-Decomposition Networks For Cooperative Multi-Agent Learning(VDN)
- mp3太大怎么压缩变小?
- request.getParameter、request.getParameterValues、request.getParameterMap用法详解
- 下载C语言标准库源码
- js原生下载excel(xlxs及xls格式)、word、png(图片格式)方法
- everedit选择_EverEdit
热门文章
- LGP (Local Gradient Patterns)特征匹配算法
- 北京联通2013年夏天大规模促销IPTV 与光纤宽带提速同步推进
- python 微信for Mac 自动发送当前热搜和天气
- Spring cloud系统架构的淘宝客之一
- STM32Cube安装固件库出现 invalid zip file or missing expected pdsc file within pack root directory
- iPhone数据恢复软件:Omni Recover for Mac
- 股票交易数据接口获取股票基础信息数据的过程
- 2022-2027年中国红薯淀粉行业市场调研及未来发展趋势预测报告
- 【三】3D匹配Matching之可变形曲面匹配Deformable Surface——serialize_deformable_surface_model()算子
- AutoCAD入门基本操作