-
Notifications
You must be signed in to change notification settings - Fork 248
Rework the opening of UDP sockets for IPv4 and IPv6 #3893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1e3e45e
5929068
c9ea8cd
fb75de3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 ); | ||
|
|
@@ -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; | ||
|
|
@@ -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 ) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not (or Or even |
||
| { | ||
| // note that IPv6 is available | ||
| bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available | ||
| } | ||
| } | ||
| } | ||
| else | ||
|
|
@@ -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; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this was the first call to |
||
| 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 ) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -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 ) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
There was a problem hiding this comment.
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?