io.systemd.Resolve 请求全链路深度分析
基于fedora44 的 systemd 259.6 源码分析(下载链接在文章结尾)。本文跟踪一个 DNS 查询请求从应用程序
gethostbyname()调用,到经过 NSS 模块、Varlink IPC、systemd-resolved、DNS 事务、再到回复返回的完整旅程。免责声明:源码的分析我大概看过一些,然后AI帮我分析了中间没必要看的部分,串起来成了这个文章。
第 0 章 背景故事
之所以捣鼓这个是因为我发现主机上突然很多遥测的域名(proj-xtrace-7e235817c9b9381c22d8b743908d469f-cn-beijing.cn-beijing.log.aliyuncs.com)出现,有点恶心人,但是很多工具都没法把域名和进程绑定到一起,只能深入了解一下getaddrinfo是怎么拿到一个域名的ip的,这样反推写一些hook再对应一下pid就能得知是什么程序。我的机器是fedora44,基于systemd-resolved,所以是走的varlink。
我粗略的epbf实现:https://github.com/scriptk1d/varlink_snoop
大概的思路就是:
- hook
connect() → 检查sockaddr_un.sun_path == /run/systemd/resolve/io.systemd.Resolve→ 把 fd 加入白名单 - hook 发送族(
write/writev/sendto/sendmsg)→ 只对白名单 fd dump payload(Varlink 明文 JSON) - 记录 PID / TID / comm / 时间戳,ring buffer 上报用户态
相比 uprobe getaddrinfo:不依赖 libc、不挑语言运行时、静态链接也照抓(因为抓的是所有客户端共同收口的那个 Varlink 连接)。
第 1 章 全景:Linux 名字解析与 Varlink 的关系
在 systemd 的世界里,/run/systemd/resolve/io.systemd.Resolve 是一个特殊的 Unix domain socket。它不是一个普通的服务监听 socket,而是一个 Varlink 协议端点——一个用 JSON 消息、NUL 分隔、单连接请求/回复模型构建的轻量级 RPC 协议。
为什么 systemd 不复用已有的 D-Bus,而要新造一个 Varlink?核心动机写在 varlink-internal.h:43-114 的注释里:D-Bus 太重,不适合作为 NSS 模块这种”每进程一次调用”的客户端。NSS 模块被加载到每个进程里,不能依赖一个长期运行的 D-Bus 客户端连接。Varlink 的设计目标是:协议极简(单 JSON 文件即可描述)、连接开销极低(一个 socket)、无类型注册和信号订阅开销、可直接被静态链接进 NSS 模块。
整个 DNS 解析链路全景如下:
1 | 应用进程 (curl, ssh, ...) systemd-resolved 守护进程 |
本文将逐站拆解这条链路。涉及的核心源文件:
| 文件 | 角色 |
|---|---|
src/nss-resolve/nss-resolve.c |
客户端 NSS 模块,把 gethostbyname 翻译成 Varlink 调用 |
src/libsystemd/sd-varlink/sd-varlink.c |
Varlink 协议库(客户端+服务端共用) |
src/libsystemd/sd-varlink/varlink-internal.h |
sd_varlink 结构与状态机定义 |
src/resolve/resolved.c |
systemd-resolved 主进程入口 |
src/resolve/resolved-manager.c |
守护进程 Manager 管理,含 manager_start() |
src/resolve/resolved-varlink.c |
Varlink 服务端 method handler 实现 |
src/resolve/resolved-dns-query.c |
DNS 查询的协议无关抽象 |
src/shared/varlink-io.systemd.Resolve.c |
io.systemd.Resolve 接口的 IDL 定义 |
第 2 章 整体架构:三套状态机的协作
这条链路最容易被忽略的设计点是:全程有三个独立状态机在协作。理解它们的边界,才能理解为什么代码这样写。
1 | 客户端 sd_varlink Varlink 协议 服务端 sd_varlink |
这三个状态机分别定义在:
- 客户端 / 服务端 Varlink 状态机:
varlink-internal.h:11-41 的VarlinkState枚举,共 21 个状态(8 个客户端、7 个服务端、6 个关闭期共享) - DNS 查询状态机:
resolved-dns-transaction.h 的DnsTransactionState,跟踪单个 DNS 数据包在网络层的旅程 - DnsQuery 状态机:
resolved-dns-query.c 中通过q->state字段管理,是 DNS 事务之上的”用户意图”层
注意第三个状态机 (DnsQuery) 是故意与 Varlink 解耦的。一个 Varlink method 调用可能触发多个 DNS transaction(因为 CNAME 链、A+AAAA 双查询、多 DNS 服务器轮询),DnsQuery 把这些聚合起来,完成后回调到 Varlink 层。这种解耦使得 systemd-resolved 可以同时服务于 Varlink、D-Bus、内置 DNS stub listener 三种客户端,共用同一套 DNS 查询逻辑。
第 3 章 服务端:socket 的诞生
跟踪一个 DNS 查询从服务端 socket 的诞生开始。systemd-resolved 的进程入口在 resolved.c:68:
1 | r = manager_new(&m); // resolved.c:68 |
manager_start() 在 resolved-manager.c:822-836 极简:
1 | int manager_start(Manager *m) { |
manager_varlink_init() 在 resolved-varlink.c:1547-1559 把工作分给两个独立 server:主查询服务 和 监控服务(后者用于 resolvectl monitor)。本报告只跟踪前者。
3.1 创建 server、绑定方法、绑定地址
varlink_main_server_init() 在 resolved-varlink.c:1492-1545 完成三件事:
1 | /* 1. 创建 server,启用 ACCOUNT_UID(按 UID 计费) */ |
这里有一个 systemd-resolved 复用 io.systemd.service 接口 的设计:同一个 server 上同时挂着 io.systemd.service.Ping、SetLogLevel、GetEnvironment,这些是所有 systemd 守护进程通用的”内省”方法,定义在 src/libsystemd/sd-varlink/varlink-io.systemd.c。
接下来绑定地址(resolved-varlink.c:1530-1537):
1 | r = sd_varlink_server_listen_auto(s); /* 先尝试 socket activation */ |
listen_auto 是 systemd 服务进程的标配:启动时 systemd 可能已经通过 sd_listen_fds() 把监听 fd 直接传给 resolved(socket activation),这时不再 bind()。如果没有,就回退到 listen_address 自己创建 socket。两条路径最终都调用 varlink_server_create_listen_fd_socket()(sd-varlink.c:3606-3634),把 fd 注册到 sd-event 上,绑定 connect_callback。
3.2 IDL:接口的”自描述”
io.systemd.Resolve.ResolveHostname 的 IDL 定义在 src/shared/varlink-io.systemd.Resolve.c:124-139:
1 | static SD_VARLINK_DEFINE_METHOD( |
这份 IDL 在编译期变成静态 sd_varlink_symbol 结构,运行时被 sd_varlink_server_add_interface() 注册到 server 的 symbols hashmap。此后当客户端的请求 JSON 进来,varlink_dispatch_method() 会把请求参数交给 varlink_idl_validate_method_call()(sd-varlink-idl.c)做类型校验,校验不过直接回 InvalidParameter 错误,不让用户回调见到非法数据。这是 IDL 作为协议契约 的关键作用。
第 4 章 客户端:NSS 模块发起连接
现在切换视角。一个普通程序 curl https://example.com,libc 内部会调用 getaddrinfo(),后者依据 /etc/nsswitch.conf 的 hosts: 行依次尝试各个 NSS 模块。配置为 hosts: files resolve dns myhostname 时,libnss_resolve.so.2 是第二个被尝试的模块。
4.1 NSS 入口被 libc 调起
libnss_resolve.so.2 的代码全部在 nss-resolve.c。导出符号形如 _nss_resolve_gethostbyname4_r(glibc 的 NSS ABI 要求这个奇怪的命名)。函数原型在 nss-resolve.c:191-347,核心是这三段:
1 | enum nss_status _nss_resolve_gethostbyname4_r( |
注意 _cleanup_(sd_varlink_unrefp) —— 整个 NSS 调用期间持有的 sd_varlink 对象用 cleanup 属性绑定了析构函数,函数任何 return 路径都会自动关闭连接。这是 systemd 代码里无处不在的 RAII 模式。
4.2 connect_to_resolved:打开 socket
connect_to_resolved() 在 nss-resolve.c:48-62:
1 | static int connect_to_resolved(sd_varlink **ret) { |
调用 sd_varlink_connect_address()(sd-varlink.c:158-208)展开:
1 | v->input_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); /* L170 */ |
三个关键细节:
-
SOCK_CLOEXEC|SOCK_NONBLOCK:exec 时关闭(防止 fd 泄漏给子进程)、非阻塞(connect()对 Unix socket 可能立即返回 EINPROGRESS) -
fd_move_above_stdio() :把 fd 移到 ≥3 的位置,避免占用 stdin/stdout/stderr 槽位——后续exec子进程时标准流不被干扰 -
v->connecting 标志:这是一个独立于VarlinkState 的”传输层子状态”。varlink-internal.h:90-114 长篇注释解释了为什么需要它:状态机是”应用层”概念,而connecting 是”传输层”细节。即使connect()未完成,也允许把消息入队,等连接就绪后自动发出。
4.3 NSS 模块的特殊考量
NSS 模块是被加载进 每个进程 的共享库,设计上有几个约束在普通 Varlink 客户端里看不到:
| 约束 | nss-resolve 的应对 | 源码位置 |
|---|---|---|
| 进程可能 setuid,不能信任环境 | 用 secure_getenv_bool 读 SYSTEMD_NSS_RESOLVE_* 旗标 |
nss-resolve.c:172-178 |
| libc 调用线程的 errno 必须保护 | PROTECT_ERRNO / UNPROTECT_ERRNO 宏对 |
nss-resolve.c:204, 273 |
| 调用结束必须关连接,不能持有 | _cleanup_(sd_varlink_unrefp) RAII |
nss-resolve.c:198 |
| h_errno 是历史遗留全局变量,需显式重置 | *h_errnop = NETDB_SUCCESS; h_errno = 0; |
nss-resolve.c:323-324 |
| 必须支持 buffer 不足时返回 TRYAGAIN | 检查 buflen < ms |
nss-resolve.c:272-277 |
第 5 章 同步 RPC:sd_varlink_call 的状态机
NSS 模块调用 sd_varlink_call(link, "io.systemd.Resolve.ResolveHostname", ...),这是一个同步阻塞 API。但 sd-varlink 内部完全是异步的,sd_varlink_call 实际是把同步语义”叠”在异步状态机之上的包装。展开 sd-varlink.c:2183-2271 的 sd_varlink_call_full():
1 | _public_ int sd_varlink_call_full(sd_varlink *v, const char *method, ...) { |
这是一个有趣的模式:NSS 模块没有事件循环,所以 sd_varlink_call_full() 自己现造了一个最小的循环。循环里反复调用 sd_varlink_process()(把所有未完成的工作推进一次),如果没工作可做就 sd_varlink_wait() 阻塞在 poll() 上等 socket 可读或可写。
5.1 varlink_enqueue_json:消息与 fd 的边界绑定
请求 JSON 构造完之后,varlink_enqueue_json()(sd-varlink.c:1963-1988)决定怎么把它放进输出队列:
1 | static int varlink_enqueue_json(sd_varlink *v, sd_json_variant *m) { |
这里有一个 fd 边界 问题:Varlink 协议规定 SCM_RIGHTS 传递的 fd 必须与某条消息绑定。如果两条消息都各自带 fd,不能简单地拼接到一个连续 buffer(否则 write 部分成功时 fd 会跨消息关联)。VarlinkJsonQueueItem(varlink-internal.h:77-82)的存在就是为了绑定”一条 JSON 消息 + 它自己的 fd 数组”:
1 | struct VarlinkJsonQueueItem { |
5.2 sd_varlink_process:状态机的发动机
sd_varlink_process()(sd-varlink.c:1457-1538)是整个协议的”心脏”,它的执行顺序设计得很讲究:
1 | _public_ int sd_varlink_process(sd_varlink *v) { |
为什么是 “write → dispatch → parse → read” 这个顺序?先 write 是为了让回复尽快出去;dispatch 已解析的 JSON 优先于 read 新数据,是为了避免 buffer 堆积;read 放在最后,这样新数据进入 buffer 后,下一轮 sd_varlink_process() 才会被 parse_message 处理。整个序列保证每一轮 process() 最多完成一次”完整事务”。
第 6 章 服务端:accept 与 dispatch
现在跟踪请求 JSON 抵达服务端之后的旅程。socket 可读时,sd-event 触发 io_callback(sd-varlink.c:2941-2950),它调用 handle_revents() + sd_varlink_process()。但这是已建立连接的回调。新连接 走的是另一个回调:connect_callback(sd-varlink.c:3567-3604):
1 | static int connect_callback(sd_event_source *source, int fd, uint32_t revents, void *userdata) { |
注意 systemd-resolved 没有设置 connect_callback(查看 resolved-varlink.c:1492-1545,只设置了 disconnect 回调),所以新连接进入后立即开始接受请求。这是合理的:DNS 查询是无状态请求,无需在连接建立时做认证。
6.1 安全检查:per-UID 配额
虽然没设 connect 回调,服务端创建时启用了 SD_VARLINK_SERVER_ACCOUNT_UID(resolved-varlink.c:1501)。sd_varlink_server_add_connection_pair()(sd-varlink.c:3466-3553)在 accept 之后会做:
1 | r = getpeercred(input_fd, &ucred); /* L3490,内核保证可信 */ |
validate_connection()(sd-varlink.c:3395-3435)执行三道闸:
1 | if (server->n_connections >= server->connections_max) { /* 默认 4096 */ |
这两层防护防的是本地恶意进程通过打开海量连接耗尽 resolved 资源。getpeercred() 利用内核的 SO_PEERCRED(Linux)在 connect 时刻固化对端 PID/UID/GID,之后即使对端 fork 也无法伪造。
6.2 dispatch_method:JSON 落到 C 回调
请求字节通过 varlink_read() → varlink_parse_message() 进入 v->current(一个 sd_json_variant *),随后 varlink_dispatch_method()(sd-varlink.c:1277-1455)把它翻译成 C 调用。这个函数是整个 sd-varlink 里最复杂的一个(179 行,圈复杂度 45),核心逻辑可拆为四步:
1 | static int varlink_dispatch_method(sd_varlink *v) { |
对我们跟踪的 io.systemd.Resolve.ResolveHostname 来说,callback 就是 vl_method_resolve_hostname。
第 7 章 DNS 查询的异步触发
vl_method_resolve_hostname()(resolved-varlink.c:342-410)是一个典型的”参数校验 + 启动查询 + 返回 1(表示稍后回复)”模式:
1 | static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *parameters, ...) { |
最后那个 return 1 是关键。varlink_dispatch_method()(sd-varlink.c:1421-1442)根据当前状态决定下一步:
1 | switch (v->state) { |
VARLINK_PENDING_METHOD 状态意味着:这条连接的 method 调用正在等外部事件(DNS 服务器响应),Varlink 状态机释放对它的占用,sd-event 转去处理其他连接。连接和查询通过 sd_varlink_ref(link) 互相锚定:link 持有 query 引用,query 也持有 link 引用。这种双向引用靠 vl_on_disconnect 回调清理(见第 9 章)。
7.1 parse_as_address:绕过网络的优化
值得专门讲一下 parse_as_address(resolved-varlink.c:304-340),它是 systemd-resolved 的一个小而美的优化:
1 | static int parse_as_address(sd_varlink *link, LookupParameters *p) { |
当用户请求 gethostbyname("127.0.0.1") 时,根本不需要发 DNS 查询,直接合成回复。这避免了不必要的网络流量和延迟。SD_RESOLVED_SYNTHETIC 标志告诉客户端这个答案是合成的。
第 8 章 回复路径:从 DNS 响应到 Varlink JSON
DNS 查询完成时(无论成功、超时、DNSSEC 失败),DNS 子系统回调 q->complete,即 vl_method_resolve_hostname_complete()(resolved-varlink.c:254-302):
1 | static void vl_method_resolve_hostname_complete(DnsQuery *query) { |
sd_varlink_replybo(sd-varlink.c:2548-2563)是 sd_varlink_reply 的可变参数便捷版。sd_varlink_reply(sd-varlink.c:2502-2546)做的事:
1 | _public_ int sd_varlink_reply(sd_varlink *v, sd_json_variant *parameters) { |
注意 IDL 校验 reply 的设计:服务端发的回复也要符合接口契约。如果 vl_method_resolve_hostname_complete 不小心构造了不合法的 JSON,varlink_idl_validate_method_reply 会记日志但不阻断发送——这是为了一个 bug 不会让客户端永久挂起。代码注释里特意写了 “Please adjust test/units/end.sh when updating the log message” 提醒不要改这个文案,因为有测试在 grep 它。
8.1 varlink_write:数据真正发出去
回到 sd_varlink_process() 的循环,下一次轮询时 varlink_write()(sd-varlink.c:721-809)会真正把 output_buffer 写到 socket:
1 | static int varlink_write(sd_varlink *v) { |
MSG_NOSIGNAL 是必须的:如果对端已关闭连接,write 会触发 SIGPIPE 把进程杀掉。NSS 模块加载在 libc 进程里,SIGPIPE 是绝对不能有的。MSG_NOSIGNAL 把这种情形转换成 EPIPE errno,由代码处理。
第 9 章 错误传播与 NSS 状态映射
9.1 varlink error → errno
DNS 查询失败时,reply_query_state()(resolved-varlink.c:86-155)把 DNS 子系统的内部状态映射成结构化的 varlink error。映射表很完整:
1 | switch (q->state) { |
注意 DNSSEC 错误带了结构化字段(result、extendedDNSErrorCode、extendedDNSErrorMessage),客户端可以精确知道是哪个验证环节失败。这是 Varlink 相比 D-Bus 的优势之一:错误也是结构化数据,而不是一个字符串。
9.2 varlink error → NSS 状态
回到客户端 NSS 模块。nss-resolve.c:230-241 收到 error_id 后做映射:
1 | r = sd_varlink_call(link, "io.systemd.Resolve.ResolveHostname", |
error_shall_fallback(nss-resolve.c:25-36)和 error_shall_try_again(nss-resolve.c:38-46)定义了三类错误:
| varlink error | NSS 状态 | 含义 |
|---|---|---|
Disconnected/Timeout/Protocol/InterfaceNotFound/MethodNotFound/MethodNotImplemented |
NSS_STATUS_UNAVAIL |
resolved 没回应或协议坏了,libc 应继续尝试 nsswitch.conf 里下一个模块 |
NoNameServers/QueryTimedOut/MaxAttemptsReached/NetworkDown |
NSS_STATUS_TRYAGAIN |
暂时性失败,libc 应该重试 |
其他所有(包括 DNSSECValidationFailed、DNSError、CNAMELoop 等) |
NSS_STATUS_NOTFOUND |
名字确实查不到 |
NoSuchResourceRecord |
NSS_STATUS_NOTFOUND + NO_DATA |
NXDOMAIN-like |
这个区分很重要:NSS_STATUS_UNAVAIL 会让 libc 继续尝试下一个模块(比如 dns),用户最终能拿到答案;NSS_STATUS_NOTFOUND 则会让 libc 终止查找链(防止 dns 模块又查一次同样的失败名字)。
9.3 vl_on_disconnect:客户端消失时清理查询
最后一个常被忽略的细节。如果客户端在 DNS 查询完成前就关闭了 socket(NSS 模块加载在调用进程里,进程崩了),resolved 不应该继续浪费资源查 DNS。这就是 vl_on_disconnect(resolved-varlink.c:157-180)的作用:
1 | static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) { |
回看第 7 章 vl_method_resolve_hostname 里 sd_varlink_set_userdata(link, q) 的设置——这个双向锚定的另一半就是给 disconnect 回调用的。整个生命周期管理靠两个 ref 形成”环”,环的解开发生在两个对称点:
- query 完成 →
sd_varlink_reply 把状态切回IDLE_SERVER→ disconnect 回调不会被触发 - 客户端消失 → disconnect 回调 →
dns_query_complete(ABORTED)→ query 释放 → link 引用计数减 1
第 10 章 端到端时序:把所有章节串起来
把前面各章的步骤串起来,一次完整的 gethostbyname("example.com") 经 varlink 的旅程如下:
1 | 时刻 进程 操作 源码位置 |
t17 → t20 之间是整个链路唯一真正的异步等待窗口(等待上游 DNS 服务器)。在这段窗口里,客户端 NSS 模块在 sd_varlink_wait 的 poll() 中阻塞,服务端 resolved 在 sd-event 循环里继续服务其他客户端。这是 Varlink 同步 RPC 模型最大的代价:客户端调用线程被完全占用,但好处是 NSS 模块代码极简,无需理解事件循环。
第 11 章 设计哲学与总结
11.1 三个核心设计原则
原则 1:协议极简,状态机丰富
Varlink 协议本身只有”JSON 消息 + NUL 分隔 + 可选 SCM_RIGHTS”——三个要素。但 sd_varlink 内部用 21 个状态来精细管理连接生命周期(varlink-internal.h:11-41)。这种”协议瘦、实现厚”的设计让客户端实现可以很简单(只需懂 JSON 即可),而把复杂性集中在一个被反复验证的库内。
原则 2:同步语义,异步实现
NSS 模块是同步 API(_nss_resolve_gethostbyname4_r 必须返回 enum nss_status),但 sd-varlink 内部完全异步。sd_varlink_call_full(sd-varlink.c:2183)用 while + process + wait 循环把异步状态机”翻译”成同步阻塞调用,而无需动用线程。这是个很经典的设计,值得在任何”需要把异步库暴露给同步 API”的场景借鉴。
原则 3:资源生命周期用双向 ref + disconnect 回调
第 7 章和第 9 章展示了 varlink link 和 DnsQuery 的互相引用:link 引用 query(在 q->varlink_request),query 引用 link(在 sd_varlink_set_userdata)。这种环看似危险,实际通过 vl_on_disconnect 回调提供了单向”切断”机制:无论客户端何时消失,query 都会被 abort 并释放,环就被解开。这是 systemd 处理”跨子系统对象生命周期”的标准模式。
11.2 核心洞察
io.systemd.Resolve 这条链路最精妙的设计,在于把 Varlink 协议的”同步 RPC”语义,优雅地映射到了 DNS 查询的”异步多步事务”模型之上——通过
q->complete 函数指针这一个间接层,让 Varlink 层完全不需要知道 DNS 查询要发几个包、查几条 CNAME、是否要重试。
这个间接层的具体体现就是 resolved-varlink.c:402 的 q->complete = vl_method_resolve_hostname_complete;。一行赋值,把”协议层”(Varlink)和”业务层”(DNS query)彻底解耦。systemd-resolved 因此可以同时为 Varlink、D-Bus、内置 DNS stub listener 三种外部接口服务,共用同一套 dns_query_* 实现。这种”业务层不知道上层是谁”的设计,是 systemd-resolved 能在 systemd 259 演化出 Hook、Delegate、Monitor 等多个新接口而无需重写核心的根本原因。
附录:核心数据结构速查
| 结构 | 定义位置 | 关键字段 |
|---|---|---|
sd_varlink |
varlink-internal.h:84-191 |
state(VarlinkState)、input_fd/output_fd、input_buffer/output_buffer、output_queue(JSON+fd 绑定)、n_pending(在飞方法数)、reply_callback、current(已解析的当前 JSON)、ucred/peer_pidfd(对端凭证) |
sd_varlink_server |
varlink-internal.h:206-236 |
methods(name→callback hashmap)、interfaces/symbols(IDL 元数据)、by_uid(per-UID 连接计数)、n_connections/connections_max |
VarlinkJsonQueueItem |
varlink-internal.h:77-82 |
data(JSON)、n_fds+fds[](绑定的 fd 数组) |
LookupParameters |
resolved-varlink.c:36-44 |
ifindex、family、name、flags —— ResolveHostname/Address 的入参聚合 |
DnsQuery |
resolved-dns-query.h |
state(DNS_TRANSACTION_*)、varlink_request(反向引用 link)、complete(完成回调)、question、answer |
Manager |
resolved-manager.h |
varlink_server(主服务)、varlink_monitor_server(监控服务)、event(sd-event)、DNS scope/server/cache 子系统 |
附录:本报告涉及的全部源文件
| 文件 | 行数 | 在本报告中的角色 |
|---|---|---|
src/libsystemd/sd-varlink/sd-varlink.c |
4351 | Varlink 协议库主体(客户端+服务端) |
src/libsystemd/sd-varlink/varlink-internal.h |
261 | sd_varlink/sd_varlink_server 结构与 21 状态枚举 |
src/libsystemd/sd-varlink/sd-varlink-idl.c |
1993 | IDL 解析与请求/回复类型校验 |
src/nss-resolve/nss-resolve.c |
729 | 客户端 NSS 模块,把 gethostbyname 翻译为 Varlink |
src/resolve/resolved.c |
~80 | systemd-resolved 进程入口 |
src/resolve/resolved-manager.c |
2364 | Manager 启动、manager_varlink_init() |
src/resolve/resolved-varlink.c |
1566 | 服务端 method handler、vl_method_resolve_* |
src/resolve/resolved-dns-query.c |
1629 | DNS 查询抽象(协议无关) |
src/shared/varlink-io.systemd.Resolve.c |
419 | io.systemd.Resolve 接口的 IDL 定义 |
基于 systemd 259.6 源码分析。所有 file:line 引用均可在源码树中直接定位。
源码下载:https://kojipkgs.fedoraproject.org//packages/systemd/259.6/1.fc44/src/systemd-259.6-1.fc44.src.rpm