Comment on page

插件及其参数

写在最前

mosdns 的插件流水线概念

一个能正常工作的简单的流水线示例。能将应答转发至上游。同时支持缓存。
什么是插件: 插件接受并返回一个“请求上下文”。在内部处理请求/应答。
“请求上下文”包含了
  • 客户端来源 IP 等信息。由服务端赋值。不可修改。
  • 客户的请求。插件可以自由修改请求的数据。
  • 应答。初始为空。插件可以自由修改应答的数据
插件流水线: 将多个插件串在一起,就构成了一条流水线。

配置文件中的插件

每个插件都是独立的,互不影响。同类型 type 插件可以初始化任意多个,只要插件的 tag 不重复就行。
多个同类型插件配置示例。点击展开
两个 forward 插件。tag 不同,参数不同。
plugin:
- tag: forward_google
type: forward
args:
upstream:
- addr: https://dns.google/dns-query
- tag: forward_cloudflare
type: forward
args:
upstream:
- addr: https://1.1.1.1/dns-query
一些常用的插件配置已经被 mosdns 预先初始化好了,tag_ 开头以便区分。可以直接调用。
mosdns 会按照配置文件中顺序将插件初始化。有些插件需要调用其他插件,比如 sequence,其调用的插件必须在之前配置。否则会因为找不到插件而初始化失败。

可执行插件

_return

预初始化的插件。什么都不做。不会将请求上下文传递给后续流水线。可以用于结束 sequence 流水线。见下文。
流水线逻辑:
  • 正向:跳过后续流水线。
  • 反向:无。

sequence 将多个插件组合成流水线

sequence 可以将多个插件组成一个流水线。sequence 也是插件,所以 sequence 里运行别的sequence 是可以的。
_return 是一个特殊的预初始化插件,可配合 sequence 使用,它只会跳过后续流水线。(注意: “跳过后续流水线”并不是直接将应答返回客户端,而是正向流水线运行结束,开始反向运行。)。
sequence 插件中调用的插件 tag 必须在 sequence 前定义,否则 sequence 找不到对应插件。
sequence 流水线图示。
初始化参数说明:
tag: ""
type: "sequence"
args:
exec:
# 可以时可执行插件的 tag。比如:
- "hosts"
- "cache"
- "ecs"
# 可以是 if 条件。
# if 为条件表达式。支持 && ||,() 以及 ! 取反。
# 预定义插件(以 _ 开头的插件),需要用 [] 括住。
- if: "(matcher && ! matcher) || [_preset_matcher]"
exec: # 条件满足,使用此分支流水线。
- "ecs"
- "forward"
else_exec: # 否则,使用此分支流水线。
- "ecs"
- "forward"
# 可以是 load_balance。
# 轮换使用其内部的分支流水线。
- load_balance:
- - "ecs_1" # 分支流水线 #1。
- "forward_1"
- - "ecs_2" # 分支流水线 #2。
- "forward_2"
# ... # 不限数量,可继续添加。
高级操作: 并发、失败自动回滚(故障转移)
tag: ""
type: "sequence"
args:
exec:
# 可以是 parallel。
# 并行运行其内部的独立流水线。取最先返回的有效(0 RCODE)应答。
- parallel:
- - "ecs_1" # 独立流水线 #1。
- "forward_1"
- - "ecs_2" # 独立流水线 #2。
- "forward_2"
# ... # 不限数量,可继续添加。
# 可以是 fallback。
# fallback 有两种触发方式。详见下文。
- primary: # 主要流水线(独立的)。
- "ecs_1"
- "forward_1"
secondary: # 次要流水线(独立的)。
- "ecs_2"
- "forward_2"
stat_length: 10 # 常规 fallback 统计长度。默认 0,禁用常规 fallback。可省略。
threshold: 5 # 常规 fallback 触发阈值。不可大于统计长度。默认 0。可省略。
fast_fallback: 200 # 快速 fallback 触发阈值。单位: 毫秒。默认: 0,禁用快速 fallback。可省略。
always_standby: true # 快速 fallback secondary 始终待命。可省略。
fallbackparallel 涉及到多线程运行,其内部流水线其实是一个独立的 sequence 插件,拥有独立的请求上下文,为了配置方便嵌套在这里。它们和当前 sequence 没有关系!在独立的 sequence 内对请求上下文修改,不会影响其他并行的流水线。同样 _returncache 等能跳过后续流水线运行的插件,也只会终止自身流水线,不会对其他流水线产生影响。但最终 fallbackparallel 会把合适的应答放在当前 sequence 的请求上下文内。比如 parallel 会取最快返回的流水线的请求上下文的应答放在当前 sequence 的请求上下文。
fallback 两种触发方式:
  • 常规 fallback: 监控主流水线最近 stat_length 次的请求状态,如果出现 threshold 次问题(没有应答或者有报错),则下次请求会一并执行次流水线。取最先返回的应答。
  • 快速 fallback: 对于每一个请求,如果主流水线运行结束了但没有返回应答,或者在 fast_fallback 毫秒后仍在运行无应答,则立即开始执行次流水线,取最先返回的应答。如果任意流水线返回了应答,这个请求就不会失败。比常规 fallback 策略更激进,次流水线可能运行的更频繁。
    • secondary 始终待命: 如果设为 true,则对于每一个请求,次流水线都会和主流水线一并执行,其结果将会在需要回滚时(没有返回应答或 fast_fallback 超时),直接被使用。进一步降低响应时间。
  • 快速和常规 fallback 可以同时启用。

