From 9c1345f44ac346547e381718a441940960bad6d5 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sat, 10 Nov 2018 22:37:02 +0000 Subject: [PATCH] IDirectPlay8Peer: Randomly select dynamic ports in Connect() method. It turns out disabling SO_LINGER on Windows doesn't /really/ make the socket go away by the time closesocket() returns, just very shortly afterwards, so rapidly destroying and re-creating connections on a slow machine can fail due to a connect() address conflict. --- src/DirectPlay8Peer.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/DirectPlay8Peer.cpp b/src/DirectPlay8Peer.cpp index 466271b..6739cde 100644 --- a/src/DirectPlay8Peer.cpp +++ b/src/DirectPlay8Peer.cpp @@ -482,17 +482,32 @@ HRESULT DirectPlay8Peer::Connect(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, I if(l_port == 0) { + /* Start at a random point in the ephemeral port range and try each one, wrapping + * around when we reach the end. + * + * Gets the "random" point by querying the performance counter rather than calling + * rand() just in case the application relies on the RNG state. + */ + + LARGE_INTEGER p_counter; + QueryPerformanceCounter(&p_counter); + + int port_range = AUTO_PORT_MAX - AUTO_PORT_MIN; + int base_port = p_counter.QuadPart % port_range; + for(int p = AUTO_PORT_MIN; p <= AUTO_PORT_MAX; ++p) { /* TODO: Only continue if creation failed due to address conflict. */ - udp_socket = create_udp_socket(l_ipaddr, p); + int port = AUTO_PORT_MIN + ((base_port + p) % (port_range + 1)); + + udp_socket = create_udp_socket(l_ipaddr, port); if(udp_socket == -1) { continue; } - listener_socket = create_listener_socket(l_ipaddr, p); + listener_socket = create_listener_socket(l_ipaddr, port); if(listener_socket == -1) { closesocket(udp_socket); @@ -502,7 +517,7 @@ HRESULT DirectPlay8Peer::Connect(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, I } local_ip = l_ipaddr; - local_port = p; + local_port = port; break; }