Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion unix/dune
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
(public_name cstruct-unix)
(foreign_stubs
(language c)
(names read_stubs write_stubs writev_stubs send_stubs recv_stubs))
(names read_stubs write_stubs writev_stubs send_stubs recv_stubs
recvfrom_stubs sendto_stubs))
(libraries cstruct unix))
2 changes: 1 addition & 1 deletion unix/recv_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ CAMLprim value stub_cstruct_recv(value val_fd, value val_c)
#ifdef WIN32
int win32err = 0;
if (Descr_kind_val(val_fd) != KIND_SOCKET)
unix_error(EINVAL, "recv", Nothing);
unix_error(EINVAL, "stub_cstruct_recv", Nothing);

SOCKET s = Socket_val(val_fd);

Expand Down
50 changes: 50 additions & 0 deletions unix/recvfrom_stubs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/custom.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/unixsupport.h>
#include <caml/bigarray.h>
#include <caml/threads.h>
#include <caml/socketaddr.h>

#include <sys/socket.h>

static int msg_flag_table[] = {
MSG_OOB, MSG_DONTROUTE, MSG_PEEK /* XXX */
};

CAMLprim value stub_cstruct_recvfrom(value val_fd, value val_c, value val_flags)
{
CAMLparam3(val_fd, val_c, val_flags);
CAMLlocal5(val_buf, val_ofs, val_len, val_addr, val_res);
uint8_t *buf;
size_t len;
ssize_t n;
int cv_flags;
union sock_addr_union addr;
socklen_param_type addr_len;

val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);
cv_flags = caml_convert_flag_list(val_flags, msg_flag_table);

buf = (uint8_t *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
len = Long_val(val_len);
addr_len = sizeof(addr);

caml_release_runtime_system();
n = recvfrom(Int_val(val_fd), buf, len, cv_flags, &addr.s_gen, &addr_len);
caml_acquire_runtime_system();

if (n == -1)
caml_uerror("recvfrom", Nothing);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
caml_uerror("recvfrom", Nothing);
caml_uerror("cstruct_recvfrom", Nothing);

I do find these Unix_errors are marginally more useful if you can grep for the function they came from


val_addr = caml_unix_alloc_sockaddr(&addr, addr_len, -1);
val_res = caml_alloc_small(2, 0);
Field(val_res, 0) = Val_int(n);
Field(val_res, 1) = val_addr;

CAMLreturn (val_res);
}
45 changes: 45 additions & 0 deletions unix/sendto_stubs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/custom.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/unixsupport.h>
#include <caml/bigarray.h>
#include <caml/threads.h>
#include <caml/socketaddr.h>

#include <sys/socket.h>

static int msg_flag_table[] = { /* XXX */
MSG_OOB, MSG_DONTROUTE, MSG_PEEK
};

CAMLprim value stub_cstruct_sendto(value val_fd, value val_c, value val_flags, value val_daddr)
{
CAMLparam4(val_fd, val_c, val_flags, val_daddr);
CAMLlocal5(val_buf, val_ofs, val_len, val_addr, val_res);
union sock_addr_union addr;
socklen_param_type addr_len;
uint8_t *buf;
size_t len;
ssize_t n;
int cv_flags;

val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);

buf = (uint8_t *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
len = Long_val(val_len);
caml_unix_get_sockaddr(val_daddr, &addr, &addr_len);
cv_flags = caml_convert_flag_list(val_flags, msg_flag_table);

caml_release_runtime_system();
n = sendto(Int_val(val_fd), buf, len, cv_flags, &addr.s_gen, addr_len);
caml_acquire_runtime_system();

if (n == -1)
caml_uerror("sendto", Nothing);

CAMLreturn (Val_int(n));
}
10 changes: 9 additions & 1 deletion unix/unix_cstruct.ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,12 @@ let recv fd x = stub_recv fd (x.Cstruct.buffer, x.Cstruct.off, x.Cstruct.len)

external stub_read: Unix.file_descr -> (buffer * int * int) -> int = "stub_cstruct_read"

let read fd x = stub_read fd (x.Cstruct.buffer, x.Cstruct.off, x.Cstruct.len)
let read fd x = stub_read fd (x.Cstruct.buffer, x.Cstruct.off, x.Cstruct.len)

external stub_recvfrom : Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> int * Unix.sockaddr = "stub_cstruct_recvfrom"

let recvfrom fd x fl = stub_recvfrom fd x fl

external stub_sendto : Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> Unix.sockaddr -> int = "stub_cstruct_sendto"

let sendto fd x fl a = stub_sendto fd x fl a
6 changes: 6 additions & 0 deletions unix/unix_cstruct.mli
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ val send: Unix.file_descr -> Cstruct.t -> int
val recv: Unix.file_descr -> Cstruct.t -> int
(** [recv fd c] receives a message from a socket. This can be used to receive a datagram.
If only a partial receive is possible, the return argument is now many bytes were received. *)

val recvfrom: Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> int * Unix.sockaddr
(** [recvfrom fd c] Like Unix.recvfrom, but for Cstruct. *)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(** [recvfrom fd c] Like Unix.recvfrom, but for Cstruct. *)
(** [recvfrom fd c] Like {! Unix.recvfrom}, but for Cstruct. *)

will link to it on the ocaml.org docs then.


val sendto: Unix.file_descr -> Cstruct.t -> Unix.msg_flag list -> Unix.sockaddr -> int
(** [sendto fd c a] Like Unix.sendto, but for Cstruct. *)
1 change: 1 addition & 0 deletions unix/writev_stubs.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#define _XOPEN_SOURCE /* IOV_MAX */
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/custom.h>
Expand Down
33 changes: 32 additions & 1 deletion unix_test/tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,45 @@ let test_send_recv () =
Thread.join t
)

let test_sendto_recvfrom () =
let test_message = Cstruct.concat test_message_list in
with_sock_dgram @@ fun s ->
with_sock_dgram @@ fun c ->
let sport = bind_random_port s in
let cport = bind_random_port c in (* So we can assert the sender on receiver port *)
let server () =
let buf = Cstruct.create 1024 in
let n, (addr:Unix.sockaddr) = Unix_cstruct.recvfrom s buf [] in
let addr, port = match addr with
| ADDR_INET (a, p) -> Unix.string_of_inet_addr a, p
| _ -> Alcotest.fail "Bad AF_FAMILY"
in
Alcotest.(check string) "recvfrom inetaddr" addr "127.0.0.1";
Alcotest.(check int) "recvfrom port" port cport;
Alcotest.(check int) "recvfrom length" (Cstruct.length test_message) n;
let expected = Cstruct.to_string test_message in
let actual = Cstruct.(to_string @@ sub buf 0 n) in
Alcotest.(check string) "read contents" expected actual
in
let client () =
let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, sport) in
let n = Unix_cstruct.sendto c test_message [] addr in
Alcotest.(check int) "sendto length" (Cstruct.length test_message) n;
in
client ();
server ()

let suite = [
"writev", [
"test read and writev via a file", `Quick, test_writev_file;
"test read and writev via a socket", `Quick, test_writev_socket;
];
"send recv", [
"test send and recv", `Quick, test_send_recv;
];
"sendto recvfrom", [
"test sendto and recvfrom", `Quick, test_sendto_recvfrom;
]
]

let () = Alcotest.run "cstruct.unix" suite
let () = Alcotest.run "cstruct.unix" suite