Sofia-SIP辅助文档八 - Sofia SIP用户代理库 - "sresolv" - 异步DNS解析

时间:2022-06-02 05:41:17

http://sofia-sip.sourceforge.net/refdocs/sresolv/index.html翻译自官网的这张网页。

有一个博客系列,对于理解DNS有帮助。http://yuelei.blog.51cto.com/202879/106921


模块信息

Sofia sresolv模块是一个异步DNS解析器,并且支持新的EDNS扩展。<sofia-sip/sresolv.h>头文件中申明了利用su_root_t使用本模块的接口。

另一套接口在<sofia-resolv/sres.h>、<sofia-resolv/sres_record.h>、<sofia-resolv/sres_async.h>和<sofia-resolv/sres_cache.h>等头文件中被定义。

参考:
RFC 1034RFC 1035RFC 1886RFC 2671RFC 2782RFC 2915
联系人:
Pekka Pessi < Pekka.Pessi@nokia-email.address.hidden>
状态:
Sofia SIP Core library
许可:
LGPL
待开发事项:
缓存策略和缓存定位

缓存策略必须能够解析那些非授权条目(non-authorized answer简单的理解就是指cache中的answer)。

为何要创建Sofia Resolver?

通常那些开源DNS库要么是同步方式(阻塞了调用线程去做查询),或者它们只能解析主机名。SIP协议除了使用A或AAAA记录(参考:https://www.ezloo.com/2011/04/a_mx_cname_txt_aaaa_ns.html)还使用NAPTR和SRV记录(参考:http://anders.com/cms/264),因此这些DNS库并不完全适合SIP应用。

Sofia resolver使用通常的DNS配置。在类Unix系统下是/etc/resolv.conf文件;在windows系统下是registry注册表。Sofia resolvers在发现配置发生了变化后会重新加载这些配置。

除了配置文件,环境变量SRES_OPTIONS和RES_OPTIONS也可以用来改变解析器的行为。

使用Sofia Resolver

Sofia resolver通常情况下以异步方式运行。即,生成一个请求,发送给DNS服务器,并且立即返回给调用者。当响应收到后请求即结束,sresolv通过一个回调函数通知应用程序。

应用程序可在解析器使用的文件描述符上显式使用poll或select然后调用底层的函数,或者使用su_root(一个指向su_root_t对象的指针)。第三种方式是同步使用解析器:sres_blocking_query()。

sresolv内部使用了一个缓存。查询函数将记录存入缓存。使用缓存就像是直接从DNS服务器得到的结果一样。

请注意,必须为每个线程创建各自独立的解析器对象。但所有的解析器将共享这个缓存。

<sofia-sip/sresolv.h>头文件中的接口

<sofia-sip/sresolv.h>头文件中定义了由su_root驱动的Sofia resolver,这种方式的使用非常简单。创建函数中提供的root对象会安排调用sres_query()sres_query_sockaddr()函数中的回调函数。

#include <sofia-sip/sresolv.h>

sres_resolver_t *sres_resolver_create(su_root_t *root,
char const *resolv_conf,
tag_type_t, tag_value_t, ...);

int sres_resolver_destroy(sres_resolver_t *res);

发送DNS查询

这里的第二部分的接口在发送DNS查询时使用:

sres_query_t *sres_query(sres_resolver_t *res,
sres_answer_f *callback,
sres_context_t *context,
int socket,
uint16_t type,
char const *domain);

sres_query_t *sres_query_sockaddr(sres_resolver_t *res,
sres_answer_f *callback,
sres_context_t *context,
int socket,
uint16_t type,
struct sockaddr const *addr);

void sres_query_bind(sres_query_t *q,
sres_answer_f *callback,
sres_context_t *context);

处理DNS记录

第三部分的接口被用来处理DNS请求返回的记录或者存储在缓存中的记录:

sres_record_t **sres_cached_answers(sres_resolver_t *res,
uint16_t type,
char const *domain);

sres_record_t **sres_cached_answers_sockaddr(sres_resolver_t *res,
uint16_t type,
struct sockaddr const *addr);

int sres_sort_answers(sres_resolver_t *res, sres_record_t **answers);

int sres_filter_answers(sres_resolver_t *sres, sres_record_t **answers,
uint16_t type);

void sres_free_answers(sres_resolver_t *res, sres_record_t **answers);

void sres_free_answer(sres_resolver_t *res, sres_record_t *answer);

<sofia-resolv/sres.h>头文件中的接口

Sofia resolver的通用接口在<sofia-resolv/sres.h>文件中定义。第一部分包括处理resolver对象的函数:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <sofia-resolv/sres.h>

sres_resolver_t *sres_resolver_new(char const *resolv_conf_path);

sres_resolver_t *sres_resolver_new_with_cache(char const *conf_file_path,
sres_cache_t *cache,
char const *options, ...);

sres_resolver_t *sres_resolver_ref(sres_resolver_t *res);
void sres_resolver_unref(sres_resolver_t *res);

sres_resolver_t *sres_resolver_copy(sres_resolver_t *);

void *sres_resolver_set_userdata(sres_resolver_t *res, void *userdata);
void *sres_resolver_get_userdata(sres_resolver_t const *res);

同步方式使用Sofia Resolver

阻塞方式的接口也在<sofia-resolv/sres.h>头文件中定义,如此一来可以采用同步方式使用Sofia resolver。即,发起DNS查询的函数直到查询请求返回了或者超时才会返回。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <sofia-resolv/sres.h>

int sres_blocking_query(sres_resolver_t *res,
uint16_t type,
char const *domain,
sres_record_t ***return_records);

int sres_blocking_query_sockaddr(sres_resolver_t *res,
uint16_t type,
struct sockaddr const *addr,
sres_record_t ***return_records);

<sofia-resolv/sres_async.h>头文件中的异步接口

如果不使用su_root_t对象,仍然可以采用异步方式使用Sofia resolver。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <sofia-resolv/sres_async.h>

sres_async_t *sres_resolver_set_async(sres_resolver_t *res,
sres_update_f *update,
sres_async_t *async,
int update_all);
sres_async_t *sres_resolver_get_async(sres_resolver_t const *res,
sres_update_f *update);

int sres_resolver_sockets(sres_resolver_t const *res, int *sockets, int n);
void sres_resolver_timer(sres_resolver_t *, int socket);

int sres_resolver_receive(sres_resolver_t *res, int socket);
int sres_resolver_error(sres_resolver_t *res, int socket);

以下是一小段代码,演示基于su_root_t对象如何使用Sofia resolver:

#define SRES_CONTEXT_T struct context

#include <sofia-sip/sresolv.h>

...

struct context
{
...
su_root_t *root;
sres_resolver_t *sres;
sres_query_t *query;
...
} *context;

...

context->sres = sres_resolver_create(context->root, NULL, TAG_END());

...

sres_record_t *results;

results = sres_cached_answers(context->sres, sres_type_naptr, domain);
if (results) {
process_natpr(context, NULL, results);
}
else {
context->query = sres_query(context->sres,
process_natpr, context,
sres_type_naptr, domain);
if (!context->query)
process_naptr(context, NULL, NULL);
}
}
...

void process_natpr(sres_context_t *context,
sres_query_t *q,
sres_record_t *answers[])
{
sres_sort_answers(context->sres, answers);

...

sres_free_answers(context->sres, answers);
}