Direct/Freedom outbound: Better Compatibility (#896)

https://github.com/XTLS/Xray-core/pull/6058
This commit is contained in:
Meow
2026-09-11 06:46:51 +08:00
committed by GitHub
parent 9125d3237c
commit b7207f4da4
30 changed files with 451 additions and 477 deletions
+16 -16
View File
@@ -4,23 +4,19 @@
Встроенный модуль DNS в Xray имеет три основных назначения:
- На этапе маршрутизации (Routing): резолвинг доменов в IP и сопоставление правил на основе полученных IP для разделения трафика. Будет ли выполняться резолвинг и разделение трафика, зависит от значения `domainStrategy` в конфигурации модуля маршрутизации. Встроенный DNS-сервер используется для запросов только при установке следующих двух значений:
- "IPIfNonMatch": при запросе домена сначала выполняется сопоставление по правилам `domain`. Если совпадений нет, выполняется запрос к встроенному DNS-серверу для получения IP, после чего снова выполняется сопоставление правил маршрутизации по IP.
- "IPOnDemand": при обнаружении любого правила, основанного на IP, домен немедленно резолвится в IP для сопоставления.
- На этапе маршрутизации: разрешение доменов в IP и сопоставление правил на основе полученных IP для разделения трафика.<br>
Разрешение домена для маршрутизации зависит от значения `routing.domainStrategy`. Встроенный DNS-сервер используется для запросов только при следующих значениях:
- `"IPIfNonMatch"`: если цель запроса задана доменным именем без сопутствующего IP, сначала выполняется проход сопоставления по остальным условиям. Если за этот проход не совпало ни одно правило маршрутизации, домен разрешается через встроенный DNS-сервер, после чего выполняется еще один проход сопоставления правил с использованием полученных IP-адресов.
- `"IPOnDemand"`: если цель запроса задана доменным именем без сопутствующего IP, при обнаружении правила, основанного на IP, домен сразу разрешается в IP для сопоставления.
- Резолвинг целевого адреса для подключения:
- Например, в `freedom` Outbound, если `domainStrategy` установлен в `UseIP`, запрос, исходящий из этого Outbound, сначала будет разрешен в IP через встроенный сервер, а затем произойдет подключение.
- Например, в `sockopt`, если `domainStrategy` установлен в `UseIP`, системное подключение, инициированное этим Outbound, сначала будет разрешено в IP встроенным сервером.
- На этапе исходящего подключения: разрешение целевых доменных имен для подключения или передачи удаленному прокси-серверу:
- Например, если в исходящем подключении VLESS задать `targetStrategy` равным `UseIP`, целевой домен проксируемого запроса сначала разрешается локальным встроенным модулем DNS, затем полученный IP передается удаленному прокси-серверу.
- Если в исходящем подключении VLESS задать `sockopt.domainStrategy` равным `UseIP`, домен сервера VLESS разрешается встроенным модулем DNS, затем устанавливается соединение с полученным IP.
- Если в исходящем подключении Freedom задать `sockopt.domainStrategy` равным `UseIP`, целевой домен запроса разрешается встроенным модулем DNS, затем устанавливается соединение с полученным IP.
- Протокол WireGuard не допускает передачу доменного имени в качестве цели, поэтому его исходящее подключение может использовать встроенный модуль DNS для разрешения доменов в IP.
- Перехват DNS-трафика в режиме TUN/Transparent Proxy через связку routing и DNS outbound, чтобы направлять DNS-трафик в этот модуль; либо работа в качестве рекурсивного DNS-сервера, открытого на порту 53.
::: tip TIP 1
DNS-сервер по умолчанию проходит сопоставление в системе маршрутизации, если только он не содержит `+local`. При использовании доменных имён внутри него обратите внимание на возможные петли (loop); может помочь `hosts`.
:::
::: tip TIP 2
Поддерживаются только базовые IP-запросы (записи A и AAAA). Записи CNAME будут запрашиваться повторно до тех пор, пока не будет возвращена запись A/AAAA. Другие типы запросов не попадают во встроенный DNS-сервер, а либо отбрасываются, либо передаются другим серверам в зависимости от вашей конфигурации Outbound.
:::
- Перехват DNS-трафика в режиме TUN/прозрачного прокси с помощью маршрутизации и исходящего подключения DNS для направления запросов в этот модуль; либо использование [Tunnel](./inbounds/tunnel.md) для открытия порта 53 и работы в качестве рекурсивного DNS-сервера.
- Поддерживаются только базовые IP-запросы (записи A и AAAA). Записи CNAME будут запрашиваться повторно до тех пор, пока не будет возвращена запись A/AAAA. Другие типы запросов не попадают во встроенный DNS-сервер, а либо отбрасываются, либо передаются другим серверам в зависимости от вашей конфигурации исходящего подключения.
## Процесс обработки DNS
@@ -136,6 +132,10 @@ DNS-клиенты, инициализированные различными п
(v1.4.0+) В [логах](./log.md) можно включить журналирование DNS-запросов.
:::
::: tip TIP 4
DNS-сервер по умолчанию проходит сопоставление в системе маршрутизации, если только он не содержит `+local`. При использовании доменных имён внутри него обратите внимание на возможные петли (loop); может помочь `hosts`.
:::
> `clientIp`: string
IP-адрес, используемый в расширении EDNS Client Subnet (ECS).
@@ -297,7 +297,7 @@ DNS-запросы, отправляемые модулем DNS, бывают д
Режим `local`: соединение устанавливается ядром напрямую во внешнюю сеть. Если адрес является доменом, он будет разрешен самой системой. Логика здесь проста.
Режим `non-local`: по умолчанию запрос рассматривается как входящий из Inbound с тегом `dns.tag` (не знаете где это? нажмите `ctrl+f` в браузере и найдите `inboundTag`). Он проходит через стандартный процесс обработки ядра и может быть направлен модулем маршрутизации в локальный `freedom` или другой удаленный Outbound. Там он будет разрешен согласно `domainStrategy` в `freedom` (осторожно, возможна петля) или передан в удаленный узел в виде домена для разрешения согласно методу сервера.
В режиме `non-local` DNS-запросы поступают в систему маршрутизации как внутренние запросы, а их `inboundTag` задается полем `tag` в конфигурации DNS. Если запрос направлен в локальное исходящее подключение Freedom, доменное имя самого DNS-сервера разрешается согласно `sockopt.domainStrategy` этого подключения (возможны петли). Если запрос направлен в исходящее подключение удаленного прокси, домен можно передать удаленной стороне для разрешения.
Поскольку обычным пользователям сложно разобраться в этой логике, рекомендуется (особенно в среде Transparent Proxy) напрямую указывать соответствующие IP для серверов с доменными именами в опции `hosts` модуля DNS, чтобы предотвратить возникновение петель (loop).
+4 -34
View File
@@ -20,10 +20,6 @@
"settings": {},
"tag": "тег",
"streamSettings": {},
"proxySettings": {
"tag": "another-outbound-tag",
"transportLayer": false
},
"mux": {},
"targetStrategy": "AsIs"
}
@@ -70,48 +66,22 @@ Xray будет использовать случайный IP-адрес из
Конфигурация транспорта для этого исходящего подключения.
> `proxySettings`: [ProxySettingsObject](#proxysettingsobject)
Конфигурация Outbound-прокси.
> `mux`: [MuxObject](#muxobject)
Настройки Mux. Mux позволяет мультиплексировать несколько TCP-соединений через одно TCP-соединение. У Mux есть дополнительная функция: передача UDP-соединений как XUDP.
> `targetStrategy`: "AsIs" | "UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4" | "ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"
Если при исходящем подключении отправляется запрос к доменному имени, эта опция управляет тем, будет ли оно разрешено (и каким образом) в IP-адрес для отправки.
Применяется к исходящим подключениям, кроме Freedom. Определяет, нужно ли локально разрешать целевое доменное имя проксируемого запроса в IP и какую стратегию разрешения использовать.
Значение по умолчанию — `AsIs`, то есть отправка на удаленный сервер «как есть». Значения всех параметров примерно соответствуют `domainStrategy` в [Sockopt](./transports/sockopt.md#sockoptobject).
Значение по умолчанию — `AsIs`: целевое доменное имя передается на удаленный сервер без изменений. Смысл стратегий в основном совпадает с `domainStrategy` в [Sockopt](./transports/sockopt.md#sockoptobject).
::: tip
Здесь контролируются **проксируемые запросы**. Если адресом исходящего прокси-сервера является доменное имя, и для этого домена необходимо выбрать стратегию разрешения, следует настроить `domainStrategy` в [Sockopt](./transports/sockopt.md#sockoptobject).
Стратегию разрешения доменных имен для Freedom также следует задавать через `sockopt.domainStrategy`.
:::
### ProxySettingsObject
```json
{
"tag": "another-outbound-tag",
"transportLayer": false
}
```
> `tag`: string
Если указан тег другого Outbound, данные, исходящие из этого Outbound, будут перенаправлены через указанный Outbound.
::: danger
Эта опция конфликтует с [Sockopt.dialerProxy](./transports/sockopt.md#sockoptobject), используйте только один из этих вариантов по необходимости.
По умолчанию этот способ пересылки **игнорирует** собственную конфигурацию транспорта этого outbound (например XHTTP, REALITY или Sockopt), поэтому `streamSettings` у данного outbound не будут работать.<br>
Если вам нужна пересылка с поддержкой `streamSettings`, используйте `Sockopt.dialerProxy` или установите здесь `transportLayer` в `true`.
:::
> `transportLayer`: true | false
`true` преобразует эту настройку в `Sockopt.dialerProxy`, чтобы пересылка использовала `streamSettings` этого outbound. По умолчанию `false`.
### MuxObject
Функция Mux позволяет мультиплексировать несколько TCP-соединений по одному TCP-соединению.
+74 -44
View File
@@ -1,6 +1,6 @@
# Freedom (fragment, noises)
Freedom — это исходящий протокол, который можно использовать для отправки (обычных) данных TCP или UDP в любую сеть.
Freedom — это протокол прямого исходящего подключения, обычно являющийся конечной точкой обработки трафика: он принимает поступающий трафик TCP или UDP, напрямую устанавливает соединение с конечным адресом назначения и передает и принимает данные.
::: warning
Для этого outbound на стороне сервера и обратного прокси действует политика безопасности по умолчанию, которая может блокировать некоторые цели; способ разрешения см. ниже в `finalRules`.
@@ -16,9 +16,8 @@ Freedom — это исходящий протокол, который можн
{
// ...
"protocol": "freedom",
// [!code focus:29]
// [!code focus:28]
"settings": {
"domainStrategy": "AsIs",
"redirect": "127.0.0.1:3366",
"userLevel": 0,
"fragment": {
@@ -51,26 +50,18 @@ Freedom — это исходящий протокол, который можн
}
```
> `domainStrategy`: "AsIs"
> "UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4"
> "ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"
Значение по умолчанию — `"AsIs"`.
Все параметры по смыслу аналогичны `domainStrategy` в [Sockopt](../transports/sockopt.md#sockoptobject).
Только использование `AsIs` в этом разделе позволяет передать доменное имя в последующий модуль `sockopt`. Если установить значение, отличное от `AsIs`, домен будет разрешен в конкретный IP, что сделает последующие настройки `sockopt.domainStrategy` и связанный с ними механизм `happyEyeballs` недействительными. (Если вы не изменяли эти настройки, негативного влияния не будет).
По определенным причинам при отправке UDP протокол Freedom игнорирует `domainStrategy` в `sockopt` и по умолчанию принудительно отдает предпочтение IPv4.
::: tip
Стратегия разрешения целевого доменного имени в Freedom задается параметром [sockopt.domainStrategy](../transports/sockopt.md#sockoptobject).
:::
> `redirect`: address_port
Freedom будет принудительно отправлять все данные на указанный адрес (а не на адрес, указанный во входящем соединении).
Freedom заменяет текущие адрес и порт назначения соединения на адрес и порт, указанные в `redirect`.
Значение — строка, например: `"127.0.0.1:80"`, `":1234"`.
Если адрес не указан, например `":443"`, Freedom не будет изменять исходный целевой адрес.
Если порт равен `0`, например `"xray.com: 0"`, Freedom не будет изменять исходный порт.
Если порт равен `0`, например `"xray.com:0"`, Freedom не будет изменять исходный порт.
> `userLevel`: number
@@ -78,35 +69,15 @@ Freedom будет принудительно отправлять все дан
Значение `userLevel` соответствует значению `level` в [policy](../policy.md#policyobject). Если не указано, по умолчанию используется значение `0`.
> `fragment`: map
> `fragment`: [FragmentObject](#fragmentobject)
Несколько пар «ключ-значение», используемых для управления исходящей фрагментацией TCP. В некоторых случаях это может обмануть системы цензуры, например, обойти черные списки SNI.
`"length"` и `"interval"` относятся к типу [Int32Range](../../development/intro/guide.md#int32range)
`"packets"`: поддерживаются два режима фрагментации: "1-3" — фрагментация потока TCP, применяется к первым трем операциям записи данных на стороне клиента; "tlshello" — фрагментация пакета TLS-рукопожатия.
`"length"`: длина фрагмента (в байтах).
`"interval"`: интервал между фрагментами (в мс).
Если значение равно `0` и установлено `"packets": "tlshello"`, фрагментированный пакет Client Hello будет отправлен в одном TCP-пакете (если его исходный размер не превышает MSS или MTU, что приводит к автоматической фрагментации системой).
> `noises`: array
> `noises`: \[ [NoiseObject](#noiseobject) \]
UDP-шум, используемый для отправки случайных данных в качестве "шума" перед установлением UDP-соединения. Наличие этой структуры считается включением. Это может обмануть снифферы, но также может нарушить нормальное соединение. Используйте на свой страх и риск. По этой причине он обходит порт 53, так как это нарушает работу DNS.
Массив, в котором можно определить несколько пакетов шума для отправки. Отдельный элемент массива определяется следующим образом:
`"type"`: тип пакета шума. В настоящее время поддерживаются `"rand"` (случайные данные), `"str"` (пользовательская строка) и `"base64"` (пользовательские двоичные данные, закодированные в Base64).
`"packet"`: содержимое пакета данных, основанное на предыдущем значении `type`.
- Если `type` равен `rand`, здесь указывается длина случайных данных. Это может быть фиксированное значение, например `"100"`, или диапазон значений, например `"50-150"`.
- Если `type` равен `str`, здесь указывается строка для отправки.
- Если `type` имеет значение `hex`, здесь указываются двоичные данные в шестнадцатеричном представлении.
- Если `type` равен `base64`, здесь указываются двоичные данные, закодированные в Base64.
`"delay"`: **задержка**, в миллисекундах. После отправки этого пакета шума ядро будет ждать указанное время, прежде чем отправить следующий пакет шума или реальные данные. По умолчанию ожидание отсутствует. Тип: [Int32Range](../../development/intro/guide.md#int32range)
Массив, в котором можно определить несколько пакетов шума для отправки. Каждый элемент — это [NoiseObject](#noiseobject).
> `proxyProtocol`: number
@@ -114,13 +85,23 @@ UDP-шум, используемый для отправки случайных
`proxyProtocol` принимает значение номера версии протокола PROXY — `1` или `2`. Если не указано, по умолчанию используется значение `0` (протокол не используется).
> `finalRules`: \[[FinalRuleObject](#finalruleobject)\]
> `finalRules`: \[ [FinalRuleObject](#finalruleobject) \]
Последовательно сопоставляет финальные правила Freedom и разрешает или блокирует целевые соединения.
По сравнению с блокировкой в `routing`, `finalRules` применяется на финальном этапе исходящего соединения Freedom: сопоставление выполняется после разрешения конечного IP и до дозвона; кроме того, для UDP сопоставление также выполняется для каждого пакета при отправке и приеме, поэтому работает строже и надежнее. Сопоставление каждого правила занимает около 50-150 нс.
По сравнению с блокировкой в `routing`, `finalRules` применяется на финальном этапе исходящего соединения Freedom, до и после установления соединения. Кроме того, для UDP проверяется каждый пакет при отправке и приеме, что делает проверку более строгой и полной. Сопоставление с каждым правилом занимает около 50-150 нс, поэтому беспокоиться о производительности не стоит.
Обратите внимание: всякий раз, когда Freedom должен применять `finalRules`, если `domainStrategy` имеет значение `AsIs`, а целью является домен, Freedom все равно сначала разрешает цель в IP через системный DNS, а затем выполняет сопоставление правил. После этого цель уже не является доменом, поэтому последующий `sockopt.domainStrategy` и связанный с ним `happyEyeballs` больше не применяются.
::: details Если цель — доменное имя
Если цель задана доменным именем и требуется применить правила, Freedom перед установлением соединения разрешает домен согласно `sockopt.domainStrategy`, затем проверяет каждый полученный IP по правилам в указанном порядке. Если заблокирован хотя бы один IP, блокируется весь запрос.
После успешного установления соединения Freedom повторно проверяет по правилам фактический IP удаленной стороны. Поэтому, если предварительное разрешение не удалось или результаты двух разрешений различаются, пакеты TCP-рукопожатия все же могут быть отправлены до перехода соединения в состояние черной дыры.
При отправке каждого UDP-пакета, адресованного доменному имени, домен также разрешается. Однако проверка отдельного пакета сопоставляет с правилами по порядку только выбранный для этого пакета IP назначения и решает, нужно ли его блокировать; все полученные при разрешении IP при этом не проверяются.
:::
::: tip
Если для этого исходящего подключения настроен `sockopt.dialerProxy`, Freedom больше не является конечным исходящим подключением, поэтому `finalRules` и описанная ниже политика безопасности по умолчанию не применяются.
:::
::: warning
На стороне сервера и обратного прокси действует резервная политика безопасности по умолчанию:
@@ -128,10 +109,59 @@ UDP-шум, используемый для отправки случайных
Если ни одно явное правило не совпало, используется встроенное резервное правило: трафик от обратного прокси VLESS по умолчанию блокирует все цели; трафик от входящих `VLESS`, `VMess`, `Trojan`, `Shadowsocks`, `Hysteria` или `WireGuard` по умолчанию блокирует частные и зарезервированные диапазоны IP; прочий трафик по умолчанию полностью разрешается.
Если серверу нужно разрешить клиентам доступ к некоторым внутренним сервисам, явно настройте правила `allow` и по возможности разрешайте только необходимые `network`, `ip` и `port`.
Если серверу также нужны возможности, которые зависят от передачи доменного имени в `sockopt` (например, `sockopt.domainStrategy` или `happyEyeballs`), то использовать эту политику безопасности по умолчанию дальше нельзя. Можно задать первым правило `allow` без каких-либо условий сопоставления, чтобы восстановить прежнее поведение; это также эквивалентно отключению этой политики безопасности по умолчанию, поэтому оцените последствия для безопасности самостоятельно.
:::
### FragmentObject
```json
{
"packets": "tlshello",
"length": "100-200",
"interval": "10-20"
}
```
> `packets`: string
Поддерживаются два режима фрагментации: `"1-3"` — фрагментация потока TCP, применяется к первым трем операциям записи данных на стороне клиента; `"tlshello"` — фрагментация пакета TLS-рукопожатия.
> `length`: [Int32Range](../../development/intro/guide.md#int32range)
Длина фрагмента (в байтах).
> `interval`: [Int32Range](../../development/intro/guide.md#int32range)
Интервал между фрагментами (в мс).
Если значение равно `0` и установлено `"packets": "tlshello"`, фрагментированный пакет Client Hello будет отправлен в одном TCP-пакете (если его исходный размер не превышает MSS или MTU, что приводит к автоматической фрагментации системой).
### NoiseObject
```json
{
"type": "base64",
"packet": "7nQBAAABAAAAAAAABnQtcmluZwZtc2VkZ2UDbmV0AAABAAE=",
"delay": "10-16"
}
```
> `type`: string
Тип пакета шума. В настоящее время поддерживаются `"rand"` (случайные данные), `"str"` (пользовательская строка) и `"base64"` (пользовательские двоичные данные, закодированные в Base64).
> `packet`: string
Содержимое пакета данных, основанное на предыдущем значении `type`.
- Если `type` равен `rand`, здесь указывается длина случайных данных. Это может быть фиксированное значение, например `"100"`, или диапазон значений, например `"50-150"`.
- Если `type` равен `str`, здесь указывается строка для отправки.
- Если `type` имеет значение `hex`, здесь указываются двоичные данные в шестнадцатеричном представлении.
- Если `type` равен `base64`, здесь указываются двоичные данные, закодированные в Base64.
> `delay`: [Int32Range](../../development/intro/guide.md#int32range)
Задержка в миллисекундах. После отправки этого пакета шума ядро ждет указанное время, прежде чем отправить следующий пакет шума или реальные данные. По умолчанию ожидание отсутствует.
### FinalRuleObject
```json
+1 -1
View File
@@ -5,7 +5,7 @@ Loopback — это outbound с возвратом трафика, которы
::: tip Применение
- В местах, где можно указать только outbound и нельзя напрямую задать `balancerTag`, Loopback позволяет косвенно использовать балансировщик.<br>
Например, `proxySettings` и `dialerProxy` в цепочках прокси, а также `fallbackTag` при балансировке нагрузки.
Например, `dialerProxy` в цепочках прокси, а также `fallbackTag` при балансировке нагрузки.
- После того как трафик уже был разделен один раз, его можно дополнительно детализировать по другим условиям.<br>
Например, TCP- и UDP-трафик, разделенные одной и той же группой правил routing, можно направить в разные outbound.
+2 -2
View File
@@ -98,9 +98,9 @@ MTU нижнего уровня tun в Wireguard.
Управляет стратегией разрешения доменных имен, когда адрес сервера Wireguard является доменным именем или целевой адрес проксируемого трафика является доменным именем.
В отличие от большинства прокси-протоколов, Wireguard не позволяет передавать доменные имена в качестве целей. Если в качестве цели передается доменное имя, оно разрешается в IP-адрес через встроенный DNS Xray. Подробнее см. поле `domainStrategy` для outbound `Freedom`. По умолчанию используется `ForceIP`.
В отличие от большинства прокси-протоколов, Wireguard не позволяет передавать доменные имена в качестве целей. Если цель задана доменным именем, перед передачей его необходимо разрешить в IP. Значения этого поля соответствуют стратегиям `Force` в [sockopt.domainStrategy](../transports/sockopt.md#sockoptobject). По умолчанию используется `ForceIP`.
В настройках `domainStrategy` outbound `Freedom` доступны опции, такие как `UseIP`, которые здесь отсутствуют, так как Wireguard должен получить действительный IP-адрес и не может выполнять откат к доменному имени после неудачного разрешения `UseIP`.<br>
В `sockopt.domainStrategy` доступны опции, такие как `UseIP`, которые здесь отсутствуют, так как Wireguard должен получить действительный IP-адрес и не может выполнять откат к доменному имени после неудачного разрешения `UseIP`.<br>
Примечание: при применении к проксируемому трафику эта опция также ограничивается опцией `address`. Например, если вы установили `ForceIPv6v4`, но в `address` не задан IPv6-адрес, то даже при наличии у целевого домена AAAA-записей они не будут разрешены.
### Peers
+28 -28
View File
@@ -93,27 +93,36 @@ Sockopt используется для настройки низкоуровн
Значение по умолчанию — `"AsIs"`.
Когда целевой адрес является доменным именем, это поле управляет тем, как outbound будет его разрешать и использовать:
Если адрес, к которому должно подключиться исходящее соединение, является доменным именем, эта настройка управляет способом его разрешения:
- При `"AsIs"` Xray никак специально не обрабатывает доменное имя и в конце использует обычный dialer Go. Приоритет фиксирован правилами RFC 6724 и обычно приводит к предпочтению IPv6.
- При любом другом значении Xray использует [встроенный DNS](../dns.md). Если `DNSObject` отсутствует, используется системный DNS. Если есть несколько подходящих IP-адресов, ядро случайным образом выбирает один.
- При `"AsIs"` Xray передает домен Go, который разрешает его с использованием DNS-настроек операционной системы и устанавливает соединение. Для TCP обычно сначала пробуется IPv6, а при затруднениях с подключением — IPv4; для UDP предпочтителен IPv4.
::: details Выбор адреса и переход между семействами при AsIs
TCP использует встроенный в Go механизм Happy Eyeballs. Предпочтительным считается семейство первого адреса в результатах разрешения. Если за 300 мс соединение не установлено, начинаются попытки с другим семейством адресов. Если все попытки с предпочтительным семейством завершились ошибкой раньше, другое семейство пробуется немедленно. Настройка Xray `sockopt.happyEyeballs` на это не влияет. См. [реализацию подключения в Go](https://go.dev/src/net/dial.go).
В сборках Xray на чистом Go адреса сортируются по упрощенным правилам RFC 6724: при прочих равных обычно предпочтителен IPv6, а `/etc/gai.conf` не читается. Большинство официальных релизных сборок Xray используют этот подход; в некоторых операционных системах или сторонних сборках поведение может немного отличаться. См. [реализацию сортировки адресов в Go](https://go.dev/src/net/addrselect.go).
UDP предпочитает IPv4 из результатов разрешения и выбирает IPv6 только при отсутствии IPv4. Ошибка отправки не вызывает автоматического перехода на другое семейство адресов. Это поведение действует и при откате стратегии `Use` к `AsIs`. См. [реализацию выбора UDP-адреса в Go](https://go.dev/src/net/ipsock.go).
:::
- При любом другом значении используется [встроенный модуль DNS](../dns.md) Xray. Если `DNSObject` не настроен, используется системный DNS. Если подходят несколько IP-адресов, по умолчанию один выбирается случайно; при включенном `sockopt.happyEyeballs` для TCP выбор выполняется с помощью гонки подключений.
- `"IPv4"` означает разрешение только IPv4. `"IPv4v6"` означает, что сначала разрешается IPv4, а IPv6 разрешается только в том случае, если разрешение IPv4 завершилось ошибкой или не вернуло ни одного IP-адреса. Если IPv4-адреса уже получены, последующая ошибка подключения не приводит к переходу на IPv6. Для `"IPv6"` и `"IPv6v4"` действует та же логика, но порядок семейств адресов обратный.
- Если во встроенном DNS также задан `"queryStrategy"`, фактическое поведение определяется пересечением двух настроек. Например, `"queryStrategy": "UseIPv4"` вместе с `"domainStrategy": "UseIP"` фактически эквивалентно `"domainStrategy": "UseIPv4"`.
- Варианты `"Use*"` делают fallback к `"AsIs"`, если результат разрешения не соответствует нужному семейству адресов.
- Варианты `"Force*"` завершают соединение ошибкой, если получить нужный тип адреса не удалось.
- Если во встроенном модуле DNS задан `"queryStrategy"`, типы разрешаемых IP определяются пересечением двух настроек: разрешаются только типы, допускаемые обеими. Например, `"queryStrategy": "UseIPv4"` вместе с `"domainStrategy": "UseIP"` фактически эквивалентно `"domainStrategy": "UseIPv4"`.
- Варианты с префиксом `"Use"` возвращаются к `AsIs`, если разрешение завершилось ошибкой или результаты не соответствуют требованиям, например если домен разрешается только в IPv4, а выбрано `UseIPv6`.
- При вариантах с префиксом `"Force"` соединение не устанавливается, если разрешение завершилось ошибкой или результаты не соответствуют требованиям.
::: tip TIP
::: tip
Если используется `"UseIP"` или `"ForceIP"` и в [OutboundObject](../outbound.md#outboundobject) задан `sendThrough`, ядро автоматически определяет нужное семейство адресов по локальному адресу. Если вручную зафиксировать, например, `UseIPv4`, а `sendThrough` указывает на IPv6-адрес, соединение завершится ошибкой.
:::
::: danger
Неправильная настройка этой функции может привести к бесконечному циклу.
:::: danger Неправильная настройка этой функции может привести к бесконечному циклу!
Чтобы подключиться к серверу, нужно дождаться результата DNS-запроса, а чтобы завершить DNS-запрос, нужно подключиться к серверу.
Коротко: чтобы подключиться к серверу, нужно дождаться DNS-результата, а чтобы завершить DNS-запрос, нужно подключиться к серверу.
Неопытным пользователям **не рекомендуется** использовать эту функцию без понимания маршрутизации.
Подробно:
::: details Подробное объяснение
1. Есть прокси-сервер `proxy.com` и встроенный DNS в не-Local режиме.
1. Адрес прокси-сервера — доменное имя (`proxy.com`), а встроенный DNS-сервер работает в режиме, отличном от Local.
2. Перед подключением к `proxy.com` Xray сначала пытается разрешить `proxy.com` через встроенный DNS.
3. Встроенный DNS устанавливает соединение с `dns.com`, чтобы узнать IP-адрес `proxy.com`.
4. Неудачные правила маршрутизации отправляют запрос из шага 3 через `proxy.com`.
@@ -129,16 +138,12 @@ Sockopt используется для настройки низкоуровн
- Использовать hosts.
- ~~Если вы до сих пор не понимаете решение, не включайте эту функцию.~~
Поэтому неопытным пользователям использовать эту возможность без понимания маршрутизации не рекомендуется.
:::
::::
> `dialerProxy`: ""
Идентификатор outbound. Если поле не пустое, для установления соединения используется указанный outbound. Это позволяет делать цепочку с учетом транспортных настроек.
::: danger
Эта настройка несовместима с `ProxySettingsObject.Tag`.
:::
Идентификатор исходящего подключения. Если поле не пустое, для установления соединения используется указанное исходящее подключение. Обычно применяется для настройки цепочек прокси.
> `acceptProxyProtocol`: true | false
@@ -205,10 +210,6 @@ Xray проверяет, присутствуют ли в запросе заг
Значение по умолчанию — `false`. Если установить `true`, включается [Multipath TCP](https://en.wikipedia.org/wiki/Multipath_TCP). Параметр относится только к клиентской стороне, поскольку начиная с Go 1.24 прослушивание уже включает MPTCP по умолчанию. Требуется Linux kernel 5.6 или новее.
> `tcpNoDelay`: true | false
Этот параметр удален, потому что Go и так включает TCP no delay по умолчанию. Если вам нужно отключить его, используйте `customSockopt`.
> `addressPortStrategy`: "none" | "SrvPortOnly" | "SrvAddressOnly" | "SrvPortAndAddress" | "TxtPortOnly" | "TxtAddressOnly" | "TxtPortAndAddress"
Позволяет использовать SRV- или TXT-записи для задания адреса и или порта цели для outbound. По умолчанию используется `none`, то есть функция выключена.
@@ -219,9 +220,9 @@ Xray проверяет, присутствуют ли в запросе заг
`PortOnly` заменяет только порт. `AddressOnly` — только адрес. `PortAndAddress` — и адрес, и порт.
Эта настройка применяется раньше `domainStrategy` внутри `sockopt`. После подмены адрес по-прежнему проходит через `domainStrategy`, если он задан. Но применяется она уже после `Freedom.domainStrategy`, поэтому если `Freedom` заранее разрешил домен в IP, этот механизм уже не сработает.
Эта настройка применяется до разрешения адреса через `sockopt.domainStrategy`. После замены адрес по-прежнему разрешается согласно `domainStrategy`.
На практике это означает, что если обычный доменный трафик попадает в `Freedom` с `AsIs`, после включения этого параметра ядро начнет пытаться переписать адрес и порт, например через SRV-запись `google.com`.
Исходящие подключения Freedom не поддерживают эту настройку.
> `customSockopt`: []
@@ -272,13 +273,12 @@ Xray проверяет, присутствуют ли в запросе заг
> `happyEyeballs`: [HappyEyeballsObject](#happyeyeballsobject)
Реализация Happy Eyeballs по RFC 8305, только для TCP. Когда целью является доменное имя, Xray запускает гонку между разрешенными адресами и выбирает первый успешный. Работает только если `Sockopt.domainStrategy` не равен `AsIs`.
Реализация Happy Eyeballs по RFC 8305, только для TCP. Когда целью является доменное имя, Xray запускает гонку между разрешенными адресами и выбирает первый успешный. Работает только если `sockopt.domainStrategy` не равен `AsIs`.
Значения `UseIPv4v6` и `ForceIPv4v6` фактически сокращают список доступных IP-адресов до IPv4 и переходят к разрешению IPv6 только в том случае, если разрешение IPv4 завершилось ошибкой или не вернуло ни одного IP-адреса. Ошибка подключения по IPv4 не запускает этот переход. Такое использование не рекомендуется. Лучше использовать `UseIP` или `ForceIP` вместе с `HappyEyeballs.interleave`.
Значения `UseIPv4v6` и `ForceIPv4v6` фактически сокращают список доступных IP-адресов до IPv4 и переходят к разрешению IPv6 только в том случае, если разрешение IPv4 завершилось ошибкой или не вернуло ни одного IP-адреса. Ошибка подключения по IPv4 не запускает этот переход. Такое использование не рекомендуется. Лучше использовать `UseIP` или `ForceIP` вместе с `happyEyeballs.interleave`.
::: warning
Не используйте эту функцию вместе с `targetStrategy` этого outbound, потому что тогда `Sockopt` увидит только конечный IP после подмены.<br>
Также ее нельзя использовать вместе с `dialerProxy`, иначе `happyEyeballs` не сработает.
Нельзя использовать эту функцию вместе с `dialerProxy`, иначе `happyEyeballs` не сработает.
:::
### HappyEyeballsObject
+4 -4
View File
@@ -34,16 +34,16 @@
Рационально используя мощные возможности встроенного DNS в Xray (Fallback, ECS, фильтрация по IP, тегирование), и тщательно настраивая порядок серверов, вы получите IP-адрес как условие маршрутизации, который будет гораздо точнее и актуальнее, чем `geosite cn/!cn`. Это связано с тем, что принадлежность IP к региону (GeoIP), особенно к Китаю, меняется довольно редко.
Прежде чем продолжить чтение, вам необходимо внимательно изучить и понять: "Основы: Краткий анализ функции маршрутизации (routing) — [Часть 1](./routing-lv1-part1.md), [Часть 2](./routing-lv1-part2.md)".
Предполагается, что вы уже досконально изучили официальное руководство по конфигурации и полностью понимаете работу `domainStrategy` в Routing и Outbound, назначение опций `sniffing` в Inbound, а также поведение системы при различных комбинациях этих параметров.
Предполагается, что вы уже досконально изучили официальное руководство по конфигурации и полностью понимаете работу `domainStrategy` в Routing и `sockopt`, назначение `targetStrategy` в Outbound, назначение опций `sniffing` в Inbound, а также поведение системы при различных комбинациях этих параметров.
Всё готово? Попробуйте осмыслить следующий абзац:
При входящих соединениях `socks` или `http` запрашивается домен. Когда запрос попадает в Routing, стратегия `domainStrategy` (если она отлична от `AsIs`) может использовать встроенный DNS для резолвинга IP, который временно используется для сопоставления правил маршрутизации. При отправке в локальный `direct` Outbound, если его `domainStrategy` не `AsIs`, встроенный DNS снова резолвит IP для исходящего соединения. Запрос, отправляемый на удаленный сервер Xray, содержит только домен; какой именно IP будет использован для доступа, зависит от `direct` Outbound на сервере.
При входящих соединениях `socks` или `http` исходный запрос адресован домену. Когда запрос попадает в модуль маршрутизации, `domainStrategy`, отличный от `AsIs`, может использовать встроенный DNS для разрешения IP, временно используемых при сопоставлении правил. Если трафик направлен в локальный `direct` Outbound, `domainStrategy` в `sockopt`, отличный от `AsIs`, может снова использовать встроенный DNS для разрешения IP исходящего соединения. Если трафик направлен на удаленный сервер Xray и `targetStrategy` локального Outbound равен `AsIs`, цель запроса по-прежнему передается в виде домена; какой именно IP будет использован, зависит от `direct` Outbound на сервере.
В режиме прозрачного прокси (Transparent Proxy) ситуация сложнее. Если включен `sniffing` на Inbound и `destOverride` содержит `[http, tls]`:
- Если `routeOnly = false`, запрошенный IP будет стерт, и дальнейший процесс аналогичен `socks` Inbound.
- Если `routeOnly = true`, то доступны и домен, и IP. В Routing можно напрямую сопоставлять правила по домену и IP, а локальный `direct` Outbound также будет использовать этот IP. Запрос к серверу Xray содержит только IP. Как сервер его обработает? Повторит описанный выше процесс.
- Если `routeOnly = true`, то доступны и домен, и IP. В модуле маршрутизации можно напрямую сопоставлять правила по домену и IP, а локальный `direct` Outbound также будет использовать этот IP. Запрос к серверу Xray содержит только IP. Как сервер его обработает? Повторит описанный выше процесс.
Возникли трудности? Вам нужно вернуться к официальному руководству и попытаться вникнуть. В противном случае вам будет сложно использовать результаты резолвинга DNS-модуля из примеров ниже для правильной маршрутизации.
@@ -266,7 +266,7 @@ graph TD
В этом сценарии, поскольку все запросы к серверу Xray передаются в виде доменов, нет необходимости использовать DNS для многократного поиска оптимального результата. Достаточно быстро определить, не "загрязнен" ли домен, и по возможности получить китайский IP, дружественный к CDN.
В этом примере китайский IP, полученный DNS-модулем, уже на 99% оптимизирован для CDN в Китае. Поэтому вы можете установить `domainStrategy` в `direct` Outbound в значение **не** `AsIs`, чтобы использовать кэш, если это необходимо.<br>
В этом примере китайский IP, полученный DNS-модулем, уже на 99% оптимизирован для CDN в Китае. Поэтому вы можете установить `sockopt.domainStrategy` в `direct` Outbound в значение **не** `AsIs`, чтобы использовать кэш, если это необходимо.<br>
Если вы стремитесь к 100% оптимизации для китайских CDN, можно установить `AsIs`, чтобы использовать системный DNS для повторного резолвинга (дополнительные затраты времени от 1 до сотен мс). Рекомендуется включить "оптимистичное кэширование" (optimistic caching) для дальнейшего снижения задержки.
## Послесловие
+13 -14
View File
@@ -113,8 +113,10 @@ lsmod | grep wireguard
"outbounds": [
{
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
"streamSettings": {
"sockopt": {
"domainStrategy": "UseIPv4"
}
}
// Измените на UseIPv4 или UseIPv6 по вашему выбору
},
@@ -124,11 +126,9 @@ lsmod | grep wireguard
"tag": "wg0",
"streamSettings": {
"sockopt": {
"mark": 255 // <mark>
"mark": 255, // <mark>
"domainStrategy": "UseIPv6"
}
},
"settings": {
"domainStrategy": "UseIPv6"
}
}, // Трафик с меткой fwmark, равной <mark>, будет направлен через UseIPv6/UseIPv4.
// <--Выберите один из вариантов--> Вариант 2: sendThrough
@@ -137,8 +137,10 @@ lsmod | grep wireguard
"protocol": "freedom",
"sendThrough": "your wg0 v4 address",
// Измените на UseIPv4 или UseIPv6 по вашему выбору
"settings": {
"domainStrategy": "UseIPv4"
"streamSettings": {
"sockopt": {
"domainStrategy": "UseIPv4"
}
}
// Измените на UseIPv4 или UseIPv6 по вашему выбору
},
@@ -146,12 +148,10 @@ lsmod | grep wireguard
{
"tag": "wg0",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
},
"streamSettings": {
"sockopt": {
"interface": "wg0"
"interface": "wg0",
"domainStrategy": "UseIPv4"
}
}
},
@@ -193,8 +193,7 @@ lsmod | grep wireguard
```
::: tip
Вы можете изменить "domainStrategy": "UseIPv6", чтобы управлять способом доступа для определенных пользователей.
По моим тестам, этот параметр имеет более высокий приоритет, чем gai.config в системе.
Вы можете управлять способом доступа для соответствующих пользователей, задав `sockopt.domainStrategy` равным `UseIPv6`.
:::
## 5. Настройка системы
+4 -8
View File
@@ -53,12 +53,10 @@ sudo curl -oL /usr/local/share/xray/geosite.dat https://github.com/Loyalsoldier/
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
},
"streamSettings": {
"sockopt": {
"mark": 2
"mark": 2,
"domainStrategy": "UseIPv4"
}
}
},
@@ -95,12 +93,10 @@ sudo curl -oL /usr/local/share/xray/geosite.dat https://github.com/Loyalsoldier/
"settings": {
"rewriteAddress": "8.8.8.8"
},
"proxySettings": {
"tag": "proxy"
},
"streamSettings": {
"sockopt": {
"mark": 2
"mark": 2,
"dialerProxy": "proxy"
}
}
}
@@ -94,12 +94,10 @@
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
},
"streamSettings": {
"sockopt": {
"mark": 255
"mark": 255,
"domainStrategy": "UseIP"
}
}
},