unbound源码分析

源码分析

初始化核心流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
main---------------------------------------unbound.c    // 程序入口
run_daemon-----------------------------unbound.c // 启动守护服务
daemon_open_shared_ports-------unbound.c // 打开监听端口
daemon_fork------------------------daemon.c // 启动服务
daemon_create_workers----------daemon.c // 创建工作线程对象和资源
worker_create--------------worker.c // 创建各个工作线程对象,每个对象负责部分部分临时端口数据
// 的接收和发送
worker_init--------------------worker.c // 工作线程初始化,worker_start_accept,
worker_start_accept--------worker.c // 启动监听并accept
worker_handle_request------worker.c // 请求处理函数
tube_setup_bg_listen-------util/tube.c // 管理命令后台监听服务
daemon_start_others(daemon)---------daemon.c // 启动非主线程的其他工作线程
thread_start--------------------daemon.c //线程工作函数
worker_work(worker)---------wroker.c // 工作线程进入工作循环
worker_work(daemon->workers[0])-----worker.c // 启动主工作线程循环处理事件,大家都监听udp端口进行数据循环处理

工作线程循环处理事件

1
2
3
4
5
6
7
8
main---------daemon/unbound.c
run_daemon---------daemon/unbound.c
daemon_fork --------------daemon/daemon.c
worker_work-----worker.c
comm_base_dispatch----------util/netevent.c
ub_event_base_dispatch-------util/ub_event.c // 事件封装
event_base_dispatch------util/mini_event.c // 进入while事件循环处理过程 《事件处理可替换为libevent》
handle_select--------util/mini_event.c // 调用selec阻塞等待事件并回调处理函数

开启端口监听过程

1
2
3
4
5
6
7
8
main---------------------------------------unbound.c    // 程序入口
run_daemon-----------------------------unbound.c // 启动守护服务
daemon_open_shared_ports-------unbound.c // 打开监听端口
listening_ports_open-------services/listen_dnsport.c //开启端口监听
ports_create_if--------services/listen_dnsport.c
。。。。--------services/listen_dnsport.c
make_sock--------services/listen_dnsport.c //创建tcp和udp端口监听句柄到
port_insert--------services/listen_dnsport.c //将创建的端口监听对象加入到daemon->ports中,在《tcp 及udp事件处理回调函数的设置》中将ports加入到事件循环中

tcp 及udp事件处理回调函数的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
worker_init-------------------------------daemon/worker.c
listen_create(ports, worker_handle_request)-------------------------services/listen_dnsport.c // 创建监听对象,并设置监听回调函数为 worker_handle_request
comm_point_create_udp_ancil-------util/netevent.c // 创建udp通信辅助事件, 配置文件中的interface-automatic 设置为yes则调用此函数
c->callback = callback; // 设置udp回调函数
c->ev->ev = ub_event_new(xxx, comm_point_udp_ancil_callback) // 创建事件管理对象,和c关联, 在 comm_point_udp_ancil_callback 中回调到c->callback函数 worker_handle_request
ub_event_add() -------------util/netevent.c // 将udp句柄加入事件监听循环中
event_add---------------util/mini_event.c // 加入监听队列
comm_point_create_udp-------------util/netevent.c // 创建udp事件
c->callback = callback; // 设置udp回调函数
c->ev->ev = ub_event_new(xxx, comm_point_udp_callback) // 创建事件管理对象,和c关联, comm_point_udp_callback中会回调到c->callback函数
ub_event_add() -------------util/netevent.c // 将udp句柄加入事件监听循环中
event_add---------------util/mini_event.c // 加入监听队列

comm_point_create_tcp---------------util/netevent.c // 创建tcp事件
c->ev->ev = = ub_event_new(comm_point_tcp_accept_callback) // 设置tcp accept请求回调处理函数
ub_event_add()------------util/netevent.c // 将udp句柄加入事件监听循环中
.....

module创建及调用过程

