dns解析相关代码分析
2014-09-26 22:47
435 查看
TrafficServer提供了DNS解析相关的功能,相关模块为iocore/dns。目前dns模块还有很多问题需要解决[1]。
首先从配置管理上分析dns模块。以下给出ts配置文件records.config中与dns相关的配置选项:
第一行配置是否启用splitdns解析,默认不启用。第二行配置dns解析文件。dns解析提供了两种功能,一种是根据默认的配置文件(resolv_conf指定)进行dns解析,另一种是提供splitdns解析功能,对应的配置文件为splitdns.config。如需更进一步了解dns以及splitdns配置相关内容,请参考[2]。
dns针对以上两种解析方式,采用如下设计逻辑进行了实现:通过DNSProcessor+DNSHandler+DNSEntry实现dns解析;通过SplitDNS+SplitDNSConfig+SplitDNSRecord提供额外的splitdns解析功能。
(1)
dns解析遵循ts的异步事件模型的设计原理[3],DNSProcessor借助DNSHandler对一个域名进行解析。
DNSEntry类实现了对一个dns请求的封装。ts内部实现封装了一组与libresolv库相同功能的方法[4],例如ink_res_int等等。
DNSHandler维护了两个队列:
1)DNSConnection con[MAX_NAMED],数组中每一个元素代表一个与dns解析服务器的连接。通常ts只使用一个dns连接,只有需要使用roundrobin功能时才会同时连接多个dns解析服务器。以下代码摘自函数DNSHandler::startEvent:
2)Queue<DNSEntry> entries,保存需要发送出去的dns请求。
根据以上分析,通过DNSHandler::startEvent,与dns解析服务器进行连接。在startEvent函数中,通过下面两行代码异步周期调用DNSHandler::mainEvent:
通过分析DNSHandler::mainEvent方法,以下几行:
通过DNSHandler::recv_dns,遍历DNSHandler维护连接的每一个dns解析服务器,取出dns响应结果。如果DNSHandler维护的DNSEntry队列不为空,则通过static类型的write_dns函数向dns服务器发送dns请求。
下面分析ts是如何将一个dns请求插入到DNSHandler维护的DNSEntry队列中去的。前面提到,dns解析遵循ts的异步事件模型的设计逻辑,这种设计逻辑通常由Processor调度Handler处理一个Continuation状态序列,在这里也不例外。DNSProcessor提供了一组get方法作域名解析,这些get方法都通过内部调用DNSProcessor::getby方法实现域名解析,以DNSProcessor::gethostbyname为例:
通过分析DNSProcessor::getby方法,其实现主要是创建了一个DNSEntry:
DNSEntry::init方法通过构造一个dns请求后,执行:
我们分析DNSEntry::mainEvent方法,这段代码中最主要的是以下两行:
可以看出,DNSEntry::mainEvent方法的主要作用就是将自己插入到对应的DNSHandler中的DNSEntry队列中去。
通过以上分析可以看出,dns解析通过DNSProcessor::getby不断处理每一个dns请求,并构造dns消息体插入到DNSHandler对应的DNSEntry队列中去,DNSHandler定期调用DNSHandler::mainEvent方法从dns服务器中取回dns响应,并根据其维护的DNSEntry队列构造新的dns请求发送给dns服务器。
dns解析是通过DNSProcessor::start方法启用的。该方法首先读取一些配置选项,然后执行DNSProcessor::dns_init加载resolv_conf变量对应的文件(默认为/etc/resolv.conf),最后执行DNSProcessor::open方法,该方法内部调用DNSHandler::startEvent,接下来按照上面描述的流程,一个dns解析服务就启用了。
(2)
splitdns是后来的ts开发团队在原有基础上增加的功能,设计逻辑非常混乱,以TrafficServer2.1.4版本为例,整个代码嵌套在了iocore/dns,iocore/hostdb,以及proxy模块中。从功能上来看,目前splitdns的功能还不是很完善,后续需要很多改进,[1]介绍了后续的一些改进方案。以下大体介绍一下splitdns相关的一些关键代码。
1)SplitDNSConfig通过SplitDNSConfig::startup方法根据配置文件选项选择是否启动splitdns功能,该函数调用SplitDNSConfig::reconfig函数。下面分析reconfig这个函数的内容:
以上第一行读取records.config配置文件中的proxy.config.dns.splitDNS.enabled,如果为0,说明不启用splitdns,返回,如果为1,说明启用,函数继续往下执行:
这行代码基本上完成了加载splitdns.config以及启用splitdns的所有工作。通过分析DNS_table的类型:
ControlMatcher是一个泛型类,一路跟踪其构造函数,直到HostMatcher<Data, Result>::NewEntry
函数体,里面有这么两行关键代码:
由于这里Data属于SplitDNSRecord类型,查看SplitDNSRecord::Init函数,其参数类型为matcher_line,这个结构体实际上保存的就是splitdns.config文件中一行的内容。分析Init函数,该函数的作用就是针对splitdns.config文件的一行,创建一个DNSHandler,并开启一个线程异步调用DNSHandle::startEvent_sdns函数,通过分析DNSHandler::startEvent_sdns函数,可以看出其功能和DNSHandler::startEvent函数的功能是一样的,可能ts开发人员不希望splitdns提供roundrobin等相关功能,所以额外写了一个与之类似的函数。
2)通过SplitDNSConfig::startup方法,当在records.config文件中enable splitdns后,splitdns已经开启。我们接着分析SplitDNSRecord这个结构体,可以看出其内部有一个成员:
继续分析DNSServer这个结构体:
仔细分析SplitDNSRecord::Init函数,可以看出,每一个SplitDNSRecord与splitdns.config文件的一行对应,而SplitDNSRecord包含的DNSServer类型的成员变量的DNSHandler类型成员变量也就与splitdns.config文件的一行绑定在了一起,splitdns.config文件的一行对应的正是一组负责解析一个域名的dns服务器。
3)SplitDNS这个结构体提供了对外的接口。SplitDNS::getDNSRecord解析参数指向的域名,并返回一个DNSServer,从而也间接得到了一个DNSHandler。将这个DNSHandler作为参数调用DNSProcessor::getby,就可以实现从splitdns模块解析一个域名了。
从上面的描述中我们可以看出,通过splitdns解析一个域名。只需要首先配置records.config文件,将CONFIGproxy.config.dns.splitDNS.enabled赋值为1;然后通过
启用splitdns;再通过
获取一个域名对应的splitdns中的DNSHandler(这里x代表一个域名),最后以这个DNSHandler显示调用DNSProcessor::getby函数,就可以实现splitdns解析功能了。
针对splitdns的功能完善问题,TS-435[5]以及TS-541[6]有更为详细的描述。
[1]https://cwiki.apache.org/confluence/display/TS/HostDBandDNS
[2]http://trafficserver.apache.org/docs/v2/admin/ConfigurationFiles下面splitdns.config
[3]http://trafficserver.apache.org/docs/v2/sdk/CreatingTSPlugins.htmlchapter2
[4]http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=/apis/resmkq.htm
[5]https://issues.apache.org/jira/browse/TS-435?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12932442#action_12932442
[6]https://issues.apache.org/jira/browse/TS-541
首先从配置管理上分析dns模块。以下给出ts配置文件records.config中与dns相关的配置选项:
CONFIG proxy.config.dns.splitDNS.enabled INT 0 CONFIG proxy.config.dns.resolv_conf STRING /etc/resolv.conf |
dns针对以上两种解析方式,采用如下设计逻辑进行了实现:通过DNSProcessor+DNSHandler+DNSEntry实现dns解析;通过SplitDNS+SplitDNSConfig+SplitDNSRecord提供额外的splitdns解析功能。
(1)
dns解析遵循ts的异步事件模型的设计原理[3],DNSProcessor借助DNSHandler对一个域名进行解析。
DNSEntry类实现了对一个dns请求的封装。ts内部实现封装了一组与libresolv库相同功能的方法[4],例如ink_res_int等等。
DNSHandler维护了两个队列:
struct DNSHandler: public Continuation { DNSConnection con[MAX_NAMED]; Queue<DNSEntry> entries; }; |
if (dns_ns_rr) {//使用roundrobin int max_nscount = m_res->nscount; if (max_nscount > MAX_NAMED) max_nscount = MAX_NAMED; n_con = 0; for (int i = 0; i < max_nscount; i++) { struct sockaddr_in *sa = &m_res->nsaddr_list[i].sin; ip = sa->sin_addr.s_addr; if (ip) { port = ntohs(sa->sin_port); dnsProcessor.handler->open_con(ip, port, false, n_con); ++n_con; Debug("dns_pas", "opened connection to %d.%d.%d.%d:%d, n_con = %d", DOT_SEPARATED(ip), port, n_con); } } dns_ns_rr_init_down = 0; } else { dnsProcessor.handler->open_con(ip, port); n_con = 1; } |
根据以上分析,通过DNSHandler::startEvent,与dns解析服务器进行连接。在startEvent函数中,通过下面两行代码异步周期调用DNSHandler::mainEvent:
SET_HANDLER(&DNSHandler::mainEvent); dnsProcessor.thread->schedule_every(this, DNS_PERIOD); |
recv_dns(event, e); if (entries.head) write_dns(this); |
下面分析ts是如何将一个dns请求插入到DNSHandler维护的DNSEntry队列中去的。前面提到,dns解析遵循ts的异步事件模型的设计逻辑,这种设计逻辑通常由Processor调度Handler处理一个Continuation状态序列,在这里也不例外。DNSProcessor提供了一组get方法作域名解析,这些get方法都通过内部调用DNSProcessor::getby方法实现域名解析,以DNSProcessor::gethostbyname为例:
inline Action * DNSProcessor::gethostbyname(Continuation * cont, const char *name, DNSHandler * adnsH, int timeout) { return getby(name, 0, T_A, cont, 0, adnsH, timeout); } |
DNSEntry *e = dnsEntryAllocator.alloc(); e->retries = dns_retries; e->init(x, len, type, cont, wait, adnsH, timeout); |
SET_HANDLER((DNSEntryHandler) & DNSEntry::mainEvent); |
dnsH->entries.enqueue(this); write_dns(dnsH); |
通过以上分析可以看出,dns解析通过DNSProcessor::getby不断处理每一个dns请求,并构造dns消息体插入到DNSHandler对应的DNSEntry队列中去,DNSHandler定期调用DNSHandler::mainEvent方法从dns服务器中取回dns响应,并根据其维护的DNSEntry队列构造新的dns请求发送给dns服务器。
dns解析是通过DNSProcessor::start方法启用的。该方法首先读取一些配置选项,然后执行DNSProcessor::dns_init加载resolv_conf变量对应的文件(默认为/etc/resolv.conf),最后执行DNSProcessor::open方法,该方法内部调用DNSHandler::startEvent,接下来按照上面描述的流程,一个dns解析服务就启用了。
(2)
splitdns是后来的ts开发团队在原有基础上增加的功能,设计逻辑非常混乱,以TrafficServer2.1.4版本为例,整个代码嵌套在了iocore/dns,iocore/hostdb,以及proxy模块中。从功能上来看,目前splitdns的功能还不是很完善,后续需要很多改进,[1]介绍了后续的一些改进方案。以下大体介绍一下splitdns相关的一些关键代码。
1)SplitDNSConfig通过SplitDNSConfig::startup方法根据配置文件选项选择是否启动splitdns功能,该函数调用SplitDNSConfig::reconfig函数。下面分析reconfig这个函数的内容:
IOCORE_ReadConfigInt32(gsplit_dns_enabled, "proxy.config.dns.splitDNS.enabled"); if (0 == gsplit_dns_enabled) return; |
params->m_DNSSrvrTable = NEW(new DNS_table("proxy.config.dns.splitdns.filename", modulePrefix, &sdns_dest_tags)); |
typedef ControlMatcher<SplitDNSRecord, SplitDNSResult> DNS_table; |
函数体,里面有这么两行关键代码:
Data *cur_d; errBuf = cur_d->Init(line_info); |
2)通过SplitDNSConfig::startup方法,当在records.config文件中enable splitdns后,splitdns已经开启。我们接着分析SplitDNSRecord这个结构体,可以看出其内部有一个成员:
DNSServer m_servers; |
struct DNSServer { unsigned int x_server_ip[MAXNS]; char x_dns_ip_line[MAXDNAME * 2]; char x_def_domain[MAXDNAME]; char x_domain_srch_list[MAXDNAME]; int x_dns_server_port[MAXNS]; DNSHandler *x_dnsH; }; |
3)SplitDNS这个结构体提供了对外的接口。SplitDNS::getDNSRecord解析参数指向的域名,并返回一个DNSServer,从而也间接得到了一个DNSHandler。将这个DNSHandler作为参数调用DNSProcessor::getby,就可以实现从splitdns模块解析一个域名了。
从上面的描述中我们可以看出,通过splitdns解析一个域名。只需要首先配置records.config文件,将CONFIGproxy.config.dns.splitDNS.enabled赋值为1;然后通过
SplitDNSConfig::startup(); |
if(x && NULL == adnsH && SplitDNSConfig::isSplitDNSEnabled()){ const char *scan = x; for(; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan); ++scan); if('\0' != *scan){ void *pSD = (void *) SplitDNSConfig::acquire(); if(0 != pSD){ void *pDS = ((SplitDNS *) pSD)->getDNSRecord(x); if(0 != pDS){ adnsH = ((DNSServer *) pDS)->x_dnsH; } } } } |
针对splitdns的功能完善问题,TS-435[5]以及TS-541[6]有更为详细的描述。
[1]https://cwiki.apache.org/confluence/display/TS/HostDBandDNS
[2]http://trafficserver.apache.org/docs/v2/admin/ConfigurationFiles下面splitdns.config
[3]http://trafficserver.apache.org/docs/v2/sdk/CreatingTSPlugins.htmlchapter2
[4]http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=/apis/resmkq.htm
[5]https://issues.apache.org/jira/browse/TS-435?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12932442#action_12932442
[6]https://issues.apache.org/jira/browse/TS-541
相关文章推荐
- dns解析相关代码分析
- traffic server dns解析相关代码分析
- suricata 3.1 源码分析34 (dns解析获取相关内容)
- netlink监听网络变化代码(转载)+流程分析(原创+转载)+数据结构以及相关宏的解析(原创)
- JavaScript 解析Json字符串的性能比较分析代码
- 转://嵌入式Linux内核移植相关代码分析(zz)(
- 数据链表的相关代码与分析
- 嵌入式Linux内核移植相关代码分析
- 嵌入式Linux内核移植相关代码分析
- Android键盘系统相关代码分析(1)
- JavaScript 解析Json字符串的性能比较分析代码
- 嵌入式Linux内核移植相关代码分析
- Android视频文件格式解析相关分析
- Xen从启动到运行的调度相关代码分析
- 结合Bootloader的相关知识,并参考ARM的汇编指令,分析下面的Bootloader代码。
- 以太网报文结构分析,与解析代码
- [深入分析BREW机制]:Mod相关概念解析
- uClinux内核移植相关代码分析
- DNS解析操作相关说明
- windows server 2008 R2 DNS 转发上网解析很慢故障分析!