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

来源:互联网 发布:mac 不显示隐藏文件 编辑:程序博客网 时间:2024/05/29 16:38


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 255struct evdns_server_question {int type;int dns_question_class;char name[1];};

flags字段包含请求中设置的DNS标志;nquestions字段是请求中的问题数;questionsevdns_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);

上述函数为请求 reqDNS 回应的结果节添加一个RR(类型分别为AAAAACNAME) 。各个函数中,name是要为之添加结果的主机名,ttl是以秒为单位的存活时间。对于AAAAA记录,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域的地址)中的一个,而且只能提供一个 ,以指示为回应提供哪种地址。hostnamePTR查询的答案。

 

接口

#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 1int 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);

这个函数为请求 reqDNS 回应添加任意RRsection字段指示添加到哪一节,其值应该是某个EVDNS_*_SECTIONname参数是RR的名字字段。type参数是RR的类型字段 ,其值应该是某个EVDNS_TYPE_*dns_class参数是RR的类别字段。RRrdatardlength字段将从data处的datalen字节中产生。如果is_nametruedata将被编码成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 0x080void 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 mostoperating 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 to127.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 dothis reliably, though, so you should think hard before putting twoquestions 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
原创粉丝点击