cache 缓存

缓存 DNS 应答的插件。
流水线逻辑:
  • 正向:如果请求命中缓存,则将缓存的应答放入“请求上下文”,跳过后续流水线。如果没有命中,则继续执行后续流水线。
  • 反向:缓存应答。
以下预初始化的插件 tag 可直接使用:
  • _default_cache: 默认配置的内存缓存。大小 1024。无 lazy cache。
初始化参数说明:
tag: ""
type: "cache"
args:
size: 1024 # 内置内存缓存大小。单位: 条。默认: 1024。每个 cache 插件的内存缓存是独立的。
compress_resp: false # (实验性)(v4.4+)启用应答压缩。大缓存场景可能会稍微降低内存占用。
# 注意: 如果使用 redis 外部缓存,切换该选项必须清空 redis 缓存。
# redis-server 外部缓存地址。填入外部缓存会自动禁用内置的内存缓存。上文的 size 参数会被忽略。
# redis-server 需要 v2 及以上版本。如果多个 cache 插件连接到同一个 redis 数据库,数据是共享的。
# cache 会为设定记录的 TTL,不会主动删除 redis-server 内的记录。内存大小清理方式等由
# redis-server 负责。
# cache 假设 redis-server 的距离很近/延时很低而且性能足够。
# TCP 连接: `redis://<user>:<password>@<host>:<port>/<db_number>`
# Unix 连接: `unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>`
redis: "redis://localhost:6379/0"
# redis-server 超时阈值。单位: 毫秒。默认: 1000。一般无需修改。
# 如果 redis-server 在该时间内无响应,cache 会临时停用缓存。同时间歇性尝试 ping
# redis-server,直至再次成功连接。
redis_timeout: 1000
# lazy cache 设定。lazy_cache_ttl > 0 会启用 lazy cache。
# lazy cache 的设计参考了 RFC 8767。所有应答都会在缓存中存留 lazy_cache_ttl 秒,但自身的 TTL 仍然有效。
# 如果命中过期的应答,则缓存会立即返回 TTL 为 lazy_cache_reply_ttl 的应答,然后自动在后台发送请求更新数据。
# 相比强行增加应答自身的 TTL 的方法,lazy cache 能提高命中率,同时还能保持一定的数据新鲜度。
lazy_cache_ttl: 0 # lazy cache 生存时间。单位: 秒。默认: 0 (禁用 lazy cache)。
# RFC 8767 的建议值 86400(1天)~ 259200(3天)
lazy_cache_reply_ttl: 5 # 返回过期应答的 TTL。默认 5。
# cache 默认只缓存有且只有一个 Question 的请求(简称"简单请求")的应答。请求的状态以运行到 cache 时为准。
# 比如一个简单请求在 cache 后被插入格外信息(比如 ECS),不会影响 cache 缓存这个请求,也不会影响 cache 后续
# 查找这个简单请求。
# cache_everything 会缓存所有请求的应答。会将完整的请求作为 key。能保证带有特殊标识的请求(比如 ECS,Cookie)
# 即使 Question 相同仍然能被区分。
# 注意: "将完整的请求作为 key" 意味着 cache 不会"精细"处理各种 EDNS0。用做公用服务器可能会大幅降低 cache 效率。
# 如需处理 EDNS0 建议使用 unbound 等专业软件。
cache_everything: false
# 当缓存命中后,将应答放入请求上下文后,需要执行的插件。
when_hit: ""
lazy cache,也叫“乐观缓存”,的优缺点:
  • 优点:可大幅提高缓存命中率。流行域名几乎 100% 命中。
  • 缺点:可能影响 DNS 更新频繁的域名正常使用,比如 DDNS,CDN。
  • 仅建议个人使用场景,有相关知识,了解其弊端的专业用户开启。
Metrics 数据:
  • query_total: 总请求数。
  • hit_total: 总命中数。(包括 lazy cache 的命中)
  • lazy_hit_total: lazy cache 总命中数
  • cache_size: 当前缓存大小。单位: 条。

hosts 域名映射 IP

流水线逻辑:
  • 正向:如果请求的域名在 hosts 内,则将应答放入“请求上下文”,跳过后续流水线。如果不在,则继续执行后续流水线。
  • 反向:无。
