【Android】(十三) DNS-Resolver

Dns-Reslover 初始化

init 进程启动后,解析 init.rc,启动了 netd

//system/netd/server/main.cpp
bool initDnsResolver() {
    ResolverNetdCallbacks callbacks = {
        .check_calling_permission = &checkCallingPermissionCallback,
        .get_network_context = &getNetworkContextCallback,
        .log = &logCallback,
        .tagSocket = &tagSocketCallback,
        .evaluate_domain_name = &evaluateDomainNameCallback,
    };
    return resolv_init(&callbacks);
}

int main(){
    //初始化配置,初始化必要的类
    if (!initDnsResolver()) {
        ALOGE("Unable to init resolver");
        exit(1);
    }
    //注册,启动MDnsService,NetdNativeService,NetdHwService等服务,生存Binder对象供客户调用
}

netd 启动过程中初始化了 Dns-Reslover。初始化过程调用了 initReslover,构造了解析器的回调函数,调用 resolv_init 具体进行初始化。

bool resolv_init(const ResolverNetdCallbacks* callbacks) {
    //..
    android::net::gDnsResolv = android::net::DnsResolver::getInstance();
    return android::net::gDnsResolv->start();
}

resolv_init 中初始化 Dns 日志系统配置,设置解析器回调函数。接着准备启动Dns-Reslover服务。

Dns-Reslover 启动

//packages/modules/DnsResolver/DnsResolver.cpp
bool DnsResolver::start() {
    if (!verifyCallbacks()) {
        LOG(ERROR) << __func__ << ": Callback verification failed";
        return false;
    }
    if (mDnsProxyListener.startListener()) {
        PLOG(ERROR) << __func__ << ": Unable to start DnsProxyListener";
        return false;
    }
    binder_status_t ret;
    if ((ret = DnsResolverService::start()) != STATUS_OK) {
        LOG(ERROR) << __func__ << ": Unable to start DnsResolverService: " << ret;
        return false;
    }
    return true;
}

DnsReslover 启动分为三步,首先检查回调函数是否有效,然后启动 DnsProxyListener 监听Dns解析请求,然后启动DnsReslover 服务。

DnsProxyListener 启动

//system/core/libsysutils/src/SocketListener.cpp
int SocketListener::startListener(int backlog) {
    
    if (mListen && listen(mSock, backlog) < 0) {//mListen==true
        SLOGE("Unable to listen on socket (%s)", strerror(errno));
        return -1;
    } else if (!mListen)
        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
    
    if (pipe2(mCtrlPipe, O_CLOEXEC)) {
        SLOGE("pipe failed (%s)", strerror(errno));
        return -1;
    }

    if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {
        SLOGE("pthread_create (%s)", strerror(errno));
        return -1;
    }
    return 0;
}

创建一个监听 socket,创建一个线程间的通信管道。创建一个监听线程,专门处理解析请求。

//system/core/libsysutils/src/SocketListener.cpp
void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

    me->runListener();
    pthread_exit(nullptr);
    return nullptr;
}

void SocketListener::runListener() {
    while (true) {
        //...
        for (SocketClient* c : pending) {
            // Process it, if false is returned, remove from the map
            SLOGV("processing fd %d", c->getSocket());
            if (!onDataAvailable(c)) {
                release(c, false);
            }
            c->decRef();
        }
    }
}

线程中处理监听线程,管道和 client 上的所有事件,将有数据到达的 client 挂在一个链表上,依次处理到达的数据。

DnsResloverService启动

//packages/modules/DnsResolver/DnsResolverService.cpp
binder_status_t DnsResolverService::start() {
    std::shared_ptr<DnsResolverService> resolverService =
            ::ndk::SharedRefBase::make<DnsResolverService>();
    binder_status_t status =
            AServiceManager_addService(resolverService->asBinder().get(), getServiceName());
    if (status != STATUS_OK) {
        return status;
    }
    ABinderProcess_startThreadPool();
    return STATUS_OK;
}

服务注册到 Binder 服务管理中,可供其他进程调用。启动 Binder 进程的线程池,以便处理来自客户端的请求。

Dns 查询

1 来自上层的DNS请求

1 Getaddrinfo

1.1 下发请求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//libcore/ojluni/src/main/java/java/net/InetAddress.java
public static InetAddress getByName(String host)
    throws UnknownHostException {
    // Android-changed: Rewritten on the top of Libcore.os.
    return impl.lookupAllHostAddr(host, NETID_UNSET)[0];
}

程序调用 InetAddress 类中的 getByName 方法来获取一个主机名的 IP 地址。getByName 内部调用到Inet6AddressImpl 中的 lookupAllHostAddr 方法。InetAdress类中还有一些其他方法内部都调用了 Inet6AddressImpl.lookupAllHostAddr,这些方法都是以下的流程。

//libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java
public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException {
    //如果已经是IP地址,直接return
    //...
    return lookupHostByName(host, netId);
}

private static InetAddress[] lookupHostByName(String host, int netId)
        throws UnknownHostException {
    //...
    StructAddrinfo hints = new StructAddrinfo();
    hints.ai_flags = AI_ADDRCONFIG;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);
    //...
    return addresses;
}

如果已经是IP地址,直接 return。接下来去cahce中查找,找不到再调用到 Libcore.os 中的 android_getaddrinfo JNI 方法。Libcore 中的所有的 JNI 方法定义在这个文件中:libcore/luni/src/main/java/libcore/io/Linux.java 。实现如下:

//libcore/luni/src/main/native/libcore_io_Linux.cpp
static jobjectArray Linux_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode,
        jobject javaHints, jint netId) {
    ScopedUtfChars node(env, javaNode);
    //...
    addrinfo* addressList = NULL;
    errno = 0;
    int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
    //...
    // Prepare output array.
}

其中调用了 android_getaddrinfofornet 方法,实现 Dns 解析。

//bionic/libc/dns/net/getaddrinfo.c
int android_getaddrinfofornet(const char *hostname, const char *servname,
    const struct addrinfo *hints, unsigned netid, unsigned mark, struct addrinfo **res)
{
        struct android_net_context netcontext = {
                .app_netid = netid,
                .app_mark = mark,
                .dns_netid = netid,
                .dns_mark = mark,
                .uid = NET_CONTEXT_INVALID_UID,
        };
        return android_getaddrinfofornetcontext(hostname, servname, hints, &netcontext, res);
}

int android_getaddrinfofornetcontext(const char *hostname, const char *servname,
    const struct addrinfo *hints, const struct android_net_context *netcontext,
    struct addrinfo **res)
{
#if defined(__ANDROID__)
    int gai_error = android_getaddrinfo_proxy(
            hostname, servname, hints, res, netcontext->app_netid);
    if (gai_error != EAI_SYSTEM) {
            return gai_error;
    }
#endif
    //...
}

android_getaddrinfofornet 调用android_getaddrinfofornetcontext,而android_getaddrinfofornetcontext调用了 android_getaddrinfo_proxy 去连接到 DnsProxyListener ,并构造一个 getaddrinfo 命令下发。接下来具体看一下 android_getaddrinfo_proxy。

//bionic/libc/dns/net/getaddrinfo.c
static int android_getaddrinfo_proxy(
    const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **res, unsigned netid)
{
        //...
        FILE* proxy = fdopen(__netdClientDispatch.dnsOpenProxy(), "r+");
        if (proxy == NULL) {
                return EAI_SYSTEM;
        }
        netid = __netdClientDispatch.netIdForResolv(netid);

        // Send the request.
        if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d %u",
                    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,
                    netid) < 0) {
         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值