From 2e09d49fc460a06bf546fdd1834529fb784917a1 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 20 Oct 2022 12:50:47 +0200 Subject: [PATCH 01/20] Return empty list on EAI_ errors for getaddrinfo@luv (#351) Unix.getaddrinfo used in u-ring ignores gar_errno and returns an empty list, this makes the luv backend follow a similar behaviour. --- lib_eio_luv/eio_luv.ml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib_eio_luv/eio_luv.ml b/lib_eio_luv/eio_luv.ml index 738e7aa23..0a7845e36 100644 --- a/lib_eio_luv/eio_luv.ml +++ b/lib_eio_luv/eio_luv.ml @@ -654,10 +654,15 @@ module Low_level = struct | _ -> 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 - + match (await_with_cancel ~request + (fun loop -> Luv.DNS.getaddrinfo ~loop ~request ~service ~node ())) + with + | Ok nl -> List.filter_map to_eio_sockaddr_t nl + | Error `EAI_ADDRFAMILY | Error `EAI_AGAIN | Error `EAI_BADFLAGS | Error `EAI_BADHINTS + | Error `EAI_CANCELED | Error `EAI_FAIL | Error `EAI_FAMILY | Error `EAI_MEMORY + | Error `EAI_NODATA | Error `EAI_NONAME| Error `EAI_OVERFLOW | Error `EAI_PROTOCOL + | Error `EAI_SERVICE | Error `EAI_SOCKTYPE -> [] + | Error e -> raise (Luv_error e) end open Low_level From 97f093d28ccc564e1940275a51afe74a172e5229 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 30 Nov 2022 20:27:28 +0100 Subject: [PATCH 02/20] works --- lib_eio_linux/eio_linux.ml | 19 +++++ lib_eio_linux/eio_linux.mli | 17 ++++ lib_eio_linux/eio_stubs.c | 149 +++++++++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 1 deletion(-) diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index 9ffa440fd..921f475c8 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -858,6 +858,25 @@ module Low_level = struct external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents" + (* keep in sync with eio_stubs.c *) + type gai_error = + | EAI_ADDRFAMILY + | EAI_AGAIN + | EAI_BADFLAGS + | EAI_BADHINTS + | EAI_FAIL + | EAI_FAMILY + | EAI_MEMORY + | EAI_NODATA + | EAI_NONAME + | EAI_SERVICE + | EAI_SOCKTYPE + | EAI_SYSTEM + + external eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> + (Unix.addr_info list, gai_error) result + = "caml_eio_getaddrinfo" + let getrandom { Cstruct.buffer; off; len } = let rec loop n = if n = len then diff --git a/lib_eio_linux/eio_linux.mli b/lib_eio_linux/eio_linux.mli index 659797e9c..fe0476b75 100644 --- a/lib_eio_linux/eio_linux.mli +++ b/lib_eio_linux/eio_linux.mli @@ -251,4 +251,21 @@ module Low_level : sig (** [getaddrinfo host] returns a list of IP addresses for [host]. [host] is either a domain name or an ipaddress. *) + type gai_error = + | EAI_ADDRFAMILY + | EAI_AGAIN + | EAI_BADFLAGS + | EAI_BADHINTS + | EAI_FAIL + | EAI_FAMILY + | EAI_MEMORY + | EAI_NODATA + | EAI_NONAME + | EAI_SERVICE + | EAI_SOCKTYPE + | EAI_SYSTEM + + val eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> (Unix.addr_info list, gai_error) result + + end diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 4d04e4875..9cd0daa3e 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,7 +9,7 @@ #include #include #include - +#include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include // Make sure we have enough space for at least one entry. #define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64)) @@ -99,3 +101,148 @@ CAMLprim value caml_eio_getdents(value v_fd) { CAMLreturn(result); } + +static value caml_unix_cst_to_constr(int n, int *tbl, int size, int deflt) +{ + int i; + for (i = 0; i < size; i++) + if (n == tbl[i]) return Val_int(i); + return Val_int(deflt); +} + +extern int caml_unix_socket_domain_table[]; /* from socket.c */ +extern int caml_unix_socket_type_table[]; /* from socket.c */ + +static value convert_addrinfo(struct addrinfo * a) +{ + CAMLparam0(); + CAMLlocal3(vres,vaddr,vcanonname); + union sock_addr_union sa; + socklen_param_type len; + + len = a->ai_addrlen; + if (len > sizeof(sa)) len = sizeof(sa); + memcpy(&sa.s_gen, a->ai_addr, len); + vaddr = caml_unix_alloc_sockaddr(&sa, len, -1); + vcanonname = caml_copy_string(a->ai_canonname == NULL ? "" : a->ai_canonname); + vres = caml_alloc_small(5, 0); + Field(vres, 0) = + caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); + Field(vres, 1) = + caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); + Field(vres, 2) = Val_int(a->ai_protocol); + Field(vres, 3) = vaddr; + Field(vres, 4) = vcanonname; + CAMLreturn(vres); +} + +/* glibc doesn't define a bunch of EAI_, so fake one since code gets copied around */ + +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY -3000 +#endif /* EAI_ADDRFAMILY */ + +#ifndef EAI_BADHINTS +#define EAI_BADHINTS -3013 +#endif /* EAI_BADHINTS */ + +#ifndef EAI_NODATA +#define EAI_NODATA -3007 +#endif /* EAI_NODATA */ + +static int gai_errors[] = { + EAI_ADDRFAMILY, + EAI_AGAIN, + EAI_BADFLAGS, + EAI_BADHINTS, + EAI_FAIL, + EAI_FAMILY, + EAI_MEMORY, + EAI_NODATA, + EAI_NONAME, + EAI_SERVICE, + EAI_SOCKTYPE, + EAI_SYSTEM +}; + +CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) +{ + CAMLparam3(vnode, vserv, vopts); + CAMLlocal3(vres, v, vret); + char * node, * serv; + struct addrinfo hints; + struct addrinfo * res, * r; + int retcode, i; + + if (! (caml_string_is_c_safe(vnode) && caml_string_is_c_safe(vserv))) + CAMLreturn (Val_emptylist); + + /* Extract "node" parameter */ + if (caml_string_length(vnode) == 0) { + node = NULL; + } else { + node = caml_stat_strdup(String_val(vnode)); + } + /* Extract "service" parameter */ + if (caml_string_length(vserv) == 0) { + serv = NULL; + } else { + serv = caml_stat_strdup(String_val(vserv)); + } + /* Parse options, set hints */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + for (/*nothing*/; vopts != Val_emptylist; vopts = Field(vopts, 1)) { + v = Field(vopts, 0); + if (Is_block(v)) + switch (Tag_val(v)) { + case 0: /* AI_FAMILY of socket_domain */ + hints.ai_family = caml_unix_socket_domain_table[Int_val(Field(v, 0))]; + break; + case 1: /* AI_SOCKTYPE of socket_type */ + hints.ai_socktype = caml_unix_socket_type_table[Int_val(Field(v, 0))]; + break; + case 2: /* AI_PROTOCOL of int */ + hints.ai_protocol = Int_val(Field(v, 0)); + break; + } + else + switch (Int_val(v)) { + case 0: /* AI_NUMERICHOST */ + hints.ai_flags |= AI_NUMERICHOST; break; + case 1: /* AI_CANONNAME */ + hints.ai_flags |= AI_CANONNAME; break; + case 2: /* AI_PASSIVE */ + hints.ai_flags |= AI_PASSIVE; break; + } + } + /* Do the call */ + caml_enter_blocking_section(); + retcode = getaddrinfo(node, serv, &hints, &res); + caml_leave_blocking_section(); + if (node != NULL) caml_stat_free(node); + if (serv != NULL) caml_stat_free(serv); + /* Convert result */ + vres = Val_emptylist; + if (retcode == 0) { + for (r = res; r != NULL; r = r->ai_next) { + v = caml_alloc_small(2, Tag_cons); + Field(v, 0) = convert_addrinfo(r); + Field(v, 1) = vres; + vres = v; + } + vret = caml_alloc_small(1, 0); /* 0 = Ok */ + Field(vret, 0) = vres; + freeaddrinfo(res); + } else { + for (i = 0; i < (sizeof(gai_errors) / sizeof(int)); i++) + if (gai_errors[i] == retcode) + break; + if (i == (sizeof(gai_errors) / sizeof(int))) + uerror("invalid gai_error", Nothing); + vret = caml_alloc_small(1, 1); /* 1 = Error */ + Field(vret, 0) = Val_int(i); + } + + CAMLreturn(vret); +} From a26ddac09acd3944e10635c2c3765edc736f082a Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 30 Nov 2022 20:41:45 +0100 Subject: [PATCH 03/20] EINVAL on bad EAI --- lib_eio_linux/eio_stubs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 9cd0daa3e..5aa693258 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -238,8 +238,10 @@ CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) for (i = 0; i < (sizeof(gai_errors) / sizeof(int)); i++) if (gai_errors[i] == retcode) break; - if (i == (sizeof(gai_errors) / sizeof(int))) + if (i == (sizeof(gai_errors) / sizeof(int))) { + errno = EINVAL; uerror("invalid gai_error", Nothing); + } vret = caml_alloc_small(1, 1); /* 1 = Error */ Field(vret, 0) = Val_int(i); } From b419150a28fe67031f0cbbf3cbe9ed6aecfa4128 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 08:02:35 +0100 Subject: [PATCH 04/20] Move things to net and implement libuv --- lib_eio/net.ml | 17 +++++++++++++++++ lib_eio/net.mli | 17 +++++++++++++++++ lib_eio_linux/eio_linux.ml | 21 ++++----------------- lib_eio_linux/eio_linux.mli | 17 ++--------------- lib_eio_luv/eio_luv.ml | 19 +++++++++++++++---- 5 files changed, 55 insertions(+), 36 deletions(-) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index 6094eed41..2c9d1c044 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -198,6 +198,23 @@ let datagram_socket ?(reuse_addr=false) ?(reuse_port=false) ~sw (t:#t) addr = let addr = (addr :> [Sockaddr.datagram | `UdpV4 | `UdpV6]) in t#datagram_socket ~reuse_addr ~reuse_port ~sw addr +(* keep in sync with C stubs *) +type getaddrinfo_error = + | EAI_ADDRFAMILY + | EAI_AGAIN + | EAI_BADFLAGS + | EAI_BADHINTS + | EAI_FAIL + | EAI_FAMILY + | EAI_MEMORY + | EAI_NODATA + | EAI_NONAME + | EAI_SERVICE + | EAI_SOCKTYPE + | EAI_SYSTEM + +exception Getaddrinfo_error of getaddrinfo_error + let getaddrinfo ?(service="") (t:#t) hostname = t#getaddrinfo ~service hostname let getaddrinfo_stream ?service t hostname = diff --git a/lib_eio/net.mli b/lib_eio/net.mli index eab8cf6ae..ae36049be 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -222,6 +222,23 @@ val recv : #datagram_socket -> Cstruct.t -> Sockaddr.datagram * int (** {2 DNS queries} *) +(* keep in sync with C stubs *) +type getaddrinfo_error = + | EAI_ADDRFAMILY + | EAI_AGAIN + | EAI_BADFLAGS + | EAI_BADHINTS + | EAI_FAIL + | EAI_FAMILY + | EAI_MEMORY + | EAI_NODATA + | EAI_NONAME + | EAI_SERVICE + | EAI_SOCKTYPE + | EAI_SYSTEM + +exception Getaddrinfo_error of getaddrinfo_error + 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. diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index 921f475c8..0902c374f 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -858,23 +858,8 @@ module Low_level = struct external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents" - (* keep in sync with eio_stubs.c *) - type gai_error = - | EAI_ADDRFAMILY - | EAI_AGAIN - | EAI_BADFLAGS - | EAI_BADHINTS - | EAI_FAIL - | EAI_FAMILY - | EAI_MEMORY - | EAI_NODATA - | EAI_NONAME - | EAI_SERVICE - | EAI_SOCKTYPE - | EAI_SYSTEM - external eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> - (Unix.addr_info list, gai_error) result + (Unix.addr_info list, Eio.Net.getaddrinfo_error) result = "caml_eio_getaddrinfo" let getrandom { Cstruct.buffer; off; len } = @@ -973,7 +958,9 @@ module Low_level = struct | _ -> None in Eio_unix.run_in_systhread @@ fun () -> - Unix.getaddrinfo node service [] + (match (eio_getaddrinfo node service []) with + | Ok l -> l + | Error e -> raise (Eio.Net.Getaddrinfo_error e)) |> List.filter_map to_eio_sockaddr_t end diff --git a/lib_eio_linux/eio_linux.mli b/lib_eio_linux/eio_linux.mli index fe0476b75..7f40f0567 100644 --- a/lib_eio_linux/eio_linux.mli +++ b/lib_eio_linux/eio_linux.mli @@ -251,21 +251,8 @@ module Low_level : sig (** [getaddrinfo host] returns a list of IP addresses for [host]. [host] is either a domain name or an ipaddress. *) - type gai_error = - | EAI_ADDRFAMILY - | EAI_AGAIN - | EAI_BADFLAGS - | EAI_BADHINTS - | EAI_FAIL - | EAI_FAMILY - | EAI_MEMORY - | EAI_NODATA - | EAI_NONAME - | EAI_SERVICE - | EAI_SOCKTYPE - | EAI_SYSTEM - - val eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> (Unix.addr_info list, gai_error) result + val eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> + (Unix.addr_info list, Eio.Net.getaddrinfo_error) result end diff --git a/lib_eio_luv/eio_luv.ml b/lib_eio_luv/eio_luv.ml index 0a7845e36..1d9abf7c5 100644 --- a/lib_eio_luv/eio_luv.ml +++ b/lib_eio_luv/eio_luv.ml @@ -654,14 +654,25 @@ module Low_level = struct | _ -> None in let request = Luv.DNS.Addr_info.Request.make () in + let r e = raise (Eio.Net.Getaddrinfo_error e) in match (await_with_cancel ~request (fun loop -> Luv.DNS.getaddrinfo ~loop ~request ~service ~node ())) with | Ok nl -> List.filter_map to_eio_sockaddr_t nl - | Error `EAI_ADDRFAMILY | Error `EAI_AGAIN | Error `EAI_BADFLAGS | Error `EAI_BADHINTS - | Error `EAI_CANCELED | Error `EAI_FAIL | Error `EAI_FAMILY | Error `EAI_MEMORY - | Error `EAI_NODATA | Error `EAI_NONAME| Error `EAI_OVERFLOW | Error `EAI_PROTOCOL - | Error `EAI_SERVICE | Error `EAI_SOCKTYPE -> [] + | Error `EAI_ADDRFAMILY -> r EAI_ADDRFAMILY + | Error `EAI_AGAIN -> r EAI_AGAIN + | Error `EAI_BADFLAGS -> r EAI_BADFLAGS + | Error `EAI_BADHINTS -> r EAI_BADHINTS + | Error `EAI_CANCELED -> r EAI_FAIL (* note *) + | Error `EAI_FAIL -> r EAI_FAIL + | Error `EAI_FAMILY -> r EAI_FAMILY + | Error `EAI_MEMORY -> r EAI_MEMORY + | Error `EAI_NODATA -> r EAI_NODATA + | Error `EAI_NONAME -> r EAI_NONAME + | Error `EAI_OVERFLOW -> r EAI_FAIL (* note *) + | Error `EAI_PROTOCOL -> r EAI_FAIL (* note *) + | Error `EAI_SERVICE -> r EAI_SERVICE + | Error `EAI_SOCKTYPE -> r EAI_SOCKTYPE | Error e -> raise (Luv_error e) end From b81b9a8153f55d1aa0d2ba37ec01b5ee722d90b3 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:10:28 +0100 Subject: [PATCH 05/20] Move getaddrinfo stubs into its own file due to copyrights --- lib_eio_linux/dune | 3 + lib_eio_linux/eio_stubs.c | 147 ----------------------- lib_eio_linux/getaddrinfo_stubs.c | 191 ++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 147 deletions(-) create mode 100644 lib_eio_linux/getaddrinfo_stubs.c diff --git a/lib_eio_linux/dune b/lib_eio_linux/dune index afdc13566..e43853e3e 100644 --- a/lib_eio_linux/dune +++ b/lib_eio_linux/dune @@ -6,4 +6,7 @@ (language c) (flags :standard -D_LARGEFILE64_SOURCE) (names eio_stubs)) + (foreign_stubs + (language c) + (names getaddrinfo_stubs)) (libraries eio eio.utils eio.unix uring logs fmt)) diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 5aa693258..547c3c565 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -101,150 +101,3 @@ CAMLprim value caml_eio_getdents(value v_fd) { CAMLreturn(result); } - -static value caml_unix_cst_to_constr(int n, int *tbl, int size, int deflt) -{ - int i; - for (i = 0; i < size; i++) - if (n == tbl[i]) return Val_int(i); - return Val_int(deflt); -} - -extern int caml_unix_socket_domain_table[]; /* from socket.c */ -extern int caml_unix_socket_type_table[]; /* from socket.c */ - -static value convert_addrinfo(struct addrinfo * a) -{ - CAMLparam0(); - CAMLlocal3(vres,vaddr,vcanonname); - union sock_addr_union sa; - socklen_param_type len; - - len = a->ai_addrlen; - if (len > sizeof(sa)) len = sizeof(sa); - memcpy(&sa.s_gen, a->ai_addr, len); - vaddr = caml_unix_alloc_sockaddr(&sa, len, -1); - vcanonname = caml_copy_string(a->ai_canonname == NULL ? "" : a->ai_canonname); - vres = caml_alloc_small(5, 0); - Field(vres, 0) = - caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); - Field(vres, 1) = - caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); - Field(vres, 2) = Val_int(a->ai_protocol); - Field(vres, 3) = vaddr; - Field(vres, 4) = vcanonname; - CAMLreturn(vres); -} - -/* glibc doesn't define a bunch of EAI_, so fake one since code gets copied around */ - -#ifndef EAI_ADDRFAMILY -#define EAI_ADDRFAMILY -3000 -#endif /* EAI_ADDRFAMILY */ - -#ifndef EAI_BADHINTS -#define EAI_BADHINTS -3013 -#endif /* EAI_BADHINTS */ - -#ifndef EAI_NODATA -#define EAI_NODATA -3007 -#endif /* EAI_NODATA */ - -static int gai_errors[] = { - EAI_ADDRFAMILY, - EAI_AGAIN, - EAI_BADFLAGS, - EAI_BADHINTS, - EAI_FAIL, - EAI_FAMILY, - EAI_MEMORY, - EAI_NODATA, - EAI_NONAME, - EAI_SERVICE, - EAI_SOCKTYPE, - EAI_SYSTEM -}; - -CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) -{ - CAMLparam3(vnode, vserv, vopts); - CAMLlocal3(vres, v, vret); - char * node, * serv; - struct addrinfo hints; - struct addrinfo * res, * r; - int retcode, i; - - if (! (caml_string_is_c_safe(vnode) && caml_string_is_c_safe(vserv))) - CAMLreturn (Val_emptylist); - - /* Extract "node" parameter */ - if (caml_string_length(vnode) == 0) { - node = NULL; - } else { - node = caml_stat_strdup(String_val(vnode)); - } - /* Extract "service" parameter */ - if (caml_string_length(vserv) == 0) { - serv = NULL; - } else { - serv = caml_stat_strdup(String_val(vserv)); - } - /* Parse options, set hints */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - for (/*nothing*/; vopts != Val_emptylist; vopts = Field(vopts, 1)) { - v = Field(vopts, 0); - if (Is_block(v)) - switch (Tag_val(v)) { - case 0: /* AI_FAMILY of socket_domain */ - hints.ai_family = caml_unix_socket_domain_table[Int_val(Field(v, 0))]; - break; - case 1: /* AI_SOCKTYPE of socket_type */ - hints.ai_socktype = caml_unix_socket_type_table[Int_val(Field(v, 0))]; - break; - case 2: /* AI_PROTOCOL of int */ - hints.ai_protocol = Int_val(Field(v, 0)); - break; - } - else - switch (Int_val(v)) { - case 0: /* AI_NUMERICHOST */ - hints.ai_flags |= AI_NUMERICHOST; break; - case 1: /* AI_CANONNAME */ - hints.ai_flags |= AI_CANONNAME; break; - case 2: /* AI_PASSIVE */ - hints.ai_flags |= AI_PASSIVE; break; - } - } - /* Do the call */ - caml_enter_blocking_section(); - retcode = getaddrinfo(node, serv, &hints, &res); - caml_leave_blocking_section(); - if (node != NULL) caml_stat_free(node); - if (serv != NULL) caml_stat_free(serv); - /* Convert result */ - vres = Val_emptylist; - if (retcode == 0) { - for (r = res; r != NULL; r = r->ai_next) { - v = caml_alloc_small(2, Tag_cons); - Field(v, 0) = convert_addrinfo(r); - Field(v, 1) = vres; - vres = v; - } - vret = caml_alloc_small(1, 0); /* 0 = Ok */ - Field(vret, 0) = vres; - freeaddrinfo(res); - } else { - for (i = 0; i < (sizeof(gai_errors) / sizeof(int)); i++) - if (gai_errors[i] == retcode) - break; - if (i == (sizeof(gai_errors) / sizeof(int))) { - errno = EINVAL; - uerror("invalid gai_error", Nothing); - } - vret = caml_alloc_small(1, 1); /* 1 = Error */ - Field(vret, 0) = Val_int(i); - } - - CAMLreturn(vret); -} diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c new file mode 100644 index 000000000..4d1b55e84 --- /dev/null +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* OCaml */ +/* */ +/* Xavier Leroy, projet Cristal, INRIA Rocquencourt, */ +/* Christiano Haesbaert, Tarides */ +/* Copyright 2004 Institut National de Recherche en Informatique et */ +/* en Automatique. */ +/* Copyright 2022 Tarides */ +/* */ +/* All rights reserved. This file is distributed under the terms of */ +/* the GNU Lesser General Public License version 2.1, with the */ +/* special exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static value caml_unix_cst_to_constr(int n, int *tbl, int size, int deflt) +{ + int i; + for (i = 0; i < size; i++) + if (n == tbl[i]) return Val_int(i); + return Val_int(deflt); +} + +extern int caml_unix_socket_domain_table[]; /* from socket.c */ +extern int caml_unix_socket_type_table[]; /* from socket.c */ + +static value convert_addrinfo(struct addrinfo * a) +{ + CAMLparam0(); + CAMLlocal3(vres,vaddr,vcanonname); + union sock_addr_union sa; + socklen_param_type len; + + len = a->ai_addrlen; + if (len > sizeof(sa)) len = sizeof(sa); + memcpy(&sa.s_gen, a->ai_addr, len); + vaddr = caml_unix_alloc_sockaddr(&sa, len, -1); + vcanonname = caml_copy_string(a->ai_canonname == NULL ? "" : a->ai_canonname); + vres = caml_alloc_small(5, 0); + Field(vres, 0) = + caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); + Field(vres, 1) = + caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); + Field(vres, 2) = Val_int(a->ai_protocol); + Field(vres, 3) = vaddr; + Field(vres, 4) = vcanonname; + CAMLreturn(vres); +} + +/* glibc doesn't define a bunch of EAI_, so fake one since code gets copied around */ + +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY -3000 +#endif /* EAI_ADDRFAMILY */ + +#ifndef EAI_BADHINTS +#define EAI_BADHINTS -3013 +#endif /* EAI_BADHINTS */ + +#ifndef EAI_NODATA +#define EAI_NODATA -3007 +#endif /* EAI_NODATA */ + +static int gai_errors[] = { + EAI_ADDRFAMILY, + EAI_AGAIN, + EAI_BADFLAGS, + EAI_BADHINTS, + EAI_FAIL, + EAI_FAMILY, + EAI_MEMORY, + EAI_NODATA, + EAI_NONAME, + EAI_SERVICE, + EAI_SOCKTYPE, + EAI_SYSTEM +}; + +#define nmemb_gai_errors (sizeof(gai_errors) / sizeof(int)) +#if 0 +CAMLprim value caml_eio_gai_strerror(value verr) +{ + CAMLparam1(verr); + int err = Int_val(verr); + const char *s; + + if (err < 0 || err >= nmemb_gai_errors) + s = "Invalid getaddrinfo error code"; + else + s = gai_strerror(gai_errors[err]); + + CAMLreturn(caml_copy_string(s)); +} +#endif + +CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) +{ + CAMLparam3(vnode, vserv, vopts); + CAMLlocal3(vres, v, vret); + char * node, * serv; + struct addrinfo hints; + struct addrinfo * res, * r; + int retcode, i; + + if (! (caml_string_is_c_safe(vnode) && caml_string_is_c_safe(vserv))) + CAMLreturn (Val_emptylist); + + /* Extract "node" parameter */ + if (caml_string_length(vnode) == 0) { + node = NULL; + } else { + node = caml_stat_strdup(String_val(vnode)); + } + /* Extract "service" parameter */ + if (caml_string_length(vserv) == 0) { + serv = NULL; + } else { + serv = caml_stat_strdup(String_val(vserv)); + } + /* Parse options, set hints */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + for (/*nothing*/; vopts != Val_emptylist; vopts = Field(vopts, 1)) { + v = Field(vopts, 0); + if (Is_block(v)) + switch (Tag_val(v)) { + case 0: /* AI_FAMILY of socket_domain */ + hints.ai_family = caml_unix_socket_domain_table[Int_val(Field(v, 0))]; + break; + case 1: /* AI_SOCKTYPE of socket_type */ + hints.ai_socktype = caml_unix_socket_type_table[Int_val(Field(v, 0))]; + break; + case 2: /* AI_PROTOCOL of int */ + hints.ai_protocol = Int_val(Field(v, 0)); + break; + } + else + switch (Int_val(v)) { + case 0: /* AI_NUMERICHOST */ + hints.ai_flags |= AI_NUMERICHOST; break; + case 1: /* AI_CANONNAME */ + hints.ai_flags |= AI_CANONNAME; break; + case 2: /* AI_PASSIVE */ + hints.ai_flags |= AI_PASSIVE; break; + } + } + /* Do the call */ + caml_enter_blocking_section(); + retcode = getaddrinfo(node, serv, &hints, &res); + caml_leave_blocking_section(); + if (node != NULL) caml_stat_free(node); + if (serv != NULL) caml_stat_free(serv); + /* Convert result */ + vres = Val_emptylist; + if (retcode == 0) { + for (r = res; r != NULL; r = r->ai_next) { + v = caml_alloc_small(2, Tag_cons); + Field(v, 0) = convert_addrinfo(r); + Field(v, 1) = vres; + vres = v; + } + vret = caml_alloc_small(1, 0); /* 0 = Ok */ + Field(vret, 0) = vres; + freeaddrinfo(res); + } else { + for (i = 0; i < nmemb_gai_errors; i++) + if (gai_errors[i] == retcode) + break; + if (i == nmemb_gai_errors) { + errno = EINVAL; + uerror("invalid gai_error", Nothing); + } + vret = caml_alloc_small(1, 1); /* 1 = Error */ + Field(vret, 0) = Val_int(i); + } + + CAMLreturn(vret); +} From 864a7426857a62bb4a5c2711292532fe3f0e0f5d Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:11:45 +0100 Subject: [PATCH 06/20] zap gai_strerror bindings, copy strings from libc --- lib_eio_linux/getaddrinfo_stubs.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index 4d1b55e84..9c959fee3 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -90,21 +90,6 @@ static int gai_errors[] = { }; #define nmemb_gai_errors (sizeof(gai_errors) / sizeof(int)) -#if 0 -CAMLprim value caml_eio_gai_strerror(value verr) -{ - CAMLparam1(verr); - int err = Int_val(verr); - const char *s; - - if (err < 0 || err >= nmemb_gai_errors) - s = "Invalid getaddrinfo error code"; - else - s = gai_strerror(gai_errors[err]); - - CAMLreturn(caml_copy_string(s)); -} -#endif CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) { From e93f944366d8a09bb7233f41b15931516f7ce890 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:12:17 +0100 Subject: [PATCH 07/20] Add Net.getaddrinfo_to_string --- lib_eio/net.ml | 15 +++++++++++++++ lib_eio/net.mli | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index 2c9d1c044..b3b13d1de 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -215,6 +215,21 @@ type getaddrinfo_error = exception Getaddrinfo_error of getaddrinfo_error +let getaddrinfo_error_to_string = function + | EAI_ADDRFAMILY -> "address family for name not supported" + | EAI_AGAIN -> "temporary failure in name resolution" + | EAI_BADFLAGS -> "invalid value for ai_flags" + | EAI_BADHINTS -> "invalid value for hints" + | EAI_FAIL -> "non-recoverable failure in name resolution" + | EAI_FAMILY -> "ai_family not supported" + | EAI_MEMORY -> "memory allocation failure" + | EAI_NODATA -> "no address associated with name" + | EAI_NONAME -> "name or service is not known" + (* XXX Add PROTOCOL *) + | EAI_SERVICE -> "service not supported for ai_socktype" + | EAI_SOCKTYPE -> "ai_socktype not supported" + | EAI_SYSTEM -> "system error" + let getaddrinfo ?(service="") (t:#t) hostname = t#getaddrinfo ~service hostname let getaddrinfo_stream ?service t hostname = diff --git a/lib_eio/net.mli b/lib_eio/net.mli index ae36049be..b1b6c47b8 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -220,7 +220,7 @@ 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} *) +(** {2 Getaddrinfo queries} *) (* keep in sync with C stubs *) type getaddrinfo_error = @@ -236,9 +236,14 @@ type getaddrinfo_error = | EAI_SERVICE | EAI_SOCKTYPE | EAI_SYSTEM + (** Possible errors raised by getaddrinfo functions, check + getaddrinfo(3) and gai_strerror(3) for more information. *) exception Getaddrinfo_error of getaddrinfo_error +val getaddrinfo_error_to_string : getaddrinfo_error -> string +(** [getaddrinfo_error_to_string e] returns a string representation of [e], like gai_strerror(3). *) + 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. From 6029e394d971ac7c7fe30ac4b6135ddab873f5e1 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:17:09 +0100 Subject: [PATCH 08/20] Add EAI_PROTOCOL and don't be sloppy with defines --- lib_eio/net.ml | 2 +- lib_eio/net.mli | 1 + lib_eio_linux/getaddrinfo_stubs.c | 11 ++++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index b3b13d1de..35bded66c 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -225,7 +225,7 @@ let getaddrinfo_error_to_string = function | EAI_MEMORY -> "memory allocation failure" | EAI_NODATA -> "no address associated with name" | EAI_NONAME -> "name or service is not known" - (* XXX Add PROTOCOL *) + | EAI_PROTOCOL -> "resolved protocol is unknown" | EAI_SERVICE -> "service not supported for ai_socktype" | EAI_SOCKTYPE -> "ai_socktype not supported" | EAI_SYSTEM -> "system error" diff --git a/lib_eio/net.mli b/lib_eio/net.mli index b1b6c47b8..9bfedd259 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -233,6 +233,7 @@ type getaddrinfo_error = | EAI_MEMORY | EAI_NODATA | EAI_NONAME + | EAI_PROTOCOL | EAI_SERVICE | EAI_SOCKTYPE | EAI_SYSTEM diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index 9c959fee3..e86052f9d 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -63,17 +63,21 @@ static value convert_addrinfo(struct addrinfo * a) /* glibc doesn't define a bunch of EAI_, so fake one since code gets copied around */ #ifndef EAI_ADDRFAMILY -#define EAI_ADDRFAMILY -3000 +#define EAI_ADDRFAMILY (-3000) #endif /* EAI_ADDRFAMILY */ #ifndef EAI_BADHINTS -#define EAI_BADHINTS -3013 +#define EAI_BADHINTS (-3013) #endif /* EAI_BADHINTS */ #ifndef EAI_NODATA -#define EAI_NODATA -3007 +#define EAI_NODATA (-3007) #endif /* EAI_NODATA */ +#ifndef EAI_PROTOCOL +#define EAI_PROTOCOL (-3014) +#endif /* EAI_PROTOCOL */ + static int gai_errors[] = { EAI_ADDRFAMILY, EAI_AGAIN, @@ -84,6 +88,7 @@ static int gai_errors[] = { EAI_MEMORY, EAI_NODATA, EAI_NONAME, + EAI_PROTOCOL, EAI_SERVICE, EAI_SOCKTYPE, EAI_SYSTEM From 2ada9c7ab0563d4a28db981db3520f130c8da793 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:21:00 +0100 Subject: [PATCH 09/20] restore eio_stubs properly now that we moved getaddrinfo --- lib_eio_linux/eio_stubs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 547c3c565..4d04e4875 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -9,7 +8,7 @@ #include #include #include -#include + #include #include @@ -17,7 +16,6 @@ #include #include #include -#include // Make sure we have enough space for at least one entry. #define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64)) From 450514801f995175eebc1b11289cbcd60fd925ea Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:21:45 +0100 Subject: [PATCH 10/20] whitespace --- lib_eio_linux/eio_linux.mli | 1 - 1 file changed, 1 deletion(-) diff --git a/lib_eio_linux/eio_linux.mli b/lib_eio_linux/eio_linux.mli index 7f40f0567..a3bfd7424 100644 --- a/lib_eio_linux/eio_linux.mli +++ b/lib_eio_linux/eio_linux.mli @@ -254,5 +254,4 @@ module Low_level : sig val eio_getaddrinfo : string -> string -> Unix.getaddrinfo_option list -> (Unix.addr_info list, Eio.Net.getaddrinfo_error) result - end From 17bc361f104779fb6d49e18b35bf0df9df64f101 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:30:53 +0100 Subject: [PATCH 11/20] EAI_PROTOCOL WHOOPS --- lib_eio/net.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index 35bded66c..fe7372291 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -209,6 +209,7 @@ type getaddrinfo_error = | EAI_MEMORY | EAI_NODATA | EAI_NONAME + | EAI_PROTOCOL | EAI_SERVICE | EAI_SOCKTYPE | EAI_SYSTEM From 82812a198a434029ebbfc7fdc2a97610c4679b7f Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 09:33:19 +0100 Subject: [PATCH 12/20] Add EAI_NONAME tests --- tests/network.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/network.md b/tests/network.md index b2860026e..1abc9b035 100644 --- a/tests/network.md +++ b/tests/network.md @@ -739,6 +739,16 @@ Both attempts time out: Exception: Eio__Net.Connection_failure Eio__Time.Timeout. ``` +## getaddrinfo + +getaddrinfo raises instead of returning an empty list: + +```ocaml +# Eio_main.run @@ fun env -> + Eio.Net.getaddrinfo env#net "el.dud.er.in.no";; +Exception: Eio__Net.Getaddrinfo_error Eio__Net.EAI_NONAME. +``` + ## read/write on SOCK_DGRAM TODO: This is wrong; see https://github.com/ocaml-multicore/eio/issues/342 From 5a2a401b48d86b2ab0b2609c44265a1961496278 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 10:08:06 +0100 Subject: [PATCH 13/20] EAI_OVERFLOW --- lib_eio/net.ml | 2 ++ lib_eio/net.mli | 1 + lib_eio_linux/getaddrinfo_stubs.c | 5 +++++ lib_eio_luv/eio_luv.ml | 4 ++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index fe7372291..81423570c 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -209,6 +209,7 @@ type getaddrinfo_error = | EAI_MEMORY | EAI_NODATA | EAI_NONAME + | EAI_OVERFLOW | EAI_PROTOCOL | EAI_SERVICE | EAI_SOCKTYPE @@ -226,6 +227,7 @@ let getaddrinfo_error_to_string = function | EAI_MEMORY -> "memory allocation failure" | EAI_NODATA -> "no address associated with name" | EAI_NONAME -> "name or service is not known" + | EAI_OVERFLOW -> "argument buffer overflow" | EAI_PROTOCOL -> "resolved protocol is unknown" | EAI_SERVICE -> "service not supported for ai_socktype" | EAI_SOCKTYPE -> "ai_socktype not supported" diff --git a/lib_eio/net.mli b/lib_eio/net.mli index 9bfedd259..4a5458253 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -233,6 +233,7 @@ type getaddrinfo_error = | EAI_MEMORY | EAI_NODATA | EAI_NONAME + | EAI_OVERFLOW | EAI_PROTOCOL | EAI_SERVICE | EAI_SOCKTYPE diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index e86052f9d..046e77c23 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -74,6 +74,10 @@ static value convert_addrinfo(struct addrinfo * a) #define EAI_NODATA (-3007) #endif /* EAI_NODATA */ +#ifndef EAI_OVERFLOW +#define EAI_OVERFLOW (-3009) +#endif /* EAI_OVERFLOW */ + #ifndef EAI_PROTOCOL #define EAI_PROTOCOL (-3014) #endif /* EAI_PROTOCOL */ @@ -88,6 +92,7 @@ static int gai_errors[] = { EAI_MEMORY, EAI_NODATA, EAI_NONAME, + EAI_OVERFLOW, EAI_PROTOCOL, EAI_SERVICE, EAI_SOCKTYPE, diff --git a/lib_eio_luv/eio_luv.ml b/lib_eio_luv/eio_luv.ml index 1d9abf7c5..3b3f3621e 100644 --- a/lib_eio_luv/eio_luv.ml +++ b/lib_eio_luv/eio_luv.ml @@ -669,8 +669,8 @@ module Low_level = struct | Error `EAI_MEMORY -> r EAI_MEMORY | Error `EAI_NODATA -> r EAI_NODATA | Error `EAI_NONAME -> r EAI_NONAME - | Error `EAI_OVERFLOW -> r EAI_FAIL (* note *) - | Error `EAI_PROTOCOL -> r EAI_FAIL (* note *) + | Error `EAI_OVERFLOW -> r EAI_OVERFLOW + | Error `EAI_PROTOCOL -> r EAI_PROTOCOL | Error `EAI_SERVICE -> r EAI_SERVICE | Error `EAI_SOCKTYPE -> r EAI_SOCKTYPE | Error e -> raise (Luv_error e) From 4e08267eacbbfe95907ec000484b86be0c53b7bc Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 10:17:00 +0100 Subject: [PATCH 14/20] Document semantics in net.mli --- lib_eio/net.mli | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib_eio/net.mli b/lib_eio/net.mli index 4a5458253..b8c7d46e7 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -220,7 +220,11 @@ 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 Getaddrinfo queries} *) +(** {2 Getaddrinfo queries} + + Note that unlike {!Unix.getaddrinfo}, EIO's [getaddrinfo] family + of functions raise an exception {!Getaddrinfo_error} with an error + code instead of returning an empty list. *) (* keep in sync with C stubs *) type getaddrinfo_error = @@ -249,6 +253,7 @@ val getaddrinfo_error_to_string : getaddrinfo_error -> string 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. + May raise {!Getaddrinfo_error} and never returns an empty list. @param service is a human friendly textual name for internet services assigned by IANA., eg. 'http', 'https', 'ftp', etc. @@ -256,10 +261,12 @@ val getaddrinfo: ?service:string -> #t -> string -> Sockaddr.t list For a more thorough treatment, see {{:https://man7.org/linux/man-pages/man3/getaddrinfo.3.html} getaddrinfo}. *) val getaddrinfo_stream: ?service:string -> #t -> string -> Sockaddr.stream list -(** [getaddrinfo_stream] is like {!getaddrinfo}, but filters out non-stream protocols. *) +(** [getaddrinfo_stream] is like {!getaddrinfo}, but filters out non-stream protocols. + May raise {!Getaddrinfo_error} and never returns an empty list. *) val getaddrinfo_datagram: ?service:string -> #t -> string -> Sockaddr.datagram list -(** [getaddrinfo_datagram] is like {!getaddrinfo}, but filters out non-datagram protocols. *) +(** [getaddrinfo_datagram] is like {!getaddrinfo}, but filters out non-datagram protocols. + May raise {!Getaddrinfo_error} and never returns an empty list. *) val getnameinfo : #t -> Sockaddr.t -> (string * string) (** [getnameinfo t sockaddr] is [(hostname, service)] corresponding to [sockaddr]. [hostname] is the From 8b135917d46d32381c2bca4521e906fe7e03158b Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 10:23:26 +0100 Subject: [PATCH 15/20] Better paranoia --- lib_eio_linux/getaddrinfo_stubs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index 046e77c23..e059674ae 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -96,7 +96,7 @@ static int gai_errors[] = { EAI_PROTOCOL, EAI_SERVICE, EAI_SOCKTYPE, - EAI_SYSTEM + EAI_SYSTEM /* NOTE: must be last */ }; #define nmemb_gai_errors (sizeof(gai_errors) / sizeof(int)) @@ -174,9 +174,10 @@ CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) for (i = 0; i < nmemb_gai_errors; i++) if (gai_errors[i] == retcode) break; + /* Paranoia keeps the world spinning */ if (i == nmemb_gai_errors) { errno = EINVAL; - uerror("invalid gai_error", Nothing); + i = gai_errors[nmemb_gai_errors - 1]; /* EAI_SYSTEM */ } vret = caml_alloc_small(1, 1); /* 1 = Error */ Field(vret, 0) = Val_int(i); From bc2a62cdad5076326bd9bfdbbd474d8e1673bb24 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 10:41:15 +0100 Subject: [PATCH 16/20] We can't really guarantee the error code since it depends on the environment. --- tests/network.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/network.md b/tests/network.md index 1abc9b035..acde51870 100644 --- a/tests/network.md +++ b/tests/network.md @@ -745,8 +745,11 @@ getaddrinfo raises instead of returning an empty list: ```ocaml # Eio_main.run @@ fun env -> - Eio.Net.getaddrinfo env#net "el.dud.er.in.no";; -Exception: Eio__Net.Getaddrinfo_error Eio__Net.EAI_NONAME. + try + Eio.Net.getaddrinfo env#net "el.dud.er.in.no" |> ignore; + traceln "getaddrinfo did not raise as expected" + with Eio.Net.Getaddrinfo_error _ -> ();; +- : unit = () ``` ## read/write on SOCK_DGRAM From 928617d80bab3511b53f78f69c7f145811d4ba75 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 11:04:25 +0100 Subject: [PATCH 17/20] fix tests --- tests/network.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/network.md b/tests/network.md index acde51870..8d2d15357 100644 --- a/tests/network.md +++ b/tests/network.md @@ -545,6 +545,12 @@ Connection refused: ## Getaddrinfo +```ocaml +let guard_getaddrinfo f = + try f () + with Eio.Net.Getaddrinfo_error _ -> []; +``` + ```ocaml # Eio_main.run @@ fun env -> Eio.Net.getaddrinfo_stream env#net "127.0.0.1";; @@ -572,6 +578,7 @@ Connection refused: ```ocaml # Eio_main.run @@ fun env -> + guard_getaddrinfo @@ fun () -> 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)] @@ -580,6 +587,7 @@ Connection refused: ```ocaml # Eio_main.run @@ fun env -> + guard_getaddrinfo @@ fun () -> 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)] @@ -588,6 +596,7 @@ Connection refused: ```ocaml # Eio_main.run @@ fun env -> + guard_getaddrinfo @@ fun () -> Eio.Net.getaddrinfo ~service:"https" env#net "google.com";; - : Eio.Net.Sockaddr.t list = [`Tcp ("Ø:ÔÎ", 443); `Udp ("Ø:ÔÎ", 443); @@ -595,6 +604,17 @@ Connection refused: `Udp ("*\000\020P@\t\b \000\000\000\000\000\000 \014", 443)] ``` +getaddrinfo raises instead of returning an empty list: + +```ocaml +# Eio_main.run @@ fun env -> + try + Eio.Net.getaddrinfo env#net "el.dud.er.in.no" |> ignore; + traceln "getaddrinfo did not raise as expected" + with Eio.Net.Getaddrinfo_error _ -> ();; +- : unit = () +``` + ## getnameinfo ```ocaml From 61b737d5be683b9be611844b16c7e73b5bece09a Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 15:37:48 +0100 Subject: [PATCH 18/20] zap duplicate --- tests/network.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/network.md b/tests/network.md index 8d2d15357..5694cb82d 100644 --- a/tests/network.md +++ b/tests/network.md @@ -759,19 +759,6 @@ Both attempts time out: Exception: Eio__Net.Connection_failure Eio__Time.Timeout. ``` -## getaddrinfo - -getaddrinfo raises instead of returning an empty list: - -```ocaml -# Eio_main.run @@ fun env -> - try - Eio.Net.getaddrinfo env#net "el.dud.er.in.no" |> ignore; - traceln "getaddrinfo did not raise as expected" - with Eio.Net.Getaddrinfo_error _ -> ();; -- : unit = () -``` - ## read/write on SOCK_DGRAM TODO: This is wrong; see https://github.com/ocaml-multicore/eio/issues/342 From 3cb06474691d1ecf545968932e9dc1b3ee6f567e Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 18:35:13 +0100 Subject: [PATCH 19/20] Just extern the declaration so we don't need to copy cst_to_constr. Many thanks to dra27 ! Somehow this function was static on first place in my head. --- lib_eio_linux/getaddrinfo_stubs.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index e059674ae..c3c38ed72 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -26,14 +26,7 @@ #include #include -static value caml_unix_cst_to_constr(int n, int *tbl, int size, int deflt) -{ - int i; - for (i = 0; i < size; i++) - if (n == tbl[i]) return Val_int(i); - return Val_int(deflt); -} - +extern value caml_unix_cst_to_constr(int n, int * tbl, int size, int deflt); extern int caml_unix_socket_domain_table[]; /* from socket.c */ extern int caml_unix_socket_type_table[]; /* from socket.c */ From 83bda023f36aadca97dc57d211b9ed597918bad9 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 1 Dec 2022 18:48:55 +0100 Subject: [PATCH 20/20] Address dra27 point #2, many thanks again ! When I removed the `e` and I violated GC #rule 5. Basically a small block must be fully initialized before we trigger the next allocation, TIL. --- lib_eio_linux/getaddrinfo_stubs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib_eio_linux/getaddrinfo_stubs.c b/lib_eio_linux/getaddrinfo_stubs.c index c3c38ed72..3cbed9f12 100644 --- a/lib_eio_linux/getaddrinfo_stubs.c +++ b/lib_eio_linux/getaddrinfo_stubs.c @@ -26,7 +26,7 @@ #include #include -extern value caml_unix_cst_to_constr(int n, int * tbl, int size, int deflt); +extern value caml_unix_cst_to_constr(int, int *, int, int); extern int caml_unix_socket_domain_table[]; /* from socket.c */ extern int caml_unix_socket_type_table[]; /* from socket.c */ @@ -44,9 +44,9 @@ static value convert_addrinfo(struct addrinfo * a) vcanonname = caml_copy_string(a->ai_canonname == NULL ? "" : a->ai_canonname); vres = caml_alloc_small(5, 0); Field(vres, 0) = - caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); + caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); Field(vres, 1) = - caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); + caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); Field(vres, 2) = Val_int(a->ai_protocol); Field(vres, 3) = vaddr; Field(vres, 4) = vcanonname; @@ -97,7 +97,7 @@ static int gai_errors[] = { CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) { CAMLparam3(vnode, vserv, vopts); - CAMLlocal3(vres, v, vret); + CAMLlocal4(vres, v, e, vret); char * node, * serv; struct addrinfo hints; struct addrinfo * res, * r; @@ -155,8 +155,9 @@ CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) vres = Val_emptylist; if (retcode == 0) { for (r = res; r != NULL; r = r->ai_next) { + e = convert_addrinfo(r); v = caml_alloc_small(2, Tag_cons); - Field(v, 0) = convert_addrinfo(r); + Field(v, 0) = e; Field(v, 1) = vres; vres = v; }