Our code bases have a motley collection of  attempts to pick safe ports
 for short-lived tests, most of which have race conditions that can
result in sporadic bogus "address in use" test failures.

Here's an approach that seems to work: Bind a socket with bind(0) and
SO_REUSEADDR - guaranteed by the OS to get you a port. Now leave that
socket open while you start your server on that port.  Our servers also
set SO_REUSEADDR for other reasons, this means the server can bind to
the same port even though we have a socket bound already. The test code
only binds, it doesn't listen, so the server is can listen as usual.
Now you can close the test socket and you're off to the races!

Am I only dreaming? Here's the code (proton/proton-c/src/test_tools.h)

/* Create a socket and bind(LOOPBACK:0) to get a free port.
   Use SO_REUSEADDR so other processes can bind and listen on this port.
   Close the returned fd when the other process is listening.
   Fail on error.
*/
static sock_t sock_bind0(void) {
  int sock =  socket(AF_INET, SOCK_STREAM, 0);
  TEST_ASSERT_ERRNO(sock >= 0, errno);
  int on = 1;
  TEST_ASSERT_ERRNO(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const 
char*)&on, sizeof(on)) == 0, errno);
  struct sockaddr_in addr = {0};
  addr.sin_family = AF_INET;    /* set the type of connection to TCP/IP */
  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  addr.sin_port = 0;            /* bind to port 0 */
  TEST_ASSERT_ERRNO(bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0, 
errno);
  return sock;
}

/* Get the real port number allocated to a socket created with sock_bind0 */
static int sock_port(sock_t sock) {
  struct sockaddr addr = {0};
  socklen_t len = sizeof(addr);
  TEST_ASSERT_ERRNO(getsockname(sock, &addr, &len) == 0, errno);
  int port = -1;
  switch (addr.sa_family) {
   case AF_INET: port = ((struct sockaddr_in*)&addr)->sin_port; break;
   case AF_INET6: port = ((struct sockaddr_in6*)&addr)->sin6_port; break;
   default: TEST_ASSERTF(false, "unknown protocol type %d\n", addr.sa_family); 
break;
  }
  return ntohs(port);
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to