Skip to main content

UDP and TCP Ingress

Traditional DNS ingress over UDP, TCP, and optional TLS on TCP (DoT).

udp_server​

Purpose​

Listens for DNS over UDP and forwards requests to entry.

Example Configuration​

- tag: udp_in
type: udp_server
args:
# Entry executor for this listener
entry: "seq_main"
# Can be written as ip:port, [ipv6]:port, or :port
listen: ":53"

Configuration Details​

entry​

  • Type: string; Required: yes; Default: none
  • Purpose: Selects the executor that handles every request arriving on this listener.
  • Example: entry: "seq_main"
  • Requirements:
    • Must reference an existing executor plugin.
    • In practice this is usually a sequence tag.
  • Runtime impact:
    • All requests entering this udp_server continue through that executor.
    • Initialization fails if the reference is missing or of the wrong type.

listen​

  • Type: string; Required: yes; Default: none
  • Purpose: Defines the UDP bind address.
  • Examples:
    • listen: "0.0.0.0:53"
    • listen: ":5353"
  • Supported forms:
    • ip:port
    • [ipv6]:port
    • :port
  • Runtime impact:
    • Determines the bound address and port.
    • :port means dual-stack [::]:port; use 0.0.0.0:port for IPv4-only.
    • Invalid addresses, port conflicts, or bind failures prevent startup.

Behavior​

  • Receives requests through a UDP socket.
  • Windows listeners disable UDP port-unreachable notifications during initialization. A 10054 receive notification caused by a client closing its port does not produce a per-event WARN; other receive errors retain their warnings. If setting the socket option fails, startup logs one warning and the listener continues with notification classification.
  • Replies to queries sent to a local unicast address use the query's original destination IP and port as their source, including IPv4, IPv6, and dual-stack wildcard listeners.
  • Wildcard listeners require per-packet destination information from the operating system. If it cannot be enabled, initialization fails with a suggestion to bind an explicit IP, such as 192.168.1.1:53. Configure separate listeners when multiple explicit addresses are needed.
  • On Windows, IPv6 listeners can still start when the IPv4 address family is confirmed to be unavailable. Explicitly configured IPv4 listeners do not depend on IPv6.
  • A datagram with missing or truncated destination information is discarded and an error is logged, preventing a reply from the wrong source address.
  • Response encoding respects the client's advertised EDNS UDP payload size.
  • Oversized responses are truncated according to DNS semantics rather than by blindly cutting raw bytes.

Good Fits​

  • Standard low-overhead DNS ingress with high concurrency.
  • The main listener for local networks.
  • Multi-protocol setups alongside tcp_server or http_server.
Notes
  • UDP and QUIC both consume UDP ports. Avoid conflicts.
  • Prefer a shared sequence entry instead of duplicating policy logic in multiple server instances.

tcp_server​

Purpose​

Listens for DNS over TCP. If cert and key are both configured, it can also serve as a DoT listener.

Example Configuration​

- tag: tcp_in
type: tcp_server
args:
# TCP requests enter the main policy chain
entry: "seq_main"
# Listen on the standard TCP DNS port
listen: ":53"
# Idle connection lifetime in seconds
idle_timeout: 10

- tag: dot_in
type: tcp_server
args:
# Reuse the same policy chain for DoT
entry: "seq_main"
# Typical DoT port
listen: ":853"
# PEM certificate chain
cert: "/etc/oxidns/server.crt"
# PEM private key
key: "/etc/oxidns/server.key"
# Keep DoT connections alive a bit longer for reuse
idle_timeout: 30

Configuration Details​

entry​

  • Type: string; Required: yes; Default: none
  • Purpose: Selects the executor used by TCP or DoT requests.
  • Example: entry: "seq_main"
  • Requirements:
    • Must reference an existing executor plugin.
  • Runtime impact:
    • Every DNS message on accepted connections is processed by that executor.

listen​

  • Type: string; Required: yes; Default: none
  • Purpose: Defines the TCP bind address.
  • Examples:
    • listen: ":53"
    • listen: "127.0.0.1:853"
  • Supported forms:
    • ip:port
    • [ipv6]:port
    • :port
  • Runtime impact:
    • Controls the bind address for plaintext TCP or DoT.
    • :port means dual-stack [::]:port; use 0.0.0.0:port for IPv4-only.

cert​

  • Type: string; Required: no; Default: none
  • Purpose: Path to the TLS certificate file.
  • Example: cert: "/etc/oxidns/server.crt"
  • Usage:
    • Enables TLS when paired with key.
  • Runtime impact:
    • Allows the same plugin type to act as a DoT listener.

key​

  • Type: string; Required: no; Default: none
  • Purpose: Path to the TLS private key file.
  • Example: key: "/etc/oxidns/server.key"
  • Usage:
    • Enables TLS when paired with cert.
  • Runtime impact:
    • TLS mode cannot start if the key is missing or invalid.

idle_timeout​

  • Type: integer; Required: no; Default: 10
  • Unit: seconds
  • Purpose: Controls idle connection lifetime.
  • Example: idle_timeout: 30
  • Runtime impact:
    • Affects keepalive behavior and long-lived connection reuse.

Behavior​

  • Without TLS it serves DNS over TCP.
  • With both cert and key it serves DNS over TLS.
  • The TLS ALPN is set to dot.
  • A single connection can carry multiple DNS messages.
  • When a client disconnects or a response write fails, the connection stops receiving and sending. Accepted requests continue through the executor chain; responses that can no longer be delivered are discarded.
  • On shutdown or reload, the server closes connections and waits for accepted requests to finish before their plugin dependencies are destroyed. There is no additional fixed drain deadline; completion depends on the executor chain and its own timeouts.

Good Fits​

  • TCP fallback ingress.
  • DoT deployments.
  • Clients that benefit from long-lived connections.
Notes
  • cert and key must be configured together.
  • Define two separate plugin instances when both plaintext TCP and DoT are required.