Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 58 additions & 19 deletions src/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ void CSocket::Init ( const quint16 iNewPortNumber,
UdpSocket6 = INVALID_SOCKET;
}

bIPv6Available = false; // re-init before opening sockets

struct sockaddr_in sa4;
socklen_t sa4len = sizeof ( sa4 );
memset ( &sa4, 0, sa4len );
Expand Down Expand Up @@ -290,8 +292,6 @@ void CSocket::Init ( const quint16 iNewPortNumber,
}
}

bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available

// set socket to non-blocking
#ifdef _WIN32
unsigned long mode = 1;
Expand All @@ -318,20 +318,27 @@ void CSocket::Init ( const quint16 iNewPortNumber,
vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF );

// initialize the listening socket
bool bSuccess;
bool bSuccess = false; // will become true if IPv4 bind succeeds

if ( bIsClient )
{
// for a client, it does not matter if the IPv4 and IPv6 sockets get bound
// to different local port numbers
if ( iPortNumber == 0 )
{
// if port number is 0, bind the client to a random available port
sa4.sin_port = sa6.sin6_port = htons ( 0 );

bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) == 0 );
bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) != -1 );

if ( UdpSocket6 != INVALID_SOCKET )
// only try to bind the IPv6 socket if IPv4 has succeeded and IPv6 socket is open
if ( bSuccess && UdpSocket6 != INVALID_SOCKET )
{
bSuccess = bSuccess && ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) == 0 );
if ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) != -1 )
{
// note that IPv6 is available
bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available
}
}
}
else
Expand All @@ -341,23 +348,38 @@ void CSocket::Init ( const quint16 iNewPortNumber,
// faulty router gets stuck and confused by a particular port (like
// the starting port). Might work around frustrating "cannot connect"
// problems (#568)
const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this was the first call to rand() after program startup, and rand() always starts from the same seed, this was not providing any randomness at all. The same start port was chosen on each invocation, so this wouldn't actually address #568 as claimed. Verified on Linux and Mac.

const quint32 startingPortNumber = static_cast<quint32> ( iPortNumber ) + QRandomGenerator::global()->bounded ( NUM_SOCKET_PORTS_TO_TRY );
Comment thread
ann0see marked this conversation as resolved.

quint16 iClientPortIncrement = 0;
bSuccess = false; // initialization for while loop

while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) )
for ( quint32 port = startingPortNumber; port < startingPortNumber + NUM_SOCKET_PORTS_TO_TRY; port++ )
{
sa4.sin_port = sa6.sin6_port = htons ( startingPortNumber + iClientPortIncrement );
// do not overflow 16-bit port number
if ( port > 65535U )
{
break;
}

bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) == 0 );
// bind IPv4 socket if not bound
if ( !bSuccess )
{
sa4.sin_port = htons ( port );
bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) != -1 );
}

if ( UdpSocket6 != INVALID_SOCKET )
// only try to bind the IPv6 socket if IPv4 has succeeded and IPv6 socket is open
if ( bSuccess && UdpSocket6 != INVALID_SOCKET && !bIPv6Available )
{
bSuccess = bSuccess && ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) == 0 );
sa6.sin6_port = htons ( port );
if ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) != -1 )
{
// note that IPv6 is available
bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available
}
}

iClientPortIncrement++;
if ( bSuccess && ( bIPv6Available || UdpSocket6 == INVALID_SOCKET ) )
{
break;
}
}
}
}
Expand All @@ -369,14 +391,31 @@ void CSocket::Init ( const quint16 iNewPortNumber,

sa4.sin_port = sa6.sin6_port = htons ( iPortNumber );

bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) == 0 );
bSuccess = ( ::bind ( UdpSocket4, (struct sockaddr*) &sa4, sa4len ) != -1 );

if ( UdpSocket6 != INVALID_SOCKET )
// only try to bind the IPv6 socket if IPv4 has succeeded and IPv6 socket is open
if ( bSuccess && UdpSocket6 != INVALID_SOCKET )
{
bSuccess = bSuccess && ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) == 0 );
if ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) != -1 )
{
// note that IPv6 is available
bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available
}
}
}

if ( UdpSocket6 != INVALID_SOCKET && !bIPv6Available )
{
// IPv6 bind failed - don't cancel bSuccess, but close the IPv6 socket
#ifdef _WIN32
closesocket ( UdpSocket6 );
#else
close ( UdpSocket6 );
#endif
UdpSocket6 = INVALID_SOCKET;
qWarning() << "IPv6 socket closed - failed to bind";
}

if ( !bSuccess )
{
// we cannot bind socket, throw error
Expand Down
1 change: 1 addition & 0 deletions src/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <QObject>
#include <QThread>
#include <QMutex>
#include <QRandomGenerator>
#include <vector>
#include <atomic>
#include "global.h"
Expand Down
Loading