Android 4.1 Netd详细分析(六)DnsProxyListener
2012-11-14 10:50
197 查看
个人邮箱: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工作原理。
首先从
DnsProxyListener 开始分析,该部分主要实现的是,DNS代理解析,从名字到地址,从名字到服务器端口号码的功能,该部分与 CommandListener 相似度较高。
构造函数,注册命令,并设置了 socket 的基本属性名字为 dnsproxyd 的有连接的 socket,它们将在后面的创建 socket 中使用到,接下来调用了 dpl->startListener()方法,相关的类继承关系Dnsproxlistener → FrameworkListener → SocketListener,因此相当于调用 SocketListener
类中的startListener(),该方法按照之前的属性创建 socket,并开始监听。这些部分均与 CommandListener中的使用相似。也就是其他部分通过socket连接的方法向“dnsproxyd”写入命令来请求功能。
调用 startListener()创建线程调用 threadStart 函数。
而后开启线程调用 runlistener()到了真正的检测 socket 状态,使用到 fd_set,selelct 函数,最终有数据接收,触发了 onDataAvailable 函数。(与前面篇章基本相似不粘贴全部代码)
随后触发 Frameworklistener 中的 dispathCommand 函数直接按照字符串格式解析,因为命令源为framework 层的 NetworkManagerService 通过调用 NativeDaemonConnector 里面的的 doCommand函数下发字符串命令。(与前面篇章基本相似不粘贴全部代码)
经过解析匹配选择处理后调用 runCommand 函数进行处理,该函数为定义在FrameworkCommand 中的纯虚函数,为子类提供接口,这里的具体的实现在 DnsProxyListener 的两个成员类 GetAddrInfoCmd,GetAddrInfoHandler 中。
而后将解析 Framework 命令中获得的数据,作为参数以 addrinfo 结构体的形式赋值给GetAddrInfoHandler 的构造函数,而后掉用其 start()方法。
最终启动新的线程调用,run()函数,通过调用 gethonstbyaddr()函数进行解析,并将解析结果发送至framework 层。其中涉及到 addrinfo 结构体,和 getaddrinfo 库函数。
至此实现了 framework 层下发命令,netd 接受并解析命令,而后并将调用处理函数后的解析结果返回给framework 层的消息通信回路,通信之间使用了 socket 进行通信。
而另外的类的 GetHostByAddrHandler 的基本框架与上文中描述的部分完全相似。以上内部类两两协作,共同实现了向 Framework 层注册的两个命令的实现。实现 DNS 解析,名字到地址到、名字到服务器端口两中功能。
在前面的几篇中我们从 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: netd中DnsProxyListener的简单介绍
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd 详细分析系列
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd详细分析(五)代码分析3
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(五)代码分析3
- Android Netd详细分析(一)概述
- Android Scroller类的详细分析
- Android 详细分析AppBarLayout的五种ScrollFlags
- Android_launcher的源码详细分析
- Android Intent和PendingIntent的区别详细分析
- Android导航栏ActionBar的详细分析