# How to Troubleshoot DNS Hijacking Issues with the Local DNS Service in MasterDnsVPN

> Troubleshoot DNS hijacking with MasterDnsVPN. Enable local DNS, bind to 127.0.0.1:53, and configure your OS resolver to secure your DNS resolution.

- Repository: [Amin Mahmoudi/MasterDnsVPN](https://github.com/masterking32/MasterDnsVPN)
- Tags: how-to-guide
- Published: 2026-05-10

---

**Enable `LOCAL_DNS_ENABLED = true` in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml), ensure the service binds to `127.0.0.1:53` without port conflicts, and verify that your OS resolver points to the loopback address to prevent external DNS hijacking.**

MasterDnsVPN includes a built-in local DNS resolver that encapsulates all DNS queries through the VPN tunnel, effectively bypassing ISP-level DNS hijacking. When configured correctly, this client-side service intercepts queries on `127.0.0.1:53` and forwards them securely through the encrypted tunnel to upstream servers. This article provides a complete troubleshooting guide based on the actual source code implementation in the `masterking32/MasterDnsVPN` repository.

## Understanding the Local DNS Architecture

The local DNS service in MasterDnsVPN operates as a **client-side DNS proxy** that tunnels queries through the VPN connection rather than sending them directly to external resolvers. According to the source code in [`internal/udpserver/server.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server.go), the system comprises several key components working together.

### Core Components

- **[`internal/config/client.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/config/client.go)** – Defines the `ClientConfig` struct containing `LOCAL_DNS_ENABLED`, `LOCAL_DNS_IP`, `LOCAL_DNS_PORT`, and cache settings that control the resolver behavior.
- **[`internal/udpserver/dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/dns_tunnel.go)** – Implements the local DNS server logic that listens for incoming queries and encapsulates them into the VPN tunnel protocol.
- **[`internal/dnscache/store.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/dnscache/store.go)** – Manages the in-memory DNS cache to avoid repeated upstream lookups and survive short-term network outages.
- **[`internal/udpserver/server_deferred.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server_deferred.go)** – Handles reassembly of fragmented UDP packets carrying DNS responses back from the server.
- **[`internal/udpserver/server.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server.go)** – Routes tunnel traffic and local DNS service, spawning workers that process queries through the DNS tunnel code.

### Data Flow

When the local resolver functions correctly, DNS queries follow this path:

