Xray-core: Add Lua script for dns and routing

https://github.com/XTLS/Xray-core/pull/6823
This commit is contained in:
Meow
2026-10-11 09:55:19 +08:00
parent 4c8f3b2b7e
commit f179efd35d
48 changed files with 4950 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
# Routing Scripts
When a script is specified through [`routing.script`](../../../config/routing.md#routingobject), Lua's `HandleRoute` function takes over outbound selection.
## Minimal Example
The following configuration fragment specifies the script file and configures direct and blackhole outbounds:
```json
{
"routing": {
"script": "routing.lua"
},
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{ "tag": "block", "protocol": "blackhole" }
]
}
```
Save `routing.lua`:
```lua
function HandleRoute(ctx)
local sourceIPs = ctx:GetSourceIPs()
if sourceIPs and #sourceIPs > 0
and sourceIPs[1]:String() == "127.0.0.1" then
return "block"
end
return "direct"
end
```
The script reads the source IP through `ctx`. It returns `block` when the source IP is `127.0.0.1`, and `direct` otherwise, corresponding to the outbound `tag` values configured above.
Merge the fragment above into a complete configuration. See [Script File Paths](../environment.md#script-file-paths) for file lookup rules.
`HandleRoute` can also receive more parameters and return a rule name and an error. See [HandleRoute](../reference/hook-routing.md) for the full calling convention and failure behavior.
## Relationship with Routing Configuration
When a routing script is enabled, neither `rules` nor `domainStrategy` takes effect. If the script does not select an outbound or encounters an error, the built-in routing rules are not evaluated either.
You can still configure `balancers`. The script selects an outbound from a balancer through [`router:PickOutbound`](../reference/module-router.md#router-pickoutbound):
```lua
local router = require("xray.router")
function HandleRoute(ctx)
local outboundTag, err = router:PickOutbound("balance")
return outboundTag, "lua-balance", err
end
```
Replace `balance` in the example with the `tag` of a balancer configured in `routing.balancers`.
## Example: Explicit DNS Queries for IP-Based Routing
The following script first handles DNS upstream query traffic, then checks the request's existing target IPs. When needed, it explicitly resolves the domain, and selects an outbound based on private address ranges.
```json
{
"dns": {
"tag": "dns-query",
"servers": ["1.1.1.1"]
},
"routing": {
"script": "routing.lua"
},
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{
"tag": "proxy",
"protocol": "vless",
"settings": {
// ...
}
}
]
}
```
```lua
local dns = require("xray.dns")
local geodata = require("xray.geodata")
local log = require("xray.log")
local privateIPMatcher = geodata.BuildIPMatcher("geoip:private")
function HandleRoute(ctx, inboundTag, sourcePort, targetPort,
localPort, targetDomain, network, protocol, user,
vlessRoute, skipDNSResolve)
if skipDNSResolve or inboundTag == "dns-query" then
return "direct", "lua-dns"
end
local ips = ctx:GetTargetIPs()
if privateIPMatcher:AnyMatch(ips) then
return "direct", "lua-private"
end
if targetDomain ~= "" then
local resolved, ttl, err =
dns.Query(targetDomain, true, true, false)
if err ~= nil then
log.Warning("Resolution of ", targetDomain, " failed: ", err)
elseif privateIPMatcher:AnyMatch(resolved) then
return "direct", "lua-resolved-private"
end
end
return "proxy", "lua-default"
end
```
DNS upstream requests in normal mode also pass through routing. The example directly selects an outbound for requests where `skipDNSResolve` is `true` or `inboundTag` is `"dns-query"`, avoiding another DNS lookup that would create a loop.
The result of `dns.Query` is only used for decisions in the script. It does not automatically change the target IPs in `ctx` or the actual connection destination. The example selects `proxy` if resolution fails; adjust this to your own policy as needed.
See [Pooled Lifecycle](./lifecycle.md#pooled-lifecycle) for top-level initialization and state retention in routing scripts.