Skip to content

Commit e92857f

Browse files
authored
Merge pull request #645 from dan0dbfe/fix/issue-509
Correctly return NOERROR even if host resolver returned empty list
2 parents 604ab7f + 427cb6d commit e92857f

File tree

5 files changed

+73
-27
lines changed

5 files changed

+73
-27
lines changed

src/hostnet/host.ml

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,10 +1294,11 @@ module Dns = struct
12941294
in
12951295
return (Ok ips)))
12961296
>>= function
1297-
| Error (`Msg _) ->
1298-
(* FIXME: error handling completely missing *)
1299-
Lwt.return []
1300-
| Ok ips -> Lwt.return ips
1297+
| Error e ->
1298+
(* FIXME: error handling completely missing *)
1299+
Lwt.return (Error e)
1300+
| Ok ips ->
1301+
Lwt.return (Ok ips)
13011302

13021303
let localhost_local = Dns.Name.of_string "localhost.local"
13031304

@@ -1306,17 +1307,19 @@ module Dns = struct
13061307
(match question with
13071308
| { q_class = Q_IN; q_name; _ } when q_name = localhost_local ->
13081309
Log.debug (fun f -> f "DNS lookup of localhost.local: return NXDomain");
1309-
Lwt.return (q_name, [])
1310+
Lwt.return (Error q_name)
13101311
| { q_class = Q_IN; q_type = Q_A; q_name; _ } ->
1311-
getaddrinfo (Dns.Name.to_string q_name) `INET >>= fun ips ->
1312-
Lwt.return (q_name, ips)
1312+
(getaddrinfo (Dns.Name.to_string q_name) `INET >>= function
1313+
| Error _ -> Lwt.return (Error q_name)
1314+
| Ok ips -> Lwt.return (Ok (q_name, ips)))
13131315
| { q_class = Q_IN; q_type = Q_AAAA; q_name; _ } ->
1314-
getaddrinfo (Dns.Name.to_string q_name) `INET6 >>= fun ips ->
1315-
Lwt.return (q_name, ips)
1316-
| _ -> Lwt.return (Dns.Name.of_string "", []))
1316+
(getaddrinfo (Dns.Name.to_string q_name) `INET6 >>= function
1317+
| Error _ -> Lwt.return (Error q_name)
1318+
| Ok ips -> Lwt.return (Ok (q_name, ips)))
1319+
| _ ->
1320+
Lwt.return (Error (Dns.Name.of_string "")))
13171321
>>= function
1318-
| _, [] -> Lwt.return []
1319-
| q_name, ips ->
1322+
| Ok (q_name, ips) ->
13201323
let answers =
13211324
List.map
13221325
(function
@@ -1338,7 +1341,9 @@ module Dns = struct
13381341
})
13391342
ips
13401343
in
1341-
Lwt.return answers
1344+
Lwt.return (Ok answers)
1345+
| Error _ ->
1346+
Lwt.return (Error ())
13421347

13431348
let resolve = resolve_getaddrinfo
13441349
end

src/hostnet/hostnet_dns.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,10 +446,11 @@ struct
446446
| _, Host ->
447447
D.resolve question
448448
>>= function
449-
| [] ->
450-
Lwt.return (Ok (Some (marshal nxdomain)))
451-
| answers ->
449+
| Ok answers ->
452450
Lwt.return (Ok (marshal_reply answers))
451+
(* Currently return all errors as NXDOMAIN *)
452+
| Error _ ->
453+
Lwt.return (Ok (Some (marshal nxdomain)))
453454
end
454455
| _ ->
455456
Lwt.return (Error (`Msg "DNS packet had multiple questions"))