初始化参数说明:
tag: ""
type: "hosts"
args:
hosts:
- "google.com 108.177.122.113" # 可以是一条记录
- "provider:hosts" # 可以是由 data-provider 提供的外部数据,格式 "provider:tag"。
虽然都叫 hosts,但本插件所用的格式和平常 Win,Linux 系统内的那个 hosts 文件不一样。
格式:
  • 域名规则在前,IP 在后。支持一行多个 IP,支持 IPv6。
  • 如果域名匹配规则的方式被省略,则默认是 full 完整匹配。域名匹配规则详见 这里
格式示例:
# 支持一行多个 IP
dns.google 8.8.8.8 2001:4860:4860::8888 ...

_prefer_ipv4/6 优先使用 ipv4/6

_prefer_ipv4_prefer_ipv6 是预初始化插件,可直接使用。下面为了表述方便,仅以 _prefer_ipv4 为例。_prefer_ipv6 同理。
_prefer_ipv4 可用让下游客户端支持双栈时优先使用 IPv4。_prefer_ipv4 会自动探测域名是否是双栈,然后只屏蔽双栈域名的 AAAA 请求。相比于强行禁用所有 AAAA 请求的方案,该插件不会影响纯 IPv6 域名的解析。
使用位置要求: 插件的后续序列中必须有转发请求的插件。
流水线逻辑:
  • 正向:同时请求 A 和 AAAA。
  • 反向:根据 A 和 AAAA 的结果,判断域名双栈支持状况,决定屏蔽策略。
sequence 示例:
- _prefer_ipv4
- ...
- forward_xxx
# 配合 if 可以实现仅特定域名生效
- if: domain_matcher
exec:
- _prefer_ipv4
- ...
- forward_xxx

ecs 为请求附加 ECS

流水线逻辑:
  • 正向:附加 ECS 至请求。
  • 反向:保证请求与应答 ECS 一致。如果源请求中无 ECS (现有 ECS 是插件新附加的),则删除应答中的 ECS 回应。
初始化参数说明:
tag: ""
type: "ecs"
args:
auto: false # 是否自动将用户请求的来源地址作为 ECS 附加到请求。
# 如果为 true,则预设地址不会被使用。
# 仅适用于公网的服务器。本地运行不要启用这个功能。
ipv4: "1.2.3.4" # 预设的 IPv4 地址。会优先附加在 A 请求里。
ipv6: "2001:dd8:1a::" # 预设的 IPv6 地址。会优先附加在 AAAA 请求里。
force_overwrite: false # 如果请求已有 ECS,是否强制覆盖。
mask4: 24 # 用于 ipv4 地址的掩码。默认: 24。
mask6: 48 # 用于 ipv6 地址的掩码。默认: 48。

_no_ecs 删除请求和应答中的 ECS

_no_ecs 是预初始化插件的 tag,可直接使用。
流水线逻辑:
  • 正向:删除请求中的 ECS。
  • 反向:删除应答中的 ECS。
一般放在转发插件前。

ttl 修改应答的生存时间

流水线逻辑:
  • 正向:修改应答的生存时间。
  • 反向:无。
使用位置:一般放在转发插件后。
初始化参数说明:
tag: ""
type: "ttl"
args:
minimal_ttl: 300 # 最小TTL。如果非零,小于这个数的应答的TTL会被修改成这个值。
maximum_ttl: 3600 # 最大TTL。如果非零,大于这个数的应答的TTL会被修改成这个值。

blackhole 丢弃应答或生成假应答

blackhole 会丢弃应答,或生成特定应答来屏蔽请求。
以下预初始化的插件 tag 可直接使用:
  • _drop_response: 丢弃已存在的应答。
  • _new_empty_response: 生成 Rcode 为 0 的空应答。放入“请求上下文”。(模拟域名存在但没有所请求类型的记录)
  • _new_servfail_response: 生成 Rcode 为 2 (SERVFAIL: 服务器失败)应答。放入“请求上下文”。
  • _new_nxdomain_response: 生成 Rcode 为 3 (NXDOMAIN: 域名不存在)应答。放入“请求上下文”。
流水线逻辑:
  • 正向:丢弃或生成对应应答。
  • 反向:无。
初始化参数说明:
tag: ""
type: "blackhole"
args:
rcode: 0 # 生成`Rcode`为该值的应答。如果负数,则将应答设为空值(丢弃已存在的应答)。
ipv4: # 如果请求类型是 A,则生成包含这些 IP 地址的应答。
- "127.0.0.1"
ipv6: # 如果请求类型是 AAAA,则生成包含这些 IP 地址的应答。
- "::1"
ipv4 和 ipv6 的优先级高于 rcode。如果都配置了, A/AAAA 请求会生成 IP 应答,其余类型会生成状态为 RCODE 的应答。

fast_forward 转发请求至上游(自带模块)

