ConnectivityService会通过netd将DNS参数设置到解析库的cache中,设置接口是_resolv_set_nameservers_for_net(),后续在DNS查询过程中,解析库会从cache中获取设置的DNS服务器地址。
Android中,将DNS信息保存到了resolv_cache_info中,该结构中与DNS有关的信息如下所示:
struct resolv_cache_info {
//网卡的netid
unsigned netid;
//所有的cache_info构成一个列表
struct resolv_cache_info* next;
//设置的DNS服务器地址的数目,即下面nameservers数组中有效数据由几个
int nscount;
//保存设置的DNS服务器地址,当前限制最多可以设置4个DNS服务器地址
char* nameservers[MAXNS];
//转换后的DNS服务器地址信息,用于查询
struct addrinfo* nsaddrinfo[MAXNS];
//见注释,DNS服务器地址每变更一次,该成员的值加1
int revision_id; // # times the nameservers have been replaced
//这两个参数用于域名搜索,具体见hostname(7),Android中基本上不使用,可以忽略
char defdname[MAXDNSRCHPATH];
int dnsrch_offset[MAXDNSRCH+1]; // offsets into defdname
};
@netid:要设置的网卡;DNS服务器地址的设定都是基于网卡的
@servers:DNS服务器地址,字符串格式,最多可以设置4个
@numservers:要设置的DNS服务器地址个数,即servers[]数组的长度
@domains:本地域名,通常为空,用于DNS域名搜索,Android中基本不适用,可以不关注
@params:DNS缓存使用的几个参数
int _resolv_set_nameservers_for_net(unsigned netid, const char** servers, unsigned numservers,
const char *domains, const struct __res_params* params)
{
char sbuf[NI_MAXSERV];
register char *cp;
int *offset;
struct addrinfo* nsaddrinfo[MAXNS];
//要设置的DNS服务器地址不能超过MAXNS,当前为4个
if (numservers > MAXNS) {
XLOG("%s: numservers=%u, MAXNS=%u", __FUNCTION__, numservers, MAXNS);
return E2BIG;
}
//下面这段逻辑对要设置的DNS服务器地址进行一种简单的校验,这种校验只是调用getaddinfo()转换一下而已
// Parse the addresses before actually locking or changing any state, in case there is an error.
// As a side effect this also reduces the time the lock is kept.
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
//必须是数字类型
.ai_flags = AI_NUMERICHOST
};
//该地址的53号端口是否有对应的服务
snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
for (unsigned i = 0; i < numservers; i++) {
// The addrinfo structures allocated here are freed in _free_nameservers_locked().
//服务为sbuf,不为空,这会校验指定的地址的端口53是否存在服务
int rt = getaddrinfo(servers[i], sbuf, &hints, &nsaddrinfo[i]);
//所设置的DNS服务器地址必须全部正确,只要有一个有错误,那么设置失败
if (rt != 0) {
for (unsigned j = 0 ; j < i ; j++) {
freeaddrinfo(nsaddrinfo[j]);
nsaddrinfo[j] = NULL;
}
XLOG("%s: getaddrinfo(%s)=%s", __FUNCTION__, servers[i], gai_strerror(rt));
return EINVAL;
}
}
//_res_cache_init()在进程内只执行一次
pthread_once(&_res_cache_once, _res_cache_init);
pthread_mutex_lock(&_res_cache_list_lock);
//如果该netid没有对应的resolv_cache_info,那么创建一个
_get_res_cache_for_net_locked(netid);
//获取该netid对应的resolv_cache_info,如果没有,那么在上一步中应该已经创建
struct resolv_cache_info* cache_info = _find_cache_info_locked(netid);
if (cache_info != NULL) {
uint8_t old_max_samples = cache_info->params.max_samples;
//如果有设置params,则使用设置的,否则使用默认的,该参数的使用见“DNS cache机制”
if (params != NULL) {
cache_info->params = *params;
} else {
_resolv_set_default_params(&cache_info->params);
}
//如果要设置的DNS服务器地址和当前保存的不相等,那么需要刷新
if (!_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
//把旧的DNS服务器地址信息释放掉,然后添加新的,这几句才是这个函数最核心的内容
_free_nameservers_locked(cache_info);
unsigned i;
for (i = 0; i < numservers; i++) {
cache_info->nsaddrinfo[i] = nsaddrinfo[i];
cache_info->nameservers[i] = strdup(servers[i]);
XLOG("%s: netid = %u, addr = %s\n", __FUNCTION__, netid, servers[i]);
}
//配置的DNS服务器地址个数
cache_info->nscount = numservers;
// Clear the NS statistics because the mapping to nameservers might have changed.
//清除掉所有的统计信息
_res_cache_clear_stats_locked(cache_info);
// increment the revision id to ensure that sample state is not written back if the
// servers change; in theory it would suffice to do so only if the servers or
// max_samples actually change, in practice the overhead of checking is higher than the
// cost, and overflows are unlikely
//修正id加1,表示该cache_info结构的DNS信息发生过一次变更
++cache_info->revision_id;
} else if (cache_info->params.max_samples != old_max_samples) {
// If the maximum number of samples changes, the overhead of keeping the most recent
// samples around is not considered worth the effort, so they are cleared instead. All
// other parameters do not affect shared state: Changing these parameters does not
// invalidate the samples, as they only affect aggregation and the conditions under
// which servers are considered usable.
_res_cache_clear_stats_locked(cache_info);
++cache_info->revision_id;
}
// Always update the search paths, since determining whether they actually changed is
// complex due to the zero-padding, and probably not worth the effort. Cache-flushing
// however is not // necessary, since the stored cache entries do contain the domain, not
// just the host name.
// code moved from res_init.c, load_domain_search_list
//这部分代码用于设置DNS搜索相关的两个成员,Android中基本不使用,可以忽略
strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
*cp = '\0';
cp = cache_info->defdname;
offset = cache_info->dnsrch_offset;
while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
while (*cp == ' ' || *cp == '\t') /* skip leading white space */
cp++;
if (*cp == '\0') /* stop if nothing more to do */
break;
*offset++ = cp - cache_info->defdname; /* record this search domain */
while (*cp) { /* zero-terminate it */
if (*cp == ' '|| *cp == '\t') {
*cp++ = '\0';
break;
}
cp++;
}
}
*offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
}
pthread_mutex_unlock(&_res_cache_list_lock);
return 0;
}
从上面可以看出,每个网卡都会有一个resolv_cache_info结构,网卡的DNS地址信息就保存在该结构的nsaddrinfo、nameservers和nscount中。
在DNS解析过程中,解析库在进行最终的DNS查询之前,会向DNS cache查询在指定网卡上面配置的DNS信息,这个任务由_resolv_populate_res_for_net()完成。
void _resolv_populate_res_for_net(res_state statp)
{
if (statp == NULL) {
return;
}
//获取锁
pthread_once(&_res_cache_once, _res_cache_init);
pthread_mutex_lock(&_res_cache_list_lock);
//根据netid查询resolv_cache_info链表
struct resolv_cache_info* info = _find_cache_info_locked(statp->netid);
if (info != NULL) {
int nserv;
struct addrinfo* ai;
XLOG("%s: %u\n", __FUNCTION__, statp->netid);
for (nserv = 0; nserv < MAXNS; nserv++) {
//需要注意的是info->nsaddrinfo保存的就是设置的DNS地址
ai = info->nsaddrinfo[nserv];
if (ai == NULL) {
break;
}
//地址长度一定够,因为nsaddrs[0]的类型为union res_sockaddr_union,是所有地址族的地址的最大结构
if ((size_t) ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
//ext结构不可能为空,因为在res_init()函数中,该结构是无条件被分配的
if (statp->_u._ext.ext != NULL) {
//将DNS地址拷贝到statp中,这里需要注意的是永远都是设置到了ext中,所以statp->nsaddr_list永远不会被使用
memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
//这里地址族为AF_UNSPEC,很重要,该参数的使用见res_send.c中get_nsaddr()
statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
} else {
//没有分配ext结构的情形,那么只能将其拷贝到statp->nsaddr_list中了,这适用于IPv4
if ((size_t) ai->ai_addrlen
<= sizeof(statp->nsaddr_list[0])) {
memcpy(&statp->nsaddr_list[nserv], ai->ai_addr,
ai->ai_addrlen);
} else {
statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
}
}
} else {
XLOG("%s: found too long addrlen", __FUNCTION__);
}
}
//设置DNS服务器地址个数
statp->nscount = nserv;
// now do search domains. Note that we cache the offsets as this code runs alot
// but the setting/offset-computer only runs when set/changed
// WARNING: Don't use str*cpy() here, this string contains zeroes.
//DNS搜索特性相关,Android中基本不使用,忽略
memcpy(statp->defdname, info->defdname, sizeof(statp->defdname));
register char **pp = statp->dnsrch;
register int *p = info->dnsrch_offset;
while (pp < statp->dnsrch + MAXDNSRCH && *p != -1) {
*pp++ = &statp->defdname[0] + *p++;
}
}
pthread_mutex_unlock(&_res_cache_list_lock);
}