Android DNS Impl - xapool/xapool.github.io GitHub Wiki

目录

Android DNS 解析实现

Framewok 层

InetAddress inetAddress = InetAddress.getByName("www.baidu.com");

以该代码为例,调用过程依次如下。

libcore/luni/src/main/java/java/net/InetAddress.java

上层是由 framework 中的 InetAddress 这个类中的几个方法连续调用:

java.net.InetAddress.getAllByName
java.net.InetAddress.getAllByNameImpl
java.net.InetAddress.lookupHostByName

lookupHostByName 方法会先从 DNS 缓存中查找,若有则返回,没有的话就调用 libcore.os.getaddrinfo 方法,方法在:

libcore/luni/src/main/java/libcore/io/ForwardingOs.java
libcore/luni/src/main/java/libcore/io/Posix.java

JNI 层

最后 Posix 类中的 getaddrinfo 方法被声明为一个 native 方法,其实现位于 JNI 层:

libcore/luni/src/main/native/libcore_io_Posix.cpp

C 层

这个 natvie Posix_getaddrinfo 方法调用 Bionic libc 标准库中的 getaddrinfo 方法:

bionic/libc/dns/net/getaddrinfo.c

这个就是 Android 的 DNS 客户端了,基于 netbsd,Android 对其进行了一些修改:

  1. resovle.conf文件的位置不再是/etc/resolv.conf,在Android中改为了/system/etc/resolv.conf
  2. 从系统属性(SystemProperties)中读取DNS服务器,比如 net.dns1net.dns2 等。每一个属性必须包括了DNS服务器的IP地址
  3. 不实现 Name ServiceSwitch
  4. 在查询时,使用一个随机的查询ID,而非每次自增1
  5. 在查询时,将本地客户端的socket绑定到一个随机端口以增强安全性

版本差异

在 4.3 及其之后的版本,libc 中的 getaddrinfo 方法调用了 android_getaddrinfoforiface 方法,而这个方法又调用了其中的 android_getaddrinfo_proxy 方法,这个方法和 netd 进行交互,按照一定格式往 /dev/socket/dnsproxyd 中写入命令,然后监听等待回答。

InetAddress.getByName ->
    getAllByNameImpl ->
    lookupHostByName ->
        Libcore.os.getaddrinfo ->    // 调用 natvie 方法
            getaddrinfo ->    // bionic/libc/netbsd/net/getaddrinfo.c
                android_getaddrinfoforiface ->
                    android_getaddrinfo_proxy -> // 这里 cache_mode 为 null,而 netd 设置的 ANDROID_DNS_MODE 环境变量只在进程中有效
                        connect    // 这里的 socket name 是 /dev/socket/dnsproxyd,也就是通过 dnsproxd 来和 netd 进程交互
                        fprintf    // 往 dnsproxyd 写 getaddrinfo 命令,接下来就交由 netd 处理 

netd 中的 DnsProxyListener GetAddrInfoHandler 方法会监听 /dev/socket/dnsproxd 中的消息,然后再调用 netbsd 中的 android_getaddrinfoforiface 方法,因为 netd 启动时已经设置了环境标量 ANDROID_DNS_MODE 的值为 local,所以不会走 android_getaddrinfo_proxy 方法了,而是调用了 explore_fqdn。explore_fqdn 又调用 nsdispatch 方法解析地址,nsdispatch 则从文件 /system/etc/hosts 或服务器来获取地址。

而在 4.3 之前,libc 中的 getaddrinfo 方法直接调用了 android_getaddrinfo_proxy 方法,该方法会首先尝试从系统属性中读取 DNS 服务器的 IP 地址,然后使用这个地址进行解析,如果没有设置相关系统属性,则采用 netd 的方式来进行解析。

相比较下,之后的版本完全经由 netd 来完成解析,并且不在对系统属性进行读取。

修改 DNS 服务器

4.3 之前的版本可以直接设置系统属性 net.dns1 net.dns2 来实现;之后的版本,可以通过 ndc 命令,或者修改底层代码来实现。参考 android 4.3以上修改DNS 及 流程

参考

⚠️ **GitHub.com Fallback** ⚠️