Android 4.1 Netd详细分析(六)DnsProxyListener
来源:互联网 发布:安全套 知乎 编辑:程序博客网 时间:2024/06/06 13:09
个人邮箱:xiaokeweng@gmail.com
在前面的几篇中我们从 main 函数入手,主要分析了 CommandListener + Netlinkmanager 两部分共同组成的可实现与 Kernel 层、Framework 层通信,并完成一套完整的功能系统。并且在文中提及到另外两个部分,DnsProxyListener 和 MDnsSdListener。顾名思义两者都是与 DNS 相关。以下是 main函数中提及到两者的部分。
其实所有android的dns查询都会proxy到netd中。这就需要了解android-dns的工作原理,是从bionic的libc中改写netbsd部分,为了实现android这种dns请求多样的系统,比如,美国verzion/ATT的彩信就会需要通过data连接查询dns,然后才能建立彩信数据通信收发彩信,如果此时wifi在连接的时候,就会需要两条dns请求通路。所以android以pid或者iface作为区分(JB43之前使用pid之后使用iface),来判断需要走wifi或data来query-dns。这个我会在后面的文章分析android dns工作原理。
//**** mian.cpp **** dpl = new <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span>(); if (dpl->startListener()) { ALOGE("Unable to start <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span> (%s)", strerror(errno)); exit(1); } //multicast_DNS_server_descript_listener 多播DNS守护进程 mdnsl = new MDnsSdListener(); if (mdnsl->startListener()) { ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno)); exit(1); }
首先从 DnsProxyListener 开始分析,该部分主要实现的是,DNS代理解析,从名字到地址,从名字到服务器端口号码的功能,该部分与 CommandListener 相似度较高。
//**** DnsProxyListener.cpp ****// 注册命令:GetAddrInfoCmd(),GetHostByAddrCmd()DnsProxyListener::DnsProxyListener() : FrameworkListener("dnsproxyd") { registerCmd(new GetAddrInfoCmd()); registerCmd(new GetHostByAddrCmd());}
构造函数,注册命令,并设置了 socket 的基本属性名字为 dnsproxyd 的有连接的 socket,它们将在后面的创建 socket 中使用到,接下来调用了 dpl->startListener()方法,相关的类继承关系Dnsproxlistener → FrameworkListener → SocketListener,因此相当于调用 SocketListener 类中的startListener(),该方法按照之前的属性创建 socket,并开始监听。这些部分均与 CommandListener中的使用相似。也就是其他部分通过socket连接的方法向“dnsproxyd”写入命令来请求功能。
//**** /system/core/libsysutils/src/SocketListener ****int SocketListener::startListener() { if (!mSocketName && mSock == -1) { SLOGE("Failed to start unbound listener"); errno = EINVAL; return -1; } else if (mSocketName) { if ((mSock = android_get_control_socket(mSocketName)) < 0) { SLOGE("Obtaining file descriptor socket '%s' failed: %s", mSocketName, strerror(errno)); return -1; } SLOGV("got mSock = %d for %s", mSock, mSocketName); } if (mListen && listen(mSock, 4) < 0) {//有链接(tcp) SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen)//无链接(udp) mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { SLOGE("pthread_create (%s)", strerror(errno)); return -1; } return 0;}
调用 startListener()创建线程调用 threadStart 函数。
void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast<SocketListener *>(obj);//获得上层//无关类型转换,获得完全相同的比特位 me->runListener(); pthread_exit(NULL); return NULL;}
而后开启线程调用 runlistener()到了真正的检测 socket 状态,使用到 fd_set,selelct 函数,最终有数据接收,触发了 onDataAvailable 函数。(与前面篇章基本相似不粘贴全部代码)
void SocketListener::runListener() { SocketClientCollection *pendingList = new SocketClientCollection(); while(1) { SocketClientCollection::iterator it; fd_set read_fds;//使用了fd_set int rc = 0; int max = -1; FD_ZERO(&read_fds);//mListener用于判断有链接(TCP)or无链接(UDP) if (mListen) { max = mSock; FD_SET(mSock, &read_fds); }…………
随后触发 Frameworklistener 中的 dispathCommand 函数直接按照字符串格式解析,因为命令源为framework 层的 NetworkManagerService 通过调用 NativeDaemonConnector 里面的的 doCommand函数下发字符串命令。(与前面篇章基本相似不粘贴全部代码)
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { FrameworkCommandCollection::iterator i; int argc = 0; char *argv[FrameworkListener::CMD_ARGS_MAX]; char tmp[255]; char *p = data; char *q = tmp; char *qlimit = tmp + sizeof(tmp) - 1; bool esc = false; bool quote = false; int k; bool haveCmdNum = !mWithSeq; memset(argv, 0, sizeof(argv)); memset(tmp, 0, sizeof(tmp)); while(*p) { if (*p == '\\') {//if (*p == '\') if (esc) { if (q >= qlimit) goto overflow; *q++ = '\\';//*q = *p++…………
经过解析匹配选择处理后调用 runCommand 函数进行处理,该函数为定义在FrameworkCommand 中的纯虚函数,为子类提供接口,这里的具体的实现在 DnsProxyListener 的两个成员类 GetAddrInfoCmd,GetAddrInfoHandler 中。
// 哪里来的 command? 答:frameworkCommand中的onDataAvailable中// 的runcommand,相当于netlinkListener 中的 onEvent。是对接收自framework层// 的调用命令(一般是该类自己注册的函数)作反应。//// arg[]: 1 2 3 4 5 6// name service flags family socktype protocol//int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, int argc, char **argv) { if (DBG) { for (int i = 0; i < argc; i++) { ALOGD("argv[%i]=%s", i, argv[i]); } } if (argc != 7) { char* msg = NULL; asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc); ALOGW("%s", msg); cli->sendMsg(ResponseCode::CommandParameterError, msg, false); free(msg); return -1; } char* name = argv[1]; if (strcmp("^", name) == 0) {//arg[1] != "^" name = NULL; } else {//name = arg[1] name = strdup(name); } char* service = argv[2];//argv[2] != "^" if (strcmp("^", service) == 0) { service = NULL; } else { service = strdup(service);//service = arg[2] } struct addrinfo* hints = NULL; int ai_flags = atoi(argv[3]);//ai_flags = argv[3] int ai_family = atoi(argv[4]);//ai_family= argv[4] int ai_socktype = atoi(argv[5]);//ai_socktype= argv[5] int ai_protocol = atoi(argv[6]);//ai_protocol = argv[6] if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo)); hints->ai_flags = ai_flags; hints->ai_family = ai_family; hints->ai_socktype = ai_socktype; hints->ai_protocol = ai_protocol; } if (DBG) { ALOGD("GetAddrInfoHandler for %s / %s", name ? name : "[nullhost]", service ? service : "[nullservice]"); } cli->incRef(); DnsProxyListener::GetAddrInfoHandler* handler = new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints);//相当于调用GetAddrInfoHandler -> run handler->start(); ////////////////////// ////////////////////////////////////////////////// return 0;}
而后将解析 Framework 命令中获得的数据,作为参数以 addrinfo 结构体的形式赋值给GetAddrInfoHandler 的构造函数,而后掉用其 start()方法。
//start ()与 threadStart()函数void DnsProxyListener::GetAddrInfoHandler::start() { pthread_create(&mThread, NULL, DnsProxyListener::GetAddrInfoHandler::threadStart, this);}void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) { GetAddrInfoHandler* handler = reinterpret_cast<GetAddrInfoHandler*>(obj); handler->run(); delete handler; pthread_exit(NULL); return NULL;}
最终启动新的线程调用,run()函数,通过调用 gethonstbyaddr()函数进行解析,并将解析结果发送至framework 层。其中涉及到 addrinfo 结构体,和 getaddrinfo 库函数。
// *************************************************************************// run()函数// 通过getaddrinfo函数,实现地址解析,解析内容为来自framework层下发的命令// 并将返回数值提交回framework层// *************************************************************************// struct addrinfo{// int ai_flags; // int ai_family;// int ai_socktype;// int ai_protocol;// socklen_t ai_addrlen;// char *ai_canonname;// struct sockaddr *ai_addr;// struct addrinfo *ai_next;// };//void DnsProxyListener::GetAddrInfoHandler::run() { if (DBG) { ALOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService); } struct addrinfo* result = NULL; uint32_t rv = getaddrinfo(mHost, mService, mHints, &result);//重要的函数,通过mHost:主机名或者地址串// mService:服务器名或者10禁止端口号// mhints:期望返回的信息类型的暗示~////本条函数的参数全部来自于,framework层下发命令格式如后文//参考:http://blog.csdn.net/xjtuse_mal/article/details/1967471 if (rv) { // getaddrinfo failed mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv)); } else { bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult); struct addrinfo* ai = result; while (ai && success) {//将解析结果返回给framework层 success = sendLenAndData(mClient, sizeof(struct addrinfo), ai) && sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr) && sendLenAndData(mClient, ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0, ai->ai_canonname); ai = ai->ai_next; //一个结构体链~ 其中有ai_next元素因为以下两种情形:////(1)以为 mHost 可能关联多个地址,则将适用于所有请//求地址簇的每个地址都返回一个对应结构。////(2)如果service参数指定的服务支持多个socket类型//则对每个socket类型都返回一个对应的结构 } success = success && sendLenAndData(mClient, 0, "");//补加结尾‘\0’ if (!success) { ALOGW("Error writing DNS result to client"); } } if (result) { freeaddrinfo(result); } mClient->decRef();}
至此实现了 framework 层下发命令,netd 接受并解析命令,而后并将调用处理函数后的解析结果返回给framework 层的消息通信回路,通信之间使用了 socket 进行通信。
//内部类 GetAddrInfoCmd (public NetdCommand )// GetAddrInfoHandler//// GetHostByAddrCmd (public NetdCommand)// GetHostByAddrHandler
而另外的类的 GetHostByAddrHandler 的基本框架与上文中描述的部分完全相似。以上内部类两两协作,共同实现了向 Framework 层注册的两个命令的实现。实现 DNS 解析,名字到地址到、名字到服务器端口两中功能。
- Android 4.1 Netd详细分析(六)DnsProxyListener
- Android 4.1 Netd详细分析(六)DnsProxyListener
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd详细分析(五)代码分析3
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd详细分析(五)代码分析3
- Android: netd中DnsProxyListener的简单介绍
- Android 4.1 Netd 详细分析系列
- Android Netd详细分析(一)概述
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(一)概述与应用实例
- android netd守护进程机制 --- netd分析
- Android 4.1 Netd详细分…
- VS2010(Asp.net)中空格加点和代码自动换行的两个有用的快捷键!
- 如何提高C语言代码效率
- initWithCapacity
- 共享库注射--injectso实例
- SQLite入门与分析(一)---简介
- Android 4.1 Netd详细分析(六)DnsProxyListener
- 图的邻接矩阵
- UVa 10360 - Rat Attack
- sql 小结
- 探索创建InitialContext过程
- 常用限制input的方法
- MII相关接口介绍
- zoj - 1091 - Knight Moves(广度优先地图记数法)
- 异或加密