mirror of
https://github.com/XTLS/Xray-docs-next.git
synced 2026-10-12 09:18:19 +03:00
Xray-core: Add Lua script for dns and routing
https://github.com/XTLS/Xray-core/pull/6823
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# 运行环境与脚本加载
|
||||
|
||||
## Lua 环境
|
||||
|
||||
当前使用 GopherLua,支持 Lua 5.1 语法、Lua 5.2 的 `goto` 语句,以及独有的 [`channel`](https://github.com/yuin/gopher-lua#lua-api)。
|
||||
|
||||
Xray 根据配置文件加载 Lua 脚本。配置的位置决定脚本用于什么功能,不同位置加载的脚本可用的 Hook 也不同。各入口可用的 Hook 及其调用约定见 [Hook 参考](./index.md#hook)。
|
||||
|
||||
## 模块加载
|
||||
|
||||
API 模块按功能组织,通过 `require` 加载:
|
||||
|
||||
```lua
|
||||
local geodata = require("xray.geodata")
|
||||
local log = require("xray.log")
|
||||
```
|
||||
|
||||
模块列表见[模块 API](./index.md#模块-api),各函数的用法见对应 API 参考。
|
||||
|
||||
加载 Lua 模块、创建对象等顶层初始化的执行时机,见[实例与生命周期](./guide/lifecycle.md)。
|
||||
|
||||
## 脚本文件路径
|
||||
|
||||
配置文件中的 `script` 字段用于指定 Lua 脚本文件的路径。空字符串或未配置表示不启用脚本;绝对路径直接定位文件。
|
||||
|
||||
相对路径按以下顺序查找;路径不存在时继续,存在但不是普通文件时立即报错:
|
||||
|
||||
1. 环境变量 `XRAY_LOCATION_CONFDIR` 指定的目录;
|
||||
2. 环境变量 `XRAY_LOCATION_CONFIG` 指定的目录;
|
||||
3. Xray 进程的当前工作目录;
|
||||
4. Xray 可执行文件所在目录。
|
||||
|
||||
这些目录的设置见[环境变量](../../config/env.md)。相对路径不会自动以配置文件所在目录为基准。
|
||||
|
||||
例如,将脚本路径设为 `routing.lua` 后,Xray 会按上述顺序查找文件。Windows 绝对路径可以写成 `C:/Xray/routing.lua`;使用反斜杠时,请按所用配置格式的要求进行转义。
|
||||
@@ -0,0 +1,155 @@
|
||||
# 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#池化生命周期)。
|
||||
@@ -0,0 +1,67 @@
|
||||
# 实例与生命周期
|
||||
|
||||
Lua 实例的创建、Hook 调用、状态保留和销毁,由脚本入口采用的实例管理方式决定。本页按生命周期类型说明这些规则。
|
||||
|
||||
## 池化生命周期
|
||||
|
||||
当前[路由脚本](./routing.md)的 [HandleRoute](../reference/hook-routing.md) 和 [DNS 脚本](./dns.md)的 [HandleDNSQuery](../reference/hook-dns.md) 使用池化实例。以下规则适用于这两个 Hook。
|
||||
|
||||
### 实例池与初始化
|
||||
|
||||
路由脚本和 DNS 脚本分别通过对应配置中的 `script` 绑定脚本,各自管理独立的 Lua 实例池。即使配置为同一个文件,也不会共享 Lua 实例内的状态。
|
||||
|
||||
Xray 启动时会读取并编译脚本一次,然后创建第一个实例,执行脚本顶层代码,检查必需的处理函数是否存在。文件读取、语法、顶层执行或处理函数检查失败时,Xray 启动失败。
|
||||
|
||||
### Hook 调用与回收
|
||||
|
||||
每次路由选择或 DNS 查询独占一个实例。有空闲实例时直接复用;并发调用需要更多实例时,使用已编译的脚本创建新实例,并重新执行顶层代码。
|
||||
|
||||
处理函数正常执行完成后,实例可归还池中复用;未捕获的 Lua 异常或执行超时会使实例被销毁。处理函数通过返回值报告业务错误,或返回值校验失败,并不等同于 Lua 执行异常,实例仍可复用。多余的空闲实例会自动被销毁。
|
||||
|
||||
有空闲实例时,每次 Hook 的调度路径很轻:**取出实例 → 执行 Hook → 归还实例**。脚本读取与编译在 Xray 启动时完成。图中的绿色节点表示这条常规路径。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
LOAD["Xray 启动<br/>读取并编译主脚本一次"] --> INIT["创建首个 Lua 实例<br/>执行顶层代码并检查 Hook"]
|
||||
INIT --> POOL[("空闲实例池")]
|
||||
|
||||
subgraph CALL["池化 Hook 调用:轻量复用路径"]
|
||||
TAKE["取出并独占空闲实例"] --> RUN["执行 HandleRoute / HandleDNSQuery"]
|
||||
RUN -->|正常执行结束| PUT["归还实例,保留状态"]
|
||||
end
|
||||
|
||||
REQUEST["路由选择 / DNS 查询"] --> AVAILABLE{"池中有空闲实例?"}
|
||||
POOL -.-> AVAILABLE
|
||||
AVAILABLE -->|有| TAKE
|
||||
PUT --> POOL
|
||||
AVAILABLE -. 无:按需扩容 .-> CREATE["使用已编译脚本创建新实例<br/>执行顶层代码并检查 Hook"]
|
||||
CREATE --> RUN
|
||||
RUN -. 未捕获 Lua 异常 / 超时 .-> DESTROY["销毁实例"]
|
||||
POOL -. 多余空闲实例 / Xray 关闭 .-> DESTROY
|
||||
|
||||
classDef reuse fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
|
||||
class TAKE,RUN,PUT reuse
|
||||
```
|
||||
|
||||
### 池化实例内状态
|
||||
|
||||
全局变量、顶层 `local`、闭包和 `require` 的模块缓存属于当前实例,会在该实例处理的多次调用之间保留,实例销毁时丢失:
|
||||
|
||||
```lua
|
||||
local calls = 0
|
||||
|
||||
function HandleRoute()
|
||||
calls = calls + 1
|
||||
return "direct", "instance-call-" .. calls
|
||||
end
|
||||
```
|
||||
|
||||
上面的计数只表示当前实例处理过的调用次数。不同实例有独立的 `calls`,请求也不保证总是分配到同一个实例。它不能用作所有连接共享的计数器或某条连接的持续状态。
|
||||
|
||||
建议将加载 Lua 模块、创建匹配器、保存 DNS 服务器对象等初始化代码放在 Hook 函数外(脚本顶层)。这些代码在每个实例创建时执行一次,结果可供同一实例后续的 Hook 调用复用。
|
||||
|
||||
### 超时与脚本更新
|
||||
|
||||
当前路由和 DNS 的每个实例初始化超时为 **120 秒**,每次处理函数调用的执行超时为 **6 秒**。调用超时从取得实例后开始计算;这些值目前没有独立的配置字段。调用 Xray API 时,实际取消行为还取决于 API 是否响应取消信号;单台 DNS 服务器还受其 `timeoutMs` 限制。
|
||||
|
||||
Xray 不自动监视或重新编译主脚本。修改主脚本后,应重启 Xray 使其生效;之后创建的新实例也使用本次启动时编译的主脚本。
|
||||
@@ -0,0 +1,119 @@
|
||||
# 路由脚本
|
||||
|
||||
通过 [`routing.script`](../../../config/routing.md#routingobject) 指定脚本后,路由选择由 Lua 的 `HandleRoute` 函数接管。
|
||||
|
||||
## 最小示例
|
||||
|
||||
下面的配置片段指定脚本文件,并配置直连和黑洞出站:
|
||||
|
||||
```json
|
||||
{
|
||||
"routing": {
|
||||
"script": "routing.lua"
|
||||
},
|
||||
"outbounds": [
|
||||
{ "tag": "direct", "protocol": "freedom" },
|
||||
{ "tag": "block", "protocol": "blackhole" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
保存 `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
|
||||
```
|
||||
|
||||
脚本通过 `ctx` 读取源 IP。源 IP 为 `127.0.0.1` 时返回 `block`,其余情况返回 `direct`,分别对应上面配置中的出站 `tag`。
|
||||
|
||||
上面的配置片段需要合并到完整配置中。文件定位规则见[脚本文件路径](../environment.md#脚本文件路径)。
|
||||
|
||||
`HandleRoute` 还可以接收更多参数,并返回规则名称和错误;完整调用约定及失败行为见 [HandleRoute](../reference/hook-routing.md)。
|
||||
|
||||
## 与路由配置的关系
|
||||
|
||||
启用路由脚本后,`rules` 和 `domainStrategy` 均不生效。脚本未选中出站或发生错误时,也不会继续匹配内置路由规则。
|
||||
|
||||
`balancers` 仍可配置,脚本通过 [`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
|
||||
```
|
||||
|
||||
将示例中的 `balance` 换成 `routing.balancers` 中已配置的负载均衡器的 `tag` 值。
|
||||
|
||||
## 示例:主动查询 DNS 后按 IP 分流
|
||||
|
||||
下面的脚本先处理 DNS 上游查询流量,然后检查请求已有的目标 IP,必要时主动解析域名,再按私有地址范围选择出站。
|
||||
|
||||
```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("解析 ", targetDomain, " 失败:", err)
|
||||
elseif privateIPMatcher:AnyMatch(resolved) then
|
||||
return "direct", "lua-resolved-private"
|
||||
end
|
||||
end
|
||||
|
||||
return "proxy", "lua-default"
|
||||
end
|
||||
```
|
||||
|
||||
普通模式的 DNS 上游请求也会进入路由。示例对 `skipDNSResolve` 为 `true` 或 `inboundTag` 为 `"dns-query"` 的请求直接选择出站,避免再次发起 DNS 解析而形成回环。
|
||||
|
||||
`dns.Query` 的结果只用于脚本判断,不会自动改写 `ctx` 的目标 IP 或实际连接目标。示例在解析失败时选择 `proxy`;可以按需要调整为自己的策略。
|
||||
|
||||
路由脚本的顶层初始化和状态保留见[池化生命周期](./lifecycle.md#池化生命周期)。
|
||||
@@ -0,0 +1,46 @@
|
||||
# Lua 脚本
|
||||
|
||||
Lua 脚本为 Xray 提供灵活的扩展方式,可用于实现自定义功能,与核心交互。
|
||||
|
||||
## 入门
|
||||
|
||||
先阅读[运行环境与脚本加载](./environment.md),了解 Lua 支持范围、模块加载方式和脚本文件定位规则。
|
||||
|
||||
## 编写指南
|
||||
|
||||
根据需要实现的功能选择指南,从最小示例开始,逐步编写自己的脚本:
|
||||
|
||||
| 指南 | 作用 | 内容 |
|
||||
| ------------------------------ | ------------------- | ---------------------------------------- |
|
||||
| [路由脚本](./guide/routing.md) | 自定义路由规则 | 出站选择、负载均衡、主动解析后按 IP 分流 |
|
||||
| [DNS 脚本](./guide/dns.md) | 自定义 DNS 查询规则 | 上游选择、查询回退、结果过滤 |
|
||||
|
||||
当前的路由和 DNS 脚本都使用池化实例。编写脚本时,请结合[池化生命周期](./guide/lifecycle.md#池化生命周期)理解顶层初始化和状态保留。
|
||||
|
||||
## 参考
|
||||
|
||||
### Hook
|
||||
|
||||
Hook 是脚本中供核心在特定时机调用的 Lua 函数。脚本入口决定可用的 Hook 及其调用约定,一个入口可以对应多个 Hook。具体需要实现哪些 Hook,以相应入口的说明为准。
|
||||
|
||||
目前支持的入口和 Hook 如下,参数、返回值和失败行为见各 Hook 的参考页:
|
||||
|
||||
| 脚本入口 | Hook |
|
||||
| ---------------- | ------------------------------------------ |
|
||||
| `routing.script` | [HandleRoute](./reference/hook-routing.md) |
|
||||
| `dns.script` | [HandleDNSQuery](./reference/hook-dns.md) |
|
||||
|
||||
### 数据类型
|
||||
|
||||
除 Lua 自带的数据类型外,Xray Lua 还使用 `net.IP`、Go slice 和 `error` 等类型,其在 Lua 中的表示与操作方式见[数据类型](./reference/data-types.md)。
|
||||
|
||||
### 模块 API
|
||||
|
||||
按功能查阅模块 API:
|
||||
|
||||
| 模块 | 用途 |
|
||||
| --------------------------------------------- | -------------------- |
|
||||
| [xray.router](./reference/module-router.md) | 策略路由 |
|
||||
| [xray.dns](./reference/module-dns.md) | 域名解析 |
|
||||
| [xray.geodata](./reference/module-geodata.md) | 域名和 IP 匹配、过滤 |
|
||||
| [xray.log](./reference/module-log.md) | 输出日志 |
|
||||
@@ -0,0 +1,215 @@
|
||||
# 数据类型
|
||||
|
||||
本页汇总各模块共用的数据类型,说明它们在 Lua 中的表示和操作方式。模块独有的数据类型见各自页面。
|
||||
|
||||
## net.IP
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ----------- | ------------------------- |
|
||||
| Go 底层类型 | `net.IP`、`common/net.IP` |
|
||||
| Lua 表现 | `userdata` |
|
||||
|
||||
通常无需在 Lua 中逐个比较 `net.IP`,尤其应避免在热路径中这样做。需要匹配 IP 时,应优先使用 [`IPMatcher`](./module-geodata.md#ipmatcher),它可以直接处理 API 返回的 `net.IP`。
|
||||
|
||||
如果确实需要直接操作 IP,请注意 `net.IP` 与 IP 地址字符串是不同的类型。可使用以下两个方法进行转换和比较:
|
||||
|
||||
### String
|
||||
|
||||
```lua
|
||||
local text = ip:String()
|
||||
```
|
||||
|
||||
将 IP 转为地址文本。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | -------- | ------------------------- |
|
||||
| `text` | `string` | IP 地址字符串,不为 `nil` |
|
||||
|
||||
### Equal
|
||||
|
||||
```lua
|
||||
local equal = ip:Equal(otherIP)
|
||||
```
|
||||
|
||||
比较两个 IP 是否相等。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| --------- | ---------------------------- | ------------------------------- |
|
||||
| `otherIP` | [`net.IP`](#net-ip) 或 `nil` | 待比较的 IP,可以显式传入 `nil` |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------- | --------- | ------------------------------------------- |
|
||||
| `equal` | `boolean` | 相等时为 `true`,否则为 `false`;不为 `nil` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
有效 IP 与 `nil` 比较时返回 `false`。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
if ip ~= nil then
|
||||
local text = ip:String()
|
||||
local equal = ip:Equal(otherIP)
|
||||
end
|
||||
```
|
||||
|
||||
## slice
|
||||
|
||||
Go slice 在 Lua 中表示为 `userdata`,保留元素类型,下标从 **1** 开始。以下两种 slice 共用相同的长度、索引和遍历操作。
|
||||
|
||||
| 类型 | Lua 表现 | 元素类型 |
|
||||
| ----------------------- | ---------- | ------------------- |
|
||||
| [`[]net.IP`](#net-ip-1) | `userdata` | [`net.IP`](#net-ip) |
|
||||
| [`[]uint32`](#uint32) | `userdata` | `number` |
|
||||
|
||||
### []net.IP
|
||||
|
||||
提示:需要匹配或筛选 IP 列表时,应优先使用 [`IPMatcher`](./module-geodata.md#ipmatcher),它可以直接处理 `[]net.IP`。
|
||||
|
||||
### []uint32
|
||||
|
||||
元素为 32 位无符号整数,取值范围为 `0 .. 4294967295`。
|
||||
|
||||
### #values
|
||||
|
||||
```lua
|
||||
local count = #values
|
||||
```
|
||||
|
||||
取得 slice 的元素数量。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------- | -------- | ------------------------- |
|
||||
| `count` | `number` | 非负整数;空 slice 为 `0` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
API 返回的列表可能为 `nil`,也可能是长度为 0 的 slice。调用上述操作前,`values` 必须非 `nil`;仅检查 `if values then` 不能判断是否有元素。
|
||||
|
||||
### values[i]
|
||||
|
||||
```lua
|
||||
local value = values[i]
|
||||
```
|
||||
|
||||
读取 slice 中的一个元素。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | -------- | ---------------------------------------- |
|
||||
| `i` | `number` | 必填,必须是 `1 .. #values` 范围内的整数 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------- | ------------------------------- | ---------------------------------------- |
|
||||
| `value` | [`net.IP`](#net-ip) 或 `number` | 分别对应 `[]net.IP` 和 `[]uint32` 的元素 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
越界访问会抛出 Lua 异常,不会返回 `nil`。避免直接修改 API 返回的 slice。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
if ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
|
||||
### values()
|
||||
|
||||
```lua
|
||||
for i, value in values() do
|
||||
-- 使用下标 i 和元素 value
|
||||
end
|
||||
```
|
||||
|
||||
取得 slice 迭代器,用于泛型 `for` 遍历,下标从 1 开始。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ---------- | ---------------------------- |
|
||||
| 迭代器 | `function` | 逐项提供下标和元素,用法如上 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
slice 不支持 `pairs` 或 `ipairs`,也可以通过数值 `for` 遍历。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
if ips ~= nil then
|
||||
for i = 1, #ips do
|
||||
local text = ips[i]:String()
|
||||
end
|
||||
|
||||
for i, ip in ips() do
|
||||
local text = ip:String()
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## IP 列表输入
|
||||
|
||||
向 `IPMatcher` 传入 IP 列表时,可以使用以下形式:
|
||||
|
||||
| 输入形式 | Lua 表现 | 说明 |
|
||||
| ------------------------ | ---------- | ------------------------------------------------- |
|
||||
| [`[]net.IP`](#net-ip-1) | `userdata` | IP 列表,允许空 slice |
|
||||
| [`net.IP`](#net-ip) 数组 | `table` | 从 1 开始连续存放 IP,允许空数组 `{}`,不能有空洞 |
|
||||
| `nil` | `nil` | 须显式传入;各方法的结果见对应条目 |
|
||||
|
||||
IP 地址字符串不会被解析为 `net.IP`。空字符串 `""` 不是合法 IP 列表,传给 `AnyMatch`、`Matches` 或 `FilterIPs` 会抛出 Lua 异常。
|
||||
|
||||
## error
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ----------- | --------------------------------- |
|
||||
| Go 底层类型 | `error` |
|
||||
| Lua 表现 | 错误为 `userdata`,无错误为 `nil` |
|
||||
|
||||
API 返回的 `err` 为 `nil` 时表示没有错误,否则为错误对象。错误对象可原样作为 Hook 的 `err` 返回值,将错误交给 Xray 核心处理。
|
||||
|
||||
**Hook 返回约定**
|
||||
|
||||
| 错误值 | 含义 |
|
||||
| --------------- | -------------------------------------------- |
|
||||
| `nil` | 没有错误 |
|
||||
| 错误 `userdata` | 保留原始错误 |
|
||||
| `string` | 脚本提供的错误消息,空字符串 `""` 仍表示错误 |
|
||||
|
||||
字符串错误仅适用于 Hook 返回。Xray API 自身返回错误或 `nil`。具体校验及失败行为见对应 Hook 的说明。
|
||||
|
||||
**输出错误消息**
|
||||
|
||||
Go 错误对象无法通过 `tostring(err)` 取得消息;需要输出时,直接将 `err` 传给 [`xray.log`](./module-log.md)。
|
||||
|
||||
```lua
|
||||
local log = require("xray.log")
|
||||
local ips, ttl, err = serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
if err ~= nil then
|
||||
log.Warning("DNS 查询失败:", err)
|
||||
elseif ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
# DNS Hook
|
||||
|
||||
核心在内置 DNS 查询中调用 `dns.script` 中的全局函数 `HandleDNSQuery`。配置接入和完整示例见[DNS 脚本指南](../guide/dns.md)。
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | ---------------------------------------- | ------------- |
|
||||
| Hook | [`HandleDNSQuery(...)`](#handlednsquery) | 处理 DNS 查询 |
|
||||
|
||||
## Hook 接口
|
||||
|
||||
### HandleDNSQuery(...)
|
||||
|
||||
```lua
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return ips, ttl, err
|
||||
end
|
||||
```
|
||||
|
||||
处理一次 DNS 查询,返回 IP slice、TTL 或错误。
|
||||
|
||||
#### 参数
|
||||
|
||||
核心始终传入全部四个参数,均非 `nil`。
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| -------- | --------- | ---------------------------- |
|
||||
| `domain` | `string` | 待查询域名,非空且已转为小写 |
|
||||
| `ipv4` | `boolean` | 是否允许查询 IPv4 地址 |
|
||||
| `ipv6` | `boolean` | 是否允许查询 IPv6 地址 |
|
||||
| `fake` | `boolean` | 本次查询是否允许 FakeDNS |
|
||||
|
||||
`ipv4`、`ipv6` 已受全局 [`queryStrategy`](../../../config/dns.md#dnsobject) 限制,至少一个为 `true`。向上游查询时通常原样传递这三个 boolean 参数。
|
||||
|
||||
#### 返回值
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | --------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) 或 `nil` | 查询结果,必须是 slice,不接受 Lua 数组 |
|
||||
| `ttl` | `number` 或 `nil` | TTL(秒);`err == nil` 时必须为 `0 .. 4294967295` 范围内的整数 |
|
||||
| `err` | [`error`](./data-types.md#error)、`string` 或 `nil` | 仅 `nil` 表示无错误 |
|
||||
|
||||
#### 行为约定
|
||||
|
||||
核心依次校验 `err`、`ttl`、`ips`,遇到错误即停止;`err` 非 `nil` 时忽略其余两项。无错误时,即使 IP 结果为空,也必须提供合法 TTL。
|
||||
|
||||
返回空响应可写 `return nil, 0, nil`;错误应放在第三个位置,例如 `return nil, nil, "查询失败"`。
|
||||
|
||||
| 脚本结果 | 核心处理 |
|
||||
| ---------------------------------------------------- | ---------------------- |
|
||||
| 校验通过且 `ips` 非空 | 本次查询成功 |
|
||||
| `err == nil`、TTL 合法,但 `ips` 为 `nil` 或空 slice | 本次查询失败(空响应) |
|
||||
| 返回错误、返回值类型错误或执行异常 | 本次查询失败 |
|
||||
|
||||
#### 示例
|
||||
|
||||
可以直接转发 [`serverObj:Query`](./module-dns.md#query) 的三个返回值:
|
||||
|
||||
```lua
|
||||
local dns = require("xray.dns")
|
||||
local serverObj = dns.Servers[1]
|
||||
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,181 @@
|
||||
# 路由 Hook
|
||||
|
||||
核心在选择出站时调用 `routing.script` 中的全局函数 `HandleRoute`。配置接入和完整示例见[路由脚本指南](../guide/routing.md)。
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | --------------------------------------- | -------------------- |
|
||||
| Hook | [`HandleRoute(...)`](#handleroute) | 为请求选择出站 |
|
||||
| 对象 | [`routing.Context`](#routing-context) | 当前请求的路由上下文 |
|
||||
| 方法 | [`ctx:GetSourceIPs()`](#getsourceips) | 取得来源 IP |
|
||||
| 方法 | [`ctx:GetTargetIPs()`](#gettargetips) | 取得目标 IP |
|
||||
| 方法 | [`ctx:GetLocalIPs()`](#getlocalips) | 取得本地 IP |
|
||||
| 方法 | [`ctx:GetAttributes()`](#getattributes) | 取得请求属性 |
|
||||
|
||||
## Hook 接口
|
||||
|
||||
### HandleRoute(...)
|
||||
|
||||
```lua
|
||||
function HandleRoute(ctx, inboundTag, sourcePort, targetPort,
|
||||
localPort, targetDomain, network, protocol, user,
|
||||
vlessRoute, skipDNSResolve)
|
||||
return outboundTag, ruleTag, err
|
||||
end
|
||||
```
|
||||
|
||||
为当前请求选择出站,并可返回规则名称或错误。
|
||||
|
||||
#### 参数
|
||||
|
||||
核心始终按上述顺序传入全部 11 个参数,均非 `nil`。函数定义可省略不使用的末尾形参,这不会改变核心传入的值。
|
||||
|
||||
| 参数 | 类型 | 说明 | 空值与缺省值 |
|
||||
| ---------------- | ------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------ |
|
||||
| `ctx` | [`routing.Context`](#routing-context) | 当前请求上下文 | 始终是有效的 `userdata` |
|
||||
| `inboundTag` | `string` | 入站标识 | 入站未设置 tag 或没有入站信息时为 `""` |
|
||||
| `sourcePort` | `number` | 来源端口,`0 .. 65535` | 没有有效来源地址时为 `0` |
|
||||
| `targetPort` | `number` | 目标端口,`0 .. 65535` | 没有有效目标地址时为 `0` |
|
||||
| `localPort` | `number` | 入站连接的本地端口,`0 .. 65535` | 没有有效本地地址时为 `0` |
|
||||
| `targetDomain` | `string` | 优先取生效的嗅探域名,否则取连接目标中的域名,统一转为小写 | 目标为 IP 且无生效的嗅探域名,或缺少目标信息时为 `""` |
|
||||
| `network` | `number` | 网络类型,与 [`router.NetworkTCP`](./module-router.md#常量) 等常量比较 | 没有出站信息或网络类型未知时为 `NetworkUnknown`(`0`) |
|
||||
| `protocol` | `string` | 嗅探得到的协议 | 没有嗅探协议信息时为 `""` |
|
||||
| `user` | `string` | 用户 email | 没有入站、用户信息或未设置 email 时为 `""` |
|
||||
| `vlessRoute` | `number` | VLESS UUID 第 7、8 字节组成的路由值,`0 .. 65535` | 没有该信息时为 `0`,值本身也可能为 0 |
|
||||
| `skipDNSResolve` | `boolean` | 为 `true` 时必须跳过 DNS 查询,避免回环 | 没有该标记或附加请求信息时为 `false` |
|
||||
|
||||
#### 返回值
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------------- | --------------------------------------------------- | --------------------------------------------- |
|
||||
| `outboundTag` | `string` 或 `nil` | 出站标识;`nil` 或 `""` 表示未选中 |
|
||||
| `ruleTag` | `string` 或 `nil` | 可选的规则名称;`nil`、省略或 `""` 表示无名称 |
|
||||
| `err` | [`error`](./data-types.md#error)、`string` 或 `nil` | 仅 `nil` 表示无错误 |
|
||||
|
||||
#### 行为约定
|
||||
|
||||
核心依次校验 `err`、`outboundTag`、`ruleTag`,遇到错误即停止;`err` 非 `nil` 时忽略前两项,未选中出站时忽略 `ruleTag`。
|
||||
|
||||
成功时可省略末尾的 `ruleTag` 和 `err`,例如 `return "direct"`。错误必须放在第三个位置,例如 `return nil, nil, "路由失败"`;`return nil, "路由失败"` 不会报告错误。
|
||||
|
||||
| 脚本结果 | 核心处理 |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
| 校验通过且 `outboundTag` 非空 | 使用对应出站,标识不存在则关闭连接 |
|
||||
| 未选中出站 | 使用默认出站 |
|
||||
| 返回错误、返回值类型错误或执行异常 | 记录错误并尝试默认出站 |
|
||||
|
||||
默认出站是配置中的第一个出站;没有可用默认出站时关闭连接。若要阻断流量应显式返回已配置的 [`blackhole`](../../../config/outbounds/blackhole.md) 出站 tag 而不是利用此特性返回不存在的标识。
|
||||
|
||||
## 关联对象
|
||||
|
||||
### routing.Context
|
||||
|
||||
`routing.Context` 表示当前请求的路由上下文,在 `HandleRoute` 中通过参数 `ctx` 访问。
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ----------- | ---------------------------------------------- |
|
||||
| Go 底层类型 | `features/routing` 中的 `routing.Context` 接口 |
|
||||
| Lua 表现 | `userdata` |
|
||||
| 取得方式 | 核心传入的 `HandleRoute` 第一个参数 |
|
||||
|
||||
下列方法均无额外参数。IP 及 slice 操作统一见[数据类型](./data-types.md)。
|
||||
|
||||
#### GetSourceIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetSourceIPs()
|
||||
```
|
||||
|
||||
取得请求的来源 IP。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ----------------------------------------------- | ------------------ |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) 或 `nil` | 对应地址的 IP 列表 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
没有入站、来源地址无效或来源地址不是 IP 时返回 `nil`。
|
||||
|
||||
#### GetTargetIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetTargetIPs()
|
||||
```
|
||||
|
||||
取得请求的目标 IP。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ----------------------------------------------- | ------------------ |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) 或 `nil` | 对应地址的 IP 列表 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
没有出站、目标地址无效或目标地址是域名时返回 `nil`。
|
||||
|
||||
#### GetLocalIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetLocalIPs()
|
||||
```
|
||||
|
||||
取得入站连接的本地 IP。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ----------------------------------------------- | ------------------ |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) 或 `nil` | 对应地址的 IP 列表 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
没有入站、本地地址无效或本地地址不是 IP 时返回 `nil`。
|
||||
|
||||
#### GetAttributes
|
||||
|
||||
```lua
|
||||
local attributes = ctx:GetAttributes()
|
||||
```
|
||||
|
||||
取得当前请求的属性。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------------ | ------------------- | ------------------------------- |
|
||||
| `attributes` | `map[string]string` | 始终返回 `userdata`,不为 `nil` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
使用 `attributes[key]` 读取属性,`key` 必须为字符串。已存在的键返回字符串(可能为 `""`),不存在的键返回 `nil`。
|
||||
|
||||
属性的含义和用途见路由配置中的 [`attrs`](../../../config/routing.md#ruleobject) 字段。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local attributes = ctx:GetAttributes()
|
||||
if attributes[":method"] == "GET" then
|
||||
return "direct", "lua-get"
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,139 @@
|
||||
# xray.dns
|
||||
|
||||
提供 DNS 查询和上游服务器访问接口。
|
||||
|
||||
```lua
|
||||
local dns = require("xray.dns")
|
||||
```
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | ----------------------------------------------------- | ------------------------ |
|
||||
| 函数 | [`dns.Query(domain, ipv4, ipv6, fake)`](#dns-query) | 通过 Xray DNS 客户端查询 |
|
||||
| 字段 | [`dns.Servers`](#dns-servers) | 上游服务器数组 |
|
||||
| 对象 | [`serverObj`](#server) | 单个 DNS 上游服务器 |
|
||||
| 字段 | [`serverObj.ID`](#id) | 服务器标识 |
|
||||
| 方法 | [`serverObj:Query(domain, ipv4, ipv6, fake)`](#query) | 直接查询指定上游 |
|
||||
|
||||
## 函数
|
||||
|
||||
### dns.Query
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = dns.Query(domain, ipv4, ipv6, fake)
|
||||
```
|
||||
|
||||
通过 Xray 的 DNS 客户端查询域名。在 DNS Hook 中不可用,因为脚本接管的就是它。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| -------- | --------- | -------------------- |
|
||||
| `domain` | `string` | 待查询域名 |
|
||||
| `ipv4` | `boolean` | 是否允许查询 IPv4 |
|
||||
| `ipv6` | `boolean` | 是否允许查询 IPv6 |
|
||||
| `fake` | `boolean` | 是否允许使用 FakeDNS |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ----------------------------------------------- | ----------------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) 或 `nil` | 查询结果 |
|
||||
| `ttl` | `number` | TTL(秒),范围 `0 .. 4294967295`,始终非 `nil` |
|
||||
| `err` | [`error`](./data-types.md#error) 或 `nil` | 查询错误,`nil` 表示无错误 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
四个参数均须显式传入且类型正确,否则抛出 Lua 异常。
|
||||
|
||||
查询遵循客户端的全局查询类型限制、Hosts 和上游配置;配置了 [DNS 脚本](../guide/dns.md)时,也会交由该脚本处理。未配置内置 DNS 时使用系统 DNS。
|
||||
|
||||
查询成功时返回非空 IP slice;查询失败或没有可用地址时返回 `nil, 0, err`。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = dns.Query("example.com", true, true, false)
|
||||
if err == nil and ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
|
||||
## 字段
|
||||
|
||||
### dns.Servers
|
||||
|
||||
```lua
|
||||
local servers = dns.Servers
|
||||
```
|
||||
|
||||
| 名称 | 类型 | 说明 |
|
||||
| ------------- | ------- | ---------------------------------------------------------- |
|
||||
| `dns.Servers` | `table` | [`serverObj`](#server) 数组,下标从 1 开始,顺序与配置相同 |
|
||||
|
||||
使用内置 DNS 时,列表包含配置的上游;未配置 DNS 或上游服务器时,列表仅包含 `localhost`(系统 DNS)。
|
||||
|
||||
可用 `ipairs` 遍历列表;下标超出范围时,`servers[i]` 为 `nil`。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
for _, serverObj in ipairs(dns.Servers) do
|
||||
local id = serverObj.ID
|
||||
end
|
||||
```
|
||||
|
||||
## 对象
|
||||
|
||||
### server
|
||||
|
||||
`serverObj` 表示一个 DNS 上游服务器,包含其标识和查询方法。
|
||||
|
||||
| 属性 | 说明 |
|
||||
| --------- | ---------------------------------------- |
|
||||
| Go 侧实现 | `app/dns` 中的 `luaDNSServer` |
|
||||
| Lua 表现 | `table` |
|
||||
| 取得方式 | [`dns.Servers`](#dns-servers) 的数组元素 |
|
||||
|
||||
#### ID
|
||||
|
||||
```lua
|
||||
local id = serverObj.ID
|
||||
```
|
||||
|
||||
| 名称 | 类型 | 说明 |
|
||||
| -------------- | -------- | ----------------------------------------------------------------------------------------- |
|
||||
| `serverObj.ID` | `string` | 对应 [DNS 服务器配置](../../../config/dns.md#dnsserverobject)中的 `id` 字段,始终非 `nil` |
|
||||
|
||||
未填写 `id` 时为 `""`,包括用字符串形式配置的服务器;自动添加的系统 DNS 上游 ID 为 `"localhost"`。
|
||||
|
||||
需要通过 ID 区分服务器时,请自行配置唯一的值,核心不会检查是否重复。
|
||||
|
||||
#### Query
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
```
|
||||
|
||||
查询 `serverObj` 对应的上游。
|
||||
|
||||
**参数与返回值**
|
||||
|
||||
参数要求和返回值类型与 [`dns.Query`](#dns-query) 相同。
|
||||
|
||||
**行为约定**
|
||||
|
||||
正常配置的上游成功时返回非空 IP slice、TTL 和 `nil`;查询失败、没有符合条件的地址,或 `fake == false` 却查询 FakeDNS 时返回 `nil, 0, err`。
|
||||
|
||||
直接查询该服务器,跳过全局 Hosts 和 DNS 脚本。服务器自身的 `queryStrategy`、缓存、`timeoutMs`、`clientIP`、`tag`,以及 `expectedIPs` / `unexpectedIPs` 和对应的 `actPrior` / `actUnprior` 仍然生效。
|
||||
|
||||
**示例**
|
||||
|
||||
在 DNS Hook 中直接返回首个上游的查询结果:
|
||||
|
||||
```lua
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return dns.Servers[1]:Query(domain, ipv4, ipv6, fake)
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,344 @@
|
||||
# xray.geodata
|
||||
|
||||
提供域名和 IP 的规则匹配功能。
|
||||
|
||||
```lua
|
||||
local geodata = require("xray.geodata")
|
||||
```
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | ---------------------------------------------------------------- | ------------------------ |
|
||||
| 函数 | [`geodata.BuildDomainMatcher(...)`](#geodata-builddomainmatcher) | 创建 `DomainMatcher` |
|
||||
| 函数 | [`geodata.BuildIPMatcher(...)`](#geodata-buildipmatcher) | 创建 `IPMatcher` |
|
||||
| 对象 | [`DomainMatcher`](#domainmatcher) | 域名匹配器 |
|
||||
| 方法 | [`domainMatcherObj:MatchAny(domain)`](#matchany) | 判断域名是否命中任意规则 |
|
||||
| 方法 | [`domainMatcherObj:Match(domain)`](#match) | 取得命中的域名规则编号 |
|
||||
| 对象 | [`IPMatcher`](#ipmatcher) | IP 匹配器 |
|
||||
| 方法 | [`ipMatcherObj:Match(ip)`](#match-1) | 匹配单个 IP |
|
||||
| 方法 | [`ipMatcherObj:AnyMatch(ips)`](#anymatch) | 判断是否有 IP 命中 |
|
||||
| 方法 | [`ipMatcherObj:Matches(ips)`](#matches) | 判断整个 IP 列表是否命中 |
|
||||
| 方法 | [`ipMatcherObj:FilterIPs(ips)`](#filterips) | 分离命中和未命中的 IP |
|
||||
| 方法 | [`ipMatcherObj:SetReverse(reverse)`](#setreverse) | 设置反选标记 |
|
||||
| 方法 | [`ipMatcherObj:ToggleReverse()`](#togglereverse) | 切换反选标记 |
|
||||
|
||||
## 函数
|
||||
|
||||
### geodata.BuildDomainMatcher
|
||||
|
||||
```lua
|
||||
local domainMatcherObj = geodata.BuildDomainMatcher(rule1, rule2, ...)
|
||||
```
|
||||
|
||||
根据域名规则创建 [`DomainMatcher`](#domainmatcher)。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ------------------- | -------- | -------------------------------- |
|
||||
| `rule1, rule2, ...` | `string` | 必填,至少一条域名规则,逐个传入 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------------------ | --------------------------------- | -------------- |
|
||||
| `domainMatcherObj` | [`DomainMatcher`](#domainmatcher) | 域名匹配器实例 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
未传入规则、参数类型错误,或规则解析、资源加载、构建失败时,抛出 Lua 异常。
|
||||
|
||||
支持 `domain:`、`full:`、`keyword:`、`regexp:`、`dotless:`、`geosite:`、`ext:`(亦可写为 `ext-domain:` / `ext-site:`),格式见[路由域名规则](../../../config/routing.md#ruleobject)。GeoSite 文件从[资源目录](../../../config/env.md#资源文件路径)加载。
|
||||
|
||||
无前缀字符串默认使用 `domain:` 规则。
|
||||
|
||||
除正则表达式外,规则在构建时转为小写。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local sitesObj = geodata.BuildDomainMatcher(
|
||||
"example.com", "full:other.example"
|
||||
)
|
||||
```
|
||||
|
||||
### geodata.BuildIPMatcher
|
||||
|
||||
```lua
|
||||
local ipMatcherObj = geodata.BuildIPMatcher(rule1, rule2, ...)
|
||||
```
|
||||
|
||||
根据 IP 规则创建 [`IPMatcher`](#ipmatcher)。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ------------------- | -------- | -------------------------------- |
|
||||
| `rule1, rule2, ...` | `string` | 必填,至少一条 IP 规则,逐个传入 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| -------------- | ------------------------- | ------------- |
|
||||
| `ipMatcherObj` | [`IPMatcher`](#ipmatcher) | IP 匹配器实例 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
未传入规则、参数类型错误、规则为空字符串,或规则解析、资源加载、构建失败时,抛出 Lua 异常。
|
||||
|
||||
支持 IP、CIDR、`geoip:`、`ext:`(亦可写为 `ext-ip:`)及 `!` 反选规则,组合方式见[路由 IP 规则](../../../config/routing.md#ruleobject)。GeoIP 文件从[资源目录](../../../config/env.md#资源文件路径)加载。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local privateIPsObj = geodata.BuildIPMatcher(
|
||||
"10.0.0.0/8", "192.168.0.0/16", "fc00::/7"
|
||||
)
|
||||
```
|
||||
|
||||
## 对象
|
||||
|
||||
### DomainMatcher
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ----------- | ------------------------------------------------------------------------- |
|
||||
| Go 底层类型 | `geodata.DomainMatcher` 接口的实现 |
|
||||
| Lua 表现 | `userdata` |
|
||||
| 取得方式 | [`geodata.BuildDomainMatcher(...)`](#geodata-builddomainmatcher) 的返回值 |
|
||||
|
||||
**通用约定**
|
||||
|
||||
匹配方法接收一个域名字符串。`nil`、省略参数、类型或参数数量错误会抛出 Lua 异常。
|
||||
|
||||
输入域名不会自动转为小写,需由脚本按需处理。
|
||||
|
||||
#### MatchAny
|
||||
|
||||
```lua
|
||||
local matched = domainMatcherObj:MatchAny(domain)
|
||||
```
|
||||
|
||||
判断域名是否命中至少一条规则。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| -------- | -------- | ---------------- |
|
||||
| `domain` | `string` | 必填,待匹配域名 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| --------- | --------- | ------------------------------- |
|
||||
| `matched` | `boolean` | 命中时为 `true`,否则为 `false` |
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local matched = sitesObj:MatchAny("www.example.com")
|
||||
```
|
||||
|
||||
#### Match
|
||||
|
||||
```lua
|
||||
local indices = domainMatcherObj:Match(domain)
|
||||
```
|
||||
|
||||
取得域名命中的规则编号。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| -------- | -------- | ---------------- |
|
||||
| `domain` | `string` | 必填,待匹配域名 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| --------- | --------------------------------------------- | ---------------------------------------------------- |
|
||||
| `indices` | [`[]uint32`](./data-types.md#uint32) 或 `nil` | 命中的规则编号;未命中时为 `nil` 或长度为 0 的 slice |
|
||||
|
||||
**行为约定**
|
||||
|
||||
规则编号从 **0** 开始,对应构建时传入的规则顺序。结果顺序不保证,可能包含重复编号;GeoSite 展开的条目沿用其所属规则的编号。
|
||||
|
||||
若将同一组规则保存在 Lua 数组中,可用 `rules[ruleNumber + 1]` 取得原规则。slice 的访问方式见[数据类型](./data-types.md#slice)。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local indices = sitesObj:Match("www.example.com")
|
||||
if indices ~= nil then
|
||||
for i = 1, #indices do
|
||||
local ruleNumber = indices[i]
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### IPMatcher
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ----------- | ----------------------------------------------------------------- |
|
||||
| Go 底层类型 | `geodata.IPMatcher` 接口的实现 |
|
||||
| Lua 表现 | `userdata` |
|
||||
| 取得方式 | [`geodata.BuildIPMatcher(...)`](#geodata-buildipmatcher) 的返回值 |
|
||||
|
||||
**通用约定**
|
||||
|
||||
匹配单个 IP 时使用 [`net.IP`](./data-types.md#net-ip);匹配或筛选列表时,输入形式见 [IP 列表输入](./data-types.md#ip-列表输入)。IP 地址字符串不会自动解析为 `net.IP`。
|
||||
|
||||
匹配和筛选方法均接受显式的 `nil`。省略参数、类型或参数数量错误会抛出 Lua 异常。
|
||||
|
||||
#### Match
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:Match(ip)
|
||||
```
|
||||
|
||||
判断单个 IP 是否命中规则。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ---- | ------------------------------------------- | ---------------- |
|
||||
| `ip` | [`net.IP`](./data-types.md#net-ip) 或 `nil` | 必填,须显式传入 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| --------- | --------- | --------------------------------------------------- |
|
||||
| `matched` | `boolean` | 有效 IP 命中时为 `true`;`nil` 或无效 IP 为 `false` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
传入 `""` 或空 table `{}` 时,会被视为空 IP 并返回 `false`;字符串不会按 IP 地址文本解析。
|
||||
|
||||
#### AnyMatch
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:AnyMatch(ips)
|
||||
```
|
||||
|
||||
判断是否至少有一个有效 IP 命中规则。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ----- | ------------------------------------------ | ---------------------------- |
|
||||
| `ips` | [IP 列表输入](./data-types.md#ip-列表输入) | 必填,须显式传入,允许 `nil` |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| --------- | --------- | ------------------------------------------------------------------ |
|
||||
| `matched` | `boolean` | 至少一个有效 IP 命中时为 `true`;`nil`、空列表或未命中时为 `false` |
|
||||
|
||||
#### Matches
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:Matches(ips)
|
||||
```
|
||||
|
||||
判断整个 IP 列表是否命中规则。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ----- | ------------------------------------------ | ---------------------------- |
|
||||
| `ips` | [IP 列表输入](./data-types.md#ip-列表输入) | 必填,须显式传入,允许 `nil` |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| --------- | --------- | ---------------------------------------------------------------------- |
|
||||
| `matched` | `boolean` | 整个列表满足匹配要求时为 `true`;`nil`、空列表或含无效 IP 时为 `false` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
组合规则可能包含多个内部匹配器,必须有一个内部匹配器命中整个列表。
|
||||
|
||||
例如,用 `geodata.BuildIPMatcher("192.168.1.0/24", "geoip:us")` 构建匹配器,输入列表同时包含 `192.168.1.1` 和 `8.8.8.8` 时,`Matches` 返回 `false`:前者命中 custom CIDR,后者命中 `geoip:us`,但两类规则属于不同的内部匹配器,没有一个能命中整个列表。
|
||||
|
||||
只需判断是否有地址命中时,使用 [`AnyMatch`](#anymatch)。
|
||||
|
||||
#### FilterIPs
|
||||
|
||||
```lua
|
||||
local matched, unmatched = ipMatcherObj:FilterIPs(ips)
|
||||
```
|
||||
|
||||
将有效 IP 分为命中和未命中两组。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ----- | ------------------------------------------ | ---------------------------- |
|
||||
| `ips` | [IP 列表输入](./data-types.md#ip-列表输入) | 必填,须显式传入,允许 `nil` |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ----------- | -------------------------------------- | ------------------------- |
|
||||
| `matched` | [`[]net.IP`](./data-types.md#net-ip-1) | 命中的 IP,始终非 `nil` |
|
||||
| `unmatched` | [`[]net.IP`](./data-types.md#net-ip-1) | 未命中的 IP,始终非 `nil` |
|
||||
|
||||
**行为约定**
|
||||
|
||||
没有结果的一组为空 slice;传入 `nil`、空列表或全部无效 IP 时,两组均为空 slice。
|
||||
|
||||
无效 IP 会被丢弃,结果顺序不保证与输入相同。
|
||||
|
||||
#### SetReverse
|
||||
|
||||
```lua
|
||||
ipMatcherObj:SetReverse(reverse)
|
||||
```
|
||||
|
||||
设置各内部匹配器的反选标记。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| --------- | --------- | --------------------------------------- |
|
||||
| `reverse` | `boolean` | 必填,`true` 开启反选,`false` 关闭反选 |
|
||||
|
||||
**返回值**
|
||||
|
||||
无返回值。
|
||||
|
||||
**行为约定**
|
||||
|
||||
`nil`、省略参数或类型错误会抛出 Lua 异常。此操作覆盖各内部匹配器原有的反选标记,包括规则中 `!` 指定的标记。反选只作用于原规则包含的地址族,且不会匹配无效 IP。
|
||||
|
||||
设置后的反选状态保存在当前匹配器对象中。如果后续 Hook 调用复用该对象,修改后的状态也会继续生效,见[池化实例内状态](../guide/lifecycle.md#池化实例内状态)。
|
||||
|
||||
**示例**
|
||||
|
||||
对 `10.0.0.0/8` 反选后,匹配该范围以外的有效 IPv4 地址:
|
||||
|
||||
```lua
|
||||
local ipMatcherObj = geodata.BuildIPMatcher("10.0.0.0/8")
|
||||
ipMatcherObj:SetReverse(true)
|
||||
```
|
||||
|
||||
#### ToggleReverse
|
||||
|
||||
```lua
|
||||
ipMatcherObj:ToggleReverse()
|
||||
```
|
||||
|
||||
切换各内部匹配器的反选标记。
|
||||
|
||||
**参数**
|
||||
|
||||
无额外参数。
|
||||
|
||||
**返回值**
|
||||
|
||||
无返回值。
|
||||
|
||||
**行为约定**
|
||||
|
||||
组合规则中的各内部匹配器分别切换反选标记,结果不等同于对整个组合结果取逻辑反。
|
||||
切换后的反选状态保存在当前匹配器对象中。如果后续 Hook 调用复用该对象,修改后的状态也会继续生效,见[池化实例内状态](../guide/lifecycle.md#池化实例内状态)。
|
||||
@@ -0,0 +1,69 @@
|
||||
# xray.log
|
||||
|
||||
将日志写入 Xray 的日志系统。
|
||||
|
||||
```lua
|
||||
local log = require("xray.log")
|
||||
```
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | ---------------------------------- | ----------------- |
|
||||
| 函数 | [`log.Debug(...)`](#log-debug) | 记录 debug 日志 |
|
||||
| 函数 | [`log.Info(...)`](#log-info) | 记录 info 日志 |
|
||||
| 函数 | [`log.Warning(...)`](#log-warning) | 记录 warning 日志 |
|
||||
| 函数 | [`log.Error(...)`](#log-error) | 记录 error 日志 |
|
||||
|
||||
## 函数
|
||||
|
||||
### log.Debug
|
||||
|
||||
```lua
|
||||
log.Debug(...)
|
||||
```
|
||||
|
||||
记录 `debug` 级别的日志。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ----- | ----------- | ------------------------------------ |
|
||||
| `...` | 任意 Lua 值 | 可选,任意数量的日志内容,按顺序拼接 |
|
||||
|
||||
**返回值**
|
||||
|
||||
无返回值。
|
||||
|
||||
**参数转换**
|
||||
|
||||
参数接受不传参数、`nil`、`""`、`{}` 和空 slice,按顺序转为文本并拼接,不自动插入空格或分隔符。
|
||||
|
||||
- [`error`](./data-types.md#error) 优先转为错误消息。
|
||||
- 其他具有函数类型 `__tostring` 的值使用该转换;转换抛出的异常向调用者传播。
|
||||
- 其余值使用默认字符串表示。`nil` 转为 `"nil"`,空字符串不增加正文;table 和 slice 不会展开为元素列表。
|
||||
|
||||
**输出条件**
|
||||
|
||||
日志以调用脚本的文件名为前缀,是否输出取决于 [`log.loglevel`](../../../config/log.md#logobject)。例如设置为 `warning` 时,`Debug` 和 `Info` 不输出,也不会调用参数的 `__tostring` 转换。
|
||||
|
||||
四个函数均没有 `err` 返回值。将调用赋给变量时,该变量为 `nil`,不能据此判断日志是否成功输出。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
log.Info("查询域名:", domain)
|
||||
log.Warning("上游查询失败:", err)
|
||||
```
|
||||
|
||||
### log.Info
|
||||
|
||||
同上。
|
||||
|
||||
### log.Warning
|
||||
|
||||
同上。
|
||||
|
||||
### log.Error
|
||||
|
||||
同上。
|
||||
@@ -0,0 +1,119 @@
|
||||
# xray.router
|
||||
|
||||
提供[路由脚本](../guide/routing.md)使用的常量和辅助接口。请求参数与上下文见[路由 Hook](./hook-routing.md)。
|
||||
|
||||
```lua
|
||||
local router = require("xray.router")
|
||||
```
|
||||
|
||||
## API 索引
|
||||
|
||||
| 分类 | 成员 | 说明 |
|
||||
| ---- | ---------------------------------------------------------- | ---------------------- |
|
||||
| 常量 | [`router.NetworkUnknown`](#常量) | 未知网络 |
|
||||
| 常量 | [`router.NetworkTCP`](#常量) | TCP |
|
||||
| 常量 | [`router.NetworkUDP`](#常量) | UDP |
|
||||
| 常量 | [`router.NetworkUNIX`](#常量) | UNIX |
|
||||
| 常量 | [`router.LocalOS`](#常量) | 运行平台 |
|
||||
| 函数 | [`router:PickOutbound(balancerTag)`](#router-pickoutbound) | 通过负载均衡器选择出站 |
|
||||
| 函数 | [`router.FindProcess(ctx)`](#router-findprocess) | 查找本地连接对应的进程 |
|
||||
|
||||
## 常量
|
||||
|
||||
| 名称 | 类型 | 值 / 说明 |
|
||||
| ----------------------- | -------- | ----------------------------------------------------- |
|
||||
| `router.NetworkUnknown` | `number` | `0`,未知网络 |
|
||||
| `router.NetworkTCP` | `number` | `2`,TCP |
|
||||
| `router.NetworkUDP` | `number` | `3`,UDP |
|
||||
| `router.NetworkUNIX` | `number` | `4`,UNIX |
|
||||
| `router.LocalOS` | `string` | 当前运行平台,例如 `"windows"`、`"linux"`、`"darwin"` |
|
||||
|
||||
可将[路由 Hook](./hook-routing.md#handleroute) 的 `network` 参数与网络常量比较:
|
||||
|
||||
```lua
|
||||
if network == router.NetworkUDP then
|
||||
return "direct", "lua-udp"
|
||||
end
|
||||
```
|
||||
|
||||
## 函数
|
||||
|
||||
### router:PickOutbound
|
||||
|
||||
```lua
|
||||
local outboundTag, err = router:PickOutbound(balancerTag)
|
||||
```
|
||||
|
||||
通过指定的负载均衡器选择出站。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ------------- | -------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `balancerTag` | `string` | 要使用的负载均衡器的 `tag` 值,在 [`routing.balancers`](../../../config/routing.md#balancerobject) 中配置 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------------- | ----------------------------------------- | ---------------------------- |
|
||||
| `outboundTag` | `string` 或 `nil` | 成功时为选中出站的非空 `tag` |
|
||||
| `err` | [`error`](./data-types.md#error) 或 `nil` | `nil` 表示成功 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
必须显式传入字符串,否则抛出 Lua 异常。空字符串 `""` 会按空 `tag` 查找。
|
||||
|
||||
使用结果前应先检查 `err`。负载均衡器不存在时返回 `nil, err`;存在但未选出出站时返回 `"", err`。配置的 `fallbackTag` 生效时,返回该出站标识和 `nil`。
|
||||
|
||||
**示例**
|
||||
|
||||
下面使用 `tag` 为 `"proxy-pool"` 的负载均衡器,并按[路由 Hook 的返回值约定](./hook-routing.md#返回值)传递结果:
|
||||
|
||||
```lua
|
||||
local outboundTag, err = router:PickOutbound("proxy-pool")
|
||||
return outboundTag, "lua-balance", err
|
||||
```
|
||||
|
||||
完整示例见[路由脚本指南](../guide/routing.md#与路由配置的关系)。
|
||||
|
||||
### router.FindProcess
|
||||
|
||||
```lua
|
||||
local pid, name, path, err = router.FindProcess(ctx)
|
||||
```
|
||||
|
||||
查找本机上与当前连接对应的进程。
|
||||
|
||||
**参数**
|
||||
|
||||
| 参数 | 类型 | 说明 / 要求 |
|
||||
| ----- | ------------------------------------------------------ | ---------------------------------- |
|
||||
| `ctx` | [`routing.Context`](./hook-routing.md#routing-context) | 当前请求的上下文,由路由 Hook 传入 |
|
||||
|
||||
**返回值**
|
||||
|
||||
| 返回值 | 类型 | 说明 |
|
||||
| ------ | ----------------------------------------- | ------------------------------- |
|
||||
| `pid` | `number` | 进程 ID;未取得时通常为 `0` |
|
||||
| `name` | `string` | 进程名称;未取得时为 `""` |
|
||||
| `path` | `string` | 可执行文件路径;未取得时为 `""` |
|
||||
| `err` | [`error`](./data-types.md#error) 或 `nil` | `nil` 表示成功 |
|
||||
|
||||
**行为约定**
|
||||
|
||||
须显式传入有效的路由上下文,否则抛出 Lua 异常。没有来源 IP、网络类型不是 TCP/UDP、查找失败或平台不支持时,返回错误。
|
||||
|
||||
`pid`、`name`、`path` 始终非 `nil`,但失败时可能保留部分信息,使用前应先检查 `err`。没有来源 IP 或网络类型不受支持时,返回 `0, "", "", err`;其他情况原样返回平台查找器的结果。
|
||||
|
||||
查询使用首个来源 IP 和来源端口;有目标 IP 时,同时传入首个目标 IP 和目标端口。具体匹配方式由平台查找器决定。
|
||||
|
||||
查找能力取决于运行平台和权限。Windows、Linux 和 macOS 提供内置实现;Android 需由运行环境注册查找器,成功时也可能没有名称或路径。iOS 和其他不支持的平台返回错误。
|
||||
|
||||
**示例**
|
||||
|
||||
```lua
|
||||
local pid, name, path, err = router.FindProcess(ctx)
|
||||
if err == nil and name == "curl" then
|
||||
return "direct", "lua-process"
|
||||
end
|
||||
```
|
||||
Reference in New Issue
Block a user