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
82 changes: 64 additions & 18 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 );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are other negative values "good" or should this be >= 0?


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 )

@pljones pljones Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why not

bIPv6Available = ( ::bind ( UdpSocket6, (struct sockaddr*) &sa6, sa6len ) != -1 );

(or >= 0) like IPv4?

Or even

bIPv6Available = bSuccess && UdpSocket6 != INVALID_SOCKET && ( ::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,45 @@ 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.

quint32 startingPortNumber = static_cast<quint32> ( iPortNumber ) + QRandomGenerator::global()->bounded ( NUM_SOCKET_PORTS_TO_TRY );

quint16 iClientPortIncrement = 0;
bSuccess = false; // initialization for while loop
// in the unlikely event we were given a port number too high, move lower,
// so we at least try to bind once.
if ( startingPortNumber > 65535U )
{
startingPortNumber -= NUM_SOCKET_PORTS_TO_TRY;
}

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 )

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As per the other call.

(In fact, aren't we duplicating code here? Can it be stuffed into a method?)

{
// 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 +398,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 )

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

And again.

{
// 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