← board

DNS resolver library (dns.pas) with selectable backends

Summary

Add a stable dns.pas resolver facade with multiple selectable resolver backends. DNS resolution is policy above PAL, not a PAL primitive: PAL should only provide file IO, UDP/TCP sockets, readiness/error primitives, and later AF_UNIX if D-Bus needs it.

The project should support all three main paths:

  1. getaddrinfo() via libc for maximum system compatibility.
  2. A pure Pascal DNS wire client over PAL UDP/TCP for libc-free executables.
  3. systemd-resolved over D-Bus for Linux systems that want systemd split-DNS and resolver policy without linking libc.

Public shape

dns.pas should be the stable facade:

lib/rtl/dns.pas
  public API
  TDnsAddress, TDnsResult, TDnsOptions
  ResolveHost, ResolveService
  async variants later, or a sibling async facade over the same core

Keep protocol parsing/state separate from transport:

dns_wire_core
  DNS packet encode/decode
  answer parser
  CNAME chain handling
  cache structs later
  /etc/hosts and resolv.conf parser for POSIX

dns_wire_blocking
  uses net.pas / blocking PAL sockets

dns_wire_async
  uses asyncnet.pas / nonblocking PAL sockets + readiness waits

The split mirrors the larger networking decision: blocking and async are different facades above the IO boundary, but the DNS packet/parser core should be shared.

Resolver backends

dns_libc

Use getaddrinfo() / freeaddrinfo().

Pros:

Cons:

dns_wire

Pure Pascal DNS client over PAL UDP/TCP.

Baseline behavior:

Policy work to implement deliberately:

Pros:

Cons:

dns_resolved

Talk to systemd-resolved over D-Bus.

Pros:

Cons:

dns_esp

ESP-IDF/lwIP resolver API backend, if platform DNS is already configured by the ESP network stack.

Pros:

Cons:

Low-priority experimental: dns_libc_dyn

Dynamic-loading libc (dlopen + getaddrinfo) is technically possible as an explicit experimental backend, but should not drive the design:

Static backend selection is cleaner.

Backend selection

Resolver choice is deployment policy, not language semantics. Prefer project or library profile configuration over a long-term language-level compiler switch. A compiler define/switch is acceptable as a first crude mechanism, but the final shape should fit the scoped profile/config system:

dns_backend = wire
dns_backend = libc
dns_backend = resolved
dns_backend = esp
dns_backend = auto
dns_backend = auto_fallback
dns_public_fallback = false

Possible define names for the early slice:

PXX_DNS_WIRE
PXX_DNS_LIBC
PXX_DNS_RESOLVED
PXX_DNS_ESP

auto should mean compile/profile-time choice based on target and enabled features. It should not silently change DNS policy at runtime.

auto_fallback should be explicit and runtime-ordered, for example:

resolved -> wire
esp -> wire
libc -> wire

Runtime fallback is useful but can surprise users because each resolver path may honor different DNS policy. It must be opt-in.

Public DNS fallback servers (1.1.1.1, 8.8.8.8, etc.) must be opt-in only and off by default. Defaulting to public resolvers breaks VPNs, private LAN names, split DNS, captive portals, enterprise policy, and privacy expectations.

Overlooked or adjacent options

Acceptance

Log