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) {