# Встроенный DNS-сервер ## DNS-сервер Встроенный модуль DNS в Xray имеет три основных назначения: - На этапе маршрутизации (Routing): резолвинг доменов в IP и сопоставление правил на основе полученных IP для разделения трафика. Будет ли выполняться резолвинг и разделение трафика, зависит от значения `domainStrategy` в конфигурации модуля маршрутизации. Встроенный DNS-сервер используется для запросов только при установке следующих двух значений: - "IPIfNonMatch": при запросе домена сначала выполняется сопоставление по правилам `domain`. Если совпадений нет, выполняется запрос к встроенному DNS-серверу для получения IP, после чего снова выполняется сопоставление правил маршрутизации по IP. - "IPOnDemand": при обнаружении любого правила, основанного на IP, домен немедленно резолвится в IP для сопоставления. - Резолвинг целевого адреса для подключения: - Например, в `freedom` Outbound, если `domainStrategy` установлен в `UseIP`, запрос, исходящий из этого Outbound, сначала будет разрешен в IP через встроенный сервер, а затем произойдет подключение. - Например, в `sockopt`, если `domainStrategy` установлен в `UseIP`, системное подключение, инициированное этим Outbound, сначала будет разрешено в 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 Домен сначала проходит проверку сопоставления Hosts (см. поле `hosts`). Если нужный IP не найден, для запроса используется DNS-сервер. Затем ядро начинает строить список серверов, сортируя их в зависимости от запрашиваемого домена по следующим правилам. - Построение списка 1: содержит серверы, у которых поле `domains` успешно совпало с запрашиваемым доменом, в порядке их появления в конфигурационном файле. - Проверка `disableFallback`: если `true`, построение списка 2 пропускается. - Проверка `disableFallbackIfMatch`: если `true` и список 1 не пуст, построение списка 2 пропускается. - Построение списка 2: содержит серверы, не входящие в список 1, у которых `skipFallback` не равно `true`, в порядке их появления в конфигурационном файле. - Итоговый список серверов = список 1 + список 2. Примечание: любой DNS-сервер с `FinalQuery`, равным `true`, напрямую усекает последующую часть списка. При выполнении DNS-запроса ядро последовательно опрашивает серверы из итогового списка, фильтруя результаты с помощью `expectedIPs` и `unexpectedIPs`; если результат пуст, переходит к следующему серверу в списке. (При `enableParallelQuery`, равном `true`, поведение немного отличается, подробнее см. описание этого поля.) ## DnsObject `DnsObject` соответствует разделу `dns` в конфигурационном файле. ```json { "dns": { "hosts": { "baidu.com": "127.0.0.1", "dns.google": ["8.8.8.8", "8.8.4.4"] }, "servers": [ "8.8.8.8", "8.8.4.4", { "address": "1.2.3.4", "port": 5353, "domains": ["domain:xray.com"], "expectedIPs": ["geoip:cn"], "skipFallback": false, "clientIP": "1.2.3.4" }, { "address": "https://8.8.8.8/dns-query", "domains": ["geosite:netflix"], "skipFallback": true, "queryStrategy": "UseIPv4" }, { "address": "https://1.1.1.1/dns-query", "domains": ["geosite:openai"], "skipFallback": true, "queryStrategy": "UseIPv6" }, "localhost" ], "clientIp": "1.2.3.4", "queryStrategy": "UseIP", "disableCache": false, "serveStale": false, "serveExpiredTTL": 0, "disableFallback": false, "disableFallbackIfMatch": false, "enableParallelQuery": false, "useSystemHosts": false, "tag": "dns_inbound" } } ``` > `hosts`: map{string: address} | map{string: [address]} Статический список сопоставлений. Значения представляют собой записи в форме "домен": "адрес" или "домен": ["адрес 1", "адрес 2"]. Адреса могут быть IP или доменными именами. При разрешении домена ядро проверяет все записи сопоставления и возвращает все совпавшие IP-адреса. Если совпадений не найдено, переходит к этапу DNS-запроса. Целевое значение маппинга может быть доменом. Когда ядро завершает сопоставление и маппинг содержит доменное имя(а), поведение меняется следующим образом: - Если маппинг содержит одновременно IP-адреса и доменные имена, доменные имена удаляются, и возвращаются только IP-адреса. - Если маппинг содержит несколько доменных имен, возникает неоднозначность: сопоставление считается неудачным, рассматривается как пропуск, и выполняется этап DNS-запроса. - Если маппинг содержит ровно одно доменное имя, это доменное имя повторно передаётся в модуль Hosts для рекурсивного разрешения, повторяя описанные шаги; максимальная глубина рекурсии — 5. - Если вышеописанная рекурсивная проверка не вернула IP, и итоговый результат содержит ровно одно доменное имя, это доменное имя заменяет исходный запрошенный домен и отправляется на этап DNS-запроса. Формат сопоставления (`domain:`, `full:` и т.д.) аналогичен `domain` в системе [маршрутизации](./routing.md#ruleobject). Отличие в том, что без префикса здесь по умолчанию используется `full:` (аналогично стандартному файлу hosts). > `servers`: \[string | [DnsServerObject](#dnsserverobject) \] Список DNS-серверов. Поддерживаются два типа: адрес DNS (строка) и [DnsServerObject](#dnsserverobject). Если значение `"localhost"`, используется конфигурация DNS локальной системы. Если значение — адрес DNS `"IP:Port"`, например, `"8.8.8.8:53"`, Xray будет использовать указанный UDP-порт этого адреса для DNS-запроса. Запрос следует правилам маршрутизации. Если порт не указан, по умолчанию используется 53. Если значение в формате `"tcp://host:port"`, например, `"tcp://8.8.8.8:53"`, Xray будет использовать `DNS over TCP`. Запрос следует правилам маршрутизации. По умолчанию порт 53. Если значение в формате `"tcp+local://host:port"`, например, `"tcp+local://8.8.8.8:53"`, Xray будет использовать `TCP Local Mode (TCPL)`. DNS-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound для снижения задержек. По умолчанию порт 53. Если значение в формате `"https://host:port/dns-query"`, например, `"https://dns.google/dns-query"`, Xray будет использовать `DNS over HTTPS` (RFC8484, сокращенно DoH). Некоторые провайдеры имеют сертификаты для IP-адресов, поэтому можно указывать IP напрямую, например, `https://1.1.1.1/dns-query`. Также можно использовать нестандартные порты и пути, например, `"https://a.b.c.d:8443/my-dns-query"`. Если значение в формате `"h2c://host:port/dns-query"`, например, `"h2c://dns.google/dns-query"`, Xray будет использовать формат запроса `DNS over HTTPS`, но отправит его в открытом виде (h2c). Это нельзя использовать напрямую; требуется настроить Freedom Outbound + streamSettings с TLS, чтобы обернуть запрос в нормальный DoH. Используется для специальных целей, например, для настройки SNI в DoH-запросе или использования `utls` отпечатков. Если значение в формате `"https+local://host:port/dns-query"`, например, `"https+local://dns.google/dns-query"`, Xray будет использовать `DoH Local Mode (DOHL)`. DoH-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound. Обычно подходит для использования на сервере. Поддерживаются нестандартные порты и пути. Если значение в формате `"quic+local://host"`, например, `"quic+local://dns.adguard.com"`, Xray будет использовать `DNS over QUIC Local Mode (DOQL)`. DNS-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound. Требуется поддержка DNS over QUIC сервером. По умолчанию используется порт 853, можно указать нестандартный. Если значение `fakedns`, используется функционал FakeDNS. ::: tip TIP 1 При использовании `localhost` DNS-запросы системы не контролируются Xray. Требуется дополнительная настройка, чтобы перенаправить системные DNS-запросы в Xray. ::: ::: tip TIP 2 DNS-клиенты, инициализированные различными правилами, будут отображаться в логе запуска Xray с уровнем `info`, например, режимы `local DOH`, `remote DOH` и `udp`. ::: ::: tip TIP 3 (v1.4.0+) В [логах](./log.md) можно включить журналирование DNS-запросов. ::: > `clientIp`: string IP-адрес, используемый в расширении EDNS Client Subnet (ECS). Должен быть валидным IPv4 или IPv6. При фактической отправке последние биты автоматически стираются: отправляются подсети /24 для IPv4 и /96 для IPv6. > `queryStrategy`: "UseIP" | "UseIPv4" | "UseIPv6" | "UseSystem" Ограничивает возможности всех серверов в модуле DNS, а также задает значение по умолчанию для типов IP-запросов, инициированных самим Xray. Значение по умолчанию `UseIP` разрешает запросы A + AAAA. Если тип IP не указан в запросе, инициированном самим Xray, у вышестоящего DNS-сервера запрашиваются одновременно A и AAAA записи. `UseIPv4` запрашивает и разрешает только A записи; `UseIPv6` запрашивает и разрешает только AAAA записи. `UseSystem` адаптируется к сетевой среде операционной системы. Перед запросом проверяется наличие шлюзов по умолчанию для IPv4 и IPv6, чтобы ограничить возможности серверов и установить тип запроса. В ОС с графическим интерфейсом проверка выполняется в реальном времени, в среде командной строки — только один раз. ```json { "dns": { "servers": [ "https://1.1.1.1/dns-query", { "address": "https://8.8.8.8/dns-query", "domains": ["geosite:netflix"], "skipFallback": true, "queryStrategy": "UseIPv4" // Для доменов netflix запрашивать A запись }, { "address": "https://1.1.1.1/dns-query", "domains": ["geosite:openai"], "skipFallback": true, "queryStrategy": "UseIPv6" // Для доменов openai запрашивать AAAA запись } ], "queryStrategy": "UseIP" // Глобально запрашивать одновременно A и AAAA записи } } ``` ::: tip TIP 1 Глобальное значение `"queryStrategy"` имеет приоритет. Если `"queryStrategy"` во вложенном элементе конфликтует с глобальным `"queryStrategy"`, запрос вложенного элемента вернет пустой ответ. ::: ::: tip TIP 2 Если параметр `"queryStrategy"` во вложенном элементе не указан, используется глобальное значение `"queryStrategy"`. Поведение аналогично версиям Xray-core до v1.8.6. ::: Например:
Глобальный `"queryStrategy": "UseIPv6"` и вложенный `"queryStrategy": "UseIPv4"` — конфликт.
Глобальный `"queryStrategy": "UseIPv4"` и вложенный `"queryStrategy": "UseIPv6"` — конфликт.
Глобальный `"queryStrategy": "UseIP"` и вложенный `"queryStrategy": "UseIPv6"` — не конфликтуют.
Глобальный `"queryStrategy": "UseIP"` и вложенный `"queryStrategy": "UseIPv4"` — не конфликтуют. ```json { "dns": { "servers": [ "https://1.1.1.1/dns-query", { "address": "https://8.8.8.8/dns-query", "domains": ["geosite:netflix"], "skipFallback": true, "queryStrategy": "UseIPv6" // Конфликт: глобальный "UseIPv4" и "UseIPv6" вложенного элемента } ], "queryStrategy": "UseIPv4" } } ``` Запрос домена Netflix получит пустой ответ из-за конфликта значений `"queryStrategy"`. Домен Netflix будет запрошен через `https://1.1.1.1/dns-query` и получит запись A. > `disableCache`: true | false `true` отключает кэширование DNS. По умолчанию `false` (не отключено). Это не влияет на `localhost` DNS (системный DNS), который всегда следует поведению кэширования DNS в Golang (cgo и pure go могут немного отличаться). > `serveStale`: true | false `true` включает оптимистичное кэширование DNS (DNS optimistic caching). По умолчанию `false` (не включено). Работает только если кэширование включено на сервере (зависит от `disableCache`). > `serveExpiredTTL`: number Срок жизни оптимистичного кэша в секундах. По умолчанию 0 (никогда не истекает). Если сервер использует кэш и включено оптимистичное кэширование: когда основной кэш истек, но оптимистичный кэш еще действителен, немедленно возвращается устаревшая запись DNS из кэша, а обновление кэша происходит в фоновом режиме. Это снижает Latency. > `disableFallback`: true | false `true` отключает fallback-запросы DNS. По умолчанию `false` (не отключено). > `disableFallbackIfMatch`: true | false `true` отключает fallback-запросы, если сработал список приоритетных доменов DNS-сервера. По умолчанию `false` (не отключено). > `enableParallelQuery`: true | false `true` включает параллельные запросы. По умолчанию `false` (не включено). Отказоустойчивость (failover) DNS по умолчанию последовательна: запрос к следующему серверу отправляется только после неудачи предыдущего или несовпадения `expectedIPs` / `unexpectedIPs`. При включении параллельных запросов они отправляются асинхронно ко всем выбранным DNS-серверам, и применяется стратегия «Динамическая группировка, гонка внутри группы, откат между группами». **Динамическая группировка**: **соседние** серверы в списке выбранных считаются одной группой, если их параметры `clientIP`, `skipFallback`, `queryStrategy`, `tag`, `domains`, `expectedIPs`, `unexpectedIPs` **полностью** совпадают. **Гонка внутри группы**: если любой DNS-сервер в группе успешно выполнил запрос и полученный IP соответствует `expectedIPs` / `unexpectedIPs`, группа считается успешной. Результаты остальных серверов группы игнорируются. **Откат между группами**: пока первая группа выполняет запрос, система ждет. Если первая группа успешна — возвращается IP. Если все серверы первой группы потерпели неудачу или IP не подошел — происходит откат (fallback) к следующей группе. Если все группы потерпели неудачу, возвращается пустой ответ. > `useSystemHosts`: true | false Если `true`, системный файл hosts добавляется к hosts встроенного DNS. > `tag`: string Для трафика запросов, исходящих от встроенного DNS (кроме режимов `localhost`, `fakedns`, `TCPL`, `DOHL` и `DOQL`), можно использовать этот тег для сопоставления в маршрутизации через `inboundTag`. ### DnsServerObject ```json { "address": "1.2.3.4", "port": 5353, "domains": ["domain:xray.com"], "expectedIPs": ["geoip:cn"], "unexpectedIPs": ["geoip:cloudflare"], "skipFallback": false, "finalQuery": false, "tag": "dns-tag", "clientIP": "1.2.3.4", "queryStrategy": "UseIPv4", "disableCache": false } ``` > `address`: address Список DNS-серверов. Поддерживаются два типа: адрес DNS (строка) и DnsServerObject. Если значение `"localhost"`, используется конфигурация DNS локальной системы. Если значение — адрес DNS `"IP"`, например, `"8.8.8.8"`, Xray будет использовать указанный UDP-порт этого адреса для DNS-запроса. Запрос следует правилам маршрутизации. По умолчанию используется порт 53. Если значение в формате `"tcp://host"`, например, `"tcp://8.8.8.8"`, Xray будет использовать `DNS over TCP`. Запрос следует правилам маршрутизации. По умолчанию порт 53. Если значение в формате `"tcp+local://host"`, например, `"tcp+local://8.8.8.8"`, Xray будет использовать `TCP Local Mode (TCPL)`. DNS-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound для снижения задержек. Если порт не указан, по умолчанию используется 53. Если значение в формате `"https://host:port/dns-query"`, например, `"https://dns.google/dns-query"`, Xray будет использовать `DNS over HTTPS` (RFC8484, сокращенно DoH). Некоторые провайдеры имеют сертификаты для IP-адресов, поэтому можно указывать IP напрямую, например, `https://1.1.1.1/dns-query`. Также можно использовать нестандартные порты и пути, например, `"https://a.b.c.d:8443/my-dns-query"`. Если значение в формате `"https+local://host:port/dns-query"`, например, `"https+local://dns.google/dns-query"`, Xray будет использовать `DoH Local Mode (DOHL)`. DoH-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound для снижения задержек. Обычно подходит для использования на сервере. Поддерживаются нестандартные порты и пути. Если значение в формате `"quic+local://host:port"`, например, `"quic+local://dns.adguard.com"`, Xray будет использовать `DOQ Local Mode (DOQL)`. DNS-запрос не проходит через компонент маршрутизации, а отправляется напрямую через Freedom outbound. Требуется поддержка DNS over QUIC сервером. По умолчанию используется порт 853, можно указать нестандартный. Если значение `fakedns`, используется функционал FakeDNS. ::: tip О режиме local и доменах самих DNS-серверов DNS-запросы, отправляемые модулем DNS, бывают двух типов: Режим `local`: соединение устанавливается ядром напрямую во внешнюю сеть. Если адрес является доменом, он будет разрешен самой системой. Логика здесь проста. Режим `non-local`: по умолчанию запрос рассматривается как входящий из Inbound с тегом `dns.tag` (не знаете где это? нажмите `ctrl+f` в браузере и найдите `inboundTag`). Он проходит через стандартный процесс обработки ядра и может быть направлен модулем маршрутизации в локальный `freedom` или другой удаленный Outbound. Там он будет разрешен согласно `domainStrategy` в `freedom` (осторожно, возможна петля) или передан в удаленный узел в виде домена для разрешения согласно методу сервера. Поскольку обычным пользователям сложно разобраться в этой логике, рекомендуется (особенно в среде Transparent Proxy) напрямую указывать соответствующие IP для серверов с доменными именами в опции `hosts` модуля DNS, чтобы предотвратить возникновение петель (loop). Кстати, DNS-запросы в режиме `non-local` автоматически пропускают этапы резолвинга `IPIfNonMatch` и `IPOnDemand` в модуле маршрутизации, чтобы их резолвинг не был отправлен обратно в модуль DNS, вызывая бесконечный цикл. ::: > `port`: number Порт DNS-сервера, например `53`. Если не указано, по умолчанию `53`. Для режимов DOH, DOHL, DOQL этот параметр недействителен; нестандартные порты следует указывать в URL. > `domains`: \[string\] Список доменов. Домены из этого списка будут приоритетно запрашиваться через данный сервер. Формат доменов аналогичен [конфигурации маршрутизации](./routing.md#ruleobject). > `expectedIPs`:\[string\] Список диапазонов IP. Формат аналогичен [конфигурации маршрутизации](./routing.md#ruleobject). Если этот параметр настроен, Xray DNS проверит возвращенный IP и вернет его только в том случае, если он входит в список `expectedIPs`. Если в списке присутствует `*`, и после фильтрации IP не найден, будет возвращен исходный IP, чтобы запрос не завершился ошибкой. > `unexpectedIPs`: [string] Обратная версия `expectedIPs`. Исключает IP, входящие в этот список. Звездочка работает так же. > `skipFallback`: true | false `true` — пропускать этот сервер при выполнении DNS fallback запросов. По умолчанию `false` (не пропускать). > `timeoutMs`: number Таймаут DNS-сервера. По умолчанию 4000 мс. Это не влияет на `localhost` DNS (системный DNS), который всегда следует поведению таймаута DNS в Golang (cgo и pure go могут немного отличаться). > `finalQuery`: true | false Если `true`, запрос к этому DNS-серверу будет последней попыткой; fallback не будет инициирован. > `queryStrategy`: "UseIP" | "UseIPv4" | "UseIPv6" | "UseSystem" Если не указано, наследуется из глобальной конфигурации. Если указано, позволяет дополнительно ограничить возможности этого сервера, а также задать значение по умолчанию для типов IP-запросов, инициированных самим Xray. Внимание: этот параметр всегда ограничен глобальным `queryStrategy`. ### Следующие параметры, если не указаны, наследуются из глобальной конфигурации, но могут переопределять её здесь > `tag`: string > `clientIP`: [string] > `disableCache`: true | false > `serveStale`: true | false > `serveExpiredTTL`: number