src/hostnet/hostnet_http.ml

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,19 @@ module Make
151151
let question =
152152
make_question ~q_class:Q_IN Q_A (Dns.Name.of_string name_or_ip)
153153
in
154-
Dns_resolver.resolve question
155-
>>= fun rrs ->
156-
(* Any IN record will do (NB it might be a CNAME) *)
157-
let rec find_ip = function
158-
| { cls = RR_IN; rdata = A ipv4; _ } :: _ ->
159-
Lwt.return (Ok (Ipaddr.V4 ipv4))
160-
| _ :: rest -> find_ip rest
161-
| [] -> errorf "Failed to lookup host: %s" name_or_ip in
162-
find_ip rrs
163-
| Ok x -> Lwt.return (Ok x)
154+
(Dns_resolver.resolve question >>= function
155+
| Ok rrs ->
156+
(* Any IN record will do (NB it might be a CNAME) *)
157+
let rec find_ip = function
158+
| { cls = RR_IN; rdata = A ipv4; _ } :: _ ->
159+
Lwt.return (Ok (Ipaddr.V4 ipv4))
160+
| _ :: rest -> find_ip rest
161+
| [] -> errorf "Failed to lookup host: %s" name_or_ip
162+
in
163+
find_ip rrs
164+
| Error _ ->
165+
Lwt.return (Error (`Msg (Printf.sprintf "Failed to lookup host: %s" name_or_ip))))
166+
| Ok ip -> Lwt.return (Ok ip)
164167

165168
let to_json t =
166169
let open Ezjsonm in

src/hostnet/sig.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ module type FILES = sig
130130
end
131131

132132
module type DNS = sig
133-
val resolve: Dns.Packet.question -> Dns.Packet.rr list Lwt.t
133+
val resolve: Dns.Packet.question -> (Dns.Packet.rr list, unit) result Lwt.t
134134
(** Given a question, find associated resource records *)
135135
end
136136

src/hostnet_test/test_dns.ml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ let test_etc_hosts_priority server config () =
110110
in
111111
run ~pcap:"test_etc_hosts_priority.pcap" t
112112

113+
let test_noerror_empty_response server config () =
114+
let name = "ipv4.tlund.se" in (* FIXME: Use a better address? *)
115+
let t _ stack =
116+
set_dns_policy config;
117+
let resolver = DNS.create stack.Client.t in
118+
(* Query for an AAAA record on a domain that might only have A records *)
119+
DNS.gethostbyname ~server ~q_type:Dns.Packet.Q_AAAA resolver name >>= function
120+
| [] -> (* Expected: NOERROR with an empty answer list *)
121+
Log.info (fun f -> f "%s returned NOERROR with no AAAA records" name);
122+
Lwt.return ()
123+
| _ ->
124+
Log.err (fun f -> f "Expected no AAAA records for %s, but got some" name);
125+
failwith ("Expected NOERROR with no AAAA records for " ^ name)
126+
in
127+
run ~pcap:"test_noerror_empty_response.pcap" t
128+
129+
let test_nxdomain_response server config () =
130+
let name = "nonexistent.example.com" in (* Use a domain that doesn’t exist *)
131+
let t _ stack =
132+
set_dns_policy config;
133+
let resolver = DNS.create stack.Client.t in
134+
DNS.gethostbyname ~server resolver name >>= function
135+
| [] -> (* Expected: NXDOMAIN (empty answer list) *)
136+
Log.info (fun f -> f "%s returned NXDOMAIN as expected" name);
137+
Lwt.return ()
138+
| _ ->
139+
Log.err (fun f -> f "Expected NXDOMAIN for %s, but got answers" name);
140+
failwith ("Expected NXDOMAIN for " ^ name)
141+
in
142+
run ~pcap:"test_nxdomain_response.pcap" t
143+
113144
let test_dns config =
114145
let prefix = Dns_policy.(Config.to_string @@ config ()) in [
115146
prefix ^ ": lookup ",
@@ -123,6 +154,12 @@ let test_dns config =
123154

124155
prefix ^ ": _etc_hosts_priority",
125156
[ "", `Quick, test_etc_hosts_priority primary_dns_ip config ];
157+
158+
prefix ^ ": noerror_empty_response",
159+
[ "", `Quick, test_noerror_empty_response primary_dns_ip config ];
160+
161+
prefix ^ ": nxdomain_response",
162+
[ "", `Quick, test_nxdomain_response primary_dns_ip config ];
126163
]
127164

128165
(* A real UDP server listening on a physical port *)

0 commit comments

Comments
 (0)