mosdns 自带模块。
流水线逻辑:
  • 正向:转发请求至上游,获取应答后放入“请求上下文”。
  • 反向:无。
初始化参数说明:
tag: ""
type: "fast_forward"
args:
# 上游服务器。至少要配置一个。可配置多个,会并发请求全部服务器。
upstream:
- addr: "https://dns.google/dns-query"
dial_addr: "8.8.8.8"
bootstrap: ""
socks5: "127.0.0.1:1080"
trusted: true
idle_timeout: 30
enable_pipeline: false
enable_http3: false
max_conns: 2
insecure_skip_verify: false
so_mark: 0
bind_to_device: ""
- addr: "tcp://8.8.8.8"
enable_pipeline: true
ca: [ ]
  • addr 必需。服务器地址。省略 scheme 默认为 UDP 协议。省略端口号会使用协议的默认值。支持 IP 和域名。格式示例:
    • UDP: 8.8.8.8, 208.67.222.222:443
    • TCP: tcp://8.8.8.8
    • DoT: tls://8.8.8.8:853, tls://dns.google
    • DoH (RFC8484 GET): https://8.8.8.8/dns-query, https://dns.google/dns-query
    • UDPME(v3.5.1+)(实验性): udpme://8.8.8.8
      • 这是个能过滤掉 UDP 抢答应答的方案。仍然是 UDP 协议。服务器必须支持 EDNS0。如果抢答者不支持 EDNS0,则可以过滤抢答应答。
      • 使用 dig 测试 dig +edns cloudflare.com @服务器地址 观察返回是否有一行 EDNS: version: 0 来确定服务器是否支持 EDNS0。绝大多数服务器都支持。
  • 优先使用 IP 地址(如果服务器官方提供)。如果 addr 包含域名,插件需要将其解析成 IP 地址。
    • dial_addr: 为包含域名的 addr 手动指定其 IP 地址。支持端口号。指定后插件连接服务器时无需先解析服务器的域名。能更快建立连接。适用于 IP 万年不变的服务器。
    • bootstrap: (v4.2+)(实验性) 手动指定用于解析 addr 中域名的 bootstrap 服务器。只能是 UDP 服务器。指定后插件每次连接服务器时会先通过该 bootstrap 服务器解析域名。bootstrap 服务器的延时越低越好。HTTP/3 暂不支持该选项。仅支持 Win,Unix 平台。Macos,Plan9 等不支持。
    • 系统默认解析: 以上两个参数留空,会使用默认系统解析方式。
    • 优先级: dial_addr > bootstrap > 系统默认。
  • socks5: socks5 服务器地址。格式 host:port。数据将会通过该代理中转。暂不支持用户名密码认证。UDP 和启用了 HTTP3 的 DoH 协议暂不支持该设定。
  • trusted: 是否是可信服务器。可信服务器的任何应答都会被接受。其余服务器只接受 RCODE 为 0 (SUCCESS) 的应答。第一个服务器一定是可信服务器且不可修改。其余默认不可信。
  • idle_timeout TCP/DoT/DoH 连接复用空连接保持时间。单位: 秒。默认 DoH: 30 ,TCP/DoT: 10。一般不需要改。
  • enable_pipeline: TCP/DoT 使用 RFC 7766 新的 query pipelining 连接复用模式。
    • 启用后可大幅提高连接利用率,减少建立连接/握手的次数,进而降低延时。
    • 并非所有服务器都支持。必须确定服务器支持后再启用该选项。
    • Tips: 已知 Google 和 Cloudflare 的 TCP/DoT 都是支持的。知名的公共 DNS 服务商大多数都支持该模式。mosdns 自带一个测试命令可以测试服务器是否支持该模式。
  • enable_http3: (v3.4.0+)(实验性) DoH 使用 HTTP/3 连接服务器。并非所有服务器都支持。必须确定服务器支持后再启用该选项。Tips: 已知 Google 和 Cloudflare 是支持的。
  • max_conns: 默认: 2。一般不需要改。大流量场景可适当调大。
    • UDP 协议: 收发数据包使用的套接字数。
    • 启用了 query pipelining 模式的 TCP/DoT 协议: 最大连接数。
    • DoH 协议: HTTP 的最大连接数。HTTP/3 除外。
  • insecure_skip_verify: 禁用 TLS 服务器证书验证。
  • Linux 套接字设定。需要 CAP_NET_ADMIN 权限
    • so_mark:(v3.7+)。设定 SO_MARK。
    • bind_to_device: (v4.3+) 设定 SO_BINDTODEVICE。
  • ca: 用于 TLS 验证的 CA 证书路径。是数组,可填入多个证书。默认使用系统证书池。

forward 转发请求至上游(dnsproxy)

这个插件使用了 AdguardHome 的请求模块 dnsproxy
流水线逻辑:
  • 正向:转发请求至上游,获取应答后放入“请求上下文”。
  • 反向:无。
