# How v2rayN Implements DNS Service for V2Ray and Singbox Cores: A Technical Deep Dive

> Explore how v2rayN uses V2rayDnsService and SingboxDnsService to generate distinct DNS configurations for V2Ray and Singbox cores. Understand their unique tagging and routing strategies.

- Repository: [2dust/v2rayN](https://github.com/2dust/v2rayN)
- Tags: deep-dive
- Published: 2026-02-27

---

**v2rayN generates DNS configurations programmatically through two dedicated services—`V2rayDnsService` for V2Ray/Xray cores and `SingboxDnsService` for Singbox—each building core-specific JSON schemas with distinct tagging strategies and routing rules.**

The v2rayN client (available at `2dust/v2rayN`) abstracts DNS configuration generation behind core-specific services that translate user settings into executable JSON configs. Whether routing traffic through V2Ray/Xray or Singbox, the application dynamically constructs DNS servers, routing rules, and resolver chains based on the selected core type. This article examines the source code implementation to reveal how v2rayN handles DNS service implementation for each proxy core.

## Architectural Overview of v2rayN DNS Implementation

v2rayN delegates DNS configuration to two specialized services located in `v2rayN/ServiceLib/Services/CoreConfig/`. For **V2Ray/Xray** cores, the `V2rayDnsService` class constructs a `Dns4Ray` object, while **Singbox** cores utilize `SingboxDnsService` to build a `Dns4Sbox` structure.

Both services follow an identical high-level workflow:

1. Check for custom DNS configurations (`RawDnsItem.Enabled`)
2. Generate server lists (direct, remote, bootstrap, hosts)
3. Create routing rules mapping domains to specific DNS servers
4. Determine the final resolver tag based on the last routing rule's outbound target

## V2Ray DNS Implementation in v2rayN

### The V2rayDnsService Class

Located in [`v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs), this service is invoked by `CoreConfigV2rayService.GenerateClientConfigContent()` at line 59. It constructs a single DNS configuration object that populates the `<dns>` section of the V2Ray configuration and creates a corresponding field rule under `<routing.rules>`.

### Configuration Generation Process

The service operates through two primary pathways:

- **Custom DNS Injection**: When `RawDnsItem.Enabled` is true, the service calls `GenDnsCustom()` to inject user-provided JSON directly into the configuration.
- **Dynamic DNS Building**: Otherwise, it executes `GenDns()` which parses **Direct**, **Remote**, and **Bootstrap** DNS addresses via `ParseDnsAddresses()`, creates server entries via `CreateDnsServer()`, and assigns the tag `Global.DnsTag` to the DNS object.

The service then creates a **field rule** binding inbound tag `dns` to the outbound tag determined by `BuildFinalRule()`.

### Code Example: Building the DNS Section

```csharp
// Inside V2rayDnsService.GenDns()
var simpleDnsItem = context.SimpleDnsItem;
var dnsItem = _coreConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();

var directDNS = ParseDnsAddresses(simpleDnsItem?.DirectDNS,
                                 Global.DomainDirectDNSAddress.First());
var remoteDNS = ParseDnsAddresses(simpleDnsItem?.RemoteDNS,
                                 Global.DomainRemoteDNSAddress.First());

// Fill server lists …
AddDnsServers(remoteDNS, proxyDomainList);
AddDnsServers(directDNS, directDomainList);

// Set the DNS tag used by the routing rule
dnsItem.tag = Global.DnsTag;

// Create a routing rule that sends DNS queries to the chosen outbound
var finalRule = BuildFinalRule();
_coreConfig.routing.rules.Add(new()
{
    type = "field",
    inboundTag = [Global.DnsTag],
    outboundTag = finalRule.outboundTag,
    balancerTag = finalRule.balancerTag
});

_coreConfig.dns = dnsItem;

```

*Source: [`V2rayDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/V2rayDnsService.cs) lines 70-84 and 96-108*

## Singbox DNS Implementation in v2rayN

### The SingboxDnsService Class

Found in [`v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs), this service is called from `CoreConfigSingboxService.GenerateClientConfigContent()` at line 54. Unlike the V2Ray implementation, Singbox uses a multi-server architecture with distinct tags for different DNS paths.

### Multi-Tag Server Architecture

The service generates a `Dns4Sbox` object containing multiple tagged servers:

- **Remote DNS**: Tagged as `Global.SingboxRemoteDNSTag` (typically `"remote_dns"`), routed through the proxy outbound via the `detour` field
- **Direct DNS**: Tagged as `Global.SingboxDirectDNSTag` (typically `"direct_dns"`), used for direct connections
- **Hosts DNS**: Tagged as `Global.SingboxHostsDNSTag` with `type = "hosts"`, containing predefined and system hosts
- **Bootstrap DNS**: Tagged as `Global.SingboxLocalDNSTag`, used for bootstrapping
- **FakeIP DNS**: Tagged as `Global.SingboxFakeIPTag` when FakeIP is enabled

### Code Example: DNS Servers and Rules

```csharp
// Inside SingboxDnsService.GenDnsServers()
var simple = context.SimpleDnsItem;
var direct = ParseDnsAddress(simple.DirectDNS ?? Global.DomainDirectDNSAddress.First());
direct.tag = Global.SingboxDirectDNSTag;
direct.domain_resolver = Global.SingboxLocalDNSTag;

var remote = ParseDnsAddress(simple.RemoteDNS ?? Global.DomainRemoteDNSAddress.First());
remote.tag = Global.SingboxRemoteDNSTag;
remote.detour = Global.ProxyTag;
remote.domain_resolver = Global.SingboxLocalDNSTag;

// hosts server (predefined + system + custom)
var hosts = new Server4Sbox { tag = Global.SingboxHostsDNSTag, type = "hosts", predefined = new() };
if (simple.AddCommonHosts) hosts.predefined = Global.PredefinedHosts;
if (simple.UseSystemHosts) hosts.predefined.AddRange(Utils.GetSystemHosts());

// Add servers to core config
_coreConfig.dns.servers.AddRange(new[] { remote, direct, hosts });

// -----------------------------------------------------------------
// Inside SingboxDnsService.GenDnsRules()
var rule = new Rule4Sbox
{
    server = Global.SingboxDirectDNSTag,
    strategy = Utils.DomainStrategy4Sbox(simple.Strategy4Freedom),
    domain = context.ProtectDomainList.ToList()
};
_coreConfig.dns.rules.Add(rule);

```

*Source: [`SingboxDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/SingboxDnsService.cs) lines 62-71 and 76-84*

## Key Differences Between V2Ray and Singbox DNS in v2rayN

| Aspect | V2Ray Implementation | Singbox Implementation |
|--------|---------------------|------------------------|
| **Configuration Object** | `Dns4Ray` (custom C# class) | `Dns4Sbox` (matches Singbox JSON schema) |

| **Server Tags** | Single tag `Global.DnsTag` for DNS inbound/outbound pair | Distinct tags: `direct_dns`, `remote_dns`, `hosts_dns`, `bootstrap_dns`, `fake_dns` |
| **Domain Strategy** | Set on freedom outbound and direct/proxy outbounds via `strategy4Freedom` and `strategy4Proxy` | Built into `Rule4Sbox` objects with `strategy` field from `Utils.DomainStrategy4Sbox()` |
| **Hosts Handling** | Merged into `dns.hosts` dictionary | Dedicated hosts server with `type = "hosts"` and `predefined` dictionary |
| **Bootstrap DNS** | Added only when `dnsServerDomains` are present | Always created via `GenBootstrapDns()` |
| **Final Resolver** | Decides default server list based on last routing rule's outbound tag | Sets `dns.final` tag to `direct_dns` or `remote_dns` based on routing heuristic |
| **Fake-IP Support** | Fake-ip server added only for remote DNS path when enabled | Dedicated `fakeip` server with logical rules for query rewriting |

## Determining the Final DNS Resolver

Both services use identical heuristics to determine whether to use direct or remote DNS as the default resolver. They analyze the last routing rule: if the final rule routes to `Global.DirectTag`, the system uses direct DNS; otherwise, it defaults to remote DNS.

For **V2Ray**, this affects which server list is added to `dnsItem.servers`:

```csharp
// V2Ray – decides default DNS servers based on the last routing rule
var useDirectDns = false;
if (rules?.LastOrDefault() is { OutboundTag: Global.DirectTag })
    useDirectDns = true;
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
dnsItem.servers.AddRange(defaultDnsServers);

```

*Source: [`V2rayDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/V2rayDnsService.cs) lines 26-38*

For **Singbox**, this sets the `dns.final` field:

```csharp
// Singbox – same heuristic, but sets the final tag
var useDirectDns = false;
if (rules?.LastOrDefault() is { OutboundTag: Global.DirectTag })
    useDirectDns = true;
_coreConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag
                                   : Global.SingboxRemoteDNSTag;

```

*Source: [`SingboxDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/SingboxDnsService.cs) lines 24-38*

## Summary

- v2rayN implements DNS generation through two dedicated services: `V2rayDnsService` for V2Ray/Xray and `SingboxDnsService` for Singbox cores.
- **V2Ray** uses a monolithic DNS configuration with a single tag and field-based routing rules, while **Singbox** employs multiple tagged servers with complex rule chains.
- Both services support custom DNS JSON injection via `GenDnsCustom()` when `RawDnsItem.Enabled` is true.
- The final DNS resolver is determined by analyzing the last routing rule's outbound tag—if it points to `Global.DirectTag`, direct DNS is used; otherwise, remote DNS is selected.
- Source constants and tags are defined in [`Global.cs`](https://github.com/2dust/v2rayN/blob/main/Global.cs), including `SingboxDirectDNSTag`, `SingboxRemoteDNSTag`, and `DnsTag`.

## Frequently Asked Questions

### How does v2rayN handle custom DNS configurations for different cores?

When `RawDnsItem.Enabled` is true, v2rayN bypasses automatic generation. For **V2Ray**, `GenDnsCustom()` merges user-provided JSON with system hosts. For **Singbox**, it deserializes the JSON directly into a `Dns4Sbox` object and applies protect customizations if needed.

### What is the difference between direct_dns and remote_dns tags in Singbox?

According to the source code in [`Global.cs`](https://github.com/2dust/v2rayN/blob/main/Global.cs), `SingboxDirectDNSTag` (mapped to `"direct_dns"`) handles queries for direct connections, while `SingboxRemoteDNSTag` (mapped to `"remote_dns"`) handles queries routed through the proxy. The `detour` field on the remote server points to `Global.ProxyTag`, ensuring DNS queries use the proxy tunnel.

### How does v2rayN decide which DNS server to use as the final resolver?

Both implementations check the last routing rule in the configuration chain. If `rules.LastOrDefault().OutboundTag` equals `Global.DirectTag`, the system selects direct DNS; otherwise, it selects remote DNS. In Singbox, this sets the `dns.final` field; in V2Ray, it determines which server list populates the default DNS entries.

### Where are the DNS service implementations located in the v2rayN source code?

The V2Ray DNS service resides in [`v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs), invoked by [`CoreConfigV2rayService.cs`](https://github.com/2dust/v2rayN/blob/main/CoreConfigV2rayService.cs) at line 59. The Singbox DNS service is located at [`v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs), called from [`CoreConfigSingboxService.cs`](https://github.com/2dust/v2rayN/blob/main/CoreConfigSingboxService.cs) at line 54. Shared constants are defined in [`v2rayN/ServiceLib/Global.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Global.cs).