现在的位置: 首页 > 综合 > 正文

Libevent参考手册:使用libevent的DNS:高层和底层功能 (2011-08-11 21:13:16)

2012年08月08日 ⁄ 综合 ⁄ 共 4268字 ⁄ 字号 评论关闭

Libevent参考手册:使用libevent的DNS:高层和底层功能

 (2011-08-11 21:13:16)

标签: 

dns

 

it

 

libevent

 

网络编程

分类: Libevent

译自http://www.wangafu.net/~nickm/libevent-book/Ref9_dns.html

libevent提供了少量用于解析DNS名字的API,以及用于实现简单DNS服务器的机制。

我们从用于名字查询的高层机制开始介绍,然后介绍底层机制和服务器机制。

注意

libevent当前的DNS客户端实现存在限制:不支持TCP查询、DNSSec以及任意记录类型。未来版本的libevent会修正这些限制。

预备:可移植的阻塞式名字解析

为移植已经使用阻塞式名字解析的程序,libevent提供了标准getaddrinfo()接口的可移植实现。对于需要运行在没有getaddrinfo()函数,或者getaddrinfo()不像我们的替代函数那样遵循标准的平台上的程序,这个替代实现很有用。

getaddrinfo()接口由RFC 3493的6.1节定义。关于libevent如何不满足其一致性实现的概述,请看下面的“兼容性提示”节。

接口

Libevent参考手册:使用libevent的DNS:高层和底层功能

evutil_getaddrinfo()函数试图根据hints给出的规则,解析指定的nodename和servname,建立一个evutil_addrinfo结构体链表,将其存储在*res中。成功时函数返回0,失败时返回非零的错误码。

必须至少提供nodename和servname中的一个。如果提供了nodename,则它是IPv4字面地址(如127.0.0.1)、IPv6字面地址(如::1),或者是DNS名字(如www.example.com)。如果提供了servname,则它是某网络服务的符号名(如https),或者是一个包含十进制端口号的字符串(如443)。

如果不指定servname,则*res中的端口号将是零。如果不指定nodename,则*res中的地址要么是localhost(默认),要么是“任意”(如果设置了EVUTIL_AI_PASSIVE)。

hints的ai_flags字段指示evutil_getaddrinfo如何进行查询,它可以包含0个或者多个以或运算连接的下述标志:

EVUTIL_AI_PASSIVE

这个标志指示将地址用于监听,而不是连接。通常二者没有差别,除非nodename为空:对于连接,空的nodename表示localhost(127.0.0.1或者::1);而对于监听,空的nodename表示任意(0.0.0.0或者::0)。

EVUTIL_AI_CANONNAME

如果设置了这个标志,则函数试图在ai_canonname字段中报告标准名称。

EVUTIL_AI_NUMERICHOST

如果设置了这个标志,函数仅仅解析数值类型的IPv4和IPv6地址;如果nodename要求名字查询,函数返回EVUTIL_EAI_NONAME错误。

EVUTIL_AI_NUMERICSERV

如果设置了这个标志,函数仅仅解析数值类型的服务名。如果servname不是空,也不是十进制整数,函数返回EVUTIL_EAI_NONAME错误。

EVUTIL_AI_V4MAPPED

这个标志表示,如果ai_family是AF_INET6,但是找不到IPv6地址,则应该以v4映射(v4-mapped)型IPv6地址的形式返回结果中的IPv4地址。当前evutil_getaddrinfo()不支持这个标志,除非操作系统支持它。

EVUTIL_AI_ALL

如果设置了这个标志和EVUTIL_AI_V4MAPPED,则无论结果是否包含IPv6地址,IPv4地址都应该以v4映射型IPv6地址的形式返回。当前evutil_getaddrinfo()不支持这个标志,除非操作系统支持它。

EVUTIL_AI_ADDRCONFIG

如果设置了这个标志,则只有系统拥有非本地的IPv4地址时,结果才包含IPv4地址;只有系统拥有非本地的IPv6地址时,结果才包含IPv6地址。

hints的ai_family字段指示evutil_getaddrinfo()应该返回哪个地址。字段值可以是AF_INET,表示只请求IPv4地址;也可以是AF_INET6,表示只请求IPv6地址;或者用AF_UNSPEC表示请求所有可用地址。

hints的ai_socktype和ai_protocol字段告知evutil_getaddrinfo()将如何使用返回的地址。这两个字段值的意义与传递给socket()函数的socktype和protocol参数值相同。

成功时函数新建一个evutil_addrinfo结构体链表,存储在*res中,链表的每个元素通过ai_next指针指向下一个元素。因为链表是在堆上分配的,所以需要调用evutil_freeaddrinfo()进行释放。

 

如果失败,函数返回数值型的错误码:

EVUTIL_EAI_ADDRFAMILY

请求的地址族对nodename没有意义。

EVUTIL_EAI_AGAIN