初始化参数说明:
tag: ""
type: "forward"
args:
# 上游服务器。可配置多个。至少要配置一个。会并发请求全部服务器。
upstream:
- addr: "https://dns.google/dns-query" # 必需。服务器地址。
# 当服务器地址为域名时,手动指定服务器的 IP 地址。可以配置多个。
# 如果配置了,则不会使用 bootstrap 服务器。会按顺序尝试连接配置的 IP。
ip_addr:
- "8.8.8.8"
- addr: "https://1.1.1.1/dns-query"
# 用于解析上游服务器域名的 bootstrap 服务器地址。可配置多个。为空会使用系统默认方式解析域名。
# 服务器地址不能包含域名。注意,dnsproxy 的 bootstrap 只会在启动时解析一次。服务器的 IP 在运行时不
# 会自动更新。如需更新服务器的 IP,请重启。
bootstrap:
- "tls://1.1.1.1"
- "https://223.5.5.5/dns-query"
# ...
timeout: 5 # 请求超时时间,单位: 秒。
insecure_skip_verify: false # 禁用 TLS 身份验证。
addr 地址格式和 AdguardHome 支持的格式一致。一些示例:
  • UDP: 8.8.8.8, 208.67.222.222:443
  • TCP: tcp://8.8.8.8
  • DoT: tls://dns.google
  • DoH: https://dns.google/dns-query
  • DoH3: h3://dns.google/dns-query
  • DoQ: quic://dns.adguard.com
  • DNS stamp: sdns://... 详见 dnscrypt.info
上述列表和参数说明不一定是最新的,请以 dnsproxy 的 README 为准。

ipset 将应答 IP 写入到系统 ipset

流水线逻辑:
  • 正向:将应答 IP 写入 ipset。
  • 反向:无。
初始化参数说明:
tag: ""
type: "ipset"
args:
set_name4: "" # 将 ipv4 地址写入这个 set。这个 set 的 family 类型需为 `inet`。
set_name6: "" # 将 ipv6 地址写入这个 set。这个 set 的 family 类型需为 `inet6`。
mask4: 0 # 写入 ipv4 地址时使用的掩码。默认: 24。
mask6: 0 # 写入 ipv6 地址时使用的掩码。默认: 32。
  • 插件添加新 IP 时相当于执行了 ipset add <set_name> <IP>/<mask> 命令。
  • 插件使用 netlink 直接与内核通信,不依靠 /sbin/ipset 命令。
  • 该插件不会创建 set。set 需要由用户提前创建。必须为 hash:net 类型。建议设置非零的 timeout 属性。实现自动清除长时间没有用到的地址。
创建 ipset 表命令示例:
ipset create <set_name4> hash:net family inet timeout 86400 maxelem 130050
ipset create <set_name6> hash:net family inet6 timeout 86400 maxelem 130050

nftset 将应答 IP 写入到系统 nftables

流水线逻辑:
  • 正向:将应答 IP 写入 nftables。
  • 反向:无。
初始化参数说明:
tag: ""
type: "nftset"
args:
table_family4: "" # 存 IPv4 地址的 table family 类型。
table_family6: "" # 存 IPv6 地址的 table family 类型。
table_name4: "" # 存 IPv4 地址 table 的名字。
table_name6: "" # 存 IPv6 地址 table 的名字。
set_name4: "" # 存 IPv4 set 的名字。
set_name6: "" # 存 IPv6 set 的名字。
mask4: 0 # 如果 set 带有 interval flag。写入 IPv4 地址时使用的掩码。默认: 24。
mask6: 0 # 如果 set 带有 interval flag。写入 IPv6 地址时使用的掩码。默认: 32。
  • 插件添加新 IP 时相当于执行了 nft add element <table_family> <table_name> <set_name> {<IP>/<mask>...} 命令。
  • 插件使用 netlink 直接与内核通信,并不依靠 /sbin/nft 命令。
  • 该插件不会创建对应的 set。set 需要由用户提前创建。
  • set 建议设置 timeout 来实现自动清除长时间没有用到的地址。
  • set 建议设置 interval flag。插件会自动识别这个 flag,然后会以 IP 段的方式存储 IP。
创建 nftables set 命令示例:
nft add table inet <table_name>
nft add set inet <table_name> <set_name4> {type ipv4_addr\; timeout 7d3h8m45s \; flags interval\;}
nft add set inet <table_name> <set_name6> {type ipv6_addr\; timeout 7d3h8m45s \; flags interval\;}

bufsize 限制请求的 EDNS0 的 UDP 负载大小

旧插件。建议使用新的 _misc_optm 插件。
流水线逻辑:
  • 正向:修改请求的 EDNS0 UDPSIZE (如果请求有 EDNS0)。
  • 反向:无。
