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();
}

你可能感兴趣的:(Android)