1. **Application to Local DNS** – Applications send DNS requests to `127.0.0.1:53` (or your configured `LOCAL_DNS_IP:LOCAL_DNS_PORT`).
2. **Cache Lookup** – The resolver checks the in-memory cache (`dnsCache`). Cache hits return immediately without tunnel overhead.
3. **Tunnel Encapsulation** – Cache misses trigger the [`dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/dns_tunnel.go) logic to encode and compress the request, sending it as a DNS-tunnel packet to the server.
4. **Upstream Resolution** – The server forwards the query to `DNS_UPSTREAM_SERVERS` defined in [`server_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_config.toml).
5. **Fragmented Response** – Large responses split into UDP fragments stored temporarily in `dnsFragments` before transmission.
6. **Reassembly and Reply** – [`dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/dns_tunnel.go) reassembles fragments, caches the result, and replies to the original local client.

## Common DNS Hijacking Symptoms and Root Causes

When the local DNS service fails or is misconfigured, traffic may leak to external resolvers, exposing you to DNS hijacking. The following table maps specific symptoms to their likely causes within the MasterDnsVPN architecture.

| Symptom | Likely Cause | Verification Method |
|---------|--------------|---------------------|
| **No DNS resolution** despite VPN connection | `LOCAL_DNS_ENABLED` is `false` or the client listens on a non-standard IP/port | Check [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml) for `LOCAL_DNS_ENABLED = true` and verify `LOCAL_DNS_IP`/`LOCAL_DNS_PORT` values |
| **"Connection refused" on port 53** | Port conflict with `systemd-resolved`, `dnsmasq`, or another local DNS daemon | Run `sudo ss -ulpn \| grep ':53'` to identify the occupying process |
| **Slow or intermittent resolution** | Fragment assembly timeout (`DNS_FRAGMENT_ASSEMBLY_TIMEOUT`) too low for your network latency | Increase timeout value and monitor logs for `dns: fragment timeout` messages |
| **Repeated "dns-fragment-timeout" errors** | Upstream resolver unreachable or server UDP queue overloaded | Verify `DNS_UPSTREAM_SERVERS` in [`server_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_config.toml) and check server CPU metrics |
| **Cache never populates** | `LOCAL_DNS_CACHE_MAX_RECORDS` set to `0` or unusually low value | Inspect client logs for `Cache size: X/Y` entries at `DEBUG` level |
| **Queries still hijacked after enabling local resolver** | OS resolver configuration ([`/etc/resolv.conf`](https://github.com/masterking32/MasterDnsVPN/blob/main//etc/resolv.conf)) bypasses the loopback address | Confirm `nameserver 127.0.0.1` exists and is prioritized in the resolver order |

## Step-by-Step Troubleshooting Guide

Follow this systematic checklist to diagnose and resolve DNS hijacking issues in MasterDnsVPN.

### 1. Verify Local DNS Activation

Confirm the feature is enabled in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml):

```toml
LOCAL_DNS_ENABLED = true
LOCAL_DNS_IP = "127.0.0.1"
LOCAL_DNS_PORT = 53

```

The `ClientConfig` struct in [`internal/config/client.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/config/client.go) parses these values at startup. If `LOCAL_DNS_ENABLED` remains `false`, the client will not spawn the DNS worker goroutine defined in [`internal/udpserver/server.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server.go).

### 2. Check for Port Conflicts

Port 53 is privileged and often occupied by system services. Identify conflicts using:

```bash
sudo ss -ulpn | grep ':53'

```

If `systemd-resolve` or another process holds the port, either stop the conflicting service (`systemctl stop systemd-resolved`) or change `LOCAL_DNS_PORT` to an alternative like `5353`, updating your OS resolver accordingly.

### 3. Optimize Cache and Timeout Settings

For networks with high latency or packet loss, tune these parameters in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml):

```toml
LOCAL_DNS_CACHE_MAX_RECORDS = 5000
LOCAL_DNS_CACHE_TTL_SECONDS = 28800.0
DNS_FRAGMENT_ASSEMBLY_TIMEOUT = 600
LOCAL_DNS_CACHE_PERSIST_TO_FILE = true

```

The `DNS_FRAGMENT_ASSEMBLY_TIMEOUT` setting (default typically lower) controls how long [`internal/udpserver/server_deferred.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server_deferred.go) waits for all fragments of a large DNS response. Increase this value if logs show frequent fragment timeouts.

### 4. Inspect Client Logs

Set `LOG_LEVEL = "DEBUG"` in your configuration and restart the client. Monitor for these specific log prefixes:

- `dns: fragment timeout` – Indicates incomplete UDP reassembly or packet loss
- `dns: cache write error` – Points to memory pressure or disk I/O issues if persistence is enabled
- `dns: upstream timeout` – Suggests the server cannot reach its configured upstream resolvers

### 5. Validate the Resolver Endpoint

Test the local DNS service directly using `dig`:

```bash
dig @127.0.0.1 example.com +short

```

Successful responses should return IP addresses with minimal latency (10-30ms for cached entries). If the command hangs, the DNS tunnel worker in [`internal/udpserver/dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/dns_tunnel.go) is not receiving or processing queries correctly.

### 6. Verify Server-Side Upstream Health

On the server host, confirm that `DNS_UPSTREAM_SERVERS` in [`server_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_config.toml) contains reachable IP addresses. Test connectivity directly:

```bash
dig @1.1.1.1 example.com

```

If the server cannot reach upstream resolvers, the client will experience DNS resolution failures regardless of local configuration.

## Configuration Examples

### Enabling the Local DNS Resolver

This complete [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml) snippet activates the local DNS with production-ready cache settings:

```toml

# client_config.toml

LOCAL_DNS_ENABLED = true
LOCAL_DNS_IP = "127.0.0.1"
LOCAL_DNS_PORT = 53
LOCAL_DNS_CACHE_MAX_RECORDS = 5000
LOCAL_DNS_CACHE_TTL_SECONDS = 28800.0
LOCAL_DNS_CACHE_PERSIST_TO_FILE = true
DNS_FRAGMENT_ASSEMBLY_TIMEOUT = 600

```

These fields correspond to the `ClientConfig` struct defined in [`internal/config/client.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/config/client.go).

