Android 4.1 Netd詳細分析(六)DnsProxyListener

個人郵箱:[email protected]

      在前面的幾篇中我們從 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 解析,名字到地址到、名字到服務器端口兩中功能。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章