可执行插件

传入一个请求,插件可以转发/修改/屏蔽/做各种乱七八糟的事。

sequence

按顺序组合多个插件。

sequence 是个可执行插件。可以被其他插件(比如服务器插件)调用。sequence 与 sequence 联动请使用自带的跳转命令 (goto, jump ...),不要使用 exec 。

Sequence 插件

cache

缓存 DNS 应答的插件。

如果请求命中缓存,则将缓存的应答放入请求。

当某一请求经过 cache 后,cache 会在这个请求处理流程结束时自动将应答放入缓存。用户无需介入。

注意: 本插件是本地缓存(适用于客户端或者局域网内的缓存)。不适用于服务器端缓存。不支持扩展 DNS 协议 (即 EDNS0),请求的 EDNS0 部分会被无视,应答的 EDNS0 会被略去。

初始化参数说明:

tag: ""
type: "cache"
args:
  size: 1024  # 内置内存缓存大小。单位: 条。默认: 1024。每个 cache 插件的内存缓存是独立的。

  # (实验性) lazy cache 设定。lazy_cache_ttl > 0 会启用 lazy cache。
  # 所有应答都会在缓存中存留 lazy_cache_ttl 秒,但自身的 TTL 仍然有效。如果命中过期的应答,
  # 则缓存会立即返回 TTL 为 5 的应答,然后自动在后台发送请求更新数据。
  # 相比强行增加应答自身的 TTL 的方法,lazy cache 能提高命中率,同时还能保持一定的数据新鲜度。
  lazy_cache_ttl: 0        # lazy cache 生存时间。单位: 秒。默认: 0 (禁用 lazy cache)。
                           # 建议值 86400(1天)~ 259200(3天)
  
  # (实验性) 将缓存保存在磁盘。插件启动时也会自动从该文件载入缓存。
  # 缓存将在 mosdns 被关闭时保存。
  dump_file: ./cache.dump
  # (实验性) 自动保存间隔。单位秒。默认 600。
  # 如果距离上次 dump 有 1024 次更新,则自动保存。
  dump_interval: 600

lazy cache,也叫“乐观缓存”,的优缺点:

  • 优点:可大幅提高缓存命中率。流行域名几乎 100% 命中。

  • 缺点:可能影响 DNS 更新频繁的域名正常使用,比如 DDNS,CDN。

  • 仅建议个人使用场景,有相关知识,了解其弊端的专业用户开启。 更多细节请参考 RFC 8767 。

Metrics 数据 mosdns_cache_<metrics_name>{tag=<plugin_tag>}:

  • query_total: 总请求数。

  • hit_total: 总命中数。(包括 lazy cache 的命中)

  • lazy_hit_total: lazy cache 总命中数

  • cache_size: 当前缓存大小。单位: 条。

API:

  • GET /flush 清空缓存

  • GET /dump 下载缓存

  • POST /load_dump 载入缓存

hosts

域名映射 IP 。如果请求的域名在 hosts 内,则将应答放入请求。

初始化参数说明:

tag: ""
type: "hosts"
args:
  entries:                         # []string
    - "google.com 108.177.122.113" # 记录
  files:                           # []string
    - "./hosts.txt"                # 从外部文件载入。

虽然都叫 hosts,但本插件所用的格式和平常 Win,Linux 系统内的那个 hosts 文件不一样。

格式:

  • 域名规则在前,IP 在后。支持一行多个 IP,支持 IPv6。

  • 如果域名匹配规则的方式被省略,则默认是 full 完整匹配。域名匹配规则详见 这里

格式示例:

# 支持一行多个 IP
dns.google 8.8.8.8 2001:4860:4860::8888 ...

forward

转发请求至上游服务器。获取应答后放入请求。

初始化参数说明:

