Android DNS之DNS参数设置
16lz
2021-01-23
概述
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};
_resolv_set_nameservers_for_net()
@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查询之前,会向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);}
更多相关文章
- 20172324 2017-2018《程序设计与数据结构》第十一周学习总结
- Android项目结构和AndroidManifest.xml
- 服务器端返回给客户端的数据格式
- Android 的源代码结构
- android开发(二):android结构