1. 程式人生 > >Android okhttp3 DNS 底層實現追蹤(二)

Android okhttp3 DNS 底層實現追蹤(二)

《Android okhttp3 DNS 底層實現追蹤(一)》中分析了okhttp3的DNS從framework通過jni到libc的過程,止步於getaddrinfo。

在getaddinfo中,DNS的解析是通過Netd代理的方式進行的。Netd是Network Daemon的縮寫,Netd在Android中負責物理埠的網路操作相關的實現,如Bandwidth,NAT,PPP,soft-ap等。Netd為Framework隔離了底層網路介面的差異,提供了統一的呼叫介面,簡化了整個網路邏輯的使用。簡單來說就是Android將監聽/dev/socket/dnsproxyd,如果系統需要DNS解析服務,那麼就需要開啟dnsproxyd,然後安裝一定的格式寫入命令,然後監聽等待目標回答。

本文主要分析Netd端的DNS解析的實現。

一、從getaddrinfo到dnsproxyd

/bionic/libc/netbsd/net/getaddrinfo.c

int getaddrinfo(const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **res)
{
    return android_getaddrinfoforiface(hostname, servname, hints, NULL, 0, res);
}

getaddrinfo直接呼叫了android_getaddrinfoforiface,主要程式碼如下:

    if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
        // we're not the proxy - pass the request to them
        return android_getaddrinfo_proxy(hostname, servname, hints, res, iface);
    }

這裡cache_mode為空,Netd設定的ANDROID_DNS_MODE環境變數只在程序中有效。
在android_getaddrinfo_proxy中首先通過socket connect

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        return EAI_NODATA;
    }

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
    memset(&proxy_addr, 0, sizeof(proxy_addr));
    proxy_addr.sun_family = AF_UNIX;
    strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd",
        sizeof(proxy_addr.sun_path));
    if (TEMP_FAILURE_RETRY(connect(sock,
                       (const struct sockaddr*) &proxy_addr,
                       sizeof(proxy_addr))) != 0) {
        close(sock);
        return EAI_NODATA;
    }

這裡的socket name是/dev/socket/dnsproxyd,也就是通過dnsproxd來和netd dameon程序互動。

然後通過fprinf往dnsproxyd寫getaddrinfo命令,接下來就交由netd程序處理。

    // Send the request.
    proxy = fdopen(sock, "r+");
    if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d %s",
            hostname == NULL ? "^" : hostname,
            servname == NULL ? "^" : servname,
            hints == NULL ? -1 : hints->ai_flags,
            hints == NULL ? -1 : hints->ai_family,
            hints == NULL ? -1 : hints->ai_socktype,
            hints == NULL ? -1 : hints->ai_protocol,
            iface == NULL ? "^" : iface) < 0) {
        goto exit;
    }

二、Netd端的實現

參考: [Android4.4]DNS流程
new DnsProxyListener //system/netd/main.cpp
–>dpl->startListener ->
—->pthread_create ->
——>SocketListener::threadStart ->
——–>me->runListener ->
———->select
———->accept
———->onDataAvailable -> //FrameworkListener.cpp 客戶端寫訊息到socket dnsproxyd中,dnsproxyd是在FrameworkListener中註冊。
————>dispatchCommand ->
————–>runCommand ->
—————->DnsProxyListener::GetAddrInfoCmd::runCommand ->
——————>new DnsProxyListener::GetAddrInfoHandler
——————>handler->start ->
——————–>DnsProxyListener::GetAddrInfoHandler::start ->
——————–>DnsProxyListener::GetAddrInfoHandler::threadStart -> //DnsProxyListener.cpp netd初始化後會啟動dnsProxyListener執行緒監聽/dev/socket/dnsproxd來的訊息。
———————–>handler->run ->
————————->DnsProxyListener::GetAddrInfoHandler::run ->
—————————>android_getaddrinfoforiface -> //這裡不會跑android_getaddrinfo_proxy了,因為此時的ANDROID_DNS_MODE值是local了,所以直接獲取dns地址。
—————————–>explore_fqdn ->
——————————->nsdispatch ->
———————————>_files_getaddrinfo //從檔案/system/etc/hosts獲取
———————————>_dns_getaddrinfo //或者從dns伺服器獲取
—————————>sendLenAndData //發回給framework端

DnsProxyListener::GetAddrInfoHandler::run()的程式碼如下:

void DnsProxyListener::GetAddrInfoHandler::run() {
    if (DBG) {
        ALOGD("GetAddrInfoHandler, now for %s / %s / %s", mHost, mService, mIface);
    }

    char tmp[IF_NAMESIZE + 1];
    int mark = mMark;
    if (mIface == NULL) {
        //fall back to the per uid interface if no per pid interface exists
        if(!_resolv_get_pids_associated_interface(mPid, tmp, sizeof(tmp)))
            _resolv_get_uids_associated_interface(mUid, tmp, sizeof(tmp));
    }

    struct addrinfo* result = NULL;
    uint32_t rv = android_getaddrinfoforiface(mHost, mService, mHints, mIface ? mIface : tmp,
            mark, &result);
    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) {
            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;
        }
        success = success && sendLenAndData(mClient, 0, "");
        if (!success) {
            ALOGW("Error writing DNS result to client");
        }
    }
    if (result) {
        freeaddrinfo(result);
    }
    mClient->decRef();
}