Android 4.1 Netd詳細分析(六)DnsProxyListener

來源:互聯網
上載者:User

個人郵箱:xiaokeweng@gmail.com

      在前面的幾篇中我們從 main 函數入手,主要分析了 CommandListener + Netlinkmanager 兩部分共同組成的可實現與 Kernel 層、Framework 層通訊,並完成一套完整的功能系統。並且在文中提及到另外兩個部分,DnsProxyListener 和 MDnsSdListener。顧名思義兩者都是與
DNS 相關。以下是 main函數中提及到兩者的部分。

//****  mian.cpp  ****    dpl = new DnsProxyListener();    if (dpl->startListener()) {        ALOGE("Unable to start DnsProxyListener (%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中的使用相似。

//****  /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 解析,名字到地址到、名字到伺服器連接埠兩中功能。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.