名字解析中发生可以恢复的错误,请稍后重试。

EVUTIL_EAI_FAIL

名字解析中发生不可恢复的错误:解析器或者DNS服务器可能已经崩溃。

EVUTIL_EAI_BADFLAGS

hints中的ai_flags字段无效。

EVUTIL_EAI_FAMILY

不支持hints中的ai_family字段。

EVUTIL_EAI_MEMORY

回应请求的过程耗尽内存。

EVUTIL_EAI_NODATA

请求的主机不存在。

EVUTIL_EAI_SERVICE

请求的服务不存在。

EVUTIL_EAI_SOCKTYPE

不支持请求的套接字类型,或者套接字类型与ai_protocol不匹配。

EVUTIL_EAI_SYSTEM

名字解析中发生其他系统错误,更多信息请检查errno。

EVUTIL_EAI_CANCEL

应用程序在解析完成前请求取消。evutil_getaddrinfo()函数从不产生这个错误,但是后面描述的evdns_getaddrinfo()可能产生这个错误。

 

调用evutil_gai_strerror()可以将上述错误值转化成描述性的字符串。

注意

如果操作系统定义了addrinfo结构体,则evutil_addrinfo仅仅是操作系统内置的addrinfo结构体的别名。类似地,如果操作系统定义了AI_*标志,则相应的EVUTIL_AI_*标志仅仅是本地标志的别名;如果操作系统定义了EAI_*错误,则相应的EVUTIL_EAI_*只是本地错误码的别名。

示例:解析主机名,建立阻塞的连接

Libevent参考手册:使用libevent的DNS:高层和底层功能

上述函数和常量是2.0.3-alpha版本新增加的,声明在event2/util.h中。

使用evdns_getaddrinfo()的非阻塞式名字解析

通常的getaddrinfo(),以及上面的evutil_getaddrinfo()的问题是,它们是阻塞的:调用线程必须等待函数查询DNS服务器,等待回应。对于libevent,这可能不是期望的行为。

对于非阻塞式应用,libevent提供了一组函数用于启动DNS请求,让libevent等待服务器回应。

接口

Libevent参考手册:使用libevent的DNS:高层和底层功能

除了不会阻塞在DNS查询上,而是使用libevent的底层DNS机制进行查询外,evdns_getaddrinfo()和evutil_getaddrinfo()是一样的。因为函数不是总能立即返回结果,所以需要提供一个evdns_getaddrinfo_cb类型的回调函数,以及一个给回调函数的可选的用户参数。

此外,调用evdns_getaddrinfo()还要求一个evdns_base指针。evdns_base结构体为libevent的DNS解析器保持状态和配置。关于如何获取evdns_base指针,请看下一节。

如果失败或者立即成功,函数返回NULL。否则,函数返回一个evdns_getaddrinfo_request指针。在解析完成之前可以随时使用evdns_getaddrinfo_cancel()和这个指针来取消解析。

注意:不论evdns_getaddrinfo()是否返回NULL,是否调用了evdns_getaddrinfo_cancel(),回调函数总是会被调用。

evdns_getaddrinfo()内部会复制nodename、servname和hints参数,所以查询进行过程中不必保持这些参数有效。

示例:使用evdns_getaddrinfo()的非阻塞查询

Libevent参考手册:使用libevent的DNS:高层和底层功能

 

Libevent参考手册:使用libevent的DNS:高层和底层功能

上述函数是2.0.3-alpha版本新增加的,声明在event2/dns.h中。

创建和配置evdns_base

使用evdns进行非阻塞DNS查询之前需要配置一个evdns_base。evdns_base存储名字服务器列表和DNS配置选项,跟踪活动的、进行中的DNS请求。

接口

Libevent参考手册:使用libevent的DNS:高层和底层功能

成功时evdns_base_new()返回一个新建的evdns_base,失败时返回NULL。如果initialize参数为true,函数试图根据操作系统的默认值配置evdns_base;否则,函数让evdns_base为空,不配置名字服务器和选项。

可以用evdns_base_free()释放不再使用的evdns_base。如果fail_request参数为true,函数会在释放evdns_base前让所有进行中的请求使用取消错误码调用其回调函数。

使用系统配置初始化evdns

如果需要更多地控制evdns_base如何初始化,可以为evdns_base_new()的initialize参数传递0,然后调用下述函数。

接口

Libevent参考手册:使用libevent的DNS:高层和底层功能

evdns_base_resolv_conf_parse()函数扫描resolv.conf格式的文件filename,从中读取flags指示的选项(关于resolv.conf文件的更多信息,请看Unix手册)。

DNS_OPTION_SEARCH

请求从resolv.conf文件读取domain和search字段以及ndots选项,使用它们来确定使用哪个域(如果存在)来搜索不是全限定的主机名。

DNS_OPTION_NAMESERVERS

请求从resolv.conf中读取名字服务器地址。

抱歉!评论已关闭.