Files
XTLS_Xray-docs-next/docs/development/lua/guide/dns.md
T

156 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DNS 脚本
通过 [`dns.script`](../../../config/dns.md#dnsobject) 指定脚本后,内置 DNS 处理流程由 Lua 的 `HandleDNSQuery` 函数接管。
## 最小示例
在已有配置中加入:
```json
{
"dns": {
"script": "dns.lua",
"servers": ["1.1.1.1"]
}
}
```
保存 `dns.lua`:
```lua
local dns = require("xray.dns")
local server = dns.Servers[1]
function HandleDNSQuery(domain, ipv4, ipv6, fake)
return server:Query(domain, ipv4, ipv6, fake)
end
```
上面的配置片段需要合并到完整配置中。文件定位规则见[脚本文件路径](../environment.md#脚本文件路径)。
示例将参数原样传给上游,并直接返回查询结果。完整参数、返回值限制及失败行为见 [HandleDNSQuery](../reference/hook-dns.md)。
## 与 DNS 配置的关系
查询到达脚本前,仍经过内置 DNS 的域名检查、全局查询类型限制和 Hosts 处理。Hosts 已给出有效 IP 或明确拒绝请求时,不调用脚本;Hosts 替换域名时,脚本查询替换后的域名。
启用脚本后,上游选择由脚本控制,因此 `domains`、`skipFallback`、`finalQuery`、`disableFallback`、`disableFallbackIfMatch` 和 `enableParallelQuery` 不再有实际意义。
查询指定上游使用 [`serverObj:Query`](../reference/module-dns.md#query);单台服务器仍生效的配置项见该 API。
::: tip 小提示
上游与过滤规则的对应关系不变时,直接配置 `expectedIPs` / `unexpectedIPs` 更方便快捷;也可以不配置这两项,交给 Lua 使用 [`ipMatcherObj:FilterIPs`](../reference/module-geodata.md#filterips) 等方法自行过滤,更灵活。
:::
## 示例:按域名分流并过滤解析结果
下面的示例按域名分类选择上游,并按需要过滤中国 IP。为服务器配置 `id`,脚本按标识查询和回退:
```json
{
"dns": {
"script": "dns.lua",
"tag": "dns-proxy",
"servers": [
{ "id": "cf", "address": "1.1.1.1" },
{ "id": "google", "address": "8.8.8.8" },
{ "id": "cn114", "address": "114.114.114.114", "tag": "dns-direct" },
{ "id": "cn223", "address": "223.5.5.5", "tag": "dns-direct" },
{
"id": "google-ecs",
"address": "8.8.8.8",
"clientIp": "222.85.85.85"
},
{
"id": "google-alt-ecs",
"address": "8.8.4.4",
"clientIp": "222.85.85.85"
}
]
},
"routing": {
"rules": [
{ "inboundTag": ["dns-direct"], "outboundTag": "direct" },
{ "inboundTag": ["dns-proxy"], "outboundTag": "proxy" }
]
},
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{
"tag": "proxy",
"protocol": "vless",
"settings": {
// ...
}
}
]
}
```
`dns-direct`、`dns-proxy` 是 DNS 上游查询的入站标识,通过上面的路由规则分别选择直连出站 `direct` 和 VLESS 代理出站 `proxy`。
脚本先按域名选择查询顺序,再逐个查询上游;查询失败或过滤后为空时,尝试下一台服务器:
```lua
local geodata = require("xray.geodata")
local dns = require("xray.dns")
local servers = {}
for _, server in ipairs(dns.Servers) do
servers[server.ID] = server
end
local googleDomainMatcher = geodata.BuildDomainMatcher("geosite:google")
local cnDomainMatcher = geodata.BuildDomainMatcher("geosite:cn")
local foreignDomainMatcher = geodata.BuildDomainMatcher("geosite:geolocation-!cn")
local cnIPMatcher = geodata.BuildIPMatcher("geoip:cn")
function HandleDNSQuery(domain, ipv4, ipv6, fake)
local queries
if googleDomainMatcher:MatchAny(domain) then
-- Google 域名使用公共 DNS。
queries = {{"cf"}, {"google"}}
elseif cnDomainMatcher:MatchAny(domain) then
-- 中国域名先查直连 DNS,只保留中国 IP,再回退到代理 DNS。
queries = {
{"cn114", "cn"}, {"cn223", "cn"}, {"cf"}, {"google"}
}
elseif foreignDomainMatcher:MatchAny(domain) then
-- 非中国域名先排除中国 IP,再尝试带 ECS 的查询。
queries = {
{"cf", "non-cn"}, {"google", "non-cn"},
{"google-ecs"}, {"google-alt-ecs"}
}
else
-- 未收录域名先通过 ECS 寻找中国 IP,再回退到普通公共 DNS。
queries = {
{"google-ecs", "cn"}, {"google-alt-ecs", "cn"},
{"cf"}, {"google"}
}
end
local lastError = "没有符合条件的 DNS 结果"
for _, query in ipairs(queries) do
local ips, ttl, err =
servers[query[1]]:Query(domain, ipv4, ipv6, fake)
if err then
lastError = err
else
if query[2] then
local inCn, outsideCn = cnIPMatcher:FilterIPs(ips)
if query[2] == "cn" then
ips = inCn
else
ips = outsideCn
end
end
if ips and #ips > 0 then
return ips, ttl, nil
end
end
end
return nil, 0, lastError
end
```
DNS 脚本的顶层初始化和状态保留见[池化生命周期](./lifecycle.md#池化生命周期)。