module创建及初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
daemon_fork--------------------------------------------daemon/daemon.c
daemon_setup_modules------------------------------daemon/daemon.c
modstack_setup---------------------------------services/modstack.c
modstack_config
module_factory // module创建工厂,创建所有module
module_funcs_avail //创建所有module列表
dns64_get_funcblock //
subnetmod_get_funcblock // 创建edns-subnet 模块
respip_get_funcblock
val_get_funcblock
iter_get_funcblock // 创建iterator
for(i=0; i<stack->num; i++) {
if(!(*stack->mod[i]->init)(env, i)) { // 调用各个module的init函数
}

module执行

1
2
3
4
5
6
7
mesh_run------------------------------------services/mesh.c
while(mstate) { // 运行各个module的operator函数,如iter_operator/subnetmod_operate
fptr_ok(fptr_whitelist_mod_operate(
mesh->mods.mod[mstate->s.curmod]->operate));
(*mesh->mods.mod[mstate->s.curmod]->operate)
(&mstate->s, ev, mstate->s.curmod, e); // 执行
}

客户端udp请求处理过程

1
2
3
4
5
worker_handle_request-------daemon/worker.c           // udp请求处理回调函数
mesh_new_client-------------services/mesh.c // 创建来自客户端的新查询
mesh_run()--------------services/mesh.c // 遍历所有模块进行数据处理,如subnetcache、validator、iterator
(*mesh->mods.mod[mstate->s.curmod]->operate)(&mstate->s, ev, mstate->s.curmod, e); // 回调各个模块的operate函数,如subnetmod_operate、iterator_operate

iterator 处理过程

1
2
3
iter_operate  ----------------------iterator/iterator.c // 递归处理入口函数
process_request-----------------iterator/iterator.c // 请求处理主要入口点
iter_handle-----------------iterator/iterator.c // 循环状态机,当前线程根据状态进行forward等查询,直到获取结果并返回信息

iterator查询结果缓存过程

1
2
3
4
5
mesh_run()--------------services/mesh.c 
iter_operate--------------------iterator/iterator.c
iter_dns_store----------------iterator/iter_util.c // iter封装的存储接口
dns_cache_store-----------services/cache/dns.c // 全局可用存储接口,根据参数决定是否只存储rrset或rrset和msg都存储
dns_cache_store_msg---services/cache/dns.c // rrset和msg都存储接口

remote 远程请求accept设置过程

只有线程序号为0的主线程进行accept请求处理,其他未业务请求

1
2
3
worker_start_accept--------worker.c     // 启动监听并accept
daemon_remote_start_accept--------daemon/remote.c // 根据配置的地址列表进行监听处理
comm_point_start_listening------util/netevent.c //

remote control 服务端启动流程

1
2
3
4
5
worker_init--------------------------------worker.c     // 工作线程初始化,worker_start_accept,
worker_start_accept--------------------worker.c // 启动监听并accept
daemon_remote_start_accept-------daemon/remote.c // 在线程0中启动 remote_control 相关监听
handle_req------------------daemon/remote.c // 启动处理请求处理过程
execute_cmd ------------daemon/remote.c // 处理具体的请求

耗时相关代码

1
2
3
4
5
6
7
8
9
10
daemon_fork							daemon/daemon.c
daemon_setup_modules daemon/daemon.c
modstack_setup services/modstack.c
modstack_desetup
fptr_whitelist_mod_deinit
(*stack->mod[i]->deinit)(env, i);
subnetmod_deinit edns-subnet/subnetmod.c
slabhash_delete util/storage/slabhash.c // 此函数修改为自定义的qy_slabhash_delete函数, 实际耗时可能在此处
lruhash_delete util/storage/lruhash.c
bin_delete util/storage/lruhash.c

重要概念

module(即插件)

unbound 中的 module 即模块(插件)。支持以下模块

模块名 作用 其他
validator 用于验证DNSSEC签名有效性,确保DNS解析查询结果的完整性和不可篡改性
respip 允许使用ACL(访问控制列表)限制哪些IP地址可以访问特定的域名 或 基于IP修改响应结果
pythonmod 允许以Python脚本的形式扩展Unbound功能,可以实现自定义的DNS处理逻辑
iterator 实现Unbound的迭代DNS解析查询功能,向远端DNS服务器递归查询所需DNS记录
subnet 用于配置Unbound服务器的子网访问控制策略,允许或禁止指定子网的访问
subnetcache 实现了支持不同子网对 DNS 解析结果进行缓存的功能。当一个请求来自于一个特定的子网时,subnetcache 模块会将 DNS 解析结果缓存下来,以便以后在该子网中的相同请求中能够快速响应 编译支持ECS时默认开启此模块
subnetcachedb 支持对存储数据进行加密,保护缓存数据的隐私和安全。可以与 subnetcache 模块结合使用,以提供更高效的DNS解析缓存策略
dns64 实现DNS64代理,将IPv4 DNS请求转换为IPv6地址并返回响应
rpz 实现RPZ(Responsibility Provenance Zone)功能,支持自定义DNS黑名单和白名单
prefetch 提前查询DNS记录,以缩短DNS响应时间,提高用户的访问速度

这些模块可以通过配置文件中的 module-config 选项进行加载和配置,以实现各种不同的功能。同时,Unbound还支持许多其他模块,如dnstap模块、subnetcachedb模块、python_certgen模块等,可以根据需要选择适当的模块来实现自己的需求。

  • 1.6.7版 编译加–enable-subnet后默认将启动三个插件,按执行顺序分别是subnetcache,validator,iterator,所以mesh_run的执行顺序是按照这个插件顺序执行的。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!