Skip to content

Commit

Permalink
Add Eio.Net.getaddrinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
bikallem authored and talex5 committed Aug 15, 2022
1 parent 5e7ad8e commit 9cc5e1e
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib_eio/mock/eio_mock.mli
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ module Net : sig
on_listen : Eio.Net.listening_socket Handler.t;
on_connect : <Eio.Net.stream_socket; Eio.Flow.close> Handler.t;
on_datagram_socket : <Eio.Net.datagram_socket; Eio.Flow.close> Handler.t;
on_getaddrinfo : Eio.Net.Sockaddr.t list Handler.t;
>

type listening_socket = <
Expand All @@ -140,6 +141,8 @@ module Net : sig
val on_datagram_socket : t -> <Eio.Net.datagram_socket; Eio.Flow.close; ..> Handler.actions -> unit
(** [on_datagram_socket t actions] configures how to create datagram sockets. *)

val on_getaddrinfo : t -> Eio.Net.Sockaddr.t list Handler.actions -> unit

val listening_socket : string -> listening_socket
(** [listening_socket label] can be configured to provide mock connections. *)

Expand Down
9 changes: 9 additions & 0 deletions lib_eio/mock/net.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ type t = <
on_listen : Eio.Net.listening_socket Handler.t;
on_connect : <Eio.Net.stream_socket; Eio.Flow.close> Handler.t;
on_datagram_socket : <Eio.Net.datagram_socket; Eio.Flow.close> Handler.t;
on_getaddrinfo : Eio.Net.Sockaddr.t list Handler.t;
>

let make label =
let on_listen = Handler.make (`Raise (Failure "Mock listen handler not configured")) in
let on_connect = Handler.make (`Raise (Failure "Mock connect handler not configured")) in
let on_datagram_socket = Handler.make (`Raise (Failure "Mock datagram_socket handler not configured")) in
let on_getaddrinfo = Handler.make (`Raise (Failure "Mock getaddrinfo handler not configured")) in
object
inherit Eio.Net.t

method on_listen = on_listen
method on_connect = on_connect
method on_datagram_socket = on_datagram_socket
method on_getaddrinfo = on_getaddrinfo

method listen ~reuse_addr:_ ~reuse_port:_ ~backlog:_ ~sw addr =
traceln "%s: listen on %a" label Eio.Net.Sockaddr.pp addr;
Expand All @@ -35,6 +38,10 @@ let make label =
let socket = Handler.run on_datagram_socket in
Switch.on_release sw (fun () -> Eio.Flow.close socket);
socket

method getaddrinfo ~service node =
traceln "%s: getaddrinfo ~service:%s %s" label service node;
Handler.run on_getaddrinfo
end

let on_connect (t:t) actions =
Expand All @@ -49,6 +56,8 @@ let on_datagram_socket (t:t) actions =
let as_socket x = (x :> <Eio.Net.datagram_socket; Eio.Flow.close>) in
Handler.seq t#on_datagram_socket (List.map (Action.map as_socket) actions)

let on_getaddrinfo (t:t) actions = Handler.seq t#on_getaddrinfo actions

type listening_socket = <
Eio.Net.listening_socket;
on_accept : (Flow.t * Eio.Net.Sockaddr.stream) Handler.t;
Expand Down
3 changes: 3 additions & 0 deletions lib_eio/net.ml
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,14 @@ class virtual t = object
method virtual listen : reuse_addr:bool -> reuse_port:bool -> backlog:int -> sw:Switch.t -> Sockaddr.stream -> listening_socket
method virtual connect : sw:Switch.t -> Sockaddr.stream -> <stream_socket; Flow.close>
method virtual datagram_socket : sw:Switch.t -> Sockaddr.datagram -> <datagram_socket; Flow.close>
method virtual getaddrinfo : service:string -> string -> Sockaddr.t list
end

