From b63b2a3b0088e6cb803c42017198c3d8de16aee3 Mon Sep 17 00:00:00 2001 From: Meow <197331664+Meo597@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:22:13 +0800 Subject: [PATCH] DNS outbound: Add `rules` (matches `qtype`, `domain`, then `action`) --- docs/config/dns.md | 2 +- docs/config/outbounds/dns.md | 163 ++++++++++++++++++++++++++---- docs/en/config/dns.md | 2 +- docs/en/config/outbounds/dns.md | 169 ++++++++++++++++++++++++++++---- docs/ru/config/dns.md | 2 +- docs/ru/config/outbounds/dns.md | 169 ++++++++++++++++++++++++++++---- 6 files changed, 447 insertions(+), 60 deletions(-) diff --git a/docs/config/dns.md b/docs/config/dns.md index a993fcb7..705cde0d 100644 --- a/docs/config/dns.md +++ b/docs/config/dns.md @@ -12,7 +12,7 @@ Xray 内置的 DNS 模块,主要有三大用途: - 如 在 `freedom` 出站中,将 `domainStrategy` 设置为 `UseIP`, 由此出站发出的请求, 会先将域名通过内置服务器解析成 IP, 然后进行连接。 - 如 在 `sockopt` 中,将 `domainStrategy` 设置为 `UseIP`, 此出站发起的系统连接,将先由内置服务器解析为 IP, 然后进行连接。 -- 透明代理时劫持 DNS 流量;或直接对外暴露 53 端口充当递归 DNS 服务器。 +- TUN/透明代理时通过路由和 DNS 出站组合,以劫持 DNS 流量到此模块;或直接对外暴露 53 端口充当递归 DNS 服务器。 ::: tip TIP 1 DNS 服务器默认进入路由系统进行匹配,除非其包含 `+local` 在其中使用域名时,注意可能的回环问题,`hosts` 可能有帮助。 diff --git a/docs/config/outbounds/dns.md b/docs/config/outbounds/dns.md index 9de4598a..9e7896e2 100644 --- a/docs/config/outbounds/dns.md +++ b/docs/config/outbounds/dns.md @@ -1,27 +1,38 @@ # DNS -DNS 是一个出站协议,主要用于拦截和转发 DNS 查询。 +DNS 是一个出站协议,用于接收由 routing 送入的 DNS 查询,并按规则转发或处理。 -此出站协议只能接收 DNS 流量(包含基于 UDP 和 TCP 协议的查询),其它类型的流量会导致错误。 +此出站只支持传统明文 DNS,即基于 UDP 和 TCP 的查询;DoH、DoT、DoQ 等非传统明文 DNS 不适用于此出站。常见场景是 TUN、透明代理或 `dokodemo-door` 接收到 DNS 流量后,再由 routing 将其分流到此出站。 -在处理 DNS 查询时,此出站协议会将 IP 查询(即 A 和 AAAA)转发给内置的 [DNS 服务器](../dns.md)。其它类型的查询流量见下的 `nonIPQuery`。 +它可以按规则将查询放行到目标 DNS 服务器、`hijack` 到内置的 [DNS 服务器](../dns.md) 进一步处理、直接丢弃或显式拒绝,也可以改写目标地址、端口和传输协议。 ## OutboundConfigurationObject ```json { - "network": "tcp", + "network": "udp", "address": "1.1.1.1", "port": 53, "userLevel": 0, - "nonIPQuery": "drop", - "blockTypes": [] + "rules": [ + { + "action": "reject", + "domain": ["domain:example.com"] + }, + { + "action": "direct", + "qtype": 65, + "domain": ["geosite:geolocation-!cn"] + } + ] } ``` -> `network`: "tcp" | "udp" +上例仅示意各字段写法,完整配置见下方示例。 -修改 DNS 流量的传输层协议,可选的值有 `"tcp"` 和 `"udp"`。当不指定时,保持来源的传输方式不变。 +> `network`: [ "tcp" | "udp" ] + +修改 DNS 流量的传输层协议,可选值为 `"tcp"` 和 `"udp"`。当不指定时,保持来源的传输方式不变。 > `address`: address @@ -35,20 +46,138 @@ DNS 是一个出站协议,主要用于拦截和转发 DNS 查询。 用户等级,连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。 -userLevel 的值,对应 [policy](../policy.md#policyobject) 中 `level` 的值。如不指定,默认为 0。 +`userLevel` 的值,对应 [policy](../policy.md#policyobject) 中 `level` 的值。如不指定,默认为 `0`。 -> `nonIPQuery`: string +> `rules`: \[[RuleObject](#ruleobject)\] -控制非 IP 查询(非 A 和 AAAA),`"drop"` 丢弃; `"skip"` 不由内置 DNS 服务器处理,将转发给目标; `"reject"` 返回一个 DNS reject 响应,直接显式拒绝请求,相比 `"drop"` 可以避免应用程序消耗过长时间等待 DNS 响应到超时。 +按顺序匹配 DNS 查询规则,并支持按 `qtype` 和 `domain` 进行细粒度控制。 -默认值为 `"reject"`。 +若未命中任何规则,则使用内置兜底规则:A 和 AAAA 查询会被导入内置 DNS 模块,其它类型会被显式拒绝。 -> `blockTypes`: array +## RuleObject -为一个 int 数组,屏蔽数组中的查询类型,如 `"blockTypes": [65,28]` 表示屏蔽 type 65(HTTPS) 和 28(AAAA). 常见用途包括屏蔽 type 65 以阻止浏览器发起 ECH. +```json +{ + "action": "hijack", + "qtype": 1, + "domain": ["geosite:cn"] +} +``` -由于 `nonIPQuery` 默认 drop 所有非 A 和 AAAA 查询, 所以需要将其设置为 skip 本选项才能进一步发挥作用。当然也可以不修改,单纯用来屏蔽 A 或者 AAAA 来屏蔽 IPv4/IPv6 查询,但非常不推荐那么做,建议在内置 DNS 的 `queryStrategy` 对相关内容进行设置。 +规则中的各匹配条件为与关系;省略某个条件时,表示对此条件不作限制。 -注意:当只使用 `blockTypes` 屏蔽 A 或 AAAA 时, 如果 `nonIPQuery` 设置为了 `reject` 那么屏蔽方式也会是返回 DNS reject 而不是丢弃。 +> `action`: [ "direct" | "hijack" | "drop" | "reject" ] -## DNS 配置实例 +定义规则命中后的动作。 + +- `direct`: 直接放行到目标 DNS 服务器;若同时配置了出站级别的 `network`、`address` 或 `port`,则按改写后的目标继续转发。 +- `hijack`: 将查询导入内置的 [DNS 服务器](../dns.md) 继续处理,可用于按照内置 DNS 的配置进一步分流;目前仅支持 A 和 AAAA 记录。 +- `drop`: 直接丢弃请求,不返回响应。 +- `reject`: 返回显式拒绝响应,相比 `drop` 可以避免应用长时间等待 DNS 超时。 + +> `qtype`: number | string + +匹配 DNS 查询类型,有三种形式: + +- `"a-b"`:`a` 和 `b` 均为整数。这个范围是一个前后闭合区间,当查询类型落在此范围内时,此规则生效。 +- `a`:`a` 为整数。当查询类型为 `a` 时,此规则生效。 +- 以上两种形式的混合,以逗号 `,` 分隔。形如:`"1,3,23-24"`。 + +常见类型编号可参考 [DNS 记录类型列表](https://zh.wikipedia.org/zh-cn/DNS%E8%AE%B0%E5%BD%95%E7%B1%BB%E5%9E%8B%E5%88%97%E8%A1%A8)。 + +省略时表示匹配所有查询类型。 + +> `domain`: [string] + +匹配域名列表,写法与 [路由规则中的 `domain`](../routing.md#ruleobject) 一致,例如 `domain:example.com`、`full:example.com`、`geosite:cn`。省略时表示不限制域名。 + +## DNS 配置实例 + +下面的示例演示一个实际场景:透明代理环境中,入站开启 `sniffing` 做域名 / SNI 分流,国外域名走代理,其余 IP 流量直连;同时通过 `dns-out` 拒绝国外域名的 HTTPS 记录,以减少客户端获取 ECH 配置后影响明文 SNI 分流的情况,并将常见的 MX、TXT、SRV 等查询转发到指定上游;代理服务器没有 IPv6 环境因此还需要屏蔽 AAAA 查询。 + +```json +{ + "inbounds": [ + { + "tag": "all-in", + "port": 12345, + "protocol": "dokodemo-door", + "settings": { + "network": "tcp,udp", + "followRedirect": true + }, + "sniffing": { + "enabled": true, + "destOverride": ["http", "tls", "quic"], + "routeOnly": true + }, + "streamSettings": { + "sockopt": { + "tproxy": "tproxy" + } + } + } + ], + "dns": { + "servers": ["https+local://1.1.1.1/dns-query"] + }, + "outbounds": [ + { + "tag": "direct", + "protocol": "freedom" + }, + { + "tag": "proxy", + "protocol": "vless", + "settings": { + // 忽略... + } + }, + { + "tag": "dns-out", + "protocol": "dns", + "settings": { + "network": "tcp", + "address": "1.1.1.1", + "port": 53, + "rules": [ + { + "action": "reject", + "qtype": "28,65", + "domain": ["geosite:geolocation-!cn"] + }, + { + "action": "direct", + "qtype": "15-16,33" + } + ] + } + } + ], + "routing": { + "domainStrategy": "AsIs", + "rules": [ + { + "inboundTag": ["all-in"], + "network": "tcp,udp", + "port": "53", + "outboundTag": "dns-out" + }, + { + "domain": ["geosite:geolocation-!cn"], + "outboundTag": "proxy" + } + ] + } +} +``` + +上例的行为如下: + +- `all-in` 开启了 `sniffing`,并使用 `routeOnly: true` 让 routing 能基于嗅探出的 HTTP、TLS、QUIC 目标域名进行分流,同时保留原始目标地址。 +- 来自 `all-in`、发往 53 端口的 UDP/TCP 明文 DNS 查询,会被 routing 规则分流到 `dns-out`。 +- 普通流量中,`geosite:geolocation-!cn` 走 `proxy`,未命中该域名规则的流量自动默认走第一个出站 `direct`。 +- `geosite:geolocation-!cn` 中域名的 `qtype` 为 `65` 的 HTTPS 记录会被显式拒绝,可用于配合基于明文 SNI 的分流。 +- `geosite:geolocation-!cn` 中域名的 `qtype` 为 `28` 的 AAAA 查询会被显式拒绝,可用于屏蔽国外域名的 IPv6 解析。 +- `qtype` 为 `15-16,33` 的查询会被直接放行,并按出站配置转发到 `1.1.1.1:53`,传输方式改为 TCP。 +- 其余未命中的查询会进入默认兜底逻辑:A 和 AAAA 查询被导入内置 DNS 模块,其它类型被显式拒绝;内置 DNS 再通过 `https+local://1.1.1.1/dns-query` 向上游发起查询,避免形成回环。 diff --git a/docs/en/config/dns.md b/docs/en/config/dns.md index 8aba1383..04253a90 100644 --- a/docs/en/config/dns.md +++ b/docs/en/config/dns.md @@ -12,7 +12,7 @@ The built-in DNS module in Xray has three main purposes: - For example, in a `freedom` outbound, if `domainStrategy` is set to `UseIP`, requests sent from this outbound will first resolve the domain to an IP using the built-in server before connecting. - For example, in `sockopt`, if `domainStrategy` is set to `UseIP`, system connections initiated by this outbound will first resolve to an IP using the built-in server before connecting. -- **DNS Traffic Hijacking (Transparent Proxy) or Acting as a Recursive DNS Server:** Directly exposing port 53 to serve as a DNS server. +- **TUN/Transparent Proxy DNS Traffic Hijacking:** Combines routing with the DNS outbound to hijack DNS traffic into this module; or directly exposes port 53 to act as a recursive DNS server. ::: tip TIP 1 The DNS server enters the routing system for matching by default unless it contains `+local`. When using domain names within it, be aware of potential routing loops; `hosts` may help. diff --git a/docs/en/config/outbounds/dns.md b/docs/en/config/outbounds/dns.md index 1089a149..2d9e115b 100644 --- a/docs/en/config/outbounds/dns.md +++ b/docs/en/config/outbounds/dns.md @@ -1,54 +1,183 @@ # DNS -DNS is an outbound protocol, mainly used to intercept and forward DNS queries. +DNS is an outbound protocol used to receive DNS queries sent in by routing, then forward or process them according to rules. -This outbound protocol can only receive DNS traffic (including queries based on UDP and TCP protocols); other types of traffic will cause errors. +This outbound only supports traditional plaintext DNS queries over UDP and TCP; non-plaintext DNS protocols such as DoH, DoT, and DoQ are not applicable to this outbound. Common scenarios include TUN, transparent proxy, or `dokodemo-door` receiving DNS traffic and then routing sending that traffic to this outbound. -When processing DNS queries, this outbound protocol forwards IP queries (i.e., A and AAAA) to the built-in [DNS server](../dns.md). For other types of query traffic, see `nonIPQuery` below. +It can allow queries to the target DNS server, `hijack` them to the built-in [DNS server](../dns.md) for further processing, drop them, or explicitly refuse them according to rules. It can also rewrite the target address, port, and transport protocol. ## OutboundConfigurationObject ```json { - "network": "tcp", + "network": "udp", "address": "1.1.1.1", "port": 53, "userLevel": 0, - "nonIPQuery": "drop", - "blockTypes": [] + "rules": [ + { + "action": "reject", + "domain": ["domain:example.com"] + }, + { + "action": "direct", + "qtype": 65, + "domain": ["geosite:geolocation-!cn"] + } + ] } ``` -> `network`: "tcp" | "udp" +The example above only demonstrates the field syntax. See the full example below for a complete configuration. -Modifies the transport layer protocol for DNS traffic. Optional values are `"tcp"` and `"udp"`. When unspecified, the source transport method remains unchanged. +> `network`: [ "tcp" | "udp" ] + +Modifies the transport protocol used for DNS traffic. Available values are `"tcp"` and `"udp"`. If omitted, the original transport method is preserved. > `address`: address -Modifies the DNS server address. When unspecified, the address specified in the source remains unchanged. +Modifies the DNS server address. If omitted, the address specified by the source is preserved. > `port`: number -Modifies the DNS server port. When unspecified, the port specified in the source remains unchanged. +Modifies the DNS server port. If omitted, the port specified by the source is preserved. > `userLevel`: number -User level. Connections will use the [Local Policy](../policy.md#levelpolicyobject) corresponding to this user level. +User level. Connections will use the [local policy](../policy.md#levelpolicyobject) corresponding to this user level. -The value of `userLevel` corresponds to the value of `level` in [policy](../policy.md#policyobject). If not specified, it defaults to 0. +The value of `userLevel` corresponds to the `level` value in [policy](../policy.md#policyobject). If omitted, it defaults to `0`. -> `nonIPQuery`: string +> `rules`: \[[RuleObject](#ruleobject)\] -Controls non-IP queries (non-A and non-AAAA). `"drop"` means discard; `"skip"` means it is not processed by the built-in DNS server and is forwarded to the destination; `"reject"` returns a DNS reject response, explicitly refusing the request immediately. Compared to `"drop"`, this avoids applications waiting too long for a DNS response until timeout. +Matches DNS query rules in order, and supports fine-grained control by `qtype` and `domain`. -The default value is `"reject"`. +If no rule is matched, the built-in fallback rule is used: A and AAAA queries are imported into the built-in DNS module, while other query types are explicitly refused. -> `blockTypes`: array +## RuleObject -An integer array used to block query types listed in the array. For example, `"blockTypes": [65,28]` means blocking type 65 (HTTPS) and 28 (AAAA). Common uses include blocking type 65 to prevent browsers from initiating ECH. +```json +{ + "action": "hijack", + "qtype": 1, + "domain": ["geosite:cn"] +} +``` -Since `nonIPQuery` drops all non-A and non-AAAA queries by default, this option requires `nonIPQuery` to be set to `skip` to take further effect on other types. Of course, you can also use it solely to block A or AAAA (IPv4/IPv6 queries), but this is highly discouraged. It is recommended to configure `queryStrategy` in the built-in DNS settings for relevant content instead. +All matching conditions in a rule are combined with AND logic. If a condition is omitted, that condition is not restricted. -Note: When using `blockTypes` to block only A or AAAA, if `nonIPQuery` is set to `reject`, the blocking method will also be to return a DNS reject response instead of dropping. +> `action`: [ "direct" | "hijack" | "drop" | "reject" ] -## DNS Configuration Examples +Defines the action to take when the rule matches. + +- `direct`: Allows the query directly to the target DNS server. If outbound-level `network`, `address`, or `port` is also configured, the query is forwarded to the rewritten target. +- `hijack`: Imports the query into the built-in [DNS server](../dns.md) for further processing. This can be used for additional routing based on the built-in DNS configuration. Currently, only A and AAAA records are supported. +- `drop`: Drops the request directly without returning a response. +- `reject`: Returns an explicit refusal response. Compared with `drop`, this can prevent applications from waiting too long for a DNS timeout. + +> `qtype`: number | string + +Matches DNS query types. It has three forms: + +- `"a-b"`: `a` and `b` are both integers. This is a closed interval; the rule takes effect when the query type falls within this range. +- `a`: `a` is an integer. The rule takes effect when the query type is `a`. +- A comma-separated mix of the two forms above. For example: `"1,3,23-24"`. + +Common type numbers can be found in the [List of DNS record types](https://en.wikipedia.org/wiki/List_of_DNS_record_types). + +If omitted, all query types are matched. + +> `domain`: [string] + +Matches a list of domains. The syntax is the same as [`domain` in routing rules](../routing.md#ruleobject), such as `domain:example.com`, `full:example.com`, and `geosite:cn`. If omitted, domains are not restricted. + +## DNS Configuration Example + +The following example demonstrates a practical scenario: in a transparent proxy environment, the inbound enables `sniffing` for domain / SNI routing, foreign domains go through the proxy, and other IP traffic goes directly. At the same time, `dns-out` refuses HTTPS records for foreign domains to reduce cases where clients obtain ECH configuration and affect plaintext SNI routing, forwards common MX, TXT, SRV, and similar queries to a specified upstream, and refuses AAAA queries because the proxy server has no IPv6 environment. + +```json +{ + "inbounds": [ + { + "tag": "all-in", + "port": 12345, + "protocol": "dokodemo-door", + "settings": { + "network": "tcp,udp", + "followRedirect": true + }, + "sniffing": { + "enabled": true, + "destOverride": ["http", "tls", "quic"], + "routeOnly": true + }, + "streamSettings": { + "sockopt": { + "tproxy": "tproxy" + } + } + } + ], + "dns": { + "servers": ["https+local://1.1.1.1/dns-query"] + }, + "outbounds": [ + { + "tag": "direct", + "protocol": "freedom" + }, + { + "tag": "proxy", + "protocol": "vless", + "settings": { + // Omitted... + } + }, + { + "tag": "dns-out", + "protocol": "dns", + "settings": { + "network": "tcp", + "address": "1.1.1.1", + "port": 53, + "rules": [ + { + "action": "reject", + "qtype": "28,65", + "domain": ["geosite:geolocation-!cn"] + }, + { + "action": "direct", + "qtype": "15-16,33" + } + ] + } + } + ], + "routing": { + "domainStrategy": "AsIs", + "rules": [ + { + "inboundTag": ["all-in"], + "network": "tcp,udp", + "port": "53", + "outboundTag": "dns-out" + }, + { + "domain": ["geosite:geolocation-!cn"], + "outboundTag": "proxy" + } + ] + } +} +``` + +The example behaves as follows: + +- `all-in` enables `sniffing` and uses `routeOnly: true`, allowing routing to split traffic based on sniffed HTTP, TLS, and QUIC target domains while preserving the original target address. +- UDP/TCP plaintext DNS queries from `all-in` to port 53 are routed to `dns-out`. +- For regular traffic, `geosite:geolocation-!cn` goes through `proxy`; traffic that does not match this domain rule automatically uses the first outbound, `direct`. +- HTTPS records with `qtype` `65` for domains in `geosite:geolocation-!cn` are explicitly refused, which can help with plaintext SNI-based routing. +- AAAA queries with `qtype` `28` for domains in `geosite:geolocation-!cn` are explicitly refused, which can be used to block IPv6 resolution for foreign domains. +- Queries with `qtype` `15-16,33` are allowed directly and forwarded to `1.1.1.1:53` according to the outbound configuration, using TCP as the transport. +- Queries that do not match any rule enter the built-in fallback logic: A and AAAA queries are imported into the built-in DNS module, while other query types are explicitly refused. The built-in DNS then queries upstream through `https+local://1.1.1.1/dns-query`, avoiding a loop. diff --git a/docs/ru/config/dns.md b/docs/ru/config/dns.md index b5c4b354..88f31891 100644 --- a/docs/ru/config/dns.md +++ b/docs/ru/config/dns.md @@ -12,7 +12,7 @@ - Например, в `freedom` Outbound, если `domainStrategy` установлен в `UseIP`, запрос, исходящий из этого Outbound, сначала будет разрешен в IP через встроенный сервер, а затем произойдет подключение. - Например, в `sockopt`, если `domainStrategy` установлен в `UseIP`, системное подключение, инициированное этим Outbound, сначала будет разрешено в IP встроенным сервером. -- Перехват DNS-трафика в режиме Transparent Proxy или работа в качестве рекурсивного DNS-сервера, открытого на порту 53. +- Перехват DNS-трафика в режиме TUN/Transparent Proxy через связку routing и DNS outbound, чтобы направлять DNS-трафик в этот модуль; либо работа в качестве рекурсивного DNS-сервера, открытого на порту 53. ::: tip TIP 1 DNS-запросы, отправляемые встроенным DNS-сервером, автоматически перенаправляются в соответствии с конфигурацией маршрутизации (Routing). diff --git a/docs/ru/config/outbounds/dns.md b/docs/ru/config/outbounds/dns.md index 0ae83ed1..83b0ce28 100644 --- a/docs/ru/config/outbounds/dns.md +++ b/docs/ru/config/outbounds/dns.md @@ -1,54 +1,183 @@ # DNS -DNS — это исходящий протокол, который в основном используется для перехвата и пересылки DNS-запросов. +DNS — это исходящий протокол, который принимает DNS-запросы, переданные routing, и пересылает или обрабатывает их по правилам. -Этот исходящий протокол может принимать только DNS-трафик (включая запросы по протоколам UDP и TCP), другие типы трафика вызовут ошибку. +Этот outbound поддерживает только традиционный открытый DNS, то есть запросы по UDP и TCP; нестандартные для него варианты, такие как DoH, DoT и DoQ, к этому outbound не применимы. Типичные сценарии: TUN, прозрачный прокси или `dokodemo-door` принимают DNS-трафик, после чего routing направляет его в этот outbound. -При обработке DNS-запросов этот исходящий протокол пересылает запросы IP-адресов (то есть A и AAAA) на встроенный [DNS-сервер](../dns.md). Другие типы запросов см. в разделе `nonIPQuery` ниже. +По правилам он может пропускать запросы к целевому DNS-серверу, выполнять `hijack` во встроенный [DNS-сервер](../dns.md) для дальнейшей обработки, отбрасывать запросы или явно отказывать в них. Также он может изменять целевой адрес, порт и транспортный протокол. ## OutboundConfigurationObject ```json { - "network": "tcp", + "network": "udp", "address": "1.1.1.1", "port": 53, "userLevel": 0, - "nonIPQuery": "drop", - "blockTypes": [] + "rules": [ + { + "action": "reject", + "domain": ["domain:example.com"] + }, + { + "action": "direct", + "qtype": 65, + "domain": ["geosite:geolocation-!cn"] + } + ] } ``` -> `network`: "tcp" | "udp" +Пример выше только демонстрирует синтаксис полей. Полную конфигурацию см. в примере ниже. -Изменяет транспортный протокол DNS-трафика. Допустимые значения: `"tcp"` и `"udp"`. Если не указано, используется исходный транспортный протокол. +> `network`: [ "tcp" | "udp" ] + +Изменяет транспортный протокол DNS-трафика. Допустимые значения: `"tcp"` и `"udp"`. Если не указано, исходный транспортный способ сохраняется. > `address`: address -Изменяет адрес DNS-сервера. Если не указано, используется адрес, указанный в источнике. +Изменяет адрес DNS-сервера. Если не указано, сохраняется адрес, указанный источником. > `port`: number -Изменяет порт DNS-сервера. Если не указано, используется порт, указанный в источнике. +Изменяет порт DNS-сервера. Если не указано, сохраняется порт, указанный источником. > `userLevel`: number -Уровень пользователя. Соединение будет использовать [локальную политику](../policy.md#levelpolicyobject), соответствующую этому уровню пользователя. +Уровень пользователя. Соединения будут использовать [локальную политику](../policy.md#levelpolicyobject), соответствующую этому уровню пользователя. -Значение `userLevel` соответствует значению `level` в [policy](../policy.md#policyobject). Если не указано, по умолчанию используется значение `0`. +Значение `userLevel` соответствует значению `level` в [policy](../policy.md#policyobject). Если не указано, по умолчанию используется `0`. -> `nonIPQuery`: string +> `rules`: \[[RuleObject](#ruleobject)\] -Управляет запросами, не относящимися к IP-адресам (не A и AAAA). `"drop"` — отклонять, `"skip"` — не обрабатывать встроенным DNS-сервером, а пересылать на целевой сервер. В отличие от `"drop"`, это позволяет избежать ситуации, когда приложение тратит слишком много времени в ожидании ответа DNS до тайм-аута. +DNS-запросы сопоставляются с правилами по порядку; поддерживается детальное управление по `qtype` и `domain`. -Значение по умолчанию — `"reject"`. +Если ни одно правило не совпало, используется встроенное правило по умолчанию: запросы A и AAAA направляются во встроенный DNS-модуль, а запросы других типов явно отклоняются. -> `blockTypes`: array +## RuleObject -Массив целых чисел (`int`), определяющий типы DNS-запросов, которые необходимо блокировать. Например, `"blockTypes": [65,28]` означает блокировку типа 65 (HTTPS) и 28 (AAAA). Распространенный сценарий использования — блокировка типа 65 для предотвращения инициализации ECH браузерами. +```json +{ + "action": "hijack", + "qtype": 1, + "domain": ["geosite:cn"] +} +``` -Поскольку опция `nonIPQuery` по умолчанию отбрасывает (`drop`) все запросы, кроме A и AAAA, необходимо переключить её в режим `skip`, чтобы данная настройка могла вступить в силу (для типов, отличных от A/AAAA). Разумеется, можно не изменять `nonIPQuery` и использовать эту опцию исключительно для блокировки A или AAAA (отключение IPv4/IPv6), однако делать это **крайне не рекомендуется**. Для этих целей лучше использовать настройку `queryStrategy` во встроенном DNS. +Все условия сопоставления внутри правила объединяются логикой AND. Если условие не указано, ограничение по этому условию не применяется. -Внимание: если вы используете `blockTypes` только для блокировки A или AAAA, и при этом `nonIPQuery` установлен в значение `reject`, то блокировка также будет осуществляться путем возврата ответа DNS reject, а не простым отбрасыванием пакета. +> `action`: [ "direct" | "hijack" | "drop" | "reject" ] -## Примеры конфигурации DNS +Определяет действие при совпадении правила. + +- `direct`: напрямую пропускает запрос к целевому DNS-серверу. Если на уровне outbound также настроены `network`, `address` или `port`, запрос пересылается к измененной цели. +- `hijack`: направляет запрос во встроенный [DNS-сервер](../dns.md) для дальнейшей обработки. Это можно использовать для дополнительного разделения трафика через конфигурацию встроенного DNS. В настоящее время поддерживаются только записи A и AAAA. +- `drop`: напрямую отбрасывает запрос и не возвращает ответ. +- `reject`: возвращает явный отказ. По сравнению с `drop`, это может предотвратить слишком долгое ожидание DNS timeout приложениями. + +> `qtype`: number | string + +Сопоставляет типы DNS-запросов. Есть три формы: + +- `"a-b"`: `a` и `b` — целые числа. Это закрытый интервал; правило срабатывает, когда тип запроса попадает в этот диапазон. +- `a`: `a` — целое число. Правило срабатывает, когда тип запроса равен `a`. +- Комбинация двух форм выше через запятую. Например: `"1,3,23-24"`. + +Распространенные номера типов можно посмотреть в [List of DNS record types](https://en.wikipedia.org/wiki/List_of_DNS_record_types). + +Если не указано, сопоставляются все типы запросов. + +> `domain`: [string] + +Сопоставляет список доменов. Синтаксис такой же, как у [`domain` в правилах routing](../routing.md#ruleobject), например `domain:example.com`, `full:example.com`, `geosite:cn`. Если не указано, домены не ограничиваются. + +## Пример конфигурации DNS + +Следующий пример показывает практический сценарий: в прозрачном прокси inbound включает `sniffing` для разделения по домену / SNI, зарубежные домены идут через proxy, а остальной IP-трафик идет напрямую. При этом `dns-out` отклоняет HTTPS-записи для зарубежных доменов, чтобы уменьшить случаи, когда клиент получает ECH-конфигурацию и это влияет на разделение по открытому SNI; распространенные запросы вроде MX, TXT и SRV пересылаются указанному upstream, а запросы AAAA блокируются, потому что у proxy-сервера нет IPv6-среды. + +```json +{ + "inbounds": [ + { + "tag": "all-in", + "port": 12345, + "protocol": "dokodemo-door", + "settings": { + "network": "tcp,udp", + "followRedirect": true + }, + "sniffing": { + "enabled": true, + "destOverride": ["http", "tls", "quic"], + "routeOnly": true + }, + "streamSettings": { + "sockopt": { + "tproxy": "tproxy" + } + } + } + ], + "dns": { + "servers": ["https+local://1.1.1.1/dns-query"] + }, + "outbounds": [ + { + "tag": "direct", + "protocol": "freedom" + }, + { + "tag": "proxy", + "protocol": "vless", + "settings": { + // Опущено... + } + }, + { + "tag": "dns-out", + "protocol": "dns", + "settings": { + "network": "tcp", + "address": "1.1.1.1", + "port": 53, + "rules": [ + { + "action": "reject", + "qtype": "28,65", + "domain": ["geosite:geolocation-!cn"] + }, + { + "action": "direct", + "qtype": "15-16,33" + } + ] + } + } + ], + "routing": { + "domainStrategy": "AsIs", + "rules": [ + { + "inboundTag": ["all-in"], + "network": "tcp,udp", + "port": "53", + "outboundTag": "dns-out" + }, + { + "domain": ["geosite:geolocation-!cn"], + "outboundTag": "proxy" + } + ] + } +} +``` + +Поведение примера: + +- `all-in` включает `sniffing` и использует `routeOnly: true`, позволяя routing разделять трафик по определенным целевым доменам HTTP, TLS и QUIC, сохраняя исходный целевой адрес. +- Открытые DNS-запросы UDP/TCP от `all-in` к порту 53 направляются правилом routing в `dns-out`. +- Для обычного трафика `geosite:geolocation-!cn` идет через `proxy`; трафик, который не совпал с этим доменным правилом, автоматически использует первый outbound — `direct`. +- HTTPS-записи с `qtype` `65` для доменов из `geosite:geolocation-!cn` явно отклоняются, что может помочь при разделении по открытому SNI. +- AAAA-запросы с `qtype` `28` для доменов из `geosite:geolocation-!cn` явно отклоняются; это можно использовать для блокировки IPv6-резолвинга зарубежных доменов. +- Запросы с `qtype` `15-16,33` напрямую разрешаются и пересылаются на `1.1.1.1:53` согласно конфигурации outbound, используя TCP в качестве транспорта. +- Запросы, которые не совпали ни с одним правилом, попадают во встроенную fallback-логику: запросы A и AAAA направляются во встроенный DNS-модуль, а другие типы запросов явно отклоняются. Затем встроенный DNS обращается к upstream через `https+local://1.1.1.1/dns-query`, избегая зацикливания.