Creating a forwarding dns server


I’d like to build a simple forwarding DNS server using ocaml-dns and its example code in

I’ve tried to add names as A records to the local database like this:

  (Ipaddr.V4.of_string_exn "")
  (Int32.of_int 86400)
  (Name.of_string "foo")

I use dig to query the forwarding DNS server:

dig @localhost -p 53530 foo A

The queried names are never found in the local database. Every request produces

ServFail: no local match for foo, forwarding...

What is the correct way to fill the trie database?
I’m using OCaml 4.06.1 with dns 1.0.1 (installed via OPAM) on Debian 9.
Thank you in advance!

This is my code:

(* Forwarding DNS server example. Looks up query locally first then forwards to another resolver. *)
open Lwt
open Dns

let rcode_to_string = function
  | Packet.NoError  -> "NoError"
  | Packet.FormErr  -> "FormErr"
  | Packet.ServFail -> "ServFail (TrieCorrupt)"
  | Packet.NXDomain -> "NXDomain"
  | Packet.NotImp   -> "NotImp"
  | Packet.Refused  -> "Refused"
  | Packet.YXDomain -> "YXDomain"
  | Packet.YXRRSet  -> "YXRRSet"
  | Packet.NXRRSet  -> "NXRRSet"
  | Packet.NotAuth  -> "NotAuth"
  | Packet.NotZone  -> "NotZone"
  | Packet.BadVers  -> "BadVers"
  | Packet.BadKey   -> "BadKey"
  | Packet.BadTime  -> "BadTime"
  | Packet.BadMode  -> "BadMode"
  | Packet.BadName  -> "BadName"
  | Packet.BadAlg   -> "BadAlg"

(* check db first, then fall back to resolver on error *)
let process db resolver ~src ~dst packet =
      let open Packet in
      match packet.questions with
      | [] -> return None; (* no questions in packet *)
      | [q] -> begin
          let answer = Query.(answer q.q_name q.q_type db.Loader.trie) in (* query local db *)
          match answer.Query.rcode with
          | Packet.NoError ->  (* local match *)
            Lwt_io.printf "Local match for %s\n" (Name.to_string q.q_name)
            >>= fun() ->
            return (Some answer)
          | rc -> (* no match, forward *)
            Lwt_io.printf "%s: no local match for %s, forwarding...\n" (rcode_to_string rc) (Name.to_string q.q_name)
            >>= fun() -> 
            Dns_resolver_unix.resolve resolver q.q_class q.q_type q.q_name 
            >>= fun result ->
            (return (Some (Dns.Query.answer_of_response result))) 
      | _::_::_ -> return None

let () = (
        let address = "" in (* listen on localhost *)
        let port = 53530 in
        let db = Loader.new_db () in (* create new empty db *)
        (* Etc_hosts.add db; *)
          (Ipaddr.V4.of_string_exn "")
          (Int32.of_int 86400)
          (Name.of_string "foo")
        (* Loader.no_more_updates db; *)
        Dns_resolver_unix.create () (* create resolver using /etc/resolv.conf *)
        >>= fun resolver ->
        let processor = ((Dns_server.processor_of_process (process db resolver)) :> (module Dns_server.PROCESSOR)) in 
        Dns_server_unix.serve_with_processor ~address ~port ~processor)

Dear @Haudegen, I’m not sure about ocaml-dns, but I recently developed a DNS implementation (both server and resolver side) in OCaml, and I actually have a stub resolver as example (which accompanies local overrides for specific zones (by using an authoritative server in front of the cache)) – this is not yet released (still need to write more tests, documentation, and the same interface as the ocaml-dns implementation to use µDNS as a drop-in replacement), but will hopefully soon be available on opam.

There’s also a ocaml-dns-forward repository based on ocaml-dns (not sure whether this implements what you are looking for) at

1 Like

thank you for your answer. µDNS looks very cool - the pure parts of OCaml only, I like it.
In the meantime I solved my DNS problem using good old dnsmasq. Still, I’d really like to know what I did wrong in this (rather trivial) usage of ocaml-dns.