概述

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);}

更多相关文章

  1. 20172324 2017-2018《程序设计与数据结构》第十一周学习总结
  2. Android项目结构和AndroidManifest.xml
  3. 服务器端返回给客户端的数据格式
  4. Android 的源代码结构
  5. android开发(二):android结构

随机推荐

  1. android样式,主题设置心得
  2. 自定义View系列教程02--onMeasure源码详
  3. Android学习小结
  4. 关于Android(安卓)LCD和键盘背光亮度 .
  5. Android(安卓)编译系统 --- 版本信息
  6. [置顶] Android应用开发 第二讲:Android系
  7. [转载]Android及Robotium学习总结【环境
  8. Android RIL 架构
  9. Android 4.4 (KitKat) SMS Apis Change—
  10. 深入剖析Android消息机制