### Detecting Port Conflicts on Linux

Use `ss` to find processes blocking port 53:

```bash
sudo ss -ulpn | grep ':53'

# Example output:

# udp   UNCONN   0    0      0.0.0.0:53      0.0.0.0:*   users:(("systemd-resolve",pid=657,fd=13))

```

If another process occupies the port, stop it or modify `LOCAL_DNS_PORT`.

### Monitoring Cache State Programmatically

While debugging, you can inspect the cache size through the [`internal/dnscache/store.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/dnscache/store.go) implementation:

```go
// Debug logging snippet (from internal/udpserver/server.go context)
log.Printf("dns: cache contains %d entries (max %d)",
    dnsCache.Size(), cfg.LocalDNSCacheMaxRecords)

```

This output appears in logs when the cache state changes, helping verify that records are being stored correctly.

## Summary

- **Enable the local resolver** by setting `LOCAL_DNS_ENABLED = true` in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml) to ensure DNS queries route through the VPN tunnel rather than external resolvers.
- **Resolve port conflicts** by checking for competing services on port 53 with `ss -ulpn` and either stopping them or changing `LOCAL_DNS_PORT`.
- **Tune timeout values** by increasing `DNS_FRAGMENT_ASSEMBLY_TIMEOUT` if you encounter fragment reassembly errors in high-latency networks.
- **Verify cache configuration** with `LOCAL_DNS_CACHE_MAX_RECORDS` set to at least 500 entries and `LOCAL_DNS_CACHE_PERSIST_TO_FILE` enabled for resilience across restarts.
- **Monitor debug logs** for entries prefixed with `dns:` to identify whether failures occur at the cache, tunnel, or upstream level according to the logic in [`internal/udpserver/dns_tunnel.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/dns_tunnel.go).

## Frequently Asked Questions

### Why do I still see DNS hijacking after enabling LOCAL_DNS_ENABLED?

If `LOCAL_DNS_ENABLED` is `true` but queries are still hijacked, your operating system is likely bypassing the local resolver. Check [`/etc/resolv.conf`](https://github.com/masterking32/MasterDnsVPN/blob/main//etc/resolv.conf) to ensure it contains `nameserver 127.0.0.1` and that no other DNS configuration (like `systemd-resolved` stub listeners) is overriding this setting. Also verify that no port conflict exists preventing the MasterDnsVPN DNS service from binding to the configured address.

### How do I fix "dns: fragment timeout" errors in the logs?

Fragment timeouts occur when [`internal/udpserver/server_deferred.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/server_deferred.go) cannot reassemble all UDP packets of a large DNS response within the `DNS_FRAGMENT_ASSEMBLY_TIMEOUT` window. Increase this value in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml) (e.g., to 600 seconds) to accommodate high-latency or lossy networks. If timeouts persist, check that the server-side `DNS_UPSTREAM_SERVERS` can resolve the domain and that the server has sufficient CPU to process fragment storage.

### Can I run the local DNS service on a port other than 53?

Yes. Set `LOCAL_DNS_PORT` to any available port (such as 5353) in [`client_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/client_config.toml). However, you must configure your operating system's resolver to use this alternate port. Most applications expect DNS on port 53, so changing this typically requires updating your OS network configuration or using a local DNS forwarder that points to the custom port.

### What causes the local DNS cache to remain empty?

An empty cache usually indicates that `LOCAL_DNS_CACHE_MAX_RECORDS` is set to `0` or too low, or that the persistence layer in [`internal/dnscache/store.go`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/dnscache/store.go) is failing to initialize. Verify your configuration includes `LOCAL_DNS_CACHE_MAX_RECORDS = 500` or higher, and check logs for disk permission errors if `LOCAL_DNS_CACHE_PERSIST_TO_FILE` is enabled.