Android 4.1 Netd详细分析(六)DnsProxyListener

来源:互联网 发布:有道词典网络连接失败 编辑:程序博客网 时间:2024/05/17 22:59

      在前面的几篇中我们从 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工作原理。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //****  mian.cpp  ****  
  2.     dpl = new <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span>();  
  3.     if (dpl->startListener()) {  
  4.         ALOGE("Unable to start <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span> (%s)", strerror(errno));  
  5.         exit(1);  
  6.     }  
  7.     //multicast_DNS_server_descript_listener  多播DNS守护进程  
  8.         
  9.     mdnsl = new MDnsSdListener();  
  10.     if (mdnsl->startListener()) {  
  11.         ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));  
  12.         exit(1);  
  13.     }  

      首先从 DnsProxyListener 开始分析,该部分主要实现的是,DNS代理解析,从名字到地址,从名字到服务器端口号码的功能,该部分与 CommandListener 相似度较高。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //**** DnsProxyListener.cpp ****  
  2. // 注册命令:GetAddrInfoCmd(),GetHostByAddrCmd()  
  3. DnsProxyListener::DnsProxyListener() :  
  4.                  FrameworkListener("dnsproxyd") {  
  5.     registerCmd(new GetAddrInfoCmd());  
  6.     registerCmd(new GetHostByAddrCmd());  
  7. }  

      构造函数,注册命令,并设置了 socket 的基本属性名字为 dnsproxyd 的有连接的 socket,它们将在后面的创建 socket 中使用到,接下来调用了 dpl->startListener()方法,相关的类继承关系Dnsproxlistener → FrameworkListener → SocketListener,因此相当于调用 SocketListener 类中的startListener(),该方法按照之前的属性创建 socket,并开始监听。这些部分均与 CommandListener中的使用相似。也就是其他部分通过socket连接的方法向“dnsproxyd”写入命令来请求功能。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //****  /system/core/libsysutils/src/SocketListener  ****  
  2. int SocketListener::startListener() {  
  3.   
  4.     if (!mSocketName && mSock == -1) {  
  5.         SLOGE("Failed to start unbound listener");  
  6.         errno = EINVAL;  
  7.         return -1;  
  8.     } else if (mSocketName) {  
  9.         if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
  10.             SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
  11.                  mSocketName, strerror(errno));  
  12.             return -1;  
  13.   
  14.         }  
  15.         SLOGV("got mSock = %d for %s", mSock, mSocketName);  
  16.     }  
  17.       
  18.     if (mListen && listen(mSock, 4) < 0) {   //有链接(tcp)  
  19.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  20.         return -1;  
  21.     } else if (!mListen)            //无链接(udp)  
  22.         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  23.   
  24.     if (pipe(mCtrlPipe)) {            
  25.         SLOGE("pipe failed (%s)", strerror(errno));  
  26.         return -1;  
  27.     }  
  28.   
  29.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
  30.         SLOGE("pthread_create (%s)", strerror(errno));  
  31.         return -1;  
  32.     }  
  33.   
  34.     return 0;  
  35. }  

      调用 startListener()创建线程调用 threadStart 函数。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void *SocketListener::threadStart(void *obj) {  
  2.     SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  3.     //获得上层  
  4.     //无关类型转换,获得完全相同的比特位       
  5.     me->runListener();  
  6.     pthread_exit(NULL);  
  7.     return NULL;  
  8. }  

      而后开启线程调用 runlistener()到了真正的检测 socket 状态,使用到 fd_set,selelct 函数,最终有数据接收,触发了 onDataAvailable 函数。(与前面篇章基本相似不粘贴全部代码)

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void SocketListener::runListener() {  
  2.   
  3.     SocketClientCollection *pendingList = new SocketClientCollection();  
  4.   
  5.     while(1) {  
  6.         SocketClientCollection::iterator it;  
  7.         fd_set read_fds;    //使用了fd_set  
  8.         int rc = 0;  
  9.         int max = -1;  
  10.   
  11.         FD_ZERO(&read_fds);  
  12.     //mListener用于判断有链接(TCP)or无链接(UDP)  
  13.         if (mListen) {  
  14.             max = mSock;  
  15.             FD_SET(mSock, &read_fds);  
  16.         }  
  17. ……  
  18. ……  

      随后触发 Frameworklistener 中的 dispathCommand 函数直接按照字符串格式解析,因为命令源为framework 层的 NetworkManagerService 通过调用 NativeDaemonConnector 里面的的 doCommand函数下发字符串命令。(与前面篇章基本相似不粘贴全部代码)

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     FrameworkCommandCollection::iterator i;  
  3.     int argc = 0;  
  4.     char *argv[FrameworkListener::CMD_ARGS_MAX];  
  5.     char tmp[255];  
  6.     char *p = data;  
  7.     char *q = tmp;  
  8.     char *qlimit = tmp + sizeof(tmp) - 1;  
  9.     bool esc = false;  
  10.     bool quote = false;  
  11.     int k;  
  12.     bool haveCmdNum = !mWithSeq;  
  13.   
  14.     memset(argv, 0, sizeof(argv));  
  15.     memset(tmp, 0, sizeof(tmp));  
  16.     while(*p) {  
  17.         if (*p == '\\') {       //if (*p == '\')  
  18.             if (esc) {            
  19.                 if (q >= qlimit)   
  20.                     goto overflow;    
  21.                 *q++ = '\\';        //*q = *p++  
  22. ……  
  23. ……  

      经过解析匹配选择处理后调用 runCommand 函数进行处理,该函数为定义在FrameworkCommand 中的纯虚函数,为子类提供接口,这里的具体的实现在 DnsProxyListener 的两个成员类 GetAddrInfoCmd,GetAddrInfoHandler 中。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //  哪里来的 command? 答:frameworkCommand中的onDataAvailable中  
  2. //  的runcommand,相当于netlinkListener 中的 onEvent。是对接收自framework层  
  3. //  的调用命令(一般是该类自己注册的函数)作反应。  
  4. //  
  5. //   arg[]:     1       2       3       4       5         6  
  6. //             name  service  flags  family  socktype  protocol  
  7. //  
  8. int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,  
  9.                                             int argc, char **argv) {  
  10.     if (DBG) {  
  11.         for (int i = 0; i < argc; i++) {  
  12.             ALOGD("argv[%i]=%s", i, argv[i]);  
  13.         }  
  14.     }  
  15.     if (argc != 7) {  
  16.         char* msg = NULL;  
  17.         asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);  
  18.         ALOGW("%s", msg);  
  19.         cli->sendMsg(ResponseCode::CommandParameterError, msg, false);  
  20.         free(msg);  
  21.         return -1;  
  22.     }  
  23.   
  24.     char* name = argv[1];         
  25.     if (strcmp("^", name) == 0) {   //arg[1] != "^"  
  26.         name = NULL;  
  27.     } else {                //name = arg[1]   
  28.         name = strdup(name);  
  29.     }  
  30.   
  31.     char* service = argv[2];        //argv[2] != "^"  
  32.     if (strcmp("^", service) == 0) {  
  33.         service = NULL;  
  34.     } else {  
  35.         service = strdup(service);  //service = arg[2]  
  36.     }  
  37.   
  38.     struct addrinfo* hints = NULL;  
  39.     int ai_flags = atoi(argv[3]);   //ai_flags = argv[3]  
  40.     int ai_family = atoi(argv[4]);  //ai_family= argv[4]  
  41.     int ai_socktype = atoi(argv[5]);    //ai_socktype= argv[5]  
  42.     int ai_protocol = atoi(argv[6]);    //ai_protocol = argv[6]  
  43.     if (ai_flags != -1 || ai_family != -1 ||  
  44.         ai_socktype != -1 || ai_protocol != -1) {  
  45.         hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo));  
  46.         hints->ai_flags = ai_flags;  
  47.         hints->ai_family = ai_family;  
  48.         hints->ai_socktype = ai_socktype;  
  49.         hints->ai_protocol = ai_protocol;  
  50.     }  
  51.   
  52.     if (DBG) {  
  53.         ALOGD("GetAddrInfoHandler for %s / %s",  
  54.              name ? name : "[nullhost]",  
  55.              service ? service : "[nullservice]");  
  56.     }  
  57.   
  58.     cli->incRef();  
  59.     DnsProxyListener::GetAddrInfoHandler* handler =  
  60.         new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints);  
  61.     //相当于调用GetAddrInfoHandler -> run  
  62.     handler->start();          //////////////////////  
  63.     //////////////////////////////////////////////////  
  64.     return 0;  
  65. }  

      而后将解析 Framework 命令中获得的数据,作为参数以 addrinfo 结构体的形式赋值给GetAddrInfoHandler 的构造函数,而后掉用其 start()方法。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //start ()与 threadStart()函数  
  2. void DnsProxyListener::GetAddrInfoHandler::start() {  
  3.     pthread_create(&mThread, NULL,  
  4.                    DnsProxyListener::GetAddrInfoHandler::threadStart, this);  
  5. }  
  6.   
  7. void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {  
  8.     GetAddrInfoHandler* handler = reinterpret_cast<GetAddrInfoHandler*>(obj);  
  9.     handler->run();  
  10.     delete handler;  
  11.     pthread_exit(NULL);  
  12.     return NULL;  
  13. }  

      最终启动新的线程调用,run()函数,通过调用 gethonstbyaddr()函数进行解析,并将解析结果发送至framework 层。其中涉及到 addrinfo 结构体,和 getaddrinfo 库函数。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // *************************************************************************  
  2. //   run()函数  
  3. //   通过getaddrinfo函数,实现地址解析,解析内容为来自framework层下发的命令  
  4. //   并将返回数值提交回framework层  
  5. // *************************************************************************  
  6. //    struct addrinfo{  
  7. //      int       ai_flags;    
  8. //      int       ai_family;  
  9. //      int       ai_socktype;  
  10. //      int       ai_protocol;  
  11. //      socklen_t ai_addrlen;  
  12. //      char    *ai_canonname;  
  13. //      struct sockaddr *ai_addr;  
  14. //      struct addrinfo *ai_next;  
  15. //    };  
  16. //  
  17. void DnsProxyListener::GetAddrInfoHandler::run() {  
  18.     if (DBG) {  
  19.         ALOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);  
  20.     }  
  21.   
  22.     struct addrinfo* result = NULL;  
  23.     uint32_t rv = getaddrinfo(mHost, mService, mHints, &result);  
  24.                 //重要的函数,通过mHost:主机名或者地址串  
  25.                 //      mService:服务器名或者10禁止端口号  
  26.                 //        mhints:期望返回的信息类型的暗示~  
  27.                 //  
  28.                 //本条函数的参数全部来自于,framework层下发命令格式如后文  
  29.                 //参考:http://blog.csdn.net/xjtuse_mal/article/details/1967471  
  30.     if (rv) {  
  31.         // getaddrinfo failed  
  32.         mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));  
  33.     } else {  
  34.         bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);  
  35.         struct addrinfo* ai = result;  
  36.         while (ai && success) { //将解析结果返回给framework层  
  37.             success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)  
  38.                 && sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr)  
  39.                 && sendLenAndData(mClient,  
  40.                                   ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0,  
  41.                                   ai->ai_canonname);  
  42.             ai = ai->ai_next;    //一个结构体链~ 其中有ai_next元素因为以下两种情形:  
  43.                 //  
  44.                 //(1)   以为 mHost 可能关联多个地址,则将适用于所有请  
  45.                 //  求地址簇的每个地址都返回一个对应结构。  
  46.                 //  
  47.                 //(2)   如果service参数指定的服务支持多个socket类型  
  48.                 //  则对每个socket类型都返回一个对应的结构  
  49.   
  50.         }  
  51.         success = success && sendLenAndData(mClient, 0, "");//补加结尾‘\0’  
  52.         if (!success) {  
  53.             ALOGW("Error writing DNS result to client");  
  54.         }  
  55.     }  
  56.     if (result) {  
  57.         freeaddrinfo(result);  
  58.     }  
  59.     mClient->decRef();  
  60. }  

      至此实现了 framework 层下发命令,netd 接受并解析命令,而后并将调用处理函数后的解析结果返回给framework 层的消息通信回路,通信之间使用了 socket 进行通信。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //内部类   GetAddrInfoCmd     (public NetdCommand )  
  2. //        GetAddrInfoHandler  
  3. //  
  4. //        GetHostByAddrCmd   (public NetdCommand)  
  5. //        GetHostByAddrHandler  

      而另外的类的 GetHostByAddrHandler 的基本框架与上文中描述的部分完全相似。以上内部类两两协作,共同实现了向 Framework 层注册的两个命令的实现。实现 DNS 解析,名字到地址到、名字到服务器端口两中功能。

0 0
原创粉丝点击