unbound模块简要说明

unbound是一个DNS递归解析服务程序,支持DNSSEC、Caching以及pymod扩展。

pymod的链接包含了pymod的基本介绍和一些用pymod实现的小玩具。由于之前用python-libunbound时候被略坑了一下,并且在pythonmod/examples/resip.py中看到了这段话:

# Please note that if this module blocks, by moving to the validator
# to validate or iterator to lookup or spawn a subquery to look up,
# then, other incoming queries are queued up onto this module and
# all of them receive the same reply.
# You can inspect the cache.

由于我的模块有网络操作,必须写成async的或者只能在这里block,我不得不从C代码中寻找我需要的东西。

unbound模块的基本介绍在这里:http://unbound.net/documentation/pythonmod/examples/example0.html。阅读这里可以了解unbound module的大概。我在这里补充一些不可或缺的细节。

可用的模块列表被hardcoded到了services/modstack.c中,初始化检查合法性时会用到util/fptr_wlist.c中的函数。需要手工patch。

模块需要提供这些函数和一个static struct module_func_block yourmodule_block

/**
* Module functionality block
*/
struct module_func_block {
/** text string name of module */
const char* name;

/**
* init the module. Called once for the global state.
* This is the place to apply settings from the config file.
* @param env: module environment.
* @param id: module id number.
* return: 0 on error
*/
int (*init)(struct module_env* env, int id);

/**
* de-init, delete, the module. Called once for the global state.
* @param env: module environment.
* @param id: module id number.
*/
void (*deinit)(struct module_env* env, int id);

/**
* accept a new query, or work further on existing query.
* Changes the qstate->ext_state to be correct on exit.
* @param ev: event that causes the module state machine to
* (re-)activate.
* @param qstate: the query state.
* Note that this method is not allowed to change the
* query state 'identity', that is query info, qflags,
* and priming status.
* Attach a subquery to get results to a different query.
* @param id: module id number that operate() is called on.
* @param outbound: if not NULL this event is due to the reply/timeout
* or error on this outbound query.
* @return: if at exit the ext_state is:
* o wait_module: next module is started. (with pass event).
* o error or finished: previous module is resumed.
* o otherwise it waits until that event happens (assumes
* the service routine to make subrequest or send message
* have been called.
*/
void (*operate)(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);

/**
* inform super querystate about the results from this subquerystate.
* Is called when the querystate is finished.
* @param qstate: the query state that is finished.
* Examine return_rcode and return_reply in the qstate.
* @param id: module id for this module.
* This coincides with the current module for the super qstate.
* @param super: the super querystate that needs to be informed.
*/
void (*inform_super)(struct module_qstate* qstate, int id,
struct module_qstate* super);

/**
* clear module specific data
*/
void (*clear)(struct module_qstate* qstate, int id);

/**
* How much memory is the module specific data using.
* @param env: module environment.
* @param id: the module id.
* @return the number of bytes that are alloced.
*/
size_t (*get_mem)(struct module_env* env, int id);
};

这里对函数的行为解释得差不多了。init()deinit()是模块全局初始化用的;operate()inform_super()clear()是在处理每个query中用的。我来说明一下struct module_envstruct module_qstate等。

在使用中其实module_env只会被创建一个,所有module的所有这些函数被传入的参数中的env都是它,env->modinfo[id]void *类型,是存放全局module信息的指针。指针的值由module在init()时候设置,这个指针指向的内存由init()自行malloc()deinit()free()get_mem()函数用来计算并返回这些东西的大小。

struct module_qstate是对每个询问创建的一个context,每个询问被创建后会按照module的顺序进行执行。计算一个query的结果时,程序自己可能需要创建一个subquery,subquery则又会对应一个独立的struct module_qstate

struct module_qstate中保存了询问相关信息、一些全局信息、模块相关信息还有一些会用到的函数指针。其中minfo[id]用来给module存放模块跟当前qstate相关的信息。ext_state[id]存放模块在处理这个query时的执行状态信息。模块执行状态信息是enum module_ext_state,包含:

/** initial state - new query */
module_state_initial = 0,
/** waiting for reply to outgoing network query */
module_wait_reply,
/** module is waiting for another module */
module_wait_module,
/** module is waiting for another module; that other is restarted */
module_restart_next,
/** module is waiting for sub-query */
module_wait_subquery,
/** module could not finish the query */
module_error,
/** module is finished with query */
module_finished

module的ext_state应该在operate()中设置,operate()会被unbound调用多次,分阶段完成module执行。如果module进行了外部询问,则应设置状态为wait_reply;如果将任务交给了下级,则应设置状态wait_module;如果生成了一个subquery,需要等待它的结果,则应设置wait_subqueryrestart_next比较特殊,一般当它下级的module跑挂了(module_error或者没给满意的结果)之后,当前module可以选择重启下级,此后下级会先被调用一次clear(),再被调用operate()重新来过。operate()会在外部请求被回答、subquery完成、下级module执行完成(errorfinished)后被调用以继续完成module的任务。

operate()被调用时,参数event用来传递被调用的原因:

enum module_ev {
/** new query */
module_event_new = 0,
/** query passed by other module */
module_event_pass,
/** reply inbound from server */
module_event_reply,
/** no reply, timeout or other error */
module_event_noreply,
/** reply is there, but capitalisation check failed */
module_event_capsfail,
/** next module is done, and its reply is awaiting you */
module_event_moddone,
/** error */
module_event_error
};

其中replynoreplycapsfail都是向外进行的询问被回答时传递的,moddonepass比较明显。noreplyreply都是字面意思,reply了空也算replycapsfail是说虽然有reply,但是这个reply是攻击者伪造的而非被询问者的回复。

caps是指Capitalisation randomisation,又叫dns0x20。是通过改变请求的大小写增加随机性避免暴力投毒的办法。穷举所有transaction_id、udp_port、src_addr(权威IP)一般只要2^30。通过这个再增加8bits(平均)的安全性…

module的clear()inform_super()的注释解释得不太具体。

clear()通常用来清除qstate相关的module数据(一般只用清理minfo[id])。这里比较特殊,module在处理query时,请求内存应该用regional_alloc()函数,具体可参考iterator/iterator.c:iter_new(),被regional_alloc()的内存无须手工释放,它会在query的计算结束时自动被释放。因此clear()最多只需要把minfo[id]重设为NULL

inform_super()在两种情况下会被调用:subquery的计算结束、subquery由于计算时间太长被程序终止。此时父query的当前模块的inform_super()函数会被调用。看名字会误以为是subquery的某个模块的inform_super()被调用。之后父query会被设置为可执行,因此不必在此时进行operate()。我不知道为什么unbound要把inform_super()分开而不是在operate()时像外界询问被回复后传递outbound_entry一样把subquery作为附加参数一起传递进来,这样inform_super()就根本不需要了。

最后,模块执行时创建subquery和创建外部询问的方法可以分别参考validator.c:generate_request()iterator.c:processQueryTargets()最后几行。

Advertisements

One response to this post.

  1. 你好,我是CNNIC的工作人员,你已经被我们列为重点观察对象。

    回复

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

w

Connecting to %s

%d 博主赞过: