On Tue, 2017-02-21 at 09:55 -0500, Chuck Rolke wrote:
> 
> ----- Original Message -----
> > From: "Alan Conway" <[email protected]>
> > To: "dev" <[email protected]>
> > Sent: Monday, February 20, 2017 4:23:50 PM
> > Subject: Picking temporary ports for tests
> > 
> > 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]
> > 
> > 
> 
> Looks like you already have this code in the (proton) code base. 
> * Does it work?
> * Are there issues with using INADDR_LOOPBACK vs INADDR_ANY?
> * It looks like it opens a port on one sa_family only (IPv4 or IPv6).
>   I remember one of the problems with temp ports as the port being
>   available on IPv4 but not on IPv6. Getting them both open was
>   necessary. This may not be an issue for your use case.

It's working for some new tests I'm writing, it probably needs to be
fleshed out for more general use - your points are valid. I'll add some
IPv6 tests (I should have that anyway) and see how it breaks...

Cheers,
Alan.


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

Reply via email to