nox-dns#
Warning. This is a work-in-progress experimental fork of ocaml-dns. Do not use it in production; use ocaml-dns instead.
A fork of ocaml-dns, imported at 10.2.5
and exposed as a single module Dns: the wire codec, the LRU cache, and the
transport-agnostic stub client, vendored verbatim. It differs from ocaml-dns in
two ways.
- The stub client's clock and RNG are injected -- see The client.
- A DNS forwarding resolver is added (
Dns.Forwardand its primitives), which ocaml-dns does not have -- see The forwarder.
Installation#
Install with opam:
$ opam install nox-dns
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install nox-dns
The client#
Dns.Client.Make (T) takes a transport T and produces a stub resolver. In
ocaml-dns the transport module type S carries two module-level values:
val rng : int -> string
val clock : unit -> int64
so each transport binds them to fixed globals (dns-client.unix, .lwt,
.mirage and .miou-unix use Mtime_clock.elapsed_ns and
Mirage_crypto_rng.generate). This fork removes them from S and passes them
to the constructor Make.v instead (renamed from ocaml-dns's create to the
repo's v); S is then pure I/O -- connect / send_recv / close:
val v :
?cache_size:int -> ?edns:... -> ?nameservers:... -> ?timeout:int64 ->
clock:(unit -> int64) -> rng:(int -> string) ->
T.stack -> t
The query building, CNAME following, response matching, RFC 7766 TCP
length-prefix framing and RFC 6761 special names are ocaml-dns's, in
lib/client.ml; only the clock/RNG plumbing differs.
The forwarder#
Dns.Forward (with Dns.Forward_config, Dns.Hosts, Dns.Id_mux,
Dns.Case0x20, Dns.Edns_policy, Dns.Server_select, Dns.Upstream_health)
is a DNS forwarding resolver in the shape of moby/vpnkit's dns_forward: decode
the query, answer /etc/hosts and cache hits locally, else race the zone-routed
upstreams with 0x20 encoding, EDNS sizing, negative caching and health tracking.
The edge (nox-dns-eio's Forward_eio) drives it over real transports.
It is specific to this fork -- upstream ocaml-dns has no forwarding resolver. It
lives entirely in its own modules (forward*.ml, hosts.ml, id_mux.ml,
case0x20.ml, edns_policy.ml, server_select.ml, upstream_health.ml) and
uses only the public wire codec, so it is independent of the codec and the
client: a consumer that wants the codec alone can ignore the Forward modules.
The codec#
The wire codec and cache are ocaml-dns's, imported at 10.2.5 (copyright the
ocaml-dns authors; ISC) and restructured into one module per concept:
lib/packet.ml, lib/rr_map.ml, lib/rr.ml (the record types), lib/name.ml
(label compression), lib/edns.ml, lib/tsig.ml, the registry enums, and the
LRU cache as lib/cache.ml. Constructors follow the repo's v convention (e.g.
Packet.v and Packet.Question.v for ocaml-dns's create).
Usage#
nox-dns is the transport-agnostic core. Provide a transport and inject a
clock + RNG:
module Client = Dns.Client.Make (My_transport)
let t = Client.v ~clock ~rng ~nameservers stack in
Client.getaddrinfo t Dns.Rr_map.A name
The Eio transport lives in this directory at lib/eio, packaged as
nox-dns-eio: it injects the Eio clock and the process RNG, and its TCP mode
(RFC 7766) needs only Eio.Net.connect, so it resolves over a unikernel's
TCP-only stack.
nox-dns-eio: the Eio transport#
Single-shot A / AAAA / TXT queries through the client above, over TCP
(RFC 7766, the default) or UDP (~proto:\Udp). A resolver picks up the nameservers from /etc/resolv.conf, falling back to Cloudflare (1.1.1.1) and Google (8.8.8.8`) if that file is missing or empty. The list can be overridden
at construction:
# let resolver =
Dns_eio.v
~nameservers:[ Ipaddr.of_string_exn "1.1.1.1" ]
~timeout_s:1.0 () in
List.map Ipaddr.to_string (Dns_eio.nameservers resolver)
- : string list = ["1.1.1.1"]
The configuration binds to a network, a switch and the monotonic clock as
a Dns_eio.client, which holds the response cache: create it once and
reuse it, so repeat lookups within a record's TTL are served without
touching the network:
let lookup_txt env name =
Eio.Switch.run @@ fun sw ->
let client =
Dns_eio.client ~sw
~net:(Eio.Stdenv.net env)
~mono:(Eio.Stdenv.mono_clock env)
~random:(Eio.Stdenv.secure_random env)
(Dns_eio.v ())
in
match Dns_eio.txt client name with
| Ok records -> List.iter print_endline records
| Error e -> Fmt.pr "DNS error: %a@." Dns_eio.pp_error e
The same pattern applies to Dns_eio.a (IPv4) and Dns_eio.aaaa (IPv6).
A name with no matching records returns Ok []; a name that does not
exist returns Error `Nxdomain.
License#
ISC, as ocaml-dns. See LICENSE.md; copyright is the ocaml-dns authors' plus this fork's changes.