Android DNS Impl - xapool/xapool.github.io GitHub Wiki
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
最后 Posix 类中的 getaddrinfo
方法被声明为一个 native 方法,其实现位于 JNI 层:
libcore/luni/src/main/native/libcore_io_Posix.cpp
这个 natvie Posix_getaddrinfo
方法调用 Bionic libc 标准库中的 getaddrinfo
方法:
bionic/libc/dns/net/getaddrinfo.c
这个就是 Android 的 DNS 客户端了,基于 netbsd,Android 对其进行了一些修改:
- resovle.conf文件的位置不再是/etc/resolv.conf,在Android中改为了/system/etc/resolv.conf
- 从系统属性(SystemProperties)中读取DNS服务器,比如
net.dns1
,net.dns2
等。每一个属性必须包括了DNS服务器的IP地址- 不实现 Name ServiceSwitch
- 在查询时,使用一个随机的查询ID,而非每次自增1
- 在查询时,将本地客户端的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 来完成解析,并且不在对系统属性进行读取。
4.3 之前的版本可以直接设置系统属性 net.dns1
net.dns2
来实现;之后的版本,可以通过 ndc 命令,或者修改底层代码来实现。参考 android 4.3以上修改DNS 及 流程