LibEvent中文帮助文档--第16章【DNS服务器接口】



LiEvent中文帮助文档--第16章【DNS服务器接口】

   返回主目录

Libevent

快速可移植非阻塞式网络编程

 

 

修订历史

版本

日期

作者

备注

V1.0

2016-11-15

周勇

Libevent编程中文帮助文档

 

文档是2009-2012年由Nick-Mathewson基于Attribution-Noncommercial-Share Alike许可协议3.0创建,未来版本将会使用约束性更低的许可来创建.

此外,本文档的源代码示例也是基于BSD"3条款""修改"条款.详情请参考BSD文件全部条款.本文档最新下载地址:

英文:http://libevent.org/

中文:http://blog.csdn.net/zhouyongku/article/details/53431750

请下载并运行"gitclonegit://github.com/nmathewson/libevent- book.git"获取本文档描述的最新版本源码.

<<上一章>>

libevent为实现不重要的 DNS服务器,响应通过 UDP传输的 DNS请求提供了简单机制。本节要求读者对 DNS协议有一定的了解。

 

16.1创建和关闭DNS服务器

 

接口

struct evdns_server_port* evdns_add_server_port_with_base(struct event_base* base,
			evutil_socket_t socket,
			int flags,
			evdns_request_callback_fn_type callback,
			void* user_data);
typedef void ( * evdns_request_callback_fn_type)(
			struct evdns_server_request* request,
			void* user_data);
void evdns_close_server_port(struct evdns_server_port* port);

要开始监听 DNS请求,调用 evdns_add_server_port_with_base()。函数要求用于事件处理的event_base、用于监听的UDP 套接字、可用的标志(现在总是0) 、一个收到DNS 查询 时 要 调 用 的 回 调 函 数 , 以 及 要 传 递 给 回 调 函 数 的 用 户 数 据 指 针 。 函 数 返 回evdns_server_port对象。

 

使用 DNS服务器完成工作后,需要调用 evdns_close_server_port()。

 

evdns_add_server_port_with_base()是 2.0.1-alpha版 本 引 入 的 , 而evdns_close_server_port()则由1.3版本引入。

 

16.2检测DNS请求

不幸的是,当前 libevent没有提供较好的获取 DNS请求的编程接口,用户需要包含event2/dns_struct.h文件,查看 evdns_server_request结构体。

 

未来版本的 libevent应该会提供更好的方法。

 

接口

struct evdns_server_request 
{
int flags;
int nquestions;
struct evdns_server_question
** questions;
};
#define EVDNS_QTYPE_AXFR 252
#define EVDNS_QTYPE_ALL 255
struct evdns_server_question 
{
int type;
int dns_question_class;
char name[1];
};

flags字段包含请求中设置的 DNS标志;nquestions字段是请求中的问题数;questions是evdns_server_question结构体指针数组。每个 evdns_server_question包含请求的资源类型(请看下面的 EVDNS_*_TYPE宏列表) 、请求类别(通常为 EVDNS_CLASS_INET) ,以及请求的主机名。

 

这些结构体在1.3版本中引入,但是1.4版之前的名字是dns_question_class。名字中的“class”会让C++用户迷惑。仍然使用原来的“class”名字的C 程序将不能在未来发布版本中正确工作。

 

接口

int evdns_server_request_get_requesting_addr(struct evdns_server_request* req,
			struct sockaddr* sa, 
			int addr_len);

有时 候 需 要 知道 某 特 定 DNS 请 求 来 自 何 方 , 这 时 调 用evdns_server_request_get_requesting_add()就可以了。 应该传入有足够存储空间以容量地址的sockaddr:建议使用sockaddr_storage 结构体。

 

这个函数在1.3版本中引入.

 

16.3响应DNS请求

DNS服务器收到每个请求后,会将请求传递给用户提供的回调函数,还带有用户数据指针 。回调函数必须响应请求或者忽略请求,或者确保请求最终会被回答或者忽略。回应请求前可以向回应中添加一个或者多个答案:

 

接口

int evdns_server_request_add_a_reply(struct evdns_server_request* req,
			const char* name, 
			int n, 
			const void * addrs, 
			int ttl);
int evdns_server_request_add_aaaa_reply(struct evdns_server_request* req,
			const char* name, 
			int n, 
			const void * addrs, 
			int ttl);
int evdns_server_request_add_cname_reply(struct evdns_server_request* req,
			const char* name, 
			const char * cname, 
			int ttl);

上述函数为请求 req的 DNS 回应的结果节添加一个 RR(类型分别为 A、AAAA和CNAME) 。各个函数中,name是要为之添加结果的主机名,ttl是以秒为单位的存活时间。对于 A和AAAA记录,n是要添加的地址个数,addrs是到原始地址的指针:对于 A记录,是以 n*4字节序列格式给出的IPv4地址; 对于AAAA 记录, 是以n*16字节序列格式给出的IPv6地址。

 

成功时函数返回0,失败时返回-1。

 

接口

int evdns_server_request_add_ptr_reply(struct evdns_server_request* req,
			struct in_addr* in, 
			const char * inaddr_name, 
			const char * hostname,
			int ttl);

这个函数为请求的结果节添加一个 PTR记录。参数 req 和 ttl 跟上面的函数相同。必须提供in(一个IPv4地址)和inaddr_name(一个arpa 域的地址)中的一个,而且只能提供一个 ,以指示为回应提供哪种地址。hostname是 PTR查询的答案。

 

接口