tag: ""
type: "forward"
args:
  concurrent: 1 # 并发数。每次请求会从下面配置的 upstreams 里随机选取
                # concurrent 个 upstreams 发送请求。取最快返回的应答。超过 3 最多选
                # 3 个。默认 1 。
  upstreams:    # []upstream, 上游服务器。至少要配置一个。
    - tag: google_doh
      addr: "https://dns.google/dns-query"
      dial_addr: "8.8.8.8"
      bootstrap: "8.8.8.8"
      bootstrap_version: 4
      socks5: "127.0.0.1:1080"
      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
  • tag: 本上游的 tag。用于标识和 log。可省略。同一插件内该 tag 必需唯一。建议全局唯一。

  • addr 必需。服务器地址。格式: [protocol://]host[:port][/path]protocol 默认为 udp 。省略端口号会使用协议的默认值。

    • 支持的 protocol 有:

      • udp: DNS over UDP 协议。host 必需为 IP。

      • tcp: 基本 DNS over TCP 协议。host 必需为 IP。

      • tls: 基本 DNS over TLS 协议。(RFC 7858)

      • https: 基本 DNS over HTTPS 协议。基于 HTTP/2 ,但为了兼容也支持 HTTP/1 。使用 GET 请求。(RFC 8484)

      • quic: DNS over QUIC 协议。 (RFC 9250)

    • 实际应用中,因为不应该让一个 DNS 解析系统依赖另一个 DNS 解析系统,因此 DNS 服务器的 IP 都是固定的。所以服务器地址应优先使用 IP 地址而非域名。这样插件连接服务器时无需先解析服务器的域名,能立刻建立连接。如果 host 必需包含域名 (比如一些 DoT/DoH 服务器可能要求请求必需包含 SNI 或 HTTP HOST 头),可以将 IP 填入 dial_addr 。更详细说明:插件会依次尝试从以下途径获取服务器域名的 IP 。

      1. dial_addr: IP 协议层面建立连接时使用的地址。如果 host 是域名,此处填入 IP 可免去每次建立连接时解析服务器域名。支持指定端口号。

      2. bootstrap: (实验性) 手动指定用于解析 host 域名的 bootstrap 服务器。只能是 UDP 服务器。不推荐使用此方式。bootstrap 仅适用于没有固定 IP 的服务器,比如自建服务器。bootstrap_version 设定 4 或 6 可控制解析 IP 的版本。默认 0 等于 4 。

      3. 让系统去解析。注意: 此方式的解析时间插件不可控,可能影响性能。并且此时 mosdns 不能是系统的 DNS 服务器,否则会出现解析死循环。不推荐使用该方式。后续版本可能会禁止此方式。

  • socks5: socks5 服务器地址。格式 host:port。数据将会通过该代理中转。暂不支持用户名密码认证。只支持基于 TCP 的协议。

  • idle_timeout TCP/DoT/DoH 连接复用空连接保持时间。单位: 秒。默认 DoH/DoQ: 30 ,TCP/DoT: 10。一般不需要改。

  • enable_pipeline: TCP/DoT 使用 RFC 7766 新的 query pipelining 连接复用模式。

    • 启用后可大幅提高连接利用率,减少建立连接/握手的次数,进而降低响应延时。

    • 并非所有服务器都支持。必须确定服务器支持后再启用该选项。

    • Tips: 已知 Google 和 Cloudflare 的 TCP/DoT 是支持该模式的。知名的公共 DNS 服务商大多数都支持该模式。可以使用 mosdns probe pipeline {tcp|tls}://server_ip[:port] 测试命令测试服务器是否支持。比如 mosdns probe pipeline tls://8.8.8.8

  • enable_http3: (实验性) DoH 使用 HTTP/3 连接服务器。

    • 并非所有服务器都支持。必须确定服务器支持后再启用该选项。

  • max_conns: 默认: 2。

    • (5.3+) 已弃用。最大连接数量由插件自动控制。

    • 启用了 query pipelining 模式的 TCP/DoT 协议: 最大连接数。大流量场景可适当调大。

    • DoH 协议: HTTP 的最大连接数。HTTP/3 除外。大流量场景可适当调大。

  • insecure_skip_verify: 禁用 TLS 服务器证书验证。

  • Linux 套接字设定。需要 CAP_NET_ADMIN 权限

    • so_mark: 设定 SO_MARK。

    • bind_to_device: 设定 SO_BINDTODEVICE。

错误处理:

如果 forward 未能从上游获取应答 (连接失败,收到错误数据,等待应答超时等),则会抛出错误。

请注意: 来自服务器的 SEVFAIL 等应答属于正常的应答。不算错误。

sequence 中的调用参数:

指定上游进行转发。而非全部。

参数: [upstream_tag] ...

metrics 数据:

设定了 upstream.tag 的上游会有 metrics 数据。

mosdns_forward_<metric_name>{tag=<plugin_tag>, upstream=<upstream_tag>}

  • query_total 该上游总请求数。

  • err_total 该上游总出错数。(连接失败,无响应。不包括返回 SERVFAIL 的情况)

  • thread 该上游的线程数。(此时有多少请求正在被处理中)

  • response_latency_millisecond 该上游应答返回的延时。

  • 底层连接 (HTTP3 无该数据):

    • conn_opened_total 该上游打开的连接数。

    • conn_closed_total 该上游已关闭的连接数。

redirect

(实验性插件) 替换(重定向)请求的域名。请求域名 A,但返回域名 B 的记录。

注意: 没有能完美替换请求域名的方法。本插件的替换方式是插入一条 CNAME 。对于常见的 A,AAAA,TXT 等仅依赖 QUESTION 和 ANSWER 的请求是有效的。但不保证兼容其他类型请求以及各种 EDNS0 扩展。

初始化参数说明:

tag: ""
type: "redirect"
args:
  rules:
  # 格式 [域名匹配规则] [重定向至域名] 
    - www.google.com dns.google
  files:  # 从文件载入规则
    - ./rediect.txt
  • 域名匹配规则: 如果匹配方式被省略,则默认是 full 完整匹配。域名匹配方式详见 域名匹配规则

arbitrary

(实验性) arbitrary 可以载入任意 zone file (RFC 1035) 记录,并在匹配到对应请求时生成包含这些记录的应答。

初始化参数说明:

tag: ""
type: "arbitrary"
args:
  rules:
    - example.com. 300 IN A 192.0.2.1
    - _sip._tcp.example.com. 86400 IN SRV 0 5 5060 sipserver.example.com.
  files:
    - ./arbitrary.txt

不同记录的文本格式请自行搜索。关键词: <记录类型> dns record wikipedia。在英文 wikipedia 可以直接找到绝大多数记录的格式。

reverse_lookup

(实验性) 用 IP 反查域名。该插件会缓存应答中的 IP 与 域名的关系。支持 PTR 和 HTTP API。

如果是 PTR 请求,并且命中缓存,则生成应答。

初始化参数说明:

tag: ""
type: "reverse_lookup"
args:
  size: 65535 # 内置缓存大小。默认 65535。
  
  # 缓存时间。秒。默认 7200 (2h)。应答记录的 TTL 也会被自动修改,限制在该值以下。
  ttl: 7200
  # 是否主动处理/响应 PTR 请求。如果 PTR 的 IP 命中缓存,则生成应答。
  # 用途举例: 使用网络监视类工具查看网络连接时,开启域名反查功能大概率会看到 IP 对应的
  # 真实域名(如果程序先请求域名再建立连接,而且域名经过了该插件处理。)
  handle_ptr: false 
                    

查找缓存中 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 域名,末尾有 .

rate_limiter

(非常实验性) 用于限制客户端的请求速率。是匹配器。超过限制的请求返回 false。

注意: 此插件目前只能提醒"友善的客户端和用户"控制其流量。不能抗恶意攻击。目前属于中看不中用的插件。后续尝试和 linux 防火墙,脚本调用(云防火墙)等进行联动。

tag: ""
type: "rate_limiter"
args:
  qps: 20    # 客户端的请求 qps 限制。默认 20
  burst: 40  # 突发数。允许客户端瞬间突发请求数。默认 40
  mask4: 32  # IPv4 网段掩码。来自同网段的请求会被视为来自同一客户端。默认 32
  mask6: 48  # IPv6 网段掩码。默认 48

使用方式举例: 在 sequence 中 rate_limiter 匹配请求。判断 false (超 qps 请求),取反,返回 REFUSE。

- matches: "!$rate_limit"
  exec: reject 3

fallback

(实验性) 失败时回滚。

tag: ""
type: "fallback"
args:
  primary: executable_tag    # 主可执行插件的 tag
  secondary: executable_tag  # 副可执行插件的 tag
  threshold: 500           # 无响应回滚阈值。单位毫秒。默认 500 。
  always_standby: true     # 副可执行插件始终待命。 

回滚机制: 如果 primary 抛出错误,或返回但没有应答,或在 threshold 毫秒内无响应,则执行 secondary 。因无响应触发 fallback 时,如果 primary 比 secondary 先返回了应答,则依旧会采用 primary 的应答。

错误处理: 如果 primary 和 secondary 都无应答 (抛出了错误,无响应直到超时,返回了但无应答),则抛出错误。

always_standby: secondary 会和 primary 一并执行。secondary 的结果将会在需要回滚时,立刻被采用。

注意: fallback 是多线程。primary 和 secondary 是独立的线程,它们收到的请求是 fallback 收到的请求的镜像。任何一方对请求做出修改不会影响对方,也不会影响 fallback 所在的 sequence 。如果 primary 和 secondary 是 sequence ,则它是一级/根 sequence。

注意: 如需提高上游的稳定性,绝大多数(99%)的用户只需设置 forward 并发多个上游。只有极少数用户需要这个插件。

ecs_handler

(实验性) (v5.3+) 实现 EDNS0 Client Subnet 相关功能。

tag: ""
type: "ecs_handler"
args:
  forward: false        # 是否转发来自下游的 ecs
  preset: 1.2.3.4       # 发送预设 ecs
  send: false           # 是否发送 ecs
  mask4: 24             # ipv4 掩码。默认 24
  mask6: 48             # ipv6 掩码。默认 48

优先级:

  1. 如果 forward == true 并且下游请求包含 ecs。则转发该 ecs。否则

  2. 如果 preset != nil。则发送该预设 ecs。否则

  3. 如果 send == true,则发送客户端地址。

Last updated