How FakeDNS Works in Xray-core: Complete Configuration Guide

FakeDNS is a built-in DNS engine in Xray-core that synthesizes deterministic IP addresses for domain names, enabling domain-based routing even when actual DNS resolution is blocked or poisoned, and requires both address: fakedns in DNS servers and destOverride: [fakedns] in inbound sniffing to operate correctly.

FakeDNS serves as a bridge between DNS resolution and traffic routing in the Xray-core proxy framework. By mapping domains to synthetic IP addresses from reserved ranges (198.18.0.0/15 for IPv4 and fc00::/18 for IPv6), it allows the proxy to handle connections based on domain names after the client has already resolved a fake IP. This implementation leverages an LRU cache for mapping management and integrates with the core's dispatcher for reverse lookups.

Architecture and Implementation

The FakeDNS engine is implemented as a Feature interface (dns.FakeDNSEngine) that components can require for DNS resolution. The architecture follows a clear pipeline from configuration detection to packet sniffing.

Configuration Detection and Loading

When the DNS server list contains an entry with address: fakedns, the core automatically loads the Fake DNS feature via core.OptionalFeatures in proxy/dns/dns.go. The configuration parsing happens in infra/conf/fakedns.go, where the FakeDNSConfig struct handles either a single pool or multiple pools. The FakeDNSPostProcessingStage in the same file validates that at least one inbound has sniffing.destOverride set to fakedns (or fakedns+others), and creates default IPv4 and IPv6 pools if none are explicitly defined.

The FakeDNSEngine Interface

The feature interface is defined in features/dns/fakedns.go as:

type FakeDNSEngine interface {
    features.Feature
    GetFakeIPForDomain(domain string) []net.Address
    GetDomainFromFakeDNS(ip net.Address) string
}

This interface exposes the two core operations: generating fake IPs for domains and reverse-mapping fake IPs back to their original domains.

IP Generation and Pool Management

The concrete implementation resides in app/dns/fakedns/fake.go within the Holder struct. This struct maintains an LRU cache for domain-to-IP mappings and manages CIDR pools for address allocation.

During Start(), the holder initializes with either user-supplied pools or defaults to the standard reserved ranges. The GetFakeIPForDomain method generates IPs by adding a timestamp-derived offset to the pool's base address, checks the LRU for collisions to ensure uniqueness, and stores the mapping in app/dns/fakedns/fake.go lines 99-130. The reverse lookup via GetDomainFromFakeDNS queries this cache and returns an empty string if the IP is not in the managed pool.

DNS Server Integration

The app/dns/nameserver_fakedns.go file implements a DNS server wrapper that forwards incoming DNS queries to the Fake DNS engine. When a query arrives, it calls GetFakeIPForDomain (or GetFakeIPForDomain3 for IPv4/IPv6-specific responses) and returns the generated IP with a TTL of 1, minimizing DNS caching on the client side.

Sniffer Integration and Reverse Lookup

The dispatcher creates a "fake DNS sniffer" implemented in app/dispatcher/fakednssniffer.go. When it detects traffic destined for a fake IP address, it queries the engine via GetDomainFromFakeDNS to retrieve the original domain. This allows routing rules that depend on domain names (such as site-based routing) to function correctly even after the connection has been NAT-ed to a synthetic IP address.

Configuration Examples

Minimal Configuration

To enable FakeDNS, you must configure both the DNS servers and the inbound sniffing override:

dns:
  servers:
    - address: fakedns

inbounds:
  - port: 1080
    protocol: vmess
    sniffing:
      enabled: true
      destOverride: [fakedns]

If the fakeDns section is omitted, FakeDNSPostProcessingStage automatically creates default pools using 198.18.0.0/15 for IPv4 and fc00::/18 for IPv6 according to the logic in infra/conf/fakedns.go lines 92-118.

Custom Pool Configuration

For explicit control over IP ranges and pool sizes:

