Sofia 的sresolv 模块实现了一个带有EDNS扩展功能的异步DNS解析器。在
还有一套可选的备用接口,由下面一组文件共同声明:
相关RFC文档包括:RFC 1034, RFC 1035, RFC 1886, RFC 2671, RFC 2782, RFC 2915。
通常的开源DNS解析库要么是同步的,要么只解析主机名,换句话说,在DNS查询时,要么堵塞线程,要么信息不够。由于SIP协议除了使用常规的A类和AAAA类查询之外,还使用NATPR和SRV查询,那么这类DNS库显然不能满足SIP应用的需求。
Sofia的解析器使用DNS的常规配置。在类Unix系统中,DNS配置存储在/etc/resolv.conf文件里;在Windows系统中,则存储在注册表中。Sofia在监测到配置修改时,会自动重新加载这些配置。
除了配置文件之外,可以利用环境变量 SRES_OPTIONS 和RES_OPTIONS改变解析器的行为。
Sofia解析器通常是异步工作的,换句话说,它生成查询请求并发给DNS服务器,并立即返回调用点。当收到DNS应答消息时,查询结束,sresolv模块通过回调函数通知应用层。
应用层既可以对解析器所使用的文件描述符显式调用poll 或select,并调用驱动函数,也可以使用一个指向 su_root_t对象的指针。此外,还有第三种选择:调用解析器提供的同步接口 sres_blocking_query()。
sresolv模块有一个内部缓存。查询函数会把DNS记录添加到缓存中,使用缓存记录的方式和直接从DNS服务器上查询的方式是一样的。
请注意:你必须为每个使用Sofia解析器的线程单独创建resolver对象。但是,resolver对象之间是可以共享缓存的。
#include
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查询接口:
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应答和记录,并把返回的记录添加到缓存:
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);
#include
#include
#include
#include
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);
#include
#include
#include
#include
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);
不用su_root_t 对象也可以实现异步查询:
#include
#include
#include
#include
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驱动解析器:
#define SRES_CONTEXT_T struct context
#include
...
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);
}