DNS
The resolver: how Apranik turns a domain into addresses. Every lookup goes through it: freedom reaching a destination, a proxy outbound reaching its server, a DNS server reaching its own hostname, and the routing resolve action. Without a dns section, the operating system resolves.
Structure
{
"dns": {
"servers": [
{
"tag": "google",
"type": "doh",
"settings": { "server": "8.8.8.8", "host": "dns.google" },
"tls": { "server_name": "dns.google" },
"outbound": "proxy"
},
{
"tag": "domestic",
"type": "udp",
"settings": { "server": "192.0.2.53" },
"outbound": "direct"
}
],
"rules": [
{
"match": { "domain_suffix": ["ir"] },
"server": "domestic"
}
],
"default_server": "google",
"ip_versions": "prefer_ipv4",
"cache": {
"disable": false,
"max_entries": 4096,
"per_server": false,
"serve_stale": { "enabled": false, "max_stale_age": "1h" }
}
}
}Fields
servers
list of DNS serversRequired
The servers lookups may ask. See DNS server.
rules
list of DNS rules
Which server answers which names; the first match wins. See DNS rule.
default_server
stringRequired
The tag of the server for names no rule matches.
ip_versions
stringDefault: prefer_ipv4
Which addresses a lookup asks for, and in what order:
| Value | Returns |
|---|---|
ipv4_only | IPv4 only |
ipv6_only | IPv6 only |
prefer_ipv4 | IPv4 first, then IPv6 |
prefer_ipv6 | IPv6 first, then IPv4 |
cache.disable
booleanDefault: false
Ask the server every time. Otherwise answers are kept for their TTL, and a name that does not exist for at most 5 minutes. Failures are never kept.
cache.max_entries
integerDefault: 4096
The most answers kept; the least recently used goes first. Each record type of a name is one answer.
cache.per_server
booleanDefault: false
One cache per server instead of one shared cache.
cache.serve_stale
objectDefault: off
enabled returns an expired answer at once and refreshes it in the background, while it is no older than max_stale_age (default 1h) past expiry (RFC 8767). It keeps connections working while a server is briefly unreachable.
Example: avoiding a lookup loop
A proxy whose server is a domain needs a DNS server that does not go through that proxy. Here the proxy's own name comes from the domestic resolver:
{
"outbounds": [
{
"tag": "proxy",
"server": "proxy.example.com",
"server_port": 443,
"transport": [{ "type": "tcp" }, { "type": "tls" }],
"protocol": {
"type": "trojan",
"settings": { "password": "correct horse" }
},
"dns_server": "domestic"
},
{
"tag": "direct",
"protocol": { "type": "freedom" }
}
],
"dns": {
"servers": [
{
"tag": "google",
"type": "doh",
"settings": { "server": "8.8.8.8", "host": "dns.google" },
"outbound": "proxy"
},
{
"tag": "domestic",
"type": "udp",
"settings": { "server": "192.0.2.53" },
"outbound": "direct"
}
],
"default_server": "google"
}
}apranik check refuses a loop it can see and names every server and outbound in it.