dns:
  servers:
    - address: fakedns
  queryStrategy: UseIPv4

fakeDns:
  pools:
    - ipPool: 198.18.0.0/15
      poolSize: 32768
    - ipPool: fc00::/18
      poolSize: 32768

inbounds:
  - port: 1080
    protocol: vmess
    sniffing:
      enabled: true
      destOverride: [fakedns]

Protocol-Specific FakeDNS

To force FakeDNS to use only IPv4 or IPv6 pools, set the queryStrategy in the DNS configuration. This setting propagates through FakeDNSPostProcessingStage which sets isIPv4Enable and isIPv6Enable flags based on the strategy, and the server calls GetFakeIPForDomain3 respecting these flags as implemented in app/dns/nameserver_fakedns.go lines 34-36.

dns:
  servers:
    - address: fakedns
  queryStrategy: UseIPv6  # Forces IPv6 fake pool only

Programmatic Usage

You can interact with the FakeDNS engine directly in Go applications:

import (
    "github.com/xtls/xray-core/app/dns/fakedns"
    "github.com/xtls/xray-core/features/dns"
    "github.com/xtls/xray-core/common/net"
)

// Initialize with default IPv4 pool
holder, _ := fakedns.NewFakeDNSHolder()
holder.Start()

// Generate fake IP for domain
ips := holder.GetFakeIPForDomain("example.com")
fakeIP := ips[0].IP()

// Reverse lookup
domain := holder.GetDomainFromFakeDNS(ips[0])

// The holder implements dns.FakeDNSEngine
var engine dns.FakeDNSEngine = holder

This code mirrors the production implementation in app/dns/fakedns/fake.go, utilizing the same Holder struct that manages the LRU cache and CIDR-based allocation.

Summary

  • FakeDNS in Xray-core generates synthetic IPs from reserved ranges (198.18.0.0/15 and fc00::/18) to enable domain-based routing.
  • Configuration requires address: fakedns in DNS servers and destOverride: [fakedns] in inbound sniffing settings.
  • Implementation centers on the dns.FakeDNSEngine interface in features/dns/fakedns.go, with concrete logic in app/dns/fakedns/fake.go.
  • Key components include the Holder struct for LRU cache management, nameserver_fakedns.go for query handling, and fakednssniffer.go for reverse IP-to-domain mapping.
  • IP generation uses timestamp-derived offsets to prevent collisions, while reverse lookup enables routing rules to function post-resolution.

Frequently Asked Questions

What are the default IP ranges used by FakeDNS?

By default, Xray-core allocates fake IPs from 198.18.0.0/15 for IPv4 and fc00::/18 for IPv6. These ranges are defined as constants FakeIPv4Pool and FakeIPv6Pool in features/dns/fakedns.go and are automatically used when no explicit pool configuration is provided.

Why is my FakeDNS configuration not working?

The most common issue is missing the destOverride: [fakedns] setting in your inbound configuration. The FakeDNSPostProcessingStage in infra/conf/fakedns.go explicitly validates that at least one inbound has sniffing enabled with fakedns in the destination override list. Without this, the sniffer cannot map fake IPs back to domains, breaking domain-based routing.

How does FakeDNS handle domain collisions?

The Holder implementation in app/dns/fakedns/fake.go uses an LRU (Least Recently Used) cache to store domain-to-IP mappings. When GetFakeIPForDomain generates a new IP using a timestamp-derived offset, it first checks the cache for existing mappings. If a collision occurs (the generated IP is already assigned), the system handles it within the cache logic to maintain deterministic one-to-one mappings between domains and fake IPs.

Can I use FakeDNS with IPv6-only networks?

Yes. Set queryStrategy: UseIPv6 in your DNS configuration to force the FakeDNS engine to only allocate addresses from the IPv6 pool (fc00::/18). The GetFakeIPForDomain3 method in the nameserver implementation respects the IPv4/IPv6 enable flags set during the post-processing stage, allowing single-stack operation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →