Skip to main content

Crate setdns

Crate setdns 

Source
Expand description

§setdns

setdns 临时修改系统 DNS,句柄关闭或丢弃时恢复原状态。支持全局 DNS 和按域名后缀分流的 split DNS。

典型用途:VPN、隧道、测试环境、企业内网域名解析,或任何需要接管系统解析器一段时间的工具。

§快速开始

use std::net::IpAddr;

use setdns::{Config, SetDns};

fn main() -> setdns::Result<()> {
    let dns = SetDns::apply(Config {
        owner: "example".to_owned(),
        servers: vec!["1.1.1.1".parse::<IpAddr>().expect("valid IP")],
        domains: Vec::new(),
        device: Some("tun0".to_owned()),
    })?;

    // Run the work that needs the temporary DNS settings here.

    dns.close()
}

close 恢复原配置,返回恢复错误。若直接丢弃句柄,Drop 也尝试恢复;因为 Drop 不能返回错误,恢复失败仅通过 log 记录。

§保留配置

若需进程退出后保留 DNS 配置,在 SetDns::apply 成功后调用 std::mem::forget 跳过 Drop 恢复:

use std::{mem, net::IpAddr};

use setdns::{Config, SetDns};

fn main() -> setdns::Result<()> {
    let dns = SetDns::apply(Config {
        owner: "vpn.example".to_owned(),
        servers: vec!["10.0.0.53".parse::<IpAddr>().expect("valid IP")],
        domains: vec!["corp.internal".to_owned()],
        device: Some("tun0".to_owned()),
    })?;

    mem::forget(dns);
    Ok(())
}

forget 的代价:丢失 close 能返回的恢复错误,原始状态也不再保留在内存中。下次进程启动后,唯有用相同 owner 再次调用 SetDns::apply,后端才有机会清理旧状态。

以下后端按 owner 清理旧状态:Linux /etc/resolv.conf、macOS /etc/resolver split DNS、Windows NRPT。systemd-resolved link DNS、macOS 全局 DNS 和 Windows 全局 DNS 的接口设置不保存跨进程可恢复的原始状态——对它们来说,forget 就是把系统 DNS 改成新值,何时改回由调用方决定。

§配置

Config 描述一次临时 DNS 修改:

字段含义
owner写入系统状态时使用的所有者标记。1–64 字节 ASCII 字符串,只能包含字母、数字、._-
servers句柄存活期间使用的 DNS 服务器。不能为空。
domains为空表示全局 DNS;非空表示 split DNS。域名必须是 ASCII DNS 后缀。*.corp.internalcorp.internal 处理。
device可选的平台目标,通常是网络接口名。各平台行为见下表。

split DNS 示例:

use std::net::IpAddr;

use setdns::{Config, SetDns};

fn main() -> setdns::Result<()> {
    let dns = SetDns::apply(Config {
        owner: "vpn.example".to_owned(),
        servers: vec!["10.0.0.53".parse::<IpAddr>().expect("valid IP")],
        domains: vec!["corp.internal".to_owned(), "dev.corp.internal".to_owned()],
        device: Some("tun0".to_owned()),
    })?;

    dns.close()
}

配置先标准化再传给平台后端:域名转小写,重复后缀去重,被父后缀覆盖的子后缀省略。

§平台行为

§Linux

全局 DNS 有两条路径:传入 device 且 systemd-resolved 可用时,配置该接口的 link DNS;否则在 /etc/resolv.conf 未由 systemd-resolved 管理时,直接替换并备份 /etc/resolv.conf

split DNS 需要 systemd-resolved,也必须传入 devicedevice 作为接口名传给 systemd-resolved。

/etc/resolv.conf 后端创建 /etc/resolv.conf.setdns.bak,仅当当前文件仍带有同一 owner 标记时才恢复,避免覆盖其他进程写入的配置。

§macOS

全局 DNS 通过 SystemConfiguration 修改目标 network service。传入 device 时按 BSD 接口名查找 service;动态 utun* 接口找不到 service 则回退到 primary service。

split DNS 在 /etc/resolver 下为每个后缀写 resolver 文件,忽略 device。macOS resolver 文件每个配置最多 3 个 nameserver。

§Windows

全局 DNS 通过 DNS Client NRPT 规则接管解析;传入 device 时,Windows 还会通过 SetInterfaceDnsSettings 设置该接口的 DNS 服务器。split DNS 只写按后缀匹配的 NRPT 规则,忽略 device

§其他平台

其他平台返回不支持错误,不修改系统 DNS。

§权限与错误处理

修改系统 DNS 通常需要管理员权限:Linux 写 /etc/resolv.conf 或访问 systemd-resolved 系统 D-Bus,macOS 改 SystemConfiguration 或 /etc/resolver,Windows 写 DNS Client NRPT 注册表规则,且在全局 DNS + device 模式下修改接口 DNS 设置。

错误分两类:Error::InvalidConfig——配置在进入平台代码前被拒绝;Error::Backend——包装系统 API、权限、I/O 或平台不支持错误。

§日志

库通过 log crate 记录后端选择、应用、恢复和 Drop 恢复失败。调用方接入 env_loggertracing-log 或其他 log 实现即可。

Structs§

Config
DNS configuration to apply with crate::SetDns.
SetDns
Applied DNS configuration handle.

Enums§

Error
Error returned by configuration validation or a platform DNS backend.

Type Aliases§

Result
Result alias used by this crate.