let listen ?(reuse_addr=false) ?(reuse_port=false) ~backlog ~sw (t:#t) = t#listen ~reuse_addr ~reuse_port ~backlog ~sw
let connect ~sw (t:#t) = t#connect ~sw

let datagram_socket ~sw (t:#t) = t#datagram_socket ~sw

let getaddrinfo ?(service="") (t:#t) hostname = t#getaddrinfo ~service hostname

let close = Flow.close
12 changes: 12 additions & 0 deletions lib_eio/net.mli
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class virtual t : object
method virtual listen : reuse_addr:bool -> reuse_port:bool -> backlog:int -> sw:Switch.t -> Sockaddr.stream -> listening_socket
method virtual connect : sw:Switch.t -> Sockaddr.stream -> <stream_socket; Flow.close>
method virtual datagram_socket : sw:Switch.t -> Sockaddr.datagram -> <datagram_socket; Flow.close>
method virtual getaddrinfo : service:string -> string -> Sockaddr.t list
end

(** {2 Out-bound Connections} *)
Expand Down Expand Up @@ -171,6 +172,17 @@ val recv : #datagram_socket -> Cstruct.t -> Sockaddr.datagram * int
returned along with the sender address and port. If the [buf] is too small then excess bytes may be discarded
depending on the type of the socket the message is received from. *)

(** {2 DNS queries} *)

val getaddrinfo: ?service:string -> #t -> string -> Sockaddr.t list
(** [getaddrinfo ?service t node] returns a list of IP addresses for [node]. [node] is either a domain name or
an IP address.
@param service is a human friendly textual name for internet services assigned by IANA., eg.
'http', 'https', 'ftp', etc.
For a more thorough treatment, @see <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html> getaddrinfo *)

(** {2 Closing} *)
val close : <close: unit; ..> -> unit
(** [close t] marks the socket as closed. It can no longer be used after this. *)
19 changes: 19 additions & 0 deletions lib_eio_linux/eio_linux.ml
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,23 @@ module Low_level = struct
read_all (files @ acc) fd
in
Eio_unix.run_in_systhread (fun () -> read_all [] fd)

(* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml *)
let getaddrinfo ~service node =
let to_eio_sockaddr_t {Unix.ai_family; ai_addr; ai_socktype; ai_protocol; _ } =
match ai_family, ai_socktype, ai_addr with
| (Unix.PF_INET | PF_INET6),
(Unix.SOCK_STREAM | SOCK_DGRAM),
Unix.ADDR_INET (inet_addr,port) -> (
match ai_protocol with
| 6 -> Some (`Tcp (Eio_unix.Ipaddr.of_unix inet_addr, port))
| 17 -> Some (`Udp (Eio_unix.Ipaddr.of_unix inet_addr, port))
| _ -> None)
| _ -> None
in
Eio_unix.run_in_systhread @@ fun () ->
Unix.getaddrinfo node service []
|> List.filter_map to_eio_sockaddr_t
end

external eio_eventfd : int -> Unix.file_descr = "caml_eio_eventfd"
Expand Down Expand Up @@ -1120,6 +1137,8 @@ let net = object
let sock = FD.of_unix ~sw ~seekable:false ~close_unix:true sock_unix in
Unix.bind sock_unix addr;
udp_socket sock

method getaddrinfo = Low_level.getaddrinfo
end

type stdenv = <
Expand Down
7 changes: 7 additions & 0 deletions lib_eio_linux/eio_linux.mli
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,11 @@ module Low_level : sig
It uses Linux's [getrandom] call, which is like reading from /dev/urandom
except that it will block (the whole domain) if used at early boot
when the random system hasn't been initialised yet. *)

(** {1 DNS functions} *)

val getaddrinfo : service:string -> string -> Eio.Net.Sockaddr.t list
(** [getaddrinfo host] returns a list of IP addresses for [host]. [host] is either a domain name or
an ipaddress. *)

end
2 changes: 1 addition & 1 deletion lib_eio_luv/dune
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(library
(name eio_luv)
(public_name eio_luv)
(libraries eio.unix luv luv_unix eio.utils logs fmt))
(libraries eio eio.unix luv luv_unix eio.utils logs fmt))
24 changes: 24 additions & 0 deletions lib_eio_luv/eio_luv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,28 @@ module Low_level = struct
Luv.Timer.start timer delay (fun () ->
if Fiber_context.clear_cancel_fn k.fiber then enqueue_thread st k ()
) |> or_raise

(* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml *)
let getaddrinfo ~service node =
let ( let* ) o f = Option.bind o f in
let to_eio_sockaddr_t {Luv.DNS.Addr_info.family; addr; socktype; protocol; _ } =
match family, socktype with
| (`INET | `INET6),
(`STREAM | `DGRAM) -> (
let* host = Luv.Sockaddr.to_string addr in
let* port = Luv.Sockaddr.port addr in
let ipaddr = Unix.inet_addr_of_string host |> Eio_unix.Ipaddr.of_unix in
match protocol with
| 6 -> Some (`Tcp (ipaddr, port))
| 17 -> Some (`Udp (ipaddr, port))
| _ -> None)
| _ -> None
in
let request = Luv.DNS.Addr_info.Request.make () in
await_with_cancel ~request (fun loop -> Luv.DNS.getaddrinfo ~loop ~request ~service ~node ())
|> or_raise
|> List.filter_map to_eio_sockaddr_t

end

open Low_level
Expand Down Expand Up @@ -647,6 +669,8 @@ let net = object
let addr = luv_addr_of_eio host port in
Luv.UDP.bind sock addr |> or_raise;
udp_socket dg_sock

method getaddrinfo = Low_level.getaddrinfo
end

let secure_random =
Expand Down
6 changes: 6 additions & 0 deletions lib_eio_luv/eio_luv.mli
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module Low_level : sig
val sleep_until : float -> unit
(** [sleep_until time] blocks until the current time is [time]. *)

(** {DNS functions} *)

val getaddrinfo : service:string -> string -> Eio.Net.Sockaddr.t list
(** [getaddrinfo ~service host] returns a list of IP addresses for [host]. [host] is either a domain name or
an ipaddress. *)

(** {1 Low-level wrappers for Luv functions} *)

module File : sig
Expand Down
42 changes: 42 additions & 0 deletions tests/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -485,3 +485,45 @@ EPIPE:
(fun () -> Eio.Flow.shutdown a `Receive);;
- : unit = ()
```

## Getaddrinfo

```ocaml
# Eio_main.run @@ fun env ->
Eio.Net.getaddrinfo env#net "127.0.0.1";;
- : Eio.Net.Sockaddr.t list =
[`Tcp ("\127\000\000\001", 0); `Udp ("\127\000\000\001", 0)]
```

```ocaml
# Eio_main.run @@ fun env ->
Eio.Net.getaddrinfo env#net "127.0.0.1" ~service:"80";;
- : Eio.Net.Sockaddr.t list =
[`Tcp ("\127\000\000\001", 80); `Udp ("\127\000\000\001", 80)]
```

<!-- $MDX non-deterministic=output -->
```ocaml
# Eio_main.run @@ fun env ->
Eio.Net.getaddrinfo ~service:"http" env#net "127.0.0.1";;
- : Eio.Net.Sockaddr.t list =
[`Tcp ("\127\000\000\001", 80); `Udp ("\127\000\000\001", 80)]
```

<!-- $MDX non-deterministic=output -->
```ocaml
# Eio_main.run @@ fun env ->
Eio.Net.getaddrinfo ~service:"ftp" env#net "127.0.0.1";;
- : Eio.Net.Sockaddr.t list =
[`Tcp ("\127\000\000\001", 21); `Udp ("\127\000\000\001", 21)]
```

<!-- $MDX non-deterministic=output -->
```ocaml
# Eio_main.run @@ fun env ->
Eio.Net.getaddrinfo ~service:"https" env#net "google.com";;
- : Eio.Net.Sockaddr.t list =
[`Tcp ("Ø:ÔÎ", 443); `Udp ("Ø:ÔÎ", 443);
`Tcp ("*\000\020P@\t\b \000\000\000\000\000\000 \014", 443);
`Udp ("*\000\020P@\t\b \000\000\000\000\000\000 \014", 443)]
```

0 comments on commit 9cc5e1e

Please sign in to comment.