mte/unikernel/duniverse/ocaml-dns/app/ozone.ml
2025-11-11 02:07:51 +01:00

72 lines
2.3 KiB
OCaml

(* (c) 2019 Hannes Mehnert, all rights reserved *)
(* goal is to check a given zonefile whether it is valid (and to-be-used
by an authoritative NS - i.e. there must be a SOA record, TTL are good)
if a NS/MX name is within the zone, it needs an address record
the name of the file is taken as the domain name *)
open Dns
let ( let* ) = Result.bind
let load_zone zone =
let* data = Bos.OS.File.read Fpath.(v zone) in
let* rrs = Dns_zone.parse data in
let domain = Domain_name.of_string_exn Fpath.(basename (v zone)) in
let bad = Domain_name.Map.filter
(fun name _ -> not (Domain_name.is_subdomain ~domain ~subdomain:name))
rrs
in
if not (Domain_name.Map.is_empty bad) then
Error (`Msg (Fmt.str "Entries of domain '%a' are not in its zone, won't handle this:@.%a"
Domain_name.pp domain Dns.Name_rr_map.pp bad))
else
Ok (Dns_trie.insert_map rrs Dns_trie.empty)
let jump _ zone old =
let* trie = load_zone zone in
let* () =
Result.map_error
(fun e -> `Msg (Fmt.to_to_string Dns_trie.pp_zone_check e))
(Dns_trie.check trie)
in
Logs.app (fun m -> m "successfully checked zone") ;
let zones =
Dns_trie.fold Soa trie
(fun name _ acc -> Domain_name.Set.add name acc)
Domain_name.Set.empty
in
if Domain_name.Set.cardinal zones = 1 then
let zone = Domain_name.Set.choose zones in
let* zone_data = Dns_server.text zone trie in
Logs.debug (fun m -> m "assembled zone data %s" zone_data) ;
(match old with
| None -> Ok ()
| Some fn ->
let* old = load_zone fn in
match Dns_trie.lookup zone Soa trie, Dns_trie.lookup zone Soa old with
| Ok fresh, Ok old when Soa.newer ~old fresh ->
Logs.debug (fun m -> m "zone %a newer than old" Domain_name.pp zone) ;
Ok ()
| _ ->
Error (`Msg "SOA comparison wrong"))
else
Error (`Msg "expected exactly one zone")
open Cmdliner
let newzone =
let doc = "New zone file" in
Arg.(required & pos 0 (some file) None & info [] ~doc ~docv:"ZONE")
let oldzone =
let doc = "Old zone file" in
Arg.(value & opt (some file) None & info [ "old" ] ~doc ~docv:"ZONE")
let cmd =
let term =
Term.(term_result (const jump $ Dns_cli.setup_log $ newzone $ oldzone))
and info = Cmd.info "ozone" ~version:"10.2.2"
in
Cmd.v info term
let () = exit (Cmd.eval cmd)