This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
|
|
@ -0,0 +1,189 @@
|
|||
let error_msgf fmt = Fmt.kstr (fun msg -> Error (`Msg msg)) fmt
|
||||
|
||||
let src = Logs.Src.create "dns-client-miou-unix"
|
||||
|
||||
module Log = (val Logs.src_log src : Logs.LOG)
|
||||
|
||||
module Transport = struct
|
||||
open Happy_eyeballs_miou_unix
|
||||
|
||||
type +'a io = 'a
|
||||
|
||||
type io_addr =
|
||||
[ `Plaintext of Ipaddr.t * int | `Tls of Tls.Config.client * Ipaddr.t * int ]
|
||||
|
||||
type t = {
|
||||
nameservers: io_addr list
|
||||
; proto: Dns.proto
|
||||
; timeout: float
|
||||
; happy: stack
|
||||
}
|
||||
and stack = Happy_eyeballs_miou_unix.t
|
||||
|
||||
type context =
|
||||
{ fd : [ `Udp of Miou_unix.file_descr
|
||||
| `Tcp of Miou_unix.file_descr
|
||||
| `Tls of Tls_miou_unix.t ]
|
||||
; timeout : float }
|
||||
|
||||
let clock = Mtime_clock.elapsed_ns
|
||||
|
||||
let same_address ipaddr' port' = function
|
||||
| `Plaintext (ipaddr, port) -> Ipaddr.compare ipaddr ipaddr' = 0 && port = port'
|
||||
| `Tls (_, ipaddr, port) -> Ipaddr.compare ipaddr ipaddr' = 0 && port = port'
|
||||
|
||||
exception Timeout
|
||||
|
||||
let with_timeout ~timeout:ts fn =
|
||||
let timeout () = Miou_unix.sleep ts; raise Timeout in
|
||||
let prm1 = Miou.async timeout in
|
||||
let prm0 = Miou.async fn in
|
||||
Miou.await_first [ prm0; prm1 ]
|
||||
|
||||
let connect_to_nameservers t =
|
||||
let ( let* ) = Result.bind in
|
||||
match t.proto with
|
||||
| `Tcp ->
|
||||
let ip_of_nameserver = function
|
||||
| `Plaintext (ipaddr, port) -> (ipaddr, port)
|
||||
| `Tls (_, ipaddr, port) -> (ipaddr, port) in
|
||||
let ips = List.map ip_of_nameserver t.nameservers in
|
||||
let* ((ipaddr, port) as addr), fd = connect_ip t.happy ips in
|
||||
begin match List.find (same_address ipaddr port) t.nameservers with
|
||||
| `Plaintext _ -> Ok (addr, `Tcp fd)
|
||||
| `Tls (config, _, _) ->
|
||||
try let fd = Tls_miou_unix.client_of_fd config fd in
|
||||
Ok (addr, `Tls fd)
|
||||
with End_of_file ->
|
||||
Miou_unix.close fd;
|
||||
error_msgf "Connection to nameservers (via TLS) impossible" end
|
||||
| `Udp ->
|
||||
let is_plaintext = function `Plaintext v -> Either.Left v | _ -> Either.Right () in
|
||||
let[@warning "-8"] (ipaddr, port) :: _, _ = List.partition_map is_plaintext t.nameservers in
|
||||
let proto_number, socket_type = Unix.((getprotobyname "udp").p_proto, SOCK_DGRAM) in
|
||||
let domain = match ipaddr with
|
||||
| Ipaddr.V4 _ -> Unix.PF_INET
|
||||
| Ipaddr.V6 _ -> Unix.PF_INET6 in
|
||||
let fd = Unix.socket domain socket_type proto_number in
|
||||
let addr = Unix.ADDR_INET (Ipaddr_unix.to_inet_addr ipaddr, port) in
|
||||
let connect () =
|
||||
Unix.connect fd addr;
|
||||
((ipaddr, port), `Udp (Miou_unix.of_file_descr fd)) in
|
||||
match with_timeout ~timeout:t.timeout connect with
|
||||
| Ok value -> Ok value
|
||||
| Error Timeout ->
|
||||
Unix.close fd;
|
||||
error_msgf "Connection to nameservers (via UDP) timeout"
|
||||
| Error exn ->
|
||||
Unix.close fd;
|
||||
error_msgf "Unexpected error: %S" (Printexc.to_string exn)
|
||||
|
||||
let nameservers { nameservers; proto; _ } = (proto, nameservers)
|
||||
let bind x f = f x
|
||||
let lift = Fun.id
|
||||
let rng = Mirage_crypto_rng.generate ?g:None
|
||||
|
||||
let connect t =
|
||||
let ( let* ) = Result.bind in
|
||||
let* ((addr, port), fd) = connect_to_nameservers t in
|
||||
Log.debug (fun m -> m "Connected to a nameserver %a:%d" Ipaddr.pp addr port);
|
||||
match fd with
|
||||
| `Tcp _ | `Tls _ -> Ok (`Tcp, { fd; timeout= t.timeout })
|
||||
| `Udp _ -> Ok (`Udp, { fd; timeout= t.timeout })
|
||||
|
||||
let send_recv_tls ~timeout ~id fd str =
|
||||
let send () = Tls_miou_unix.write fd str in
|
||||
let recv () =
|
||||
let rec go buf rx_len =
|
||||
let expected_len =
|
||||
if rx_len >= 2 then Some (Bytes.get_uint16_be buf 0) else None in
|
||||
match expected_len with
|
||||
| None ->
|
||||
let len = Tls_miou_unix.read fd buf ~off:rx_len in
|
||||
if rx_len + len >= 2 && len > 0 then go buf (rx_len + len)
|
||||
else failwith "TLS connection closed by nameserver"
|
||||
| Some expected_len when rx_len >= expected_len + 2 ->
|
||||
let id' = Bytes.get_uint16_be buf 2 in
|
||||
if id = id'
|
||||
then Bytes.sub_string buf 0 (expected_len + 2)
|
||||
else
|
||||
let buf' = Bytes.make 2048 '\000' in
|
||||
let rx_len' = rx_len - (expected_len + 2) in
|
||||
Bytes.blit buf (expected_len + 2) buf' 0 rx_len';
|
||||
go buf' rx_len'
|
||||
| Some expected_len when Bytes.length buf >= expected_len + 2 ->
|
||||
let len = (expected_len + 2) - rx_len in
|
||||
Tls_miou_unix.really_read fd buf ~off:rx_len ~len;
|
||||
go buf (rx_len + len)
|
||||
| Some expected_len ->
|
||||
(* NOTE(dinosaure): in this branch, [buf] is not large enough to store
|
||||
the DNS packet. We allocate a new buffer which can store the actual
|
||||
DNS packet and use it for the next [go] iteration. *)
|
||||
let buf' = Bytes.make (expected_len + 2) '\000' in
|
||||
Bytes.blit buf 0 buf' 0 rx_len;
|
||||
go buf' rx_len in
|
||||
go (Bytes.make 2048 '\000') 0 in
|
||||
let ( >>= ) = Result.bind in
|
||||
match with_timeout ~timeout send >>= fun () ->
|
||||
with_timeout ~timeout recv with
|
||||
| Ok _ as rx -> rx
|
||||
| Error Timeout -> error_msgf "DNS request timeout"
|
||||
| Error (Failure msg) -> Error (`Msg msg)
|
||||
| Error (End_of_file | Tls_miou_unix.Closed_by_peer) ->
|
||||
error_msgf "End of file reading from nameserver"
|
||||
| Error exn ->
|
||||
error_msgf "Got an unexpected exception: %s"
|
||||
(Printexc.to_string exn)
|
||||
|
||||
let send_recv { fd; timeout } str =
|
||||
if String.length str > 4 then begin
|
||||
match fd with
|
||||
| `Tls fd ->
|
||||
let id = String.get_uint16_be str 2 in
|
||||
send_recv_tls ~timeout ~id fd str
|
||||
| `Udp fd | `Tcp fd ->
|
||||
let fd = Miou_unix.to_file_descr fd in
|
||||
Unix.clear_nonblock fd;
|
||||
let send () =
|
||||
Log.debug (fun m -> m "sending a dns packet to resolver");
|
||||
Unix.setsockopt_float fd Unix.SO_SNDTIMEO timeout;
|
||||
let len = Unix.send_substring fd str 0 (String.length str) [] in
|
||||
if len <> String.length str
|
||||
then failwith "Broken write to upstream nameserver" in
|
||||
let recv () =
|
||||
let buffer = Bytes.make 2048 '\000' in
|
||||
Unix.setsockopt_float fd Unix.SO_RCVTIMEO timeout;
|
||||
let len = Unix.recv fd buffer 0 (Bytes.length buffer) [] in
|
||||
(* TODO(dinosaure): should we check rx_len and continue until we got
|
||||
the full packet (only for tcp/ip)? *)
|
||||
if len > 0 && len <= Bytes.length buffer
|
||||
then Bytes.sub_string buffer 0 len
|
||||
else failwith "Reading from nameserver socket failed" in
|
||||
let ( >>= ) = Result.bind in
|
||||
match with_timeout ~timeout send >>= fun () ->
|
||||
with_timeout ~timeout recv with
|
||||
| Ok _ as rx -> rx
|
||||
| Error Timeout -> error_msgf "DNS request timeout"
|
||||
| Error (Failure msg) -> Error (`Msg msg)
|
||||
| Error exn ->
|
||||
error_msgf "Got an unexpected exception: %s"
|
||||
(Printexc.to_string exn)
|
||||
end
|
||||
else error_msgf "Invalid context (data length <= 4)"
|
||||
|
||||
let close { fd; _ } = match fd with
|
||||
| `Tcp fd | `Udp fd -> Miou_unix.close fd
|
||||
| `Tls fd -> Tls_miou_unix.close fd
|
||||
|
||||
let of_ns ns = Int64.to_float ns /. 1_000_000_000.
|
||||
|
||||
let create ?nameservers ~timeout happy =
|
||||
let proto, nameservers =
|
||||
match nameservers with
|
||||
| None -> (`Udp, [ `Plaintext (Ipaddr.of_string_exn "8.8.8.8", 53) ])
|
||||
| Some (a, nss) -> (a, nss)
|
||||
in
|
||||
{ nameservers; proto; timeout= of_ns timeout; happy }
|
||||
end
|
||||
|
||||
include Dns_client.Make (Transport)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
module Transport : Dns_client.S
|
||||
with type io_addr = [ `Plaintext of Ipaddr.t * int | `Tls of Tls.Config.client * Ipaddr.t * int ]
|
||||
and type +'a io = 'a
|
||||
and type stack = Happy_eyeballs_miou_unix.t
|
||||
|
||||
include module type of Dns_client.Make (Transport)
|
||||
5
unikernel/duniverse/ocaml-dns/miou/client/dune
Normal file
5
unikernel/duniverse/ocaml-dns/miou/client/dune
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
(library
|
||||
(name dns_client_miou_unix)
|
||||
(modules dns_client_miou_unix)
|
||||
(public_name dns-client-miou-unix)
|
||||
(libraries dns-client tls-miou-unix happy-eyeballs-miou-unix))
|
||||
Loading…
Add table
Add a link
Reference in a new issue