Response Processing
These executors adjust TTLs, address selection, interception results, and reverse-lookup data on existing or synthetic responses.
ttl
Purpose
Rewrites response TTL values.
Example Configuration
Full object form:
- tag: ttl_main
type: ttl
args:
# First force TTL to 300
fix: 300
# Then keep a lower bound
min: 60
# And cap the upper bound
max: 600
Configuration Details
fix
- Type:
duration; Required: no - Purpose: Force all TTLs to one fixed value.
min
- Type:
duration; Required: no - Purpose: Lower bound for TTLs.
max
- Type:
duration; Required: no - Purpose: Upper bound for TTLs.
quick setup
- exec: "ttl 300"
- exec: "ttl 60-600"
Behavior
- Adjusts TTLs on the response path.
- Can fix, clamp, or normalize TTLs.
Typical Uses
- Stabilize answer retention
- Avoid extreme upstream TTL values
ip_selector
ip_selector was first introduced in v1.2.0 and is currently in an experimental stage. Configuration fields, default values, probing strategies, and cache semantics may be adjusted in future releases based on real-world usage feedback. It is recommended to validate the plugin in non-critical environments first and monitor the release notes for related changes.
Purpose
Response IP selector. It only shapes A/AAAA records already present in response, using cached probe scores and bounded active probing to produce a stable order or a smaller answer set.
ip_selector does not race upstream DNS responses, suppress dual-stack families, or carry its own domain-rule system. Use forward / fallback for upstream racing, prefer_ipv4 / prefer_ipv6 for dual-stack policy, and sequence with matchers/providers for domain-specific routing.
Example Configuration
- tag: ip_select
type: ip_selector
args:
selection_mode: first_success
outbound: remote
probe_methods: ["tcp:443", "tcp:80"]
probe_stagger: 200
probe_timeout: 600
max_wait: 1000
top_n: 1
dnssec_policy: reorder_only
max_parallel_probes: 256
cache:
enabled: true
size: 4096
ttl: 3600
failure_ttl: 60
Recommended sequence placement:
- exec: $ip_select
- exec: $cache_main
- matches: !$has_resp
exec: $forward_main
- exec: accept
This lets cache store the original upstream response while ip_selector applies final response shaping on the return path.
Configuration Details
selection_mode
- Type:
string; Required: no; Default:first_success - Values:
first_success,best_within_budget,background - Purpose:
first_success: use the first candidate IP with a successful probe.best_within_budget: choose the lowest-latency IP withinmax_wait.background: return immediately using the original or cached order while refreshing probe scores in the background.
probe_methods
- Type:
array<string> | string; Required: no; Default:["tcp:443", "tcp:80"] - Values:
tcp:<port>,ping,none - Purpose: Probe methods used to score answer IPs.
pingis best-effort and fails open when unavailable.
outbound
- Type:
string; Required: no; Default:network.outbound.default - Purpose: References a
network.outbound.profilesentry so TCP probes can reuse the profile proxy. - Notes:
- Only
tcp:<port>probes use proxy settings;pingalways runs locally. - This field only uses the profile proxy. Target IPs are already known and are not resolved again.
- Only
socks5
- Type:
string; Required: no; Default: none - Purpose: Local SOCKS5 proxy override for TCP probes.
- Notes:
- Uses the same format as
upstreams[].socks5. - When both
outboundandsocks5are configured, localsocks5overrides the profile proxy. - Only
tcp:<port>probes use SOCKS5;pingalways runs locally.
- Uses the same format as
probe_stagger
- Type:
integer; Unit: milliseconds; Required: no; Default:200 - Purpose: Delay between launching different probe methods.
probe_timeout
- Type:
integer; Unit: milliseconds; Required: no; Default:600 - Purpose: Timeout for one probe attempt.
max_wait
- Type:
integer; Unit: milliseconds; Required: no; Default:1000 - Purpose: Maximum per-response wait budget for selection.
top_n
- Type:
integer; Required: no; Default:1 - Purpose: Keep the first N selected addresses. Use
0to reorder only without deleting records.
dnssec_policy
- Type:
string; Required: no; Default:reorder_only - Values:
reorder_only,skip - Purpose: When the request carries DO bit or the response contains RRSIGs covering A/AAAA, the default policy only reorders records.
skipleaves the response unchanged.
max_parallel_probes
- Type:
integer; Required: no; Default:256 - Purpose: Plugin-wide active probe concurrency limit.
cache
- Type:
object; Required: no - Fields:
enabled: enable probe score cache, defaulttrue.size: target cache capacity, default4096.ttl: successful score retention in seconds, default3600.failure_ttl: failed score retention in seconds, default60.
quick setup
- exec: "ip_selector"
- exec: "ip_selector best_within_budget tcp:443,tcp:80,ping"
ip_selector accepts only the OxiDNS-native names listed above and does not provide compatibility aliases.
Metrics
ip_selector_probe_total{method,result}ip_selector_probe_latency_count{method}ip_selector_probe_latency_sum_ms{method}ip_selector_selected_total{source="probe|cache|fallback"}ip_selector_cache_entriesip_selector_dropped_probe_total{reason="parallel_limit|inflight"}
prefer_ipv4 / prefer_ipv6
Purpose
Biases dual-stack results toward one address family.
Example Configuration
- tag: probe_v4
type: forward
args:
upstreams:
- addr: "udp://1.1.1.1:53"
- tag: prefer_v4
type: prefer_ipv4
args:
# Optional: execute only for the internal preferred-QTYPE probe
probe_executor: probe_v4
# Cache whether the preferred family exists
cache: true
# Keep the preference cache for one hour
cache_ttl: 3600
Configuration Details
probe_executor
- Type:
string; Required: no; Default: unset - Purpose: Specifies the executor tag used only for the internal preferred-QTYPE probe. Use a plain tag without
$. - May reference
forward,sequence, or another executor that can independently produce a DNS response. - Inline quick setup is not supported. The dependency graph rejects missing or non-executor references, self-references, and indirect cycles at startup.
- When omitted, the existing continuation probe mode is preserved, so existing configurations need no changes.
cache
- Type:
boolean; Required: no; Default:true - Purpose: Cache preference decisions.
cache_ttl
- Type:
integer; Required: no; Default:3600 - Unit: seconds
- Purpose: Retention for the preference cache.
quick setup
- exec: "prefer_ipv4"
- exec: "prefer_ipv6"
- Quick setup uses compatibility mode and the defaults: no
probe_executor,cache: true, andcache_ttl: 3600. - Use the full plugin configuration to select a dedicated probe executor, disable the cache, or change its TTL.
Behavior
- Helps make A and AAAA selection more stable when both families exist.
- Preferred-family queries pass through normally and warm the preference cache when they return preferred-family answers.
- Non-preferred-family queries are blocked immediately when the cache says the preferred family exists. On a cache miss, the original query continues through the outer downstream chain.
- With
probe_executor, the preferred-family probe runs only the selected executor. Without it, compatibility mode runs the outer downstream chain for the probe as well. - A preferred-family answer suppresses the original family. A non-truncated
NOERRORwithout that answer orNXDOMAINis a definitive miss and uses the original result. No response, execution error, timeout, truncation, or another failure RCODE is unknown, uses the original result, and does not write a negative cache entry.
Subquery and Context Isolation
- The preferred-family query is an internal availability probe, not a replacement response for the client request. When the probe succeeds, the outer context keeps the original QTYPE and generates a
NOERROR/NODATAresponse for that request. - On a cache miss, the original query and preferred-family probe use isolated
DnsContextinstances. The probe inherits ingress data and marks present before entering the selector, but its request, response, later marks, runtime extensions, execution path, andExecStepare not merged into the outer context. After suppression, outer marks therefore remain as they were when the selector was entered. - If the preferred probe has no answer, fails, or times out and the original result is used, the complete original subquery result, including its marks, becomes the outer result.
- This isolation keeps a live probe hit consistent with a direct cache hit: neither exposes preferred-probe state to outer post-processing plugins, and marks must not be used to infer internal probe outcomes.
- Compatibility mode without
probe_executormay execute the chain after the selector twice. Do not place executors with logging, counting, learning, scripts, webhooks, or other non-idempotent side effects in that probe path unless duplicate execution is explicitly acceptable. - Keep a dedicated probe executor free of side effects where possible. Cancellation can prevent pending work, but it cannot roll back external operations that already completed.
- The selector cache is still keyed only by domain name. Disable
cachewhen a probe sequence depends on the client, marks, randomness, rate limits, or other request-scoped state.
Typical Uses
- Prefer the family that works better on a given network
- Reduce dual-stack instability
- Preference is not a substitute for fixing broken transport paths.
- Prefer a dedicated
forwardorsequencethroughprobe_executorso the probe does not repeat the outer downstream chain. Omitting it retains continuation compatibility.
black_hole
Purpose
Generates local interception responses for matched DNS queries, covering every qtype.
Example Configuration
- tag: sinkhole
type: black_hole
args:
mode: custom
ips:
# Returned for A queries
- "0.0.0.0"
# Returned for AAAA queries
- "::"
short_circuit: true
- tag: block_nxdomain
type: black_hole
args:
mode: nxdomain
short_circuit: true
Configuration Details
mode
- Type:
string; Required: no; Default:nxdomainwhenipsis empty,customwhenipsis configured - Values:
nxdomain: returnsNXDOMAINand adds an SOA authority record for negative caching.nodata: returns an emptyNOERRORresponse and adds an SOA authority record for negative caching.null: returns0.0.0.0forA,::forAAAA, and NODATA for other qtypes.custom: returns matching address-family values fromipsforA/AAAA; missing families and other qtypes return NODATA.refused: returns an emptyREFUSEDresponse.
- Compatibility: legacy
ips+short_circuitconfig does not needmode; it is treated ascustom.
ips
- Type:
array; Required: no; Default: empty array - Purpose: Local synthetic addresses used by
custommode. - Operational impact:
- IPv4 addresses are used only for A responses.
- IPv6 addresses are used only for AAAA responses.
ipsis allowed only for implicit or explicitcustommode.
short_circuit
- Type:
bool; Required: no; Default:false - Purpose: Stops the remaining executor chain after an interception response is generated.
quick setup
- exec: "black_hole"
- exec: "black_hole nxdomain short_circuit=true"
- exec: "black_hole nodata"
- exec: "black_hole null"
- exec: "black_hole custom 0.0.0.0 :: short_circuit=true"
# legacy form, equivalent to custom
- exec: "black_hole 0.0.0.0 ::"
Behavior
- Plain
black_holereturnsNXDOMAIN. nxdomain,nodata, andrefusedcover every qtype.nullandcustomreturn addresses for A/AAAA and NODATA for other qtypes.- Requests without a question pass through unchanged.
- By default it keeps running the remaining chain after a match; enable
short_circuitto stop immediately.
Metrics
Exported through the global GET /api/metrics endpoint:
blackhole_block_total
Typical Uses
- Blocking ads, trackers, and malicious domains
- Returning NXDOMAIN / NODATA / REFUSED by policy
- Returning null or custom placeholder addresses
drop_resp
Purpose
Drops the current response.
Example Configuration
- tag: clear_response
type: drop_resp
# No standalone args; execution simply clears the current response
Configuration Details
No standalone configuration fields.
quick setup
- exec: "drop_resp"
Behavior
- Clears the existing response from context so later rules can continue.
Typical Uses
- Discard unwanted intermediate results
- Force a later branch to rebuild the answer
reverse_lookup
Purpose
Maintains a reverse IP-to-name cache and optionally handles PTR requests.
Example Configuration
- tag: reverse_lookup_main
type: reverse_lookup
args:
# Reverse-cache capacity
size: 65535
# Retention time for IP -> name mappings
ttl: 7200
# Answer PTR directly from the learned cache
handle_ptr: true
Configuration Details
size
- Type:
integer; Required: no - Purpose: Reverse cache capacity.
handle_ptr
- Type:
boolean; Required: no; Default:false - Purpose: Answer PTR requests from the reverse cache.
ttl
- Type:
duration; Required: no - Purpose: Reverse cache retention TTL.
Behavior
- Learns from successful responses.
- Can expose cached domain names for IP lookups and PTR handling.
Plugin API
GET /plugins/<tag>?ip=<ip_addr>
Typical Uses
- Debugging resolved destinations
- Supporting PTR-like introspection for learned answers
- This is an auxiliary index, not a replacement for authoritative PTR data.