#define EVDNS_ANSWER_SECTION 0
#define EVDNS_AUTHORITY_SECTION 1
#define EVDNS_ADDITIONAL_SECTION 2
#define EVDNS_TYPE_A 1
#define EVDNS_TYPE_NS 2
#define EVDNS_TYPE_CNAME 5
#define EVDNS_TYPE_SOA 6
#define EVDNS_TYPE_PTR 12
#define EVDNS_TYPE_MX 15
#define EVDNS_TYPE_TXT 16
#define EVDNS_TYPE_AAAA 28
#define EVDNS_CLASS_INET 1
int evdns_server_request_add_reply(struct evdns_server_request* req,
			int section, 
			const char* name, 
			int type, 
			int dns_class, 
			int ttl,
			int datalen, 
			int is_name, 
			const char* data);

这个函数为请求 req的 DNS 回应添加任意 RR。section字段指示添加到哪一节,其值应该是某个 EVDNS_*_SECTION。name参数是 RR的名字字段。type参数是 RR的类型字段 ,其值应该是某个 EVDNS_TYPE_*。dns_class参数是 RR的类别字段。RR的 rdata和rdlength字段将从 data处的 datalen字节中产生。如果 is_name为 true,data将被编码成DNS名字(例如,使用 DNS名字压缩) 。否则,data将被直接包含到 RR中。

 

接口

int evdns_server_request_respond(struct evdns_server_request* req, int err);
int evdns_server_request_drop(struct evdns_server_request* req);

evdns_server_request_respond()函数为请求发送DNS 回应,带有用户添加的所有RR, 以及错误码err。 如果不想回应某个请求, 可以调用evdns_server_request_drop()来忽略请求 ,释放请求关联的内存和结构体。

 

接口

#define EVDNS_FLAGS_AA 0x400
#define EVDNS_FLAGS_RD 0x080
void evdns_server_request_set_flags(struct evdns_server_request* req,int flags);

如果要为回应消息设置任何标志,可以在发送回应前的任何时候调用这个函数。除了 evdns_server_request_set_flags()首次在2.0.1-alpha版本中出现外,本节描述的所有函数都在1.3版本中引入。

 

16.4DNS服务器示例

 

接口

#include <event2/dns.h>
#include <event2/dns_struct.h>
#include <event2/util.h>
#include <event2/event.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
/* Let’s try binding to 5353. Port 53 is more traditional, but on most
operating systems it requires root privileges.*/
#define LISTEN_PORT 5353
#define LOCALHOST_IPV4_ARPA "1.0.0.127.in-addr.arpa"
#define LOCALHOST_IPV6_ARPA ("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0." \"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa")
const ev_uint8_t LOCALHOST_IPV4[] = { 127, 0, 0, 1 };
const ev_uint8_t LOCALHOST_IPV6[] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1 };
#define TTL 4242
/* This toy DNS server callback answers requests for localhost (mapping it to
127.0.0.1 or ::1) and for 127.0.0.1 or ::1 (mapping them to localhost).*/
void server_callback(struct evdns_server_request* request, void * data)
{
	int i;
	int error=DNS_ERR_NONE;
	/* We should try to answer all the questions. Some DNS servers don’t do
	this reliably, though, so you should think hard before putting two
	questions in one request yourself.*/
	for (i=0; i < request->nquestions; ++i) 
	{
		const struct evdns_server_question* q = request->questions[i];
		int ok=-1;
		/* We don’t use regular strcasecmp here, since we want a locale-
		independent comparison.*/
		if (0 == evutil_ascii_strcasecmp(q->name, "localhost")) 
		{
			if (q->type == EVDNS_TYPE_A)
				ok = evdns_server_request_add_a_reply(request,
					q->name, 
					1, 
					LOCALHOST_IPV4, 
					TTL);
			else if (q->type == EVDNS_TYPE_AAAA)
				ok = evdns_server_request_add_aaaa_reply(request,
					q->name, 
					1, 
					LOCALHOST_IPV6, 
					TTL);
		} 
		else if (0 == evutil_ascii_strcasecmp(q->name, LOCALHOST_IPV4_ARPA)) 
		{
			if (q->type == EVDNS_TYPE_PTR)
			ok = evdns_server_request_add_ptr_reply(request, 
					NULL, 
					q->name, 
					"LOCALHOST", 
					TTL);
		} 
		else if (0 == evutil_ascii_strcasecmp(q->name, LOCALHOST_IPV6_ARPA)) 
		{
			if (q->type == EVDNS_TYPE_PTR)
			ok = evdns_server_request_add_ptr_reply(request, 
					NULL, 
					q->name, 
					"LOCALHOST", 
					TTL);
		} 
		else 
		{
			error = DNS_ERR_NOTEXIST;
		}
		if (ok<0 && error==DNS_ERR_NONE)
			error = DNS_ERR_SERVERFAILED;
	}
	/* Now send the reply.*/
	evdns_server_request_respond(request, error);
}
int main(int argc, char** argv)
{
	struct event_base* base;
	struct evdns_server_port* server;
	evutil_socket_t server_fd;
	struct sockaddr_in listenaddr;
	base = event_base_new();
	if (!base)
		return 1;
	server_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (server_fd < 0)
		return 2;
	memset(&listenaddr, 0, sizeof(listenaddr));listenaddr.sin_family = AF_INET;
	listenaddr.sin_port = htons(LISTEN_PORT);
	listenaddr.sin_addr.s_addr = INADDR_ANY;
	if (bind(server_fd, (struct sockaddr * )&listenaddr, sizeof(listenaddr))<0)
		return 3;
	server = evdns_add_server_port_with_base(base, server_fd, 0,
	server_callback, NULL);
	event_base_dispatch(base);
	evdns_close_server_port(server);
	event_base_free(base);
	return 0;
}


<<下一章>>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值