(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *) open Dns let empty = Dns_cache.empty 100 let ip = Ipaddr.of_string_exn let ip4 = Ipaddr.V4.of_string_exn let ip6 = Ipaddr.V6.of_string_exn let name = Domain_name.of_string_exn let sec = Duration.of_sec let invalid_soa = Dns_resolver_utils.invalid_soa let root_servers = List.map (fun (_, ip4, _) -> ip4) Dns_resolver_root.root_servers let a_root = List.hd root_servers let rng i = String.make i '\000' let follow_res = let module M = struct type t = [ `Out of Rcode.t * Rrsig.t option * Name_rr_map.t * Name_rr_map.t | `Query of [ `raw ] Domain_name.t ] * Dns_cache.t let pp ppf (r, _) = match r with | `Out (rcode, signed, answer, authority) -> Fmt.pf ppf "out %a signed %a answer %a authority %a" Rcode.pp rcode Fmt.(option ~none:(any "no") Rrsig.pp) signed Name_rr_map.pp answer Name_rr_map.pp authority | `Query name -> Fmt.pf ppf "query %a" Domain_name.pp name let equal (a, _) (b, _) = match a, b with | `Out (rc, s, an, au), `Out (rc', s', an', au') -> Rcode.compare rc rc' = 0 && (match s, s' with | None, None -> true | Some s, Some s' -> Rrsig.compare s s' = 0 | _ -> false) && Name_rr_map.equal an an' && Name_rr_map.equal au au' | `Query name, `Query name' -> Domain_name.equal name name' | _, _ -> false end in (module M: Alcotest.TESTABLE with type t = M.t) let follow_cname_cycle () = let cname = 250l, name "foo.com" in let circ_map = Name_rr_map.singleton (name "foo.com") Cname cname in let cache = Dns_cache.set empty 0L (name "foo.com") Cname (AuthoritativeAnswer None) (`Entry cname) in Alcotest.check follow_res "CNAME single cycle is detected" (`Out (Rcode.NoError, None, circ_map, Name_rr_map.empty), cache) (Dns_resolver_cache.follow_cname cache 0L A ~name:(name "foo.com") 250l ~alias:(name "foo.com")); Alcotest.check follow_res "CNAME single cycle after timeout errors" (`Query (name "foo.com"), cache) (Dns_resolver_cache.follow_cname cache (sec 251) A ~name:(name "foo.com") 250l ~alias:(name "foo.com")); let a = 250l, name "bar.com" and b = 500l, name "foo.com" in let cache = Dns_cache.set (Dns_cache.set empty 0L (name "bar.com") Cname (AuthoritativeAnswer None) (`Entry b)) 0L (name "foo.com") Cname (AuthoritativeAnswer None) (`Entry a) in let c_map = Name_rr_map.add (name "bar.com") Cname b (Name_rr_map.singleton (name "foo.com") Cname a) in Alcotest.check follow_res "CNAME cycle is detected" (`Out (Rcode.NoError, None, c_map, Name_rr_map.empty), cache) (Dns_resolver_cache.follow_cname cache 0L A ~name:(name "bar.com") 250l ~alias:(name "foo.com")); Alcotest.check follow_res "Query foo.com (since it timed out)" (`Query (name "foo.com"), cache) (Dns_resolver_cache.follow_cname cache (sec 251) A ~name:(name "bar.com") 250l ~alias:(name "foo.com")) let follow_cnames () = let cname = 250l, name "bar.com" in let map = Name_rr_map.singleton (name "foo.com") Cname cname in let cache = Dns_cache.set empty 0L (name "foo.com") Cname (AuthoritativeAnswer None) (`Entry cname) in Alcotest.check follow_res "CNAME is followed" (`Query (name "bar.com"), cache) (Dns_resolver_cache.follow_cname cache 0L A ~name:(name "foo.com") 250l ~alias:(name "foo.com")); Alcotest.check follow_res "CNAME after timeout errors" (`Query (name "foo.com"), cache) (Dns_resolver_cache.follow_cname cache (sec 251) A ~name:(name "foo.com") 250l ~alias:(name "foo.com")); let a_val = (250l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4")) in let cache = Dns_cache.set cache 0L (name "bar.com") A (AuthoritativeAnswer None) (`Entry a_val) in let map = Name_rr_map.add (name "bar.com") A a_val map in Alcotest.check follow_res "CNAME is followed" (`Out (Rcode.NoError, None, map, Name_rr_map.empty), cache) (Dns_resolver_cache.follow_cname cache 0L A ~name:(name "foo.com") 250l ~alias:(name "foo.com")) let follow_cname_tests = [ "follow_cname cycles", `Quick, follow_cname_cycle ; "follow_cname works", `Quick, follow_cnames ; ] let entry_eq t a b = match a, b with | `Entry b, `Entry b' -> Rr_map.equal_rr t b b' | `No_data (name, soa), `No_data (name', soa') -> Domain_name.equal name name' && Dns.Soa.compare soa soa' = 0 | `No_domain (name, soa), `No_domain (name', soa') -> Domain_name.equal name name' && Dns.Soa.compare soa soa' = 0 | `Serv_fail (name, soa), `Serv_fail (name', soa') -> Domain_name.equal name name' && Dns.Soa.compare soa soa' = 0 | _, _ -> false (* once again the complete thingy since I don't care about list ordering (Alcotest.list is order-enforcing) *) let res = let module M = struct type t = ([ `raw ] Domain_name.t * Dns_resolver_utils.e * Dns_cache.rank) list let pp ppf xs = let pp_elem ppf (n, Dns_resolver_utils.E (t, e), r) = Fmt.pf ppf "%a %a (%a): %a" Domain_name.pp n Rr_map.ppk (K t) Dns_cache.pp_rank r (Dns_cache.pp_entry t) e in Fmt.pf ppf "%a" Fmt.(list ~sep:(any ";@,") pp_elem) xs let equal a a' = let eq (n, Dns_resolver_utils.E (t, e), r) (n', Dns_resolver_utils.E (t', e'), r') = Domain_name.equal n n' && match Rr_map.K.compare t t' with | Gmap.Order.Eq -> Dns_cache.compare_rank r r' = 0 && entry_eq t e e' | _ -> false in List.length a = List.length a' && List.for_all (fun e -> List.exists (eq e) a') a end in (module M: Alcotest.TESTABLE with type t = M.t) let rcode = let module M = struct type t = Rcode.t let pp = Rcode.pp let equal a b = Rcode.compare a b = 0 end in (module M: Alcotest.TESTABLE with type t = M.t) let res = Alcotest.(result res rcode) let header = (0, Packet.Flags.empty) let scrub_empty () = let name = name "foo.com" in let q = name, `K (Rr_map.K A) in let dns = Packet.create header q (`Answer Packet.Answer.empty) in let bad_soa = invalid_soa name in Alcotest.check res "empty frame results in empty scrub" (Ok [ name, E (A, `No_data (name, bad_soa)), Additional ]) (Dns_resolver_utils.scrub name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer Packet.Answer.empty) in Alcotest.check res "empty authoritative frame results in empty scrub" (Ok [ name, E (A, `No_data (name, bad_soa)), Additional ]) (Dns_resolver_utils.scrub name ~signed:false (snd q) dns') let scrub_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let answer = Name_rr_map.singleton q_name A a in let dns = Packet.create header q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "A record results in scrubbed A" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "authoritative A record results in scrubbed A" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.(add (ip4 "1.2.3.4") (singleton (ip4 "1.2.3.5"))) in let answer = Name_rr_map.singleton q_name A a in let dns = Packet.create header q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "A records results in scrubbed A with same records" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "authoritative A records results in scrubbed A with same records" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_cname () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let cname = (1l, name "bar.com") in let answer = Name_rr_map.singleton q_name Cname cname in let dns = Packet.create header q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "CNAME record results in scrubbed CNAME with same record" (Ok [ q_name, E (Cname, `Entry cname), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "authoritative CNAME record results in scrubbed CNAME with same record" (Ok [ q_name, E (Cname, `Entry cname), AuthoritativeAnswer None]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_soa () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let soa = { Soa.nameserver = name "a" ; hostmaster = name "b" ; serial = 1l ; refresh = 2l ; retry = 3l ; expiry = 4l ; minimum = 5l } in let authority = Name_rr_map.singleton q_name Soa soa in let dns = Packet.create header q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (q_name, soa)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (q_name, soa)), AuthoritativeAuthority None]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_bad_soa () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let soa = { Soa.nameserver = name "a" ; hostmaster = name "b" ; serial = 1l ; refresh = 2l ; retry = 3l ; expiry = 4l ; minimum = 5l } in let authority = Name_rr_map.singleton (name "bar.com") Soa soa in let dns = Packet.create header q (`Answer (Name_rr_map.empty, authority)) in let bad_soa = invalid_soa q_name in Alcotest.check res "bad SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (q_name, bad_soa)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative bad SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (q_name, bad_soa)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_soa_super () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let soa = { Soa.nameserver = name "a" ; hostmaster = name "b" ; serial = 1l ; refresh = 2l ; retry = 3l ; expiry = 4l ; minimum = 5l } in let authority = Name_rr_map.singleton (name "com") Soa soa in let dns = Packet.create header q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (name "com", soa)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = let flags = Packet.Flags.singleton `Authoritative in (fst header, flags) in let dns' = Packet.create hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative SOA record results in NoData SOA" (Ok [ q_name, E (A, `No_data (name "com", soa)), AuthoritativeAuthority None]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_cname_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let alias = (1l, name "bar.com") and a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let answer = let an = Domain_name.Map.singleton q_name Rr_map.(add Cname alias (singleton A a)) in `Answer (an, Name_rr_map.empty) in let dns = Packet.create header q answer in Alcotest.check res "CNAME and A record results in the A record :" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let q' = (q_name, `K (Rr_map.K Cname)) in let dns' = Packet.create header q' answer in Alcotest.check res "CNAME and A record, asking for CNAME results in the cname record :" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q') dns') ; let hdr = (fst header, Packet.Flags.singleton `Authoritative) in let dns' = Packet.create hdr q answer in Alcotest.check res "authoritative CNAME and A record results in the A record" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns'); let dns' = Packet.create hdr q' answer in Alcotest.check res "authoritative CNAME and A record, asking for CNAME, results in the CNAME record" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q') dns') let scrub_authority_ns () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let authority = Name_rr_map.singleton q_name Ns ns in let dns = Packet.create header q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "NS in authority results in NoData foo.com and NoErr NS" (Ok [ q_name, E (Ns, `Entry ns), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = (fst header, Packet.Flags.singleton `Authoritative) in let dns' = Packet.create hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative NS in authority results in NoData foo.com and NoErr NS" (Ok [ q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let answer, authority = Name_rr_map.singleton q_name A a, Name_rr_map.singleton q_name Ns ns in let dns = Packet.create header q (`Answer (answer, authority)) in Alcotest.check res "NS in authority, and A in answer results in NoErr foo.com and NoErr NS" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = (fst header, Packet.Flags.singleton `Authoritative) in let dns' = Packet.create hdr q (`Answer (answer, authority)) in Alcotest.check res "authoritative NS in authority, and A in answer results in NoErr foo.com and NoErr NS" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_add_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let answer, authority, additional = Name_rr_map.singleton q_name A a, Name_rr_map.singleton q_name Ns ns, Name_rr_map.singleton (name "ns1.foo.com") A a in let dns = Packet.create ~additional header q (`Answer (answer, authority)) in Alcotest.check res "NS in authority, A in answer, glue in additional results in NoErr foo.com, NoErr NS, NoErr ns1.foo.com A" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = (fst header, Packet.Flags.singleton `Authoritative) in let dns' = Packet.create ~additional hdr q (`Answer (answer, authority)) in Alcotest.check res "authoritative NS in authority, A in answer, glue in additional results in NoErr foo.com, NoErr NS, NoErr ns1.foo.com A" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_bad_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let answer, authority, additional = Name_rr_map.singleton q_name A a, Name_rr_map.singleton q_name Ns ns, Name_rr_map.singleton (name "ns2.foo.com") A a in let dns = Packet.create ~additional header q (`Answer (answer, authority)) in Alcotest.check res "NS in authority, A in answer, crap in additional results in NoErr foo.com and NoErr NS" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create ~additional hdr q (`Answer (answer, authority)) in Alcotest.check res "authoritative NS in authority, A in answer, crap in additional results in NoErr foo.com and NoErr NS" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_add_a_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and a' = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.5") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let answer, authority, additional = Name_rr_map.singleton q_name A a, Name_rr_map.singleton q_name Ns ns, Domain_name.Map.singleton (name "ns1.foo.com") Rr_map.(add A a' (singleton A a)) in let dns = Packet.create ~additional header q (`Answer (answer, authority)) in Alcotest.check res "NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a'), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = (fst header, Packet.Flags.singleton `Authoritative) in let dns' = Packet.create ~additional hdr q (`Answer (answer, authority)) in Alcotest.check res "authoritative NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a'), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_ns_add_a_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and a' = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.5") and ns = 1l, Domain_name.Host_set.(add (Domain_name.host_exn (name "ns2.foo.com")) (singleton (Domain_name.host_exn (name "ns1.foo.com")))) in let answer, authority, additional = Name_rr_map.singleton q_name A a, Name_rr_map.singleton q_name Ns ns, Name_rr_map.add (name "ns1.foo.com") A a (Name_rr_map.singleton (name "ns2.foo.com") A a') in let dns = Packet.create ~additional header q (`Answer (answer, authority)) in Alcotest.check res "NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a), Additional ; name "ns2.foo.com", E (A, `Entry a'), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create ~additional hdr q (`Answer (answer, authority)) in Alcotest.check res "authoritative NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a), Additional ; name "ns2.foo.com", E (A, `Entry a'), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_bad_ns_add_a_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and a' = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.5") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) and ns' = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns2.foo.com")) in let answer, additional = `Answer (Name_rr_map.singleton q_name A a, Name_rr_map.add q_name Ns ns (Name_rr_map.singleton (name "com") Ns ns')), Name_rr_map.add (name "ns1.foo.com") A a (Name_rr_map.singleton (name "ns2.foo.com") A a') in let dns = Packet.create ~additional header q answer in Alcotest.check res "NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create ~additional hdr q answer in Alcotest.check res "authoritative NS in authority, A in answer, multiple A in additional results in NoErr foo.com, NoErr NS, NoErr As" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_authority_ns_add_a_bad () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) and ns' = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns3.foo.com")) and a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let authority, additional = Name_rr_map.singleton q_name Ns ns, Name_rr_map.add (name "ns1.foo.com") A a (Name_rr_map.singleton (name "ns1.foo.com") Ns ns') in let dns = Packet.create ~additional header q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "NS in authority, A and NS in additional results in NoErr NS, NoErr As" (Ok [ q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create ~additional hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative NS in authority, A and NS in additional results in NoErr NS, NoErr As" (Ok [ q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_authority_ns_add_a_aaaa () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) and aaaa = 1l, Ipaddr.V6.Set.singleton (ip6 "::1") in let authority, additional = Name_rr_map.singleton q_name Ns ns, Domain_name.Map.singleton (name "ns1.foo.com") Rr_map.(add A a (singleton Aaaa aaaa)) in let dns = Packet.create ~additional header q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "NS in authority, A and AAAA in additional results in NoErr NS, NoErr A, NoErr AAAA" (Ok [ q_name, E (Ns, `Entry ns), Additional ; name "ns1.foo.com", E (A, `Entry a), Additional ; name "ns1.foo.com", E (Aaaa, `Entry aaaa), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create ~additional hdr q (`Answer (Name_rr_map.empty, authority)) in Alcotest.check res "authoritative NS in authority, A and AAAA in additional results in NoErr NS, NoErr A, NoErr AAAA" (Ok [ q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ; name "ns1.foo.com", E (A, `Entry a), Additional ; name "ns1.foo.com", E (Aaaa, `Entry aaaa), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_a_authority_ns_a () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let a = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") and a' = 1l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.5") and ns = 1l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.foo.com")) in let answer = `Answer (Name_rr_map.singleton q_name A a, Domain_name.Map.singleton q_name Rr_map.(add Ns ns (singleton A a'))) in let dns = Packet.create header q answer in Alcotest.check res "NS and crap in authority, A in answer results in NoErr foo.com, NoErr NS" (Ok [ q_name, E (A, `Entry a), NonAuthoritativeAnswer ; q_name, E (Ns, `Entry ns), Additional ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create hdr q answer in Alcotest.check res "authoritative NS and crap in authority, A in answer results in NoErr foo.com, NoErr NS" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_bad_packets () = let q_name = name "foo.com" in let q = q_name, `K (Rr_map.K A) in let answer = Domain_name.Map.singleton (name "bar.com") Rr_map.(singleton A (1l, (Ipaddr.V4.Set.singleton (ip4 "1.2.3.4")))) in let dns = Packet.create header q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "No results in scrubbed A with bad A" (Ok [ q_name, E (A, `No_data (q_name, invalid_soa q_name)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let hdr = fst header, Packet.Flags.singleton `Authoritative in let dns' = Packet.create hdr q (`Answer (answer, Name_rr_map.empty)) in Alcotest.check res "authoritative no results in scrubbed A with bad A" (Ok [ q_name, E (A, `No_data (q_name, invalid_soa q_name)), Additional]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns') let scrub_rfc2308_2_1 () = let q_name = name "an.example" in let q = q_name, `K (Rr_map.K A) in let soa = { Soa.nameserver = name "ns1.xx" ; hostmaster = name "hostmaster.ns1.xx" ; serial = 1l ; refresh = 1l ; retry = 2l ; expiry = 3l ; minimum = 4l } and ns = 1l, Domain_name.Host_set.(add (Domain_name.host_exn (name "ns1.xx")) (singleton (Domain_name.host_exn (name "ns2.xx")))) and alias = 1l, name "tripple.xx" and additional = Name_rr_map.add (name "ns1.xx") A (1l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.2")) (Name_rr_map.singleton (name "ns2.xx") A (1l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.3"))) in let answer = Name_rr_map.singleton q_name Cname alias and authority = Domain_name.Map.singleton (name "xx") Rr_map.(add Soa soa (singleton Ns ns)) in let dns = Packet.create ~additional header q (`Rcode_error (Rcode.NXDomain, Opcode.Query, Some (answer, authority))) in (* considering what is valid in the response, it turns out only the cname is *) Alcotest.check res "Sec 2.1 type 1" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let authority = Name_rr_map.singleton (name "xx") Soa soa in let dns = Packet.create header q (`Rcode_error (Rcode.NXDomain, Opcode.Query, Some (answer, authority))) in Alcotest.check res "Sec 2.1 type 2" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let dns = Packet.create header q (`Rcode_error (Rcode.NXDomain, Opcode.Query, Some (answer, Name_rr_map.empty))) in Alcotest.check res "Sec 2.1 type 3" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let authority = Name_rr_map.singleton (name "xx") Ns ns in let dns = Packet.create ~additional header q (`Rcode_error (Rcode.NXDomain, Opcode.Query, Some (answer, authority))) in Alcotest.check res "Sec 2.1 type 4" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let dns = Packet.create ~additional header q (`Answer (answer, authority)) in Alcotest.check res "Sec 2.1 type referral response" (Ok [ q_name, E (Cname, `Entry alias), NonAuthoritativeAnswer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) (* bailiwick thingies (may repeat above tests): - q_name is foo, q_type is A! - answer contains additional A records [ foo A 1.2.3.4 ; bar A 1.2.3.4 ] - answer contains cname and A: [ foo cname bar ; bar A 1.2.3.4 ] - authority contains [ foo CNAME bar ] [ bar NS boo ] - answer cname and authority NS [ foo cname bar ] [ bar NS boo ] - additional contains glue [ foo CNAME bar ] [] [ bar A 1.2.3.4 ] - additional contains glue with au [ foo CNAME bar ] [ bar NS boo ] [ boo A 1.2.3.4 ] *) let bailiwick_a () = let q_name = name "foo" in let q = q_name, `K (Rr_map.K A) in let hdr = fst header, Packet.Flags.singleton `Authoritative in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let answer = `Answer (Name_rr_map.add q_name A a (Name_rr_map.singleton (name "bar") A a), Name_rr_map.empty) in let dns = Packet.create hdr q answer in Alcotest.check res "additional A records" (Ok [ q_name, E (A, `Entry a), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let alias = 300l, name "bar" in let answer = `Answer (Name_rr_map.add q_name Cname alias (Name_rr_map.singleton (name "bar") A a), Name_rr_map.empty) in let dns = Packet.create hdr q answer in Alcotest.check res "A and CNAME record" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "boo")) in let answer = `Answer (Name_rr_map.add q_name Cname alias (Name_rr_map.singleton (name "bar") Ns ns), Name_rr_map.empty) in let dns = Packet.create hdr q answer in Alcotest.check res "CNAME and NS record in answer" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let answer = `Answer (Name_rr_map.singleton q_name Cname alias, Name_rr_map.singleton q_name Ns ns) in let dns = Packet.create hdr q answer in Alcotest.check res "CNAME and NS record in authority" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let answer = `Answer (Name_rr_map.add q_name Cname alias (Name_rr_map.singleton (name "foobar") Ns ns), Name_rr_map.empty) in let dns = Packet.create hdr q answer in Alcotest.check res "CNAME and unrelated NS record in answer" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let answer = `Answer (Name_rr_map.singleton q_name Cname alias, Name_rr_map.singleton (name "foobar") Ns ns) in let dns = Packet.create hdr q answer in Alcotest.check res "CNAME and unrelated NS record in authority" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let answer = `Answer (Name_rr_map.singleton q_name Cname alias, Name_rr_map.empty) in let additional = Name_rr_map.singleton (name "bar") A a in let dns = Packet.create ~additional hdr q answer in Alcotest.check res "CNAME and glue record in additional" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let answer = `Answer (Name_rr_map.singleton q_name Cname alias, Name_rr_map.singleton q_name Ns ns) in let additional = Name_rr_map.singleton (name "boo") A a in let dns = Packet.create ~additional hdr q answer in Alcotest.check res "CNAME and glue record in additional" (Ok [ q_name, E (Cname, `Entry alias), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) (* similar, but with MX records: - query is MX, "foo" - answer = [ "foo" MX 10 "bar" ; "foo" A 1.2.3.4 ] - answer = [ "foo" MX 10 "bar" ; "bar" MX 10 "bar" ] - answer = [ "foo" MX 10 "bar" ; "bar" A 1.2.3.4 ] - answer = [ "foo" MX 10 "bar" ], additional = [ "bar" A 1.2.3.4 ] - answer = [ "foo" MX 10 "bar" ], authority = [ "foo" NS "foobar" ], additional = [ "bar" A 1.2.3.4 ; "foobar" A 1.2.3.4 ] <- takes au - answer = [ "foo" MX 10 "bar" ], authority = [ "bar" NS "foobar" ], additional = [ "bar" A 1.2.3.4 ; "foobar" A 1.2.3.4 ] <- only answer *) let bailiwick_mx () = let q_name = name "foo" in let q = q_name, `K (Rr_map.K Mx) in let hdr = fst header, Packet.Flags.singleton `Authoritative in let mx = 300l, Rr_map.Mx_set.singleton { Mx.preference = 10 ; mail_exchange = Domain_name.host_exn (name "bar") } and a = 300l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let mx_a = Domain_name.Map.singleton q_name Rr_map.(add A a (singleton Mx mx)) in let dns = Packet.create hdr q (`Answer (mx_a, Name_rr_map.empty)) in Alcotest.check res "additional A record" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let mx_mx = Domain_name.Map.add q_name Rr_map.(add A a (singleton Mx mx)) (Name_rr_map.singleton (name "bar") Mx mx) in let dns = Packet.create hdr q (`Answer (mx_mx, Name_rr_map.empty)) in Alcotest.check res "additional MX records" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let mx_amx = Domain_name.Map.add q_name Rr_map.(add A a (singleton Mx mx)) (Name_rr_map.singleton (name "bar") A a) in let dns = Packet.create hdr q (`Answer (mx_amx, Name_rr_map.empty)) in Alcotest.check res "MX record and an A record" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let mx', additional = Domain_name.Map.singleton q_name Rr_map.(add A a (singleton Mx mx)), Name_rr_map.singleton (name "bar") A a in let dns = Packet.create ~additional hdr q (`Answer (mx', Name_rr_map.empty)) in Alcotest.check res "MX record and additional A record" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "foobar")) in let mx_au, additional = (Name_rr_map.singleton q_name Mx mx, Name_rr_map.singleton q_name Ns ns), Name_rr_map.add (name "bar") A a (Name_rr_map.singleton (name "foobar") A a) in let dns = Packet.create ~additional hdr q (`Answer mx_au) in Alcotest.check res "MX record and authority and additional A record" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ; q_name, E (Ns, `Entry ns), AuthoritativeAuthority None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let mx_au' = Name_rr_map.singleton q_name Mx mx, Name_rr_map.singleton (name "bar") Ns ns in let dns = Packet.create ~additional hdr q (`Answer mx_au') in Alcotest.check res "MX record and bad authority and additional A record" (Ok [ q_name, E (Mx, `Entry mx), AuthoritativeAnswer None ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) (* similar, but with NS records: - query is NS, "foo" - answer = [ "foo" NS "bar" ; "foo" A 1.2.3.4 ] - answer = [ "foo" NS "bar" ; "bar" NS "bar" ] - answer = [ "foo" NS "bar" ; "bar" A 1.2.3.4 ] - answer = [ "foo" NS "bar" ], additional = [ "foo" A 1.2.3.4 ] - answer = [ "foo" NS "bar" ], additional = [ "bar" A 1.2.3.4 ] - answer = [ "foo" NS "bar" ], authority = [ "foo" NS "foobar" ], additional = [ "bar" A 1.2.3.4 ; "foobar" A 1.2.3.4 ] <- takes au - answer = [ "foo" NS "bar" ], authority = [ "bar" NS "foobar" ], additional = [ "bar" A 1.2.3.4 ; "foobar" A 1.2.3.4 ] <- only answer *) let bailiwick_ns () = let q_name = name "foo" in let q = q_name, `K (Rr_map.K Ns) in let hdr = fst header, Packet.Flags.singleton `Authoritative in let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "bar")) and a = 300l, Ipaddr.V4.Set.singleton (ip4 "1.2.3.4") in let answer = q_name, Dns_resolver_utils.E (Ns, `Entry ns), Dns_cache.AuthoritativeAnswer None in let data = Domain_name.Map.singleton q_name Rr_map.(add Ns ns (singleton A a)) in let dns = Packet.create hdr q (`Answer (data, Name_rr_map.empty)) in (* fail atm - get NS and A *) Alcotest.check res "additional A record" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let data = Name_rr_map.add q_name Ns ns (Name_rr_map.singleton (name "bar") Ns ns) in let dns = Packet.create hdr q (`Answer (data, Name_rr_map.empty)) in Alcotest.check res "additional NS records" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let data = Name_rr_map.add q_name Ns ns (Name_rr_map.singleton (name "bar") A a) in let dns = Packet.create hdr q (`Answer (data, Name_rr_map.empty)) in Alcotest.check res "NS record and an A record" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let data, additional = Name_rr_map.singleton q_name Ns ns, Name_rr_map.singleton q_name A a in let dns = Packet.create ~additional hdr q (`Answer (data, Name_rr_map.empty)) in (* should glue be respected? don't think it's worth it *) Alcotest.check res "NS record and additional A record" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let data, additional = Name_rr_map.singleton q_name Ns ns, Name_rr_map.singleton (name "bar") A a in let dns = Packet.create ~additional hdr q (`Answer (data, Name_rr_map.empty)) in Alcotest.check res "NS record and additional A record with NS name" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let ns' = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "foobar")) in let data, au, additional = Name_rr_map.singleton q_name Ns ns, Name_rr_map.singleton q_name Ns ns', Name_rr_map.add (name "bar") A a (Name_rr_map.singleton (name "foobar") A a) in let dns = Packet.create ~additional hdr q (`Answer (data, au)) in let answer' = [ answer ; q_name, E (Ns, `Entry ns'), AuthoritativeAuthority None ] in Alcotest.check res "NS record and authority and additional A record" (Ok answer') (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) ; let au' = Name_rr_map.singleton (name "bar") Ns ns' in let dns = Packet.create ~additional hdr q (`Answer (data, au')) in Alcotest.check res "NS record and bad authority and additional A record" (Ok [ answer ]) (Dns_resolver_utils.scrub q_name ~signed:false (snd q) dns) let scrub_tests = [ "empty", `Quick, scrub_empty ; "scrub A", `Quick, scrub_a ; "scrub A A", `Quick, scrub_a_a ; "scrub CNAME", `Quick, scrub_cname ; "scrub SOA", `Quick, scrub_soa ; "scrub bad SOA", `Quick, scrub_bad_soa ; "scrub SOA super", `Quick, scrub_soa_super ; "scrub CNAME A", `Quick, scrub_cname_a ; "scrub authority NS", `Quick, scrub_authority_ns ; "scrub A authority NS", `Quick, scrub_a_authority_ns ; "scrub A authority NS add A", `Quick, scrub_a_authority_ns_add_a ; "scrub A authority NS bad A", `Quick, scrub_a_authority_ns_bad_a ; "scrub A authority NS add A A", `Quick, scrub_a_authority_ns_add_a_a ; "scrub A authority NS NS add A A" ,`Quick, scrub_a_authority_ns_ns_add_a_a ; "scrub A authority NS badNS add A A", `Quick, scrub_a_authority_ns_bad_ns_add_a_a ; "scrub authority NS add A NS", `Quick, scrub_authority_ns_add_a_bad ; "scrub authority NS add A AAAA", `Quick, scrub_authority_ns_add_a_aaaa ; "scrub A authority NS A", `Quick, scrub_a_authority_ns_a ; "bad packets", `Quick, scrub_bad_packets ; "rfc2308 2.1", `Quick, scrub_rfc2308_2_1 ; "bailiwick a", `Quick, bailiwick_a ; "bailiwick mx", `Quick, bailiwick_mx ; "bailiwick ns", `Quick, bailiwick_ns ; ] let handle_query_res = let module M = struct type t = [ | `Reply of Packet.Flags.t * Packet.reply * Name_rr_map.t option | `Queries of ([`raw] Domain_name.t * ([`raw] Domain_name.t * Packet.Question.qtype list) * Ipaddr.t) list ] * Dns_cache.t let pp ppf = function | `Reply (flags, reply, _additional), _ -> Fmt.pf ppf "reply flags %a, %a" Fmt.(list ~sep:(any ", ") Packet.Flag.pp_short) (Packet.Flags.elements flags) Packet.pp_reply reply | `Queries qs, _ -> let pp_query ppf (zone, (qname, qtypes), ips) = Fmt.pf ppf "zone %a, query %a (%a), IP %a" Domain_name.pp zone Domain_name.pp qname Fmt.(list ~sep:(any ", ") Packet.Question.pp_qtype) qtypes Ipaddr.pp ips in Fmt.(list ~sep:(any ", ") pp_query) ppf qs let equal a b = match fst a, fst b with | `Reply (f1, r1, a1), `Reply (f2, r2, a2) -> Packet.Flags.equal f1 f2 && Packet.equal_reply r1 r2 && (match a1, a2 with | None, None -> true | Some a, Some b -> Name_rr_map.equal a b | None, _ | _, None -> false) | `Queries a, `Queries b -> let eq_query (z1, (q1, t1), ip1) (z2, (q2, t2), ip2) = Domain_name.equal z1 z2 && Domain_name.equal q1 q2 && List.for_all2 (fun t1 t2 -> Packet.Question.compare_qtype t1 t2 = 0) t1 t2 && Ipaddr.compare ip1 ip2 = 0 in List.for_all2 eq_query a b | _ -> false end in (module M: Alcotest.TESTABLE with type t = M.t) (*let handle_query_with_cname_dnssec_good () = let cache = let cname = 300l, name "reynir.dk" in let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.reynir.dk")) in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set empty 0L (name "www.reynir.dk") Cname (AuthoritativeAnswer true) (`Entry cname) in let cache = Dns_cache.set cache 0L (name "reynir.dk") Ns (AuthoritativeAnswer true) (`Entry ns) in let cache = Dns_cache.set cache 0L (name "ns.reynir.dk") A (AuthoritativeAnswer true) (`Entry a) in let cache = Dns_cache.set cache 0L (name "reynir.dk") Dnskey (AuthoritativeAnswer true) (`No_data (name "reynir.dk", invalid_soa (name "reynir.dk"))) in Dns_cache.set cache 0L (name "reynir.dk") Ds (AuthoritativeAnswer true) (`No_data (name "reynir.dk", invalid_soa (name "reynir.dk"))) in Alcotest.check handle_query_res "..." (`Query (name "reynir.dk", (name "reynir.dk", [ `K (Rr_map.K A) ]), ip "127.0.0.1"), cache) (Dns_resolver_cache.handle_query cache ~dnssec:true ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.reynir.dk", `K (Rr_map.K A))); let f = Packet.Flags.(add `Authentic_data (add `Recursion_available (singleton `Recursion_desired))) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "www.reynir.dk") Cname (300l, name "reynir.dk"), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~dnssec:true ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.reynir.dk", `K (Rr_map.K Cname))) *) let handle_query_with_cname_dnssec_bad () = let cache = let cname = 300l, name "reynir.dk" in let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.reynir.dk")) in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set empty 0L (name "www.reynir.dk") Cname (AuthoritativeAnswer None) (`Entry cname) in let cache = Dns_cache.set cache 0L (name "reynir.dk") Ns (AuthoritativeAnswer None) (`Entry ns) in let cache = Dns_cache.set cache 0L (name "ns.reynir.dk") A (AuthoritativeAnswer None) (`Entry a) in let cache = Dns_cache.set cache 0L (name "reynir.dk") Dnskey (AuthoritativeAnswer None) (`No_data (name "reynir.dk", invalid_soa (name "reynir.dk"))) in Dns_cache.set cache 0L (name "reynir.dk") Ds (AuthoritativeAnswer None) (`No_data (name "reynir.dk", invalid_soa (name "reynir.dk"))) in Alcotest.check handle_query_res "..." (`Queries [ name "reynir.dk", (name "www.reynir.dk", [ `K (Rr_map.K A) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:true ~dnssec:true ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.reynir.dk", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Queries [ name "reynir.dk", (name "www.reynir.dk", [ `K (Rr_map.K Cname) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:true ~dnssec:true ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.reynir.dk", `K (Rr_map.K Cname))) let handle_query_with_a () = let cache = let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set empty 0L (name "ns.reynir.dk") A (AuthoritativeAnswer None) (`Entry a) in let cache = Dns_cache.set cache 0L (name "nx.reynir.dk") Cname (AuthoritativeAnswer None) (`No_domain (name "reynir.dk", invalid_soa (name "reynir.dk"))) in let cache = Dns_cache.set cache 0L (name "sf.reynir.dk") A (AuthoritativeAnswer None) (`Serv_fail (name "sf.reynir.dk", invalid_soa (name "reynir.dk"))) in let cache = Dns_cache.set cache 0L (name "no.reynir.dk") A (AuthoritativeAnswer None) (`No_data (name "no.reynir.dk", invalid_soa (name "reynir.dk"))) in let cname = 300l, name "r.reynir.dk" in let cache = Dns_cache.set cache 0L (name "cname.reynir.dk") Cname (AuthoritativeAnswer None) (`Entry cname) in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let ns = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.reynir.dk")) in let cache = Dns_cache.set cache 0L (name "reynir.dk") Ns (AuthoritativeAnswer None) (`Entry ns) in let cache = Dns_cache.set cache 0L (name "ns.reynir.dk") A (AuthoritativeAnswer None) (`Entry a) in cache in let f = Packet.Flags.(add `Recursion_available (singleton `Recursion_desired)) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "ns.reynir.dk") A (300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1")), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "ns.reynir.dk", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Reply (f, `Rcode_error (Rcode.NXDomain, Opcode.Query, Some (Domain_name.Map.empty, Name_rr_map.singleton (name "reynir.dk") Soa (invalid_soa (name "reynir.dk")))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "nx.reynir.dk", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Reply (f, `Rcode_error (Rcode.ServFail, Opcode.Query, Some (Domain_name.Map.empty, Name_rr_map.singleton (name "sf.reynir.dk") Soa (invalid_soa (name "reynir.dk")))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "sf.reynir.dk", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Domain_name.Map.empty, Name_rr_map.singleton (name "no.reynir.dk") Soa (invalid_soa (name "reynir.dk"))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "no.reynir.dk", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Queries [ name "reynir.dk", (name "r.reynir.dk", [ `K (Rr_map.K A) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "cname.reynir.dk", `K (Rr_map.K A))); let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set cache 0L (name "r.reynir.dk") A (AuthoritativeAnswer None) (`Entry a) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.add (name "r.reynir.dk") A a (Name_rr_map.singleton (name "cname.reynir.dk") Cname (300l, name "r.reynir.dk")), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "cname.reynir.dk", `K (Rr_map.K A))); let cname = 300l, name "nx.reynir.dk" in let cache = Dns_cache.set cache 0L (name "cname.reynir.dk") Cname (AuthoritativeAnswer None) (`Entry cname) in Alcotest.check handle_query_res "..." (`Reply (f, `Rcode_error (Rcode.NXDomain, Opcode.Query, Some (Name_rr_map.singleton (name "cname.reynir.dk") Cname cname, Name_rr_map.singleton (name "reynir.dk") Soa (invalid_soa (name "reynir.dk")))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "cname.reynir.dk", `K (Rr_map.K A))); let cname = 300l, name "no.reynir.dk" in let cache = Dns_cache.set cache 0L (name "cname.reynir.dk") Cname (AuthoritativeAnswer None) (`Entry cname) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer ((Name_rr_map.singleton (name "cname.reynir.dk") Cname cname, Name_rr_map.singleton (name "no.reynir.dk") Soa (invalid_soa (name "reynir.dk")))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "cname.reynir.dk", `K (Rr_map.K A))); let cname = 300l, name "sf.reynir.dk" in let cache = Dns_cache.set cache 0L (name "cname.reynir.dk") Cname (AuthoritativeAnswer None) (`Entry cname) in Alcotest.check handle_query_res "..." (`Reply (f, `Rcode_error (Rcode.ServFail, Opcode.Query, Some (Name_rr_map.singleton (name "cname.reynir.dk") Cname cname, Name_rr_map.singleton (name "sf.reynir.dk") Soa (invalid_soa (name "reynir.dk")))), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "cname.reynir.dk", `K (Rr_map.K A))) let handle_query_tests = [ (* "cname dnssec ok", `Quick, handle_query_with_cname_dnssec_good ; *) "cname dnssec bad", `Quick, handle_query_with_cname_dnssec_bad ; "a", `Quick, handle_query_with_a ; ] let loop_between_domains () = (* we have domain foo.com pointing to NS in bar.com, and bar.com NS pointing to foo.com *) (* there's no glue delivered, but we should not loop infinitely *) let cache = let ns_bar = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.bar.com")) in let ns_foo = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.foo.com")) in let ns_root = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.root")) in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set empty 0L (name "foo.com") Ns (AuthoritativeAnswer None) (`Entry ns_bar) in let cache = Dns_cache.set cache 0L (name "bar.com") Ns (AuthoritativeAnswer None) (`Entry ns_foo) in let cache = Dns_cache.set cache 0L Domain_name.root Ns (AuthoritativeAnswer None) (`Entry ns_root) in Dns_cache.set cache 0L (name "ns.root") A (AuthoritativeAnswer None) (`Entry a) in Alcotest.check handle_query_res "..." (`Queries [ name "", (name "ns.foo.com", [ `K (Rr_map.K A) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.foo.com", `K (Rr_map.K A))) let missing_glue () = (* we have domain foo.com pointing to NS ns.foo.com, and lack glue *) (* we should query .com for NS foo.com again *) let cache = let ns_foo = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns.foo.com")) in let ns_com = 300l, Domain_name.Host_set.singleton (Domain_name.host_exn (name "ns1.com")) in let a = 300l, Ipaddr.V4.Set.singleton (ip4 "127.0.0.1") in let cache = Dns_cache.set empty 0L (name "foo.com") Ns (AuthoritativeAnswer None) (`Entry ns_foo) in let cache = Dns_cache.set cache 0L (name "com") Ns (AuthoritativeAnswer None) (`Entry ns_com) in Dns_cache.set cache 0L (name "ns1.com") A (AuthoritativeAnswer None) (`Entry a) in Alcotest.check handle_query_res "..." (`Queries [ name "com", (name "www.foo.com", [ `K (Rr_map.K A) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.foo.com", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Queries [ name "com", (name "foo.com", [ `K (Rr_map.K Ns) ]), ip "127.0.0.1" ], cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:true ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "www.foo.com", `K (Rr_map.K A))) let loop_cname_1 () = (* we have a.foo.com CNAME a.foo.com *) let cache = let a_cname = 300l, name "a.foo.com" in Dns_cache.set empty 0L (name "a.foo.com") Cname (AuthoritativeAnswer None) (`Entry a_cname) in let f = Packet.Flags.(add `Recursion_available (singleton `Recursion_desired)) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "a.foo.com") Cname (300l, name "a.foo.com"), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "a.foo.com", `K (Rr_map.K Cname))); Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "a.foo.com") Cname (300l, name "a.foo.com"), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "a.foo.com", `K (Rr_map.K A))) let loop_cname_2 () = (* we have a.foo.com CNAME b.foo.com, and b.foo.com CNAME a.foo.com *) let cache = let b_cname = 300l, name "b.foo.com" in let a_cname = 300l, name "a.foo.com" in let cache = Dns_cache.set empty 0L (name "a.foo.com") Cname (AuthoritativeAnswer None) (`Entry b_cname) in Dns_cache.set cache 0L (name "b.foo.com") Cname (AuthoritativeAnswer None) (`Entry a_cname) in let f = Packet.Flags.(add `Recursion_available (singleton `Recursion_desired)) in Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "a.foo.com") Cname (300l, name "b.foo.com"), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "a.foo.com", `K (Rr_map.K Cname))); Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.singleton (name "b.foo.com") Cname (300l, name "a.foo.com"), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "b.foo.com", `K (Rr_map.K Cname))); Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.add (name "b.foo.com") Cname (300l, name "a.foo.com") (Name_rr_map.singleton (name "a.foo.com") Cname (300l, name "b.foo.com")), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "a.foo.com", `K (Rr_map.K A))); Alcotest.check handle_query_res "..." (`Reply (f, `Answer (Name_rr_map.add (name "a.foo.com") Cname (300l, name "b.foo.com") (Name_rr_map.singleton (name "b.foo.com") Cname (300l, name "a.foo.com")), Domain_name.Map.empty), None), cache) (Dns_resolver_cache.handle_query cache ~qname_minimisation:false ~dnssec:false ~dnssec_ok:false ~rng `Ipv4_only 0L (name "b.foo.com", `K (Rr_map.K A))) let resolver_well_behaved = [ "loop between domains", `Quick, loop_between_domains; "missing glue", `Quick, missing_glue; "loop using cname", `Quick, loop_cname_1; "loop using cname with two entries", `Quick, loop_cname_2; ] let tests = [ "follow_cname", follow_cname_tests ; "scrub", scrub_tests ; "handle query", handle_query_tests ; "well behaved", resolver_well_behaved ; ] let () = Logs.set_reporter (Logs_fmt.reporter ()); Logs.set_level ~all:true (Some Logs.Debug); Alcotest.run "DNS resolver tests" tests