ocaml-multicore / eio

Effects-based direct-style IO for multicore OCaml
Other
548 stars 66 forks source link

Add `listening_socket#listening_addr` #555

Closed mefyl closed 8 months ago

mefyl commented 1 year ago

This adds the ability to retrieve the address a socket is listening on. More than just a convenience, this is required to let the OS pick a random free port by passing 0 and then determine what port was actually picked.

The actual implementation returns None if the socket was closed, while the mock always returns None - I'm unsure whether this is an issue as I'm only just starting to dip my toes in eio.

patricoferris commented 1 year ago

Thanks! This looks sensible to me, I think the Eio_windows implementation would be the same as the rest using the Unix module.

As with other APIs I think a function wrapping the method call would be useful so people don't have to use # if they don't want to. This could then document the semantics of calling listening_addr.

avsm commented 1 year ago

Does the bind to 0 port trick work on macOS? It's not documented there, and I remember encountering issues with this some years ago when trying it for Docker for Desktop...

mefyl commented 1 year ago

Does the bind to 0 port trick work on macOS? It's not documented there, and I remember encountering issues with this some years ago when trying it for Docker for Desktop...

Looks like it does on some MacOS at least:

$ uname -a
Darwin mac1gitlabroutineco.local 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:19 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T8103 arm64
$ cat net.c 
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
  int sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock == -1)
    handle_error("socket");
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(0);
  if (bind(sock, (struct sockaddr *) &addr, sizeof addr) == -1)
    handle_error("bind");
  if (listen(sock, 5) == -1)
    handle_error("listen");
  socklen_t length = sizeof addr;
  if (getsockname(sock,(struct sockaddr *) &addr, &length) == -1) 
    handle_error("getsockname");
  printf("listening on %d\n", ntohs(addr.sin_port));
}
$ gcc net.c -o net
$ ./net
listening on 49176
$ ./net
listening on 49177
$ ./net
listening on 49178
$ ./net
listening on 49179
$ 

To be frank I always assumed it was part of POSIX, I'm surprised it's that sparsely documented. I've always used it as the standard way to listen on a random port, especially in test suites that were running on multiples architecture, including macs. But it looks indeed like it's not that clear cut.

mefyl commented 8 months ago

May I humbly ask for a status on this ? It's something I rely upon a lot, especially in test suites. I don't mind porting it to the current API if we're willing to accept the feature.

nojb commented 8 months ago

Hello, I recently needed this (see #668); note that this should also support datagram (udp) sockets to be complete.

talex5 commented 2 months ago

To be frank I always assumed it was part of POSIX, I'm surprised it's that sparsely documented.

Apparently the bind 0 behaviour has just been added: https://sortix.org/blog/posix-2024/