初始化参数说明:
tag: ""
type: "bufsize"
args:
size: 512 # EDNS0 UDP 负载大小上限。默认 512。最大 4096。

padding 填充 DNS 报文至固定长度 (实验性)

参考 RFC 8467,使用 EDNS0 的 Padding Option 将 DNS 报文用 0 填充至固定长度。据 Google 说 (Google Plublic DNS 文档) 能防止流量分析保护隐私。包含 3 个预定义插件。
_pad_query: 填充请求
  • 流水线逻辑:
    • 正向:将请求填充至 128 bytes。
    • 反向:无。
  • 使用位置要求: 该插件到转发插件之间不能有修改请求的插件。否则请求大小可能会变,影响效果。
_enable_conditional_response_padding_enable_response_padding: 填充应答:
  • 流水线逻辑:
    • 正向:无。
    • 反向:填充应答至 468 bytes。_enable_response_padding 是如果客户端请求包含 EDNS0,就会填充应答。而 _enable_conditional_response_padding 则是如果客户端对请求进行了填充,才会填充应答。
  • 使用位置要求: 反向流水线填充应答后,直至流水线结束,不能有修改应答的插件。否则应答大小可能会变,影响效果。

redirect 替换请求的域名 (实验性)

替换(重定向)请求的域名。请求域名 A,但返回域名 B 的记录。
流水线逻辑:
  • 正向:替换请求的 QUESTION NAME。比如将请求域名 A 替换为 B。
  • 反向:修改应答的 ANSWER 的 NAME,比如将 B 替换回 A。使之看起来像请求 A 的记录。
注意: 没有能完美替换请求域名的方法。本插件的替换方式对于常见的 A,AAAA,TXT 等仅依赖 QUESTION 和 ANSWER 的请求是有效的。但不保证兼容其他类型请求以及各种 EDNS0 扩展。
初始化参数说明:
tag: ""
type: "redirect"
args:
rule:
# 格式 [域名匹配规则] [重定向至域名]
- www.google.com dns.google
- provider:tag # 可以是由 data-provider 提供的外部数据。
  • 域名匹配规则: 如果匹配方式被省略,则默认是 full 完整匹配。域名匹配方式详见 域名匹配规则

arbitrary 返回任意记录 (实验性) (v4.0+)

arbitrary 可以载入任意记录,并在匹配到对应请求时生成包含这些记录的应答,
流水线逻辑:
  • 正向:如果找到对应记录,则生成应答,终止跳过后续流水线。如果不在,则继续执行后续流水线。
  • 反向:无。
初始化参数说明:
tag: ""
type: "arbitrary"
args:
rr:
- example.com. 300 IN A 192.0.2.1 # 可以是文本格式的记录。
- _sip._tcp.example.com. 86400 IN SRV 0 5 5060 sipserver.example.com.
不同记录的文本格式请自行搜索。关键词: <记录类型> dns record wikipedia。在英文 wikipedia 可以直接找到绝大多数记录的格式。
暂不支持从外部数据载入记录。

_misc_optm 转发服务器优化大杂烩 (实验性) (v4.0+)

预定义插件,可直接使用。
适用于转发服务器。
流水线逻辑:
  • 正向:
    • 屏蔽掉互联网公共上游不能处理的请求(多个 QUESTION 和非 IN CLASS)。(立刻返回 REFUSE,跳过后续流水线。)
    • 将 EDNS0 UDPSIZE 限定在 1200。(v4.5+)
  • 反向
    • 去除 A/AAAA 应答的 CNAME 记录。减小应答长度。
    • 去除应答的 Padding。减小应答长度。
    • 随机打乱 A/AAAA 应答的 IP。负载均衡。
    • 如果请求无 EDNS0,去除上游应答的 EDNS0。保持 EDNS0 状态一致。
建议流水线位置: 流水线起始。

reverse_lookup IP 反查域名 API (v4.2+)(实验性)

该插件会缓存应答中的 IP 与 域名的关系。可在本地实现无延时的"有效 IP -> 域名"查询。支持 PTR 和 HTTP API。
流水线逻辑:
  • 正向:如果是 PTR 请求,并且命中缓存,则生成应答,跳过后续流水线。如果没有命中缓存,则继续执行后续流水线。
  • 反向:将 A/AAAA 应答的 IP 写入缓存。
初始化参数说明:
tag: ""
type: "reverse_lookup"
args:
size: 65535 # 内置缓存大小。默认 65535。
# redis-server 外部缓存地址。填入外部缓存会自动禁用内置的内存缓存。上文的 size 参数会被忽略。
# redis-server 需要 v2 及以上版本。如果多个 cache 插件连接到同一个 redis 数据库,数据是共享的。
# reverse_lookup 会为设定记录的 TTL,不会主动删除 redis-server 内的记录。内存大小清理方式等由
# redis-server 负责。
# reverse_lookup 假设 redis-server 的距离很近/延时很低而且性能足够。
# TCP 连接: `redis://<user>:<password>@<host>:<port>/<db_number>`
# Unix 连接: `unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>`
redis: "redis://localhost:6379/0"
# 缓存时间。秒。默认 1800 (30min)。应答记录的 TTL 也会被自动修改,限制在该值以下。
ttl: 1800
# 是否主动处理/响应 PTR 请求。如果 PTR 的 IP 命中缓存,则生成应答后自动 _return,跳过后续插件运行。
# 没有命中缓存则不会主动响应。会继续传递给后续插件。
# 该功能的一个用途是,当下游设备使用网络监视类工具查看网络连接时,开启域名反查功能大概率会看到 IP 对应的
# 真实域名(如果程序先请求域名再建立连接,而且域名经过了该插件处理。)
handle_ptr: false
建议流水线位置: 流水线起始。务必放在 cache 前,防止 cache 命中缓存后不执行 reverse_lookup 导致无法记录 IP。
查找缓存中 IP 对应域名的 API:http://<api_addr>/plugins/<plugin_tag>?ip=<ip_addr>
返回: 如果缓存中找到对应域名,则返回域名。如果缓存中没有找到对应域名,则返回空。如果参数不对则返回 HTTP 400。
示例: http://127.0.0.1:8080/plugins/my_reverse_lookup?ip=8.8.8.8,返回 dns.google. 注意返回的是 fully qualified 域名,末尾有 .

metrics_collector 统计数据收集器 (v4.2+)(实验性)

收集 prometheus metrics 数据。
流水线逻辑:
  • 正向:query_total =+1,thread =+1,response_latency_millisecond 开始计时。
  • 反向:err_total =+1 (如果出错),thread =-1,response_latency_millisecond 结束计时。
tag: ""
type: "metrics_collector"
# 没有 args 参数。
请求经过插件时,插件会收集
  • query_total 经过该插件的总请求数。
  • err_total 插件所在 sequence 的后续插件的总出错数。
  • thread 当前经过该插件的线程数。
  • response_latency_millisecond 插件所在 sequence 的后续插件的应答延时。
数据项名称为 mosdns_plugin_<plugin_tag>_<name>
建议流水线位置: 放在流水线起始位置就可以统计本条流水线的数据。

client_limiter 限制客户端的 QPS (v4.2+)(实验性)

限制客户端(以 IP 或 IP 段区分)的最大每秒请求数 QPS。
流水线逻辑:
  • 正向:如果客户端 QPS 超过阈值,则生成 REFUSE 应答,跳过后续流水线。
  • 反向:无。
tag: ""
type: "client_limiter"
args:
max_qps: 100 # 最大 QPS。客户端的请求数超过时,会 REFUSED 然后 _return。
v4_mask: 32 # ipv4 的 mask。用于限制整个 IP 段。默认 32。
v6_mask: 48 # ipv6 的 mask。默认 48。
建议流水线位置: 流水线起始。
注意: 该插件只能用于向非恶意客户端“友好”地回复 REFUSED。不能防攻击。

edns0_filter 过滤请求的 EDNS0 OPTION (实验性)(v4.4.1+)

过滤/限制客户端可以使用 EDNS0 OPTION。
TIps: 如果后面配置有启用了 cache_everything 的 cache 插件,该插件可用于提高缓存利用率。比如移除 Cookie 和 Padding,或只允许 ECS。
流水线逻辑:
  • 正向:从请求中移除/保留指定的 EDNS0 OPTION。
  • 反向:无。
以下预初始化的插件 tag 可直接使用:
  • _edns0_filter_no_edns0: 删除应答的 EDNS0。
  • _edns0_filter_ecs_only: 只保留 ECS。
初始化参数说明:
tag: ""
type: "edns0_filter"
args:
accept: [0,1,2] # 仅接受这些 EDNS0 OPTION。其余的将被移除。
discard: [0,1,2] # 仅移除这些 EDNS0 OPTION。其余的将被保留。
# accept 和 discard 只生效一个。优先级 accept > discard。如果全为空,则插件会
# 移除所有的 EDNS0 OPTION (相当于不接受任何 EDNS0 OPTION)。
建议流水线位置: 流水线起始,cache 前,转发插件前。

_query_summary (v4.5.3+) 记录请求摘要

在 info 级别记录请求。包括请求的域名,类型,客户端地址,应答状态,请求处理时间等。
建议流水线位置: 流水线起始,cache 前。
注: 如果请求没有或有多个 QUESTION,则不会有记录。

sleep 延时执行

调试。模拟延时用。
流水线逻辑:
  • 正向:延时。
  • 反向:无。
初始化参数说明:
tag:: ""
type: "sleep"
args:
duration: 100 # 等待时间。单位: 毫秒。

匹配器插件

匹配器插件匹配请求和应答状态,返回 true or false

query_matcher 匹配请求的特征

