How to get the actual sockaddr bound by `Unix.(bind (INET_ADDR (inet_addr_any, 0)))`?

lwt
unix

#1

How to get the actual sockaddr bound by Unix.(bind (INET_ADDR (inet_addr_any, 0))) ?

Or Lwt_io.establish_server?

I want to let system to choose a unused port, and get the port number to tell the client to connect.


#2

Do you mean you want to use Unix.listen?

If you bind, you have to specify the port as far as I know.

Take a look at http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch04lev1sec4.html


#3

I found it: Unix.getsockname


#4

With Lwt_io.establish_server, I believe you currently have to create your own socket, and pass it in with Lwt_io.establish_server ~fd:my_fd .... You can then call Unix.getsockname my_fd.


#5

Any chance you’ve got an example of that working @hackwaly?

I’ve found that if I setup a port and call Lwt_unix.getsockname before it’s bound I’ll get port 0.
If you bind the port it’ll work but passing it to Lwt_io.establish_server_with_client_address will result in an EINVAL error when it tries to bind an already bound port.

This doesn’t work for me:

  (* Create a new socket *)
  let default_protocol = 0 in
  let socket = Lwt_unix.socket PF_INET SOCK_STREAM default_protocol in
  let _ = Lwt_unix.setsockopt socket SO_REUSEADDR true in

  (* Create a socket address *)
  let port = 0 in
  let host = Unix.inet_addr_loopback in
  let addr = Unix.ADDR_INET (host, port) in

  (* Determine port *)
  let sockaddr = Lwt_unix.getsockname socket in
  let bound_port = match sockaddr with
    | Unix.ADDR_INET (_, port) -> port
    | Unix.ADDR_UNIX _ -> assert false; in
  let%lwt _ = Lwt_io.eprintf "Port %d\n%!" bound_port in
  (* > Port 0 *)


#6

@seanpoulter, in the code you have included, you don’t ever call bind, which is the function that associates an address with a socket. One way to see that this can’t work, is that there is no call, direct or indirect, to which both socket and addr are ever both passed. In fact, in the code you have included, addr is an unused variable.

As for establish_server and its variants, you shouldn’t call bind yourself, but you should let establish_server do that. The purpose of passing in a socket for establish_server to use isn’t to call bind on it yourself, but to have a reference to the server socket, so you can call getsockname on it (or whatever you’d like to do), after establish_server has initialized it.


#7

The establish_server interface is arguably confusing. Perhaps it would be better if there was a function

server_socket : Lwt_io.server -> Lwt_unix.file_descr

for getting the socket after initialization, which is natural to think about, because you only get access to an Lwt_io.server after the socket initialization is complete, and bind has already been called.


#8

Thanks for the help again @antron ! I didn’t think to check for the bound port after calling establish_server. :man_facepalming:

Here’s the snipped for context that’s starting a loopback server on any available port:

let establish_server f =
  (* Create a new socket *)
  let default_protocol = 0 in
  let socket = Lwt_unix.socket PF_INET SOCK_STREAM default_protocol in
  let _ = Lwt_unix.setsockopt socket SO_REUSEADDR true in

  (* Create a socket address *)
  let host = Unix.inet_addr_loopback in
  let port = 0 in
  let addr = Unix.ADDR_INET (host, port) in

  (* Start server *)
  let max_pending_requests = 0 in
  let%lwt _ = Lwt_io.establish_server_with_client_address
    ~backlog:max_pending_requests ~fd:socket addr f in

  (* Determine bound port *)
  let sockaddr = Lwt_unix.getsockname socket in
  let bound_port = match sockaddr with
    | Unix.ADDR_INET (_, port) -> port
    | Unix.ADDR_UNIX _ -> assert false; in
  let%lwt _ = Lwt_io.eprintf "Port %d\n%!" bound_port; in
  Lwt.return ()

It works! :tada: