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
+35
View File
@@ -0,0 +1,35 @@
# Среда выполнения и загрузка скриптов
## Среда Lua
Сейчас используется GopherLua, который поддерживает синтаксис Lua 5.1, оператор `goto` из Lua 5.2 и собственный [`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`; при использовании обратной косой черты экранируйте её согласно требованиям формата конфигурации.
+155
View File
@@ -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
-- Использовать публичный DNS для доменов Google.
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
-- Для доменов вне списков сначала искать китайские IP через ECS, затем перейти к обычному публичному 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, сохранение состояния и уничтожение экземпляров. На этой странице правила описаны по типам жизненного цикла.
## Жизненный цикл пула
Сейчас [HandleRoute](../reference/hook-routing.md) в [скриптах маршрутизации](./routing.md) и [HandleDNSQuery](../reference/hook-dns.md) в [скриптах DNS](./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 секунд**. Тайм-аут вызова отсчитывается после получения экземпляра; отдельных полей конфигурации для этих значений пока нет. При вызовах API Xray фактическая отмена также зависит от того, реагирует ли API на сигнал отмены; отдельный DNS-сервер дополнительно ограничен настройкой `timeoutMs`.
Xray не отслеживает и не перекомпилирует основной скрипт автоматически. После его изменения перезапустите Xray, чтобы изменения вступили в силу. Экземпляры, созданные позже, также используют основной скрипт, скомпилированный при текущем запуске.
+119
View File
@@ -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
```
Скрипт читает IP-адрес источника через `ctx`. При адресе `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` в примере значением `tag` балансировщика, настроенного в `routing.balancers`.
## Пример: явный 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` используется только для принятия решения в скрипте. Он не меняет автоматически IP-адреса назначения в `ctx` или фактическую цель соединения. При ошибке разрешения пример выбирает `proxy`; при необходимости измените это поведение согласно своей политике.
Инициализация верхнего уровня и сохранение состояния скриптов маршрутизации описаны в разделе [Жизненный цикл пула](./lifecycle.md#жизненныи-цикл-пула).
+46
View File
@@ -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 и `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` |
Обычно не нужно сравнивать значения `net.IP` по одному в Lua, особенно в часто выполняемом коде. Для сопоставления IP используйте прежде всего [`IPMatcher`](./module-geodata.md#ipmatcher), который напрямую работает со значениями `net.IP`, возвращаемыми API.
Если нужно работать с 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 представлен в Lua как `userdata`, сохраняет тип элементов и индексируется начиная с **1**. Следующие два типа срезов используют одинаковые операции получения длины, доступа по индексу и перебора.
| Тип | Представление в 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
```
Возвращает число элементов среза.
**Возвращаемые значения**
| Значение | Тип | Описание |
| -------- | -------- | -------------------------------------------------- |
| `count` | `number` | Неотрицательное целое число; `0` для пустого среза |
**Поведение**
Списки, возвращаемые API, могут быть `nil` или срезами длины 0. Перед выполнением этих операций `values` не должен быть `nil`; одной проверки `if values then` недостаточно, чтобы определить наличие элементов.
### values[i]
```lua
local value = values[i]
```
Читает один элемент среза.
**Параметры**
| Параметр | Тип | Описание |
| -------- | -------- | -------------------------------------------------- |
| `i` | `number` | Обязателен; целое число в диапазоне `1 .. #values` |
**Возвращаемые значения**
| Значение | Тип | Описание |
| -------- | -------------------------------- | ------------------------------------------------ |
| `value` | [`net.IP`](#net-ip) или `number` | Элемент `[]net.IP` или `[]uint32` соответственно |
**Поведение**
Выход за границы вызывает исключение Lua, а не возвращает `nil`. Избегайте прямого изменения срезов, возвращаемых API.
**Пример**
```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
```
Возвращает итератор среза для обобщённого цикла `for`; индексы начинаются с 1.
**Параметры**
Дополнительных параметров нет.
**Возвращаемые значения**
| Значение | Тип | Описание |
| -------- | ---------- | ----------------------------------------------------------------- |
| Итератор | `function` | Последовательно предоставляет индекс и элемент, как показано выше |
**Поведение**
Срезы не поддерживают `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
При передаче списка IP в `IPMatcher` доступны следующие формы:
| Форма ввода | Представление в Lua | Описание |
| -------------------------- | ------------------- | ------------------------------------------------------------------------------ |
| [`[]net.IP`](#net-ip-1) | `userdata` | Список IP; допускается пустой срез |
| Массив [`net.IP`](#net-ip) | `table` | IP последовательно с индекса 1; допускается пустой массив `{}`, но не пропуски |
| `nil` | `nil` | Нужно передать явно; результат указан в описании каждого метода |
Строки IP-адресов не преобразуются в `net.IP`. Пустая строка `""` не является допустимым списком IP; её передача в `AnyMatch`, `Matches` или `FilterIPs` вызывает исключение Lua.
## error
| Свойство | Описание |
| ------------------- | ---------------------------------------------- |
| Базовый тип Go | `error` |
| Представление в Lua | `userdata` при ошибке; `nil` при её отсутствии |
Если возвращаемое API значение `err` равно `nil`, ошибки нет; иначе это объект ошибки. Его можно без изменений вернуть как `err` из Hook, чтобы передать ошибку ядру Xray.
**Соглашение о возврате из Hook**
| Значение ошибки | Значение |
| ----------------- | ------------------------------------------------------------------------ |
| `nil` | Ошибки нет |
| `userdata` ошибки | Сохраняет исходную ошибку |
| `string` | Сообщение об ошибке от скрипта; пустая строка `""` также означает ошибку |
Строковые ошибки допустимы только в возвращаемых значениях Hook. Сами API Xray возвращают ошибку или `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 @@
# Hook DNS
Во время запросов встроенного DNS ядро вызывает глобальную функцию `HandleDNSQuery` из `dns.script`. Настройка и полные примеры приведены в [руководстве по скриптам DNS](../guide/dns.md).
## Указатель API
| Категория | Член API | Описание |
| --------- | ---------------------------------------- | ---------------------- |
| Hook | [`HandleDNSQuery(...)`](#handlednsquery) | Обработка DNS-запросов |
## Интерфейс Hook
### HandleDNSQuery(...)
```lua
function HandleDNSQuery(domain, ipv4, ipv6, fake)
return ips, ttl, err
end
```
Обрабатывает один DNS-запрос и возвращает срез IP, TTL или ошибку.
#### Параметры
Ядро всегда передаёт все четыре параметра; ни один из них не равен `nil`.
| Параметр | Тип | Описание |
| -------- | --------- | ------------------------------------------------ |
| `domain` | `string` | Запрашиваемый домен; непустой, в нижнем регистре |
| `ipv4` | `boolean` | Разрешены ли запросы адресов IPv4 |
| `ipv6` | `boolean` | Разрешены ли запросы адресов IPv6 |
| `fake` | `boolean` | Разрешён ли FakeDNS для этого запроса |
`ipv4` и `ipv6` уже ограничены глобальным [`queryStrategy`](../../../config/dns.md#dnsobject); хотя бы один равен `true`. Обычно при запросах к вышестоящему серверу эти три булевых параметра передаются без изменений.
#### Возвращаемые значения
| Значение | Тип | Описание |
| -------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) или `nil` | Результаты запроса; нужен срез, массив Lua не принимается |
| `ttl` | `number` или `nil` | TTL в секундах; при `err == nil` должен быть целым числом в диапазоне `0 .. 4294967295` |
| `err` | [`error`](./data-types.md#error), `string` или `nil` | Только `nil` означает отсутствие ошибки |
#### Поведение
Ядро последовательно проверяет `err`, `ttl` и `ips`, останавливаясь при первой ошибке. Если `err` не равен `nil`, два остальных значения игнорируются. При отсутствии ошибки нужно указать допустимый TTL, даже если результат IP пуст.
Для пустого ответа используйте `return nil, 0, nil`. Ошибка должна быть в третьей позиции, например `return nil, nil, "ошибка запроса"`.
| Результат скрипта | Действие ядра |
| ------------------------------------------------------------------ | -------------------------------------------- |
| Проверка успешна, `ips` непустой | Запрос успешен |
| `err == nil`, TTL допустим, но `ips` равен `nil` или пустому срезу | Запрос завершается неудачей с пустым ответом |
| Возвращена ошибка, неверный тип значения или исключение выполнения | Запрос завершается неудачей |
#### Пример
Можно напрямую передать все три возвращаемых значения [`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 маршрутизации
При выборе исходящего подключения ядро вызывает глобальную функцию `HandleRoute` из `routing.script`. Настройка и полные примеры приведены в [руководстве по скриптам маршрутизации](../guide/routing.md).
## Указатель API
| Категория | Член 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` | Тег входящего подключения | `""`, если тег не задан или нет входящей информации |
| `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` | Значение маршрута из 7-го и 8-го байтов UUID VLESS, `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), а не используйте это поведение с несуществующим тегом.
## Связанные объекты
### routing.Context
`routing.Context` представляет контекст маршрутизации текущего запроса и доступен в `HandleRoute` через параметр `ctx`.
| Свойство | Описание |
| ------------------- | ------------------------------------------------- |
| Базовый тип Go | Интерфейс `routing.Context` из `features/routing` |
| Представление в Lua | `userdata` |
| Получение | Первый параметр `HandleRoute`, передаваемый ядром |
Все методы ниже не принимают дополнительных параметров. Операции с IP и срезами описаны в разделе [Типы данных](./data-types.md).
#### GetSourceIPs
```lua
local ips = ctx:GetSourceIPs()
```
Получает IP-адреса источника запроса.
**Параметры**
Дополнительных параметров нет.
**Возвращаемые значения**
| Значение | Тип | Описание |
| -------- | ------------------------------------------------ | --------------------------------- |
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) или `nil` | Список IP соответствующего адреса |
**Поведение**
Возвращает `nil`, если нет входящего подключения, адрес источника недопустим или не является IP-адресом.
#### 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 соответствующего адреса |
**Поведение**
Возвращает `nil`, если нет входящего подключения, локальный адрес недопустим или не является IP-адресом.
#### 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
| Категория | Член API | Описание |
| --------- | ----------------------------------------------------- | ---------------------------------- |
| Функция | [`dns.Query(domain, ipv4, ipv6, fake)`](#dns-query) | Запрос через DNS-клиент Xray |
| Поле | [`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)
```
Запрашивает домен через DNS-клиент Xray. В Hook DNS недоступен, поскольку скрипт заменяет именно этот процесс обработки запросов.
**Параметры**
| Параметр | Тип | Описание |
| -------- | --------- | ------------------------- |
| `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; при ошибке запроса или отсутствии доступных адресов — `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 | `luaDNSServer` из `app/dns` |
| Представление в Lua | `table` |
| Получение | Элемент массива [`dns.Servers`](#dns-servers) |
#### ID
```lua
local id = serverObj.ID
```
| Имя | Тип | Описание |
| -------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `serverObj.ID` | `string` | Поле `id` в [конфигурации DNS-сервера](../../../config/dns.md#dnsserverobject); никогда не `nil` |
Если `id` не задан, значение равно `""`, в том числе у серверов, настроенных строками. Автоматически добавленный системный DNS имеет ID `"localhost"`.
Если нужно различать серверы по ID, самостоятельно задайте уникальные значения: ядро не проверяет дубликаты.
#### Query
```lua
local ips, ttl, err = serverObj:Query(domain, ipv4, ipv6, fake)
```
Запрашивает вышестоящий сервер, представленный `serverObj`.
**Параметры и возвращаемые значения**
Требования к параметрам и типы возвращаемых значений такие же, как у [`dns.Query`](#dns-query).
**Поведение**
Обычно настроенный сервер при успехе возвращает непустой срез IP, TTL и `nil`. При ошибке запроса, отсутствии адресов, удовлетворяющих условиям, или запросе FakeDNS с `fake == false` возвращается `nil, 0, err`.
Запрашивает этот сервер напрямую, обходя глобальные Hosts и скрипт DNS. Собственные настройки сервера `queryStrategy`, кеш, `timeoutMs`, `clientIP`, `tag`, а также `expectedIPs` / `unexpectedIPs` и соответствующие `actPrior` / `actUnprior` продолжают действовать.
**Пример**
Вернуть результат запроса к первому серверу напрямую из Hook DNS:
```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
| Категория | Член 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, ...)
```
Создаёт [`IPMatcher`](#ipmatcher) из правил IP.
**Параметры**
| Параметр | Тип | Описание / Требования |
| ------------------- | -------- | ---------------------------------------------------------------- |
| `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 |
**Поведение**
Номера правил начинаются с **0** и соответствуют порядку правил, переданных при создании. Порядок результатов не гарантируется; возможны повторяющиеся номера. Записи, развёрнутые из GeoSite, сохраняют номер исходного правила.
Если те же правила сохранены в массиве Lua, исходное правило можно получить через `rules[ruleNumber + 1]`. Доступ к срезам описан в разделе [Типы данных](./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` | `true`, если допустимый IP совпал; `false` для `nil` или недопустимого IP |
**Поведение**
`""` или пустая таблица `{}` трактуются как пустой IP и возвращают `false`. Строки не разбираются как текст IP-адреса.
#### AnyMatch
```lua
local matched = ipMatcherObj:AnyMatch(ips)
```
Проверяет, совпадает ли с правилами хотя бы один допустимый IP.
**Параметры**
| Параметр | Тип | Описание / Требования |
| -------- | ------------------------------------------------ | -------------------------------------------------- |
| `ips` | [Ввод списка IP](./data-types.md#ввод-списка-ip) | Обязателен; нужно передать явно; допускается `nil` |
**Возвращаемые значения**
| Значение | Тип | Описание |
| --------- | --------- | ----------------------------------------------------------------------------------------------------------- |
| `matched` | `boolean` | `true`, если совпал хотя бы один допустимый IP; `false` для `nil`, пустого списка или отсутствия совпадений |
#### Matches
```lua
local matched = ipMatcherObj:Matches(ips)
```
Проверяет, совпадает ли с правилами весь список IP.
**Параметры**
| Параметр | Тип | Описание / Требования |
| -------- | ------------------------------------------------ | -------------------------------------------------- |
| `ips` | [Ввод списка IP](./data-types.md#ввод-списка-ip) | Обязателен; нужно передать явно; допускается `nil` |
**Возвращаемые значения**
| Значение | Тип | Описание |
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------- |
| `matched` | `boolean` | `true`, если весь список удовлетворяет условиям; `false` для `nil`, пустого списка или списка с недопустимыми IP |
**Поведение**
Сочетание правил может содержать несколько внутренних объектов сопоставления. Один внутренний объект должен сопоставить весь список.
Например, для `geodata.BuildIPMatcher("192.168.1.0/24", "geoip:us")` список с `192.168.1.1` и `8.8.8.8` даёт `false` у `Matches`: первый IP совпадает с пользовательским 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` |
**Поведение**
Группа без результатов является пустым срезом. При передаче `nil`, пустого списка или только недопустимых IP обе группы — пустые срезы.
Недопустимые 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
| Категория | Член 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`, `""`, `{}` и пустые срезы. Аргументы преобразуются в текст и объединяются по порядку без автоматической вставки пробелов или разделителей.
- Значения [`error`](./data-types.md#error) в первую очередь преобразуются в сообщения об ошибках.
- Для остальных значений с функцией `__tostring` используется это преобразование; исключения из него передаются вызывающему коду.
- Остальные значения используют строковое представление по умолчанию. `nil` становится `"nil"`, пустая строка не добавляет текст; таблицы и срезы не разворачиваются в списки элементов.
**Условия вывода**
Сообщения имеют префикс с именем файла вызывающего скрипта. Вывод зависит от [`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
| Категория | Член 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"` |
Параметр `network` из [Hook маршрутизации](./hook-routing.md#handleroute) можно сравнивать с сетевыми константами:
```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
```