# Sequence 插件

sequence 是一个 [可执行插件](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md) ，可以被其他插件调用。因其功能较多，单独写在本页面。

## 写在最前

### 参数帮助说明

* `{ arg1 | arg2 | ... }` 代表这几个参数必需选择其中一个(多选一)。
* `[ arg ]` 代表该参数是可选的。可省略。
* `...` 表示之前的参数可以无限重复。`*n` 表示之前的参数可以重复 n 次 。
* 没有符号的 `arg` 表示该参数不可省略。

## 参数

初始化参数说明:

```yaml
tag: ""
type: "sequence"
args:  # []rule
  - matches:                # []string, 匹配条件。多个匹配条件为 AND 关系。
    - qname $ad_domain      # [!]{ 内置匹配名称 | $已定义的匹配器插件tag } [参数]
    - "!qname $ad_domain"   # !前缀代表取反。
    exec: reject 3          # string, { 内置操作名称 | $已定义的可执行插件tag } [参数]
  - exec: $forward_google   # matches 条件可省略。
```

sequence 插件引用的所有已定义的插件 tag 必须在 sequence 前定义，否则 sequence 找不到对应插件。

一个 sequence 由多个 rule 构成。一个 rule 由零个或多个条件和一个操作构成。sequence 会依次执行每个 rule 。

可以把 sequence 近似成处理 DNS 请求的 iptables / nftables 的 chain 。复杂的处理逻辑可以配置多个 sequence 然后配合 `accept/reject/jump/goto/return` 操作在 sequence 之间跳跃。

可执行插件列表见：

{% content-ref url="/pages/VqNBFSilZJASwrqMIFlN" %}
[可执行插件](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md)
{% endcontent-ref %}

错误处理:

如果遇到插件抛出错误，sequence 不会继续执行后续规则，并继续抛出该错误。

{% code title="配置示例:" lineNumbers="true" %}

```yaml
tag: ""
type: "sequence"
args:
  # 屏蔽某些域名，比如广告域名。
  - matches:
      - qname $ad_domain
    exec: reject 3
  - ...
  
  # 将内网请求转交给 handle_local_query sequence 另行处理。
  - matches:
      - qname local internal.my.domain
    exec: goto handle_local_query
  - matches:
      - ptr_ip 10.0.0.0/8
    exec: goto handle_local_query

  # 让某些域名跳过 cache，比如的 DDNS 域名。然后仅
  # 修改 DDNS 域名的 TTL。
  - matches:
      - "!qname my.ddns.domain"
    exec: cache 1024
  - exec: forward 8.8.8.8
  - matches:
      - qname my.ddns.domain
    exec: ttl 1

  # env 实现"开关功能"。
  # 运行 mosdns 时设定 MOSDNS_ENABLE_CACHE 环境变量即可开启 cache 功能。
  - matches:
      - env MOSDNS_ENABLE_CACHE
    exec: cache 1024
  - ...
```

{% endcode %}

## 内建匹配器

### resp\_ip

匹配应答 A 和 AAAA 记录的 IP 。如果应答有多个 IP 只要任意 IP 匹配即为 true 。

参数: &#x20;

```
resp_ip { ip | $ip_set_tag | &ip_list_file } ...
```

示例: &#x20;

```
resp_ip 192.168.1.1/24 8.8.8.8 $local_ips &./local_ips.txt
```

* `ip` 可以是 IPv4/6, CIDR 。
* `ip_set_tag` 是类型为 [ip\_set](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/shu-ju-cha-jian.md#ip_set) 插件的 tag 。&#x20;
* `ip_list_file` 是文本类型的 IP/CIDR 表。

### client\_ip

匹配客户端的 IP 。

参数和示例和上文的 [resp\_ip](#resp_ip) 相同。

### ptr\_ip

匹配 PTR 请求的 IP。

参数和示例和上文的 [resp\_ip](#resp_ip) 相同。

### qname

匹配请求的 QNAME 。

参数:  `qname { domain_rule | $domain_set_tag | &domain_list_file } ...`

示例:  `qname google.com full:google.com $local_domains &./local_domains.txt`

* `domain_rule` 是域名表达式。支持扩展。详见 [域名匹配规则](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/yu-ming-pi-pei-gui-ze.md)。
* `domain_set_tag` 是类型为 [domain\_set](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/shu-ju-cha-jian.md#domain_set) 插件的 tag 。
* `domain_list_file` 是文本类型的域名表达式表 。

### cname

匹配应答的 CNAME 记录。如果应答有多个 CNAME 只要任意 CNAME 匹配即为 true 。

参数和示例和上文的 [qname](#qname) 相同。

### qclass

匹配请求的 QCLASS。

参数:  `qclass [uint16] ...`

示例:  `qclass 1 2 3`

### qtype

匹配请求的 QTYPE。

参数和示例和上文的 [qclass](#qclass) 相同。

### rcode

匹配应答的 RCODE。

参数和示例和上文的 [qclass](#qclass) 相同。

### has\_resp

如果有应答，返回 true 。

### has\_wanted\_ans

如果有期望的 ANSWER，返回 true 。"期望的ANSWER "指应答的 ANSWER 中包含和 QUESTOIN 类型相同的 record。比如请求 A 记录，应答里返回了 A 记录，则该判断为 true。请求 AAAA 记录，虽然返回了SUCCESS 应答但无 AAAA 记录，返回 false 。

### mark

参数:  `mark [uint32] ...`

示例:  `mark 1 2 3`

请求存在任意指定的标记即为 true 。

### string\_exp

(实验性) 字符串匹配器。

参数:  `string_exp string_src op [arg_string]`

示例:  `string_exp` server\_name`eq a.b.c d.e.f`

`string_src`: 匹配的字符串来源。可以是&#x20;

* url\_path: 来自 DoH 服务端请求的 url path 。
* server\_name: 来自服务端 TLS (DoH,DoT,DoQ) 的 SNI。
* $env: 以 $ 开头，环境变量的值。

`op` 是匹配方式。可以是 `eq` 相等，`prefix` 包含前缀，`suffix` 包含后缀，`contains` 包含，`regexp` 正则，`zl` 字符串长度为零。&#x20;

`arg_string` 是匹配参数，目前除了 zl 无参数，其余匹配方式均可包含多个字符串作为匹配规则。任意规则匹配即返回 true 。

注意: 受格式限制，目前不支持带空格的规则参数。

### 其他匹配器

#### \_true 和 \_false

永远返回 true 和 false 。方便脚本生成占位符。

`_` 前缀没有别的意义，只是防止匹配器名在某些配置格式里被误识别成 bool 类型。

#### env

参数:  `env env_key [env_value]`

示例:  `env CACHE_ENABLED` ， `env NETWORK_TYPE lan` 。

启动时查找 `env_key` ，如果值和 `env_value` 一致则返回 true。如果 `env_value` 省略，则 `env_key` 存在(包括零值的情况)即返回 true 。

#### random

参数:  `random prob_float64`

示例:  `random 0.5`&#x20;

以 `prob_float64` 的概率返回 true 。

## 内建操作

### forward

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

参数:  `forward upstream [upstream]...`

示例:  `forward 8.8.8.8 tls://8.8.8.8`

可配置多个上游。会并发请求，取最先返回的应答。如果超过 3 个上游，会随机选取 3 个上游并发。

`upstream` 支持四种协议。格式示例:

* 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`
* Tips: 使用 IP 地址能更快建立连接，因为不用解析服务器的 IP。除非情况特殊，否则不要使用带域名的地址。

这是个便捷设置。如需更多高级功能 (HTTP3，Socks5，SO\_MARK...)，可配置独立的 [forward](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md#forward) 插件。

### cache

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

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

不支持扩展 DNS 协议 (即 EDNS0)。

参数:  `cache [size]`，`size` 是缓存大小，单位: 条，默认 1024。

示例:  `cache 1024`

这是个便捷设置。如需更多高级功能 (lazy cache，缓存存盘...)，可配置独立的 [cache](/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/ke-zhi-xing-cha-jian.md#cache) 插件。

### black\_hole

生成包含特定 IP 的应答。

参数:  `black_hole [ip] ...`

示例:  `black_hole 127.0.0.1 ::1 0.0.0.0`

会自动识别参数中的 IPv4/6 。如果参数中指定了多个 IPv4/6，会全部包含在应答内。如果某一 IP 类型未指定，则不会生成对应类型的应答。

### drop\_resp

丢弃应答。用于触发 fallback。

### ecs

附加 ECS (EDNS0 Client Subnet) 至请求。支持 IPv4/6。IPv4 会使用 /24 mask，IPv6 使用 /48 。

参数 (v5.3+):  `ecs [ip]`&#x20;

参数 (v5.2 以前)(弃用):  `ecs [ip/mask] [ip/mask]` 。v5.3+ 后只会用第一个 IP，mask 会忽略。

示例:  `ecs 1.2.3.4`

### forward\_edns0opt

&#x20;(实验性)(v5.3+) 允许客户端的这些 EDNS0 Option 转发至上游。

参数:  `forward_edns0opt [option_code] ...`&#x20;

注意:&#x20;

* 此插件不可以执行多次! 否则转发至上游的请求中 EDNS0 Option 将出现重复，导致协议错误。
* 并不是所有 EDNS0 Option 都能被转发。此插件设计的目是转发 10 (Cookie) 和 15 (Extended DNS Error)。

### ipset

(实验性) 写入应答 IP 至 ipset 。仅支持 Linux 。插件会直接用 netlink 和内核通信，不依赖也不运行 ipset 命令，效率很高。

参数:  `ipset [set_name,set_family,mask]*2`

示例:  `ipset my_set,inet,24 my_set6,inet6,48`

* `set_family` = `{ inet | inet6 }`
* `mask` 是写入 IP 时使用的掩码

### nftset

(实验性) 写入应答 IP 至 nftables 。仅支持 Linux 。插件会直接用 netlink 和内核通信，不依赖也不运行 nft 命令，效率很高。

参数: &#x20;

```
nftset [table_family,table_name,set_name,set_dtype,mask]*2
```

示例: &#x20;

```
nftset inet,my_table,my_set,ipv4_addr,24 inet,my_table,my_set,ipv6_addr,48
```

* `table_family` = `{ ip | ip6 | inet }` table 表的类型。
* `set_dtype` = `{ ipv4_addr | ipv6_addr }` set 的类型。
* `mask` 是写入 IP 时使用的掩码。插件会自动判断 set 是否带有 interval 这个 flag，如果 set 有 interval flag 的话，会以 IP 段的形式写入 set 。

<details>

<summary>ipset/nftables 配合 cache 示例</summary>

以 ipset 为例:

```yaml
tag: ""
type: "sequence"
args:
  - exec: cache 1024
  - matches:        # 如果缓存命中，则不会转发请求至上游。
    - "!has_resp"
    exec: forward 8.8.8.8
  - exec: ipset ...  # ipset 每次请求都能被刷新
```

</details>

### query\_summary

以 info 级别在 log 打印请求的摘要。包含: 请求的域名，类型，客户端 IP，应答的 RCODE，错误信息，后续规则处理请求的用时。

参数: `query_summary [ msg_title ]`

示例: `query_summary query from server 1`

* `msg_title` 的文字会写在 log 的 `msg` 键上。

如果一个请求经过了多个 query\_summary，log 打印的顺序是反的，即先打印最后经过的 query\_summary 的 log。这不是 bug 。

### metrics\_collector

(实验性) 收集 prometheus metrics 数据。

参数: `metrics_collector collector_name`&#x20;

* `collector_name` 不可重复。

暴露的 metrics 数据项名称为

`mosdns_metrics_collector_<metric_name>{name=collector_name}`。

插件会收集以下 metric:

* query\_total 经过该收集器的总请求数。
* err\_total 该收集器的后续规则的总出错数。
* thread 该收集器后续规则的线程数。(此时有多少请求正在被后续规则处理中)
* response\_latency\_millisecond 该收集器后续命令返回的延时。

### prefer\_ipv4/6

希望客户端解析双栈域名 IP 时，只返回 IPv4/6 。但不影响纯 IPv6/4 (单栈)域名的解析。

`prefer_ipv4` 和 `prefer_ipv6` 是两个操作。下面为了表述方便，仅以 `prefer_ipv4` 为例。`prefer_ipv6` 同理。

`prefer_ipv4` 让下游客户端支持双栈时优先使用 IPv4。`prefer_ipv4` 处理 AAAA 请求时会自动发送 A 请求测试域名是否是双栈，该新产生的 A 请求会和原 AAAA 请求一并继续执行后续规则。如果最后 A 和 AAAA 请求都获得到了 IP (域名为双栈)，则原 AAAA 请求被屏蔽。这样客户端只能收到 A 记录，进而强迫客户端使用 IPv4

(实验性特性)(v5.3+) 插件会缓存域名的双栈支持情况，以避免频繁测试。缓存大小 65535 。缓存时长 1 小时。暂不支持自定义。

使用位置要求: 后续规则必须能生成应答 (比如有 forward)。

sequence 示例:

```yaml
# 所有请求都优先返回 ipv4，屏蔽 ipv6。
- exec: prefer_ipv4
- ...
- exec: forward 8.8.8.8

# 配合匹配器实现仅特定域名生效。
- matches:
    - qname my.domain
  exec:
    - prefer_ipv4
- ...
- exec: forward 8.8.8.8
```

### ttl&#x20;

修改应答的 TTL。

参数: `ttl { fix | min-max }`

示例: `ttl 300` 将 ttl 固定在 300， `ttl 300-600` 限制 ttl 在 300 到 600 之间。`ttl 300-0` 限制 ttl 不小于 300 。

### mark

在请求上打标记。

参数:  `mark [uint32] ...`

示例:  `mark 1 2 3`

### 调试用

#### sleep

等待一段时间。增加/模拟延时用。

参数: `sleep millisecond`

#### debug\_print

以 info 级别在 log 打印完整的请求和应答信息。

参数: `debug_print [msg_title]`

示例: `debug_print query from server 1`

* `msg_title` 的文字会写在 log 的 `msg` 键上

## 内建跳转操作

### accept

停止后续规则执行。如果本 sequence 存在上级 sequence，所有上级的 sequence 也不再继续执行后续规则。

### reject

参数: `reject [rcode]`

示例: `reject 5`

和 accept 一样，如果本 sequence 存在上级 sequence，所有上级的 sequence 也不再继续执行后续规则。同时生成 `rcode` 应答。不指定 `rcode` 默认为 5 (REFUSED) 。

### return

停止后续规则执行。如果所在 sequence 是从另一 sequence jump 过来的，则返回源 sequence 继续执行。

### goto

参数: `goto sequence_tag`

示例: `goto my_next_sequence`

`sequence_tag` 是目标 sequence 插件的 tag。将不再执行本 sequence 后续规则，开始执行目标 sequence 。

### jump

参数: `jump sequence_tag`

示例: `jump my_next_sequence`

`sequence_tag` 是目标 sequence 插件的 tag。将开始执行目标 sequence，目标 sequence 结束或 return 后，返回本 sequence 继续执行。目标 sequence accept/reject 后，本 sequence 和所有上级的 sequence 不再继续执行后续规则。

## mark 高级用法示例

实现逻辑或，单次匹配后执行多个 exec，以及实现 if ... else。

```yaml
- tag: main
  type: sequence
  args:
  
    # 匹配成功后打标
    # 打相同标签可实现"逻辑或"
    - matches:
      - "match_A"
      exec: mark 123456789
    - matches:
      - "match_B"
      exec: mark 123456789

    # 连续执行
    - matches: mark 123456789
      exec: do_something_1
    - matches: mark 123456789
      exec: do_something_2
    # ...
    # 连续执行 else
    - matches: "!mark 123456789"
      exec: else_do_something_1
    - matches: "!mark 123456789"
      exec: else_do_something_2
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://irine-sistiana.gitbook.io/mosdns-wiki/mosdns-v5/ru-he-pei-zhi-mosdns/sequence-cha-jian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