以下预初始化的插件 tag 可直接使用:
  • _qtype_AAAA: 请求的 QTYPE 是 AAAA。
  • _qtype_A_AAAA: 请求的 QTYPE 是 A 或 AAAA。
  • _query_edns0: 请求包含 EDNS0。
初始化参数说明:
tag: ""
type: "query_matcher"
args:
qtype: [1, 2, 3] # 匹配 QTYPE
qclass: [1, 2, 3] # 匹配 QCLASS
domain: # 匹配请求域名。这是个`域名匹配器`。详见下文。
- "google.com"
client_ip: # 匹配客户端 IP 。这是个 `IP 匹配器`。详见下文。
- "192.168.1.1/24"
ecs: # 匹配 ECS 的 IP 。这是个 `IP 匹配器`。详见下文。
- "192.168.1.1/24"
如果一个匹配器内设定了多个条件,则必须满足全部条件这个匹配器才会为 true

response_matcher 匹配应答的特征

以下预初始化的插件 tag 可直接使用:
  • _response_valid_answer: 应答包含有效的 ANSWER 记录(ANSWER 中有记录能与 QUESTION 对应)。(v4.0.0+)
初始化参数说明:
tag: ""
type: "response_matcher"
args:
rcode: [1, 2, 3] # 匹配 RCODE。
ip: # 匹配应答 A 和 AAAA 记录的 IP 。这是个 `IP 匹配器`。详见下文。
- "192.168.1.1/24"
cname: # 匹配应答 CNAME 域名。这是个`域名匹配器`。详见下文。
- "google.com"
如果一个匹配器内设定了多个条件,则必须满足全部条件这个匹配器才会为 true

域名匹配器

配置示例:
# 可以是文本数据。域名匹配规则详见下文。
- "google.com"
- "keyword:google"
- "regexp:.+\.google\.com$"
# 可以是由 data-provider 提供的外部数据,格式 "provider:tag"。
# 外部数据可以是包含多条文本数据的纯文本数据。
- "provider:data_tag"
# 也可以是 v2ray 域名数据。多个标签用 "," 分割,标签后支持用 "@" 只载入有对应 attr 的数据。
# 多个 "@" 为"或"关系,域名只要有其中任意 attr 就会被载入。
- "provider:geosite:cn@attr@attr,us@attr..."
文本域名数据一条规则一行。如果域名匹配方式被省略,则默认是 domain 域匹配。
域名匹配规则说明详见: 这里

IP 匹配器

配置示例:
# 可以是文本数据。每行一个 CIDR 或 IP,支持 IPv6。
- "192.168.1.1/24"
- "2001:250::/31"
- "192.168.1.1"
# 可以是由 data-provider 提供的外部数据,格式 "provider:tag"。
- "provider:data_tag" # 可以是纯文本文件。包含多条文本数据。
- "provider:geoip:cn,us..." # 可以是 v2ray geoip 文件。多个标签用 "," 分割。

关于 v2ray 数据包

mosdns 支持从 v2ray 数据包 (俗称 dat 文件)载入数据,但受限于 v2ray 数据包的打包格式,mosdns 从中读取数据,无论读取多少,都要完整解包。多个插件读取多次会出现重复解包的情况。性能不好的设备(主要是嵌入式路由等)上可能造成 mosdns 启动时间显著变长(数秒)。其他设备影响不大。
优化 v2ray 数据包加载速度:
  • (推荐)自己提前解包: mosdns v2dat 小工具命令可以解包数据文件成独立的文本文件。
  • 尽可能使用多标签 geoip.dat:cn,us... 一次载入全部所需数据,而非多行。写成多行会造成重复解包。

补充

域名匹配规则

域名规则有多个匹配方式:
  • domain: 开头,域匹配。e.g: domain:google.com 会匹配自身 google.com,以及其子域名 www.google.com, maps.l.google.com 等。
  • full: 开头,完整匹配。e.g: full:google.com 只会匹配自身。
  • keyword: 开头,关键字匹配。e.g: keyword:google.com 会匹配包含这个字段的域名,如 google.com.hk, www.google.com.hk
  • regexp: 开头,正则匹配(Golang 标准)。e.g: regexp:.+\.google\.com$
如果没有指定匹配方式,不同插件有不同的缺省值。
匹配方式按如下顺序生效: full > domain > regexp > keyword
相同匹配方式的规则按如下顺序生效:
  • domain 规则: 子域名优先。比如如果同时存在规则 google.comcomwww.google.com 会优先匹配 google.com,然后 com。(v3.9.0+)
  • regexpkeyword 规则生效顺序为规则导入的顺序。
性能:
  • domainfull 匹配使用 HashMap,复杂度 O(1)。每 1w 域名约占用 1M 内存。
  • keywordregexp 匹配需遍历,复杂度 O(n)。
到底了!没有找到想要的功能么?提交一个 Feature request
Last modified 1yr ago