From cffbe2313c0457cf526f1c1699f3a2013a4efb04 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 30 May 2026 10:31:42 +0100 Subject: [PATCH 1/6] Auto-enable IPv6 by default, unless disabled Removes -6 and --enableipv6, replacing with --noipv6. If --noipv6 is not specified, it will try first to open an IPv6 socket, and if the host does not support it, will fall back to an IPv4 socket. CSocket informs CClient or CServer of IPv6 availability via a reference. The bool bEnableIPv6 is gone, and the actual availability of IPv6 is published by a method in CClient or CServer. --- src/client.cpp | 8 ++-- src/client.h | 10 +++-- src/clientdlg.cpp | 8 ++-- src/clientdlg.h | 2 - src/connectdlg.cpp | 10 +++-- src/connectdlg.h | 4 +- src/main.cpp | 29 ++++++------- src/server.cpp | 10 ++--- src/server.h | 10 ++--- src/serverdlg.cpp | 4 +- src/serverlist.cpp | 26 +++++++----- src/serverlist.h | 13 ++++-- src/socket.cpp | 103 ++++++++++++++++++++++++++------------------- src/socket.h | 45 ++++++++++++++------ src/util.cpp | 20 ++++----- src/util.h | 8 ++-- 16 files changed, 174 insertions(+), 136 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index d5c27fcd62..b1638ab272 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -54,7 +54,7 @@ CClient::CClient ( const quint16 iPortNumber, const QString& strConnOnStartupAddress, const bool bNoAutoJackConnect, const QString& strNClientName, - const bool bNEnableIPv6, + const bool bNDisableIPv6, const bool bNMuteMeInPersonalMix ) : ChannelInfo(), strClientName ( strNClientName ), @@ -72,7 +72,8 @@ CClient::CClient ( const quint16 iPortNumber, bIsInitializationPhase ( true ), bMuteOutStream ( false ), fMuteOutStreamGain ( 1.0f ), - Socket ( &Channel, iPortNumber, iQosNumber, "", bNEnableIPv6 ), + bIPv6Available ( false ), + Socket ( &Channel, iPortNumber, iQosNumber, "", bNDisableIPv6, bIPv6Available ), Sound ( AudioCallback, this, bNoAutoJackConnect, strNClientName ), iAudioInFader ( AUD_FADER_IN_MIDDLE ), bReverbOnLeftChan ( false ), @@ -90,7 +91,6 @@ CClient::CClient ( const quint16 iPortNumber, bEnableAudioAlerts ( false ), bEnableOPUS64 ( false ), bJitterBufferOK ( true ), - bEnableIPv6 ( bNEnableIPv6 ), bMuteMeInPersonalMix ( bNMuteMeInPersonalMix ), iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ), bRawAudioIsSupported ( false ) @@ -621,7 +621,7 @@ void CClient::SetRemoteChanPan ( const int iId, const float fPan ) bool CClient::SetServerAddr ( QString strNAddr ) { CHostAddress HostAddress; - if ( NetworkUtil::ParseNetworkAddress ( strNAddr, HostAddress, bEnableIPv6 ) ) + if ( NetworkUtil::ParseNetworkAddress ( strNAddr, HostAddress, bIPv6Available ) ) { // apply address to the channel Channel.SetAddress ( HostAddress ); diff --git a/src/client.h b/src/client.h index 2c5e204d97..27c78cdf3d 100644 --- a/src/client.h +++ b/src/client.h @@ -153,7 +153,7 @@ class CClient : public QObject const QString& strConnOnStartupAddress, const bool bNoAutoJackConnect, const QString& strNClientName, - const bool bNEnableIPv6, + const bool bNDisableIPv6, const bool bNMuteMeInPersonalMix ); virtual ~CClient(); @@ -164,6 +164,9 @@ class CClient : public QObject bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } bool SetServerAddr ( QString strNAddr ); + // IPv6 Available + bool IsIPv6Available() { return bIPv6Available; } + double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } @@ -391,7 +394,9 @@ class CClient : public QObject float fMuteOutStreamGain; CVector vecCeltData; - CHighPrioSocket Socket; + bool bIPv6Available; // must be before Socket - passed by reference to Socket + CHighPrioSocket Socket; + CSound Sound; CStereoSignalLevelMeter SignalLevelMeter; @@ -427,7 +432,6 @@ class CClient : public QObject bool bEnableOPUS64; bool bJitterBufferOK; - bool bEnableIPv6; bool bMuteMeInPersonalMix; QMutex MutexDriverReinit; diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 145a14a24a..09409d7eaf 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -54,19 +54,17 @@ CClientDlg::CClientDlg ( CClient* pNCliP, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, const bool bMuteStream, - const bool bNEnableIPv6, QWidget* parent ) : CBaseDlg ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons pClient ( pNCliP ), pSettings ( pNSetP ), bConnectDlgWasShown ( false ), bDetectFeedback ( false ), - bEnableIPv6 ( bNEnableIPv6 ), eLastRecorderState ( RS_UNDEFINED ), // for SetMixerBoardDeco eLastDesign ( GD_DEFAULT ), // " ClientSettingsDlg ( pNCliP, pNSetP, parent ), ChatDlg ( parent ), - ConnectDlg ( pNSetP, bNewShowComplRegConnList, bNEnableIPv6, parent ), + ConnectDlg ( pNCliP, pNSetP, bNewShowComplRegConnList, parent ), AnalyzerConsole ( pNCliP, parent ) { setupUi ( this ); @@ -619,12 +617,12 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // Don't use SRV resolution when resolving update servers. - if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK1_ADDRESS, UpdateServerHostAddress, bEnableIPv6 ) ) + if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK1_ADDRESS, UpdateServerHostAddress, pClient->IsIPv6Available() ) ) { pClient->CreateCLServerListReqVerAndOSMes ( UpdateServerHostAddress ); } - if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK2_ADDRESS, UpdateServerHostAddress, bEnableIPv6 ) ) + if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK2_ADDRESS, UpdateServerHostAddress, pClient->IsIPv6Available() ) ) { pClient->CreateCLServerListReqVerAndOSMes ( UpdateServerHostAddress ); } diff --git a/src/clientdlg.h b/src/clientdlg.h index bc80507d1f..687fe811f4 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -105,7 +105,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, const bool bMuteStream, - const bool bNEnableIPv6, QWidget* parent = nullptr ); protected: @@ -130,7 +129,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase bool bConnected; bool bConnectDlgWasShown; bool bDetectFeedback; - bool bEnableIPv6; ERecorderState eLastRecorderState; EGUIDesign eLastDesign; QTimer TimerSigMet; diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index d3604b34ec..05127ded37 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -122,8 +122,9 @@ bool CMappedTreeWidgetItem::operator<( const QTreeWidgetItem& other ) const return lhs.toString() < rhs.toString(); } -CConnectDlg::CConnectDlg ( CClientSettings* pNSetP, const bool bNewShowCompleteRegList, const bool bNEnableIPv6, QWidget* parent ) : +CConnectDlg::CConnectDlg ( CClient* pNCliP, CClientSettings* pNSetP, const bool bNewShowCompleteRegList, QWidget* parent ) : CBaseDlg ( parent, Qt::Dialog ), + pClient ( pNCliP ), pSettings ( pNSetP ), strSelectedAddress ( "" ), strSelectedServerName ( "" ), @@ -132,8 +133,7 @@ CConnectDlg::CConnectDlg ( CClientSettings* pNSetP, const bool bNewShowCompleteR bReducedServerListReceived ( false ), bServerListItemWasChosen ( false ), bListFilterWasActive ( false ), - bShowAllMusicians ( true ), - bEnableIPv6 ( bNEnableIPv6 ) + bShowAllMusicians ( true ) { setupUi ( this ); @@ -892,7 +892,9 @@ void CConnectDlg::OnTimerPing() // try to parse host address string which is stored as user data // in the server list item GUI control element // the data to be parsed is just IP:port, so no SRV discovery is needed - if ( NetworkUtil::ParseNetworkAddressBare ( pCurListViewItem->data ( LVC_NAME, Qt::UserRole ).toString(), haServerAddress, bEnableIPv6 ) ) + if ( NetworkUtil::ParseNetworkAddressBare ( pCurListViewItem->data ( LVC_NAME, Qt::UserRole ).toString(), + haServerAddress, + pClient->IsIPv6Available() ) ) { // if address is valid, send ping message using a new thread #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) diff --git a/src/connectdlg.h b/src/connectdlg.h index a8aa8be694..2d82a8c454 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -84,7 +84,7 @@ class CConnectDlg : public CBaseDlg, private Ui_CConnectDlgBase Q_OBJECT public: - CConnectDlg ( CClientSettings* pNSetP, const bool bNewShowCompleteRegList, const bool bNEnableIPv6, QWidget* parent = nullptr ); + CConnectDlg ( CClient* pNCliP, CClientSettings* pNSetP, const bool bNewShowCompleteRegList, QWidget* parent = nullptr ); void SetShowAllMusicians ( const bool bState ) { ShowAllMusicians ( bState ); } bool GetShowAllMusicians() { return bShowAllMusicians; } @@ -127,6 +127,7 @@ class CConnectDlg : public CBaseDlg, private Ui_CConnectDlgBase void EmitCLServerListPingMes ( const CHostAddress& haServerAddress, const bool bNeedVersion ); void UpdateDirectoryComboBox(); + CClient* pClient; CClientSettings* pSettings; QTimer TimerPing; @@ -141,7 +142,6 @@ class CConnectDlg : public CBaseDlg, private Ui_CConnectDlgBase bool bServerListItemWasChosen; bool bListFilterWasActive; bool bShowAllMusicians; - bool bEnableIPv6; public slots: void OnServerListItemDoubleClicked ( QTreeWidgetItem* Item, int ); diff --git a/src/main.cpp b/src/main.cpp index 2bcb53cc88..db45277ad3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -118,7 +118,7 @@ int main ( int argc, char** argv ) bool bNoAutoJackConnect = false; bool bUseTranslation = true; bool bCustomPortNumberGiven = false; - bool bEnableIPv6 = false; + bool bDisableIPv6 = false; int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS; quint16 iPortNumber = DEFAULT_PORT_NUMBER; int iJsonRpcPortNumber = INVALID_PORT; @@ -262,12 +262,12 @@ int main ( int argc, char** argv ) continue; } - // Enable IPv6 --------------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) + // Disable IPv6 --------------------------------------------------------- + if ( GetFlagArgument ( argv, i, "--noipv6", "--noipv6" ) ) { - bEnableIPv6 = true; - qInfo() << "- IPv6 enabled"; - CommandLineOptions << "--enableipv6"; + bDisableIPv6 = true; + qInfo() << "- IPv6 disabled"; + CommandLineOptions << "--noipv6"; continue; } @@ -943,7 +943,8 @@ int main ( int argc, char** argv ) #ifndef SERVER_ONLY if ( bIsClient ) { - CClient Client ( iPortNumber, iQosNumber, strConnOnStartupAddress, bNoAutoJackConnect, strClientName, bEnableIPv6, bMuteMeInPersonalMix ); + CClient + Client ( iPortNumber, iQosNumber, strConnOnStartupAddress, bNoAutoJackConnect, strClientName, bDisableIPv6, bMuteMeInPersonalMix ); // Create Settings with the client pointer CClientSettings Settings ( &Client, strIniFileName ); @@ -968,14 +969,8 @@ int main ( int argc, char** argv ) } // GUI object - CClientDlg ClientDlg ( &Client, - &Settings, - strConnOnStartupAddress, - bShowComplRegConnList, - bShowAnalyzerConsole, - bMuteStream, - bEnableIPv6, - nullptr ); + CClientDlg + ClientDlg ( &Client, &Settings, strConnOnStartupAddress, bShowComplRegConnList, bShowAnalyzerConsole, bMuteStream, nullptr ); // show dialog ClientDlg.show(); @@ -1014,7 +1009,7 @@ int main ( int argc, char** argv ) bUseMultithreading, bDisableRecording, bDelayPan, - bEnableIPv6, + bDisableIPv6, eLicenceType ); #ifndef NO_JSON_RPC @@ -1116,7 +1111,7 @@ QString UsageArguments ( char** argv ) " -Q, --qos set the QoS value. Default is 128. Disable with 0\n" " (see the Jamulus website to enable QoS on Windows)\n" " -t, --notranslation disable translation (use English language)\n" - " -6, --enableipv6 enable IPv6 addressing (IPv4 is always enabled)\n" + " --noipv6 disable IPv6 addressing (IPv4 is always enabled)\n" "\n" "Server only:\n" " -d, --discononquit disconnect all Clients on quit\n" diff --git a/src/server.cpp b/src/server.cpp index b4f3a551f2..67274965bc 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -65,31 +65,31 @@ CServer::CServer ( const int iNewMaxNumChan, const bool bNUseMultithreading, const bool bDisableRecording, const bool bNDelayPan, - const bool bNEnableIPv6, + const bool bNDisableIPv6, const ELicenceType eNLicenceType ) : bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ), bUseMultithreading ( bNUseMultithreading ), iMaxNumChannels ( iNewMaxNumChan ), iCurNumChannels ( 0 ), bDisableRaw ( bNDisableRaw ), - Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNEnableIPv6 ), + bIPv6Available ( false ), + Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNDisableIPv6, bIPv6Available ), Logging(), iFrameCount ( 0 ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), - ServerListManager ( iPortNumber, + ServerListManager ( this, + iPortNumber, strDirectoryAddress, strServerListFileName, strServerInfo, strServerPublicIP, strServerListFilter, iNewMaxNumChan, - bNEnableIPv6, &ConnLessProtocol ), JamController ( this ), bDisableRecording ( bDisableRecording ), bAutoRunMinimized ( false ), bDelayPan ( bNDelayPan ), - bEnableIPv6 ( bNEnableIPv6 ), eLicenceType ( eNLicenceType ), bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ), pSignalHandler ( CSignalHandler::getSingletonP() ) diff --git a/src/server.h b/src/server.h index ac8bf8ac2d..2e6b13e835 100644 --- a/src/server.h +++ b/src/server.h @@ -125,7 +125,7 @@ class CServer : public QObject, public CServerSlots const bool bNUseMultithreading, const bool bDisableRecording, const bool bNDelayPan, - const bool bNEnableIPv6, + const bool bNDisableIPv6, const ELicenceType eNLicenceType ); virtual ~CServer(); @@ -146,8 +146,8 @@ class CServer : public QObject, public CServerSlots void CreateCLServerListReqVerAndOSMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqVersionAndOSMes ( InetAddr ); } - // IPv6 Enabled - bool IsIPv6Enabled() { return bEnableIPv6; } + // IPv6 Available + bool IsIPv6Available() { return bIPv6Available; } // GUI settings ------------------------------------------------------------ int GetClientNumAudioChannels ( const int iChanNum ) { return vecChannels[iChanNum].GetNumAudioChannels(); } @@ -291,6 +291,7 @@ class CServer : public QObject, public CServerSlots CVector vecChannelLevels; // actual working objects + bool bIPv6Available; // must be before Socket - passed by reference to Socket CHighPrioSocket Socket; // logging @@ -314,9 +315,6 @@ class CServer : public QObject, public CServerSlots // for delay panning bool bDelayPan; - // enable IPv6 - bool bEnableIPv6; - // messaging QString strWelcomeMessage; ELicenceType eLicenceType; diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index 8e9b4245ff..47578e361c 100644 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -506,12 +506,12 @@ CServerDlg::CServerDlg ( CServer* pNServP, CServerSettings* pNSetP, const bool b // Don't use SRV resolution when resolving update servers. - if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK1_ADDRESS, UpdateServerHostAddress, pServer->IsIPv6Enabled() ) ) + if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK1_ADDRESS, UpdateServerHostAddress, pServer->IsIPv6Available() ) ) { pServer->CreateCLServerListReqVerAndOSMes ( UpdateServerHostAddress ); } - if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK2_ADDRESS, UpdateServerHostAddress, pServer->IsIPv6Enabled() ) ) + if ( NetworkUtil::ParseNetworkAddressBare ( UPDATECHECK2_ADDRESS, UpdateServerHostAddress, pServer->IsIPv6Available() ) ) { pServer->CreateCLServerListReqVerAndOSMes ( UpdateServerHostAddress ); } diff --git a/src/serverlist.cpp b/src/serverlist.cpp index f3bf6e6770..90cdff4938 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -84,10 +84,10 @@ CServerListEntry CServerListEntry::parse ( QString strHAddr, QString strCountry, QString strNumClients, bool isPermanent, - bool bEnableIPv6 ) + bool bIPv6Available ) { CHostAddress haServerHostAddr; - NetworkUtil::ParseNetworkAddress ( strHAddr, haServerHostAddr, bEnableIPv6 ); + NetworkUtil::ParseNetworkAddress ( strHAddr, haServerHostAddr, bIPv6Available ); if ( CHostAddress() == haServerHostAddr ) { // do not proceed without server host address! @@ -95,7 +95,7 @@ CServerListEntry CServerListEntry::parse ( QString strHAddr, } CHostAddress haServerLocalAddr; - NetworkUtil::ParseNetworkAddress ( strLHAddr, haServerLocalAddr, bEnableIPv6 ); + NetworkUtil::ParseNetworkAddress ( strLHAddr, haServerLocalAddr, bIPv6Available ); if ( haServerLocalAddr.iPort == 0 ) { haServerLocalAddr.iPort = haServerHostAddr.iPort; @@ -142,17 +142,17 @@ QString CServerListEntry::toCSV() } // --- CServerListManager --- -CServerListManager::CServerListManager ( const quint16 iNPortNum, +CServerListManager::CServerListManager ( CServer* pServer, + const quint16 iNPortNum, const QString& sNDirectoryAddress, const QString& strServerListFileName, const QString& strServerInfo, const QString& strServerListFilter, const QString& strServerPublicIP, const int iNumChannels, - const bool bNEnableIPv6, CProtocol* pNConLProt ) : + pServer ( pServer ), DirectoryType ( AT_NONE ), - bEnableIPv6 ( bNEnableIPv6 ), ServerListFileName ( strServerListFileName ), strDirectoryAddress ( "" ), bIsDirectory ( false ), @@ -180,7 +180,7 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, qDebug() << "Using" << qhaServerPublicIP.toString() << "as external IP."; ServerPublicIP = CHostAddress ( qhaServerPublicIP, iNPortNum ); - if ( bEnableIPv6 ) + if ( pServer->IsIPv6Available() ) { // set the server internal address, including internal port number QHostAddress qhaServerPublicIP6; @@ -829,7 +829,7 @@ bool CServerListManager::Load() // This uses ParseNetworkAddressBare because it is just parsing ip:host that was saved to the file. // Therefore no SRV lookup is appropriate. - NetworkUtil::ParseNetworkAddressBare ( slLine[0], haServerHostAddr, bEnableIPv6 ); + NetworkUtil::ParseNetworkAddressBare ( slLine[0], haServerHostAddr, pServer->IsIPv6Available() ); int iIdx = IndexOf ( haServerHostAddr ); if ( iIdx != INVALID_INDEX ) { @@ -837,8 +837,14 @@ bool CServerListManager::Load() continue; } - serverListEntry = - CServerListEntry::parse ( slLine[0], slLine[1], slLine[2], slLine[3], slLine[4], slLine[5], slLine[6].toInt() != 0, bEnableIPv6 ); + serverListEntry = CServerListEntry::parse ( slLine[0], + slLine[1], + slLine[2], + slLine[3], + slLine[4], + slLine[5], + slLine[6].toInt() != 0, + pServer->IsIPv6Available() ); // We expect servers to have addresses... if ( ( CHostAddress() == serverListEntry.HostAddr ) ) diff --git a/src/serverlist.h b/src/serverlist.h index 74793bf8b9..f18c40678d 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -97,6 +97,9 @@ Note: this mechanism will not work in a private network. #include "util.h" #include "protocol.h" +// Forward declaration - see end of this file for the actual #include +class CServer; + /* Classes ********************************************************************/ class CServerListEntry : public CServerInfo { @@ -155,14 +158,14 @@ class CServerListManager : public QObject Q_OBJECT public: - CServerListManager ( const quint16 iNPortNum, + CServerListManager ( CServer* pServer, + const quint16 iNPortNum, const QString& sNDirectoryAddress, const QString& strServerListFileName, const QString& strServerInfo, const QString& strServerListFilter, const QString& strServerPublicIP, const int iNumChannels, - const bool bNEnableIPv6, CProtocol* pNConLProt ); void SetServerName ( const QString& strNewName ); @@ -210,11 +213,11 @@ class CServerListManager : public QObject QMutex Mutex; + CServer* pServer; + CHostAddress DirectoryAddress; EDirectoryType DirectoryType; - bool bEnableIPv6; - CHostAddress ServerPublicIP; CHostAddress ServerPublicIP6; @@ -256,3 +259,5 @@ public slots: signals: void SvrRegStatusChanged(); }; + +#include "server.h" diff --git a/src/socket.cpp b/src/socket.cpp index e6d50a4249..5c2f2a82c5 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -62,13 +62,18 @@ // we have different connections for client and server, created after Init in corresponding constructor -CSocket::CSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) : +CSocket::CSocket ( CChannel* pNewChannel, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ) : pChannel ( pNewChannel ), bIsClient ( true ), bJitterBufferOK ( true ), - bEnableIPv6 ( bEnableIPv6 ) + bIPv6Available ( bIPv6Available ) { - Init ( iPortNumber, iQosNumber, strServerBindIP ); + Init ( iPortNumber, iQosNumber, strServerBindIP, bDisableIPv6 ); // client connections: QObject::connect ( this, &CSocket::ProtocolMessageReceived, pChannel, &CChannel::OnProtocolMessageReceived ); @@ -78,13 +83,18 @@ CSocket::CSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint QObject::connect ( this, static_cast ( &CSocket::NewConnection ), pChannel, &CChannel::OnNewConnection ); } -CSocket::CSocket ( CServer* pNServP, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) : +CSocket::CSocket ( CServer* pNServP, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ) : pServer ( pNServP ), bIsClient ( false ), bJitterBufferOK ( true ), - bEnableIPv6 ( bEnableIPv6 ) + bIPv6Available ( bIPv6Available ) { - Init ( iPortNumber, iQosNumber, strServerBindIP ); + Init ( iPortNumber, iQosNumber, strServerBindIP, bDisableIPv6 ); // server connections: QObject::connect ( this, &CSocket::ProtocolMessageReceived, pServer, &CServer::OnProtocolMessageReceived ); @@ -99,7 +109,7 @@ CSocket::CSocket ( CServer* pNServP, const quint16 iPortNumber, const quint16 iQ QObject::connect ( this, &CSocket::ServerFull, pServer, &CServer::OnServerFull ); } -void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, const QString& strNewServerBindIP ) +void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, const QString& strNewServerBindIP, const bool bDisableIPv6 ) { uSockAddr UdpSocketAddr; @@ -124,57 +134,60 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, memset ( &UdpSocketAddr, 0, sizeof ( UdpSocketAddr ) ); - if ( bEnableIPv6 ) + if ( !bDisableIPv6 ) { // try to create a IPv6 UDP socket UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0 ); - if ( UdpSocket == -1 ) + if ( UdpSocket != -1 ) { - // IPv6 requested but not available, throw error - throw CGenErr ( "IPv6 requested but not available on this system.", "Network Error" ); - } - // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols. - // On Linux it's false by default on most (all?) distros, but on Windows it is true by default - const int no = 0; - if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ) == -1 ) - { - throw CGenErr ( "request to support IPv4 over IPv6 failed", "Network Error" ); - } + // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols. + // On Linux it's false by default on most (all?) distros, but on Windows it is true by default + const int no = 0; + if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ) == -1 ) + { + throw CGenErr ( "request to support IPv4 over IPv6 failed", "Network Error" ); + } - // set the QoS - const int tos = (int) iQosNumber; // Quality of Service + // set the QoS + const int tos = (int) iQosNumber; // Quality of Service #if !defined( Q_OS_WIN ) - if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ) == -1 ) - { - throw CGenErr ( "request to set ToS for IPv6 failed", "Network Error" ); - } + if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ) == -1 ) + { + throw CGenErr ( "request to set ToS for IPv6 failed", "Network Error" ); + } #endif #if !defined( Q_OS_BSD4 ) && !defined( Q_OS_WIN ) - // set the QoS for IPv4 as well, as this is a dual-protocol socket - if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) - { - throw CGenErr ( "request to set ToS for IPv4 over IPv6 failed", "Network Error" ); - } + // set the QoS for IPv4 as well, as this is a dual-protocol socket + if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) + { + throw CGenErr ( "request to set ToS for IPv4 over IPv6 failed", "Network Error" ); + } #endif - UdpSocketAddr.sa6.sin6_family = AF_INET6; - UdpSocketAddr.sa6.sin6_addr = in6addr_any; - UdpSocketAddrLen = sizeof ( UdpSocketAddr.sa6 ); + UdpSocketAddr.sa6.sin6_family = AF_INET6; + UdpSocketAddr.sa6.sin6_addr = in6addr_any; + UdpSocketAddrLen = sizeof ( UdpSocketAddr.sa6 ); + + UdpPort = &UdpSocketAddr.sa6.sin6_port; // where to put the port number - UdpPort = &UdpSocketAddr.sa6.sin6_port; // where to put the port number + // FIXME: If binding a dual-protocol interface to a specific address, does it cease to be dual-protocol? - // FIXME: If binding a dual-protocol interface to a specific address, does it cease to be dual-protocol? + // TODO - ALLOW IPV6 ADDRESS + // if ( !strServerBindIP.isEmpty() ) + //{ + // UdpSocketInAddr.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() ); + //} + // END TODO - ALLOW IPV6 ADDRESS - // TODO - ALLOW IPV6 ADDRESS - // if ( !strServerBindIP.isEmpty() ) - //{ - // UdpSocketInAddr.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() ); - //} - // END TODO - ALLOW IPV6 ADDRESS + bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available + + qInfo() << "IPv6 socket created"; + } } - else + + if ( !bIPv6Available ) { // create the UDP socket for IPv4 UdpSocket = socket ( AF_INET, SOCK_DGRAM, 0 ); @@ -204,6 +217,8 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, { UdpSocketAddr.sa4.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() ); } + + qInfo() << "IPv4 socket created"; } #ifdef Q_OS_IOS @@ -371,7 +386,7 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, const CHostAddr { if ( HostAddr.InetAddr.protocol() == QAbstractSocket::IPv4Protocol ) { - if ( bEnableIPv6 ) + if ( bIPv6Available ) { // Linux and Mac allow to pass an AF_INET address to a dual-stack socket, // but Windows does not. So use a V4MAPPED address in an AF_INET6 sockaddr, @@ -419,7 +434,7 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, const CHostAddr sizeof ( UdpSocketAddr.sa4 ) ); } } - else if ( bEnableIPv6 ) + else if ( bIPv6Available ) { UdpSocketAddr.sa6.sin6_family = AF_INET6; UdpSocketAddr.sa6.sin6_port = htons ( HostAddr.iPort ); diff --git a/src/socket.h b/src/socket.h index 6e60caefd2..b81ea4d69d 100644 --- a/src/socket.h +++ b/src/socket.h @@ -75,8 +75,19 @@ class CSocket : public QObject Q_OBJECT public: - CSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ); - CSocket ( CServer* pNServP, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ); + CSocket ( CChannel* pNewChannel, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ); + + CSocket ( CServer* pNServP, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ); virtual ~CSocket(); @@ -86,7 +97,7 @@ class CSocket : public QObject void Close(); protected: - void Init ( const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP ); + void Init ( const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, const bool bDisableIPv6 ); quint16 iPortNumber; quint16 iQosNumber; QString strServerBindIP; @@ -108,7 +119,9 @@ class CSocket : public QObject bool bJitterBufferOK; - bool bEnableIPv6; + // This is a reference to CClient::bIPv6Available or CServer::bIPv6Available, + // to inform the Client or Server which type of socket was created at startup. + bool& bIPv6Available; public: void OnDataReceived(); @@ -138,20 +151,24 @@ class CHighPrioSocket : public QObject Q_OBJECT public: - CHighPrioSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) : - Socket ( pNewChannel, iPortNumber, iQosNumber, strServerBindIP, bEnableIPv6 ) + CHighPrioSocket ( CChannel* pNewChannel, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ) : + Socket ( pNewChannel, iPortNumber, iQosNumber, strServerBindIP, bDisableIPv6, bIPv6Available ) { Init(); } - CHighPrioSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint16 iQosNumber, bool bEnableIPv6 ) : - Socket ( pNewChannel, iPortNumber, iQosNumber, "", bEnableIPv6 ) - { - Init(); - } - - CHighPrioSocket ( CServer* pNewServer, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) : - Socket ( pNewServer, iPortNumber, iQosNumber, strServerBindIP, bEnableIPv6 ) + CHighPrioSocket ( CServer* pNewServer, + const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strServerBindIP, + const bool bDisableIPv6, + bool& bIPv6Available ) : + Socket ( pNewServer, iPortNumber, iQosNumber, strServerBindIP, bDisableIPv6, bIPv6Available ) { Init(); } diff --git a/src/util.cpp b/src/util.cpp index 1b8f027ae4..90b1506206 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -764,7 +764,7 @@ QSize CMinimumStackedLayout::sizeHint() const * Other Classes * \******************************************************************************/ // Network utility functions --------------------------------------------------- -bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, bool bEnableIPv6 ) +bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, const bool bIPv6Available ) { // try to get host by name, assuming // that the string contains a valid host name string or IP address @@ -780,7 +780,7 @@ bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress& { // qInfo() << qUtf8Printable ( QString ( "Resolved network address to %1 for proto %2" ) .arg ( HostAddr.toString() ) .arg ( // HostAddr.protocol() ) ); - if ( HostAddr.protocol() == QAbstractSocket::IPv4Protocol || ( bEnableIPv6 && HostAddr.protocol() == QAbstractSocket::IPv6Protocol ) ) + if ( HostAddr.protocol() == QAbstractSocket::IPv4Protocol || ( bIPv6Available && HostAddr.protocol() == QAbstractSocket::IPv6Protocol ) ) { InetAddr = HostAddr; return true; @@ -790,7 +790,7 @@ bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress& } #ifndef DISABLE_SRV_DNS -bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ) +bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ) { // init requested host address with invalid address first HostAddress = CHostAddress(); @@ -838,7 +838,7 @@ bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& Hos QString ( "resolved %1 to a single SRV record: %2:%3" ).arg ( strAddress ).arg ( record.target() ).arg ( record.port() ) ); QHostAddress InetAddr; - if ( ParseNetworkAddressString ( record.target(), InetAddr, bEnableIPv6 ) ) + if ( ParseNetworkAddressString ( record.target(), InetAddr, bIPv6Available ) ) { HostAddress = CHostAddress ( InetAddr, record.port() ); return true; @@ -847,20 +847,20 @@ bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& Hos } #endif -bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ) +bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ) { #ifndef DISABLE_SRV_DNS // Try SRV-based discovery first: - if ( ParseNetworkAddressSrv ( strAddress, HostAddress, bEnableIPv6 ) ) + if ( ParseNetworkAddressSrv ( strAddress, HostAddress, bIPv6Available ) ) { return true; } #endif // Try regular connect via plain IP or host name lookup (A/AAAA): - return ParseNetworkAddressBare ( strAddress, HostAddress, bEnableIPv6 ); + return ParseNetworkAddressBare ( strAddress, HostAddress, bIPv6Available ); } -bool NetworkUtil::ParseNetworkAddressBare ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ) +bool NetworkUtil::ParseNetworkAddressBare ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ) { QHostAddress InetAddr; unsigned int iNetPort = DEFAULT_PORT_NUMBER; @@ -925,7 +925,7 @@ bool NetworkUtil::ParseNetworkAddressBare ( QString strAddress, CHostAddress& Ho // first try if this is an IP number an can directly applied to QHostAddress if ( InetAddr.setAddress ( strAddress ) ) { - if ( !bEnableIPv6 && InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) + if ( !bIPv6Available && InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) { // do not allow IPv6 addresses if not enabled // qInfo() << qUtf8Printable ( QString ( "IPv6 addresses disabled" ) ); @@ -941,7 +941,7 @@ bool NetworkUtil::ParseNetworkAddressBare ( QString strAddress, CHostAddress& Ho return false; // invalid address } - if ( !ParseNetworkAddressString ( strAddress, InetAddr, bEnableIPv6 ) ) + if ( !ParseNetworkAddressString ( strAddress, InetAddr, bIPv6Available ) ) { // no valid address found // qInfo() << qUtf8Printable ( QString ( "No IP address found for hostname" ) ); diff --git a/src/util.h b/src/util.h index dc1511a33a..c104d7ede7 100644 --- a/src/util.h +++ b/src/util.h @@ -1076,13 +1076,13 @@ class CNetworkTransportProps class NetworkUtil { public: - static bool ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, bool bEnableIPv6 ); + static bool ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, const bool bIPv6Available ); #ifndef DISABLE_SRV_DNS - static bool ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ); + static bool ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ); #endif - static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ); - static bool ParseNetworkAddressBare ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 ); + static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ); + static bool ParseNetworkAddressBare ( QString strAddress, CHostAddress& HostAddress, const bool bIPv6Available ); static QString FixAddress ( const QString& strAddress ); static CHostAddress GetLocalAddress(); From 10967eac46085e5cabd327da29cafe23829dbb26 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 31 May 2026 17:56:10 +0100 Subject: [PATCH 2/6] Issue warning if --serverbindip given for a dual-protocol socket --- src/socket.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index 5c2f2a82c5..9d17fcaf40 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -174,16 +174,16 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // FIXME: If binding a dual-protocol interface to a specific address, does it cease to be dual-protocol? - // TODO - ALLOW IPV6 ADDRESS - // if ( !strServerBindIP.isEmpty() ) - //{ - // UdpSocketInAddr.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() ); - //} - // END TODO - ALLOW IPV6 ADDRESS + // It is not possible to bind a dual-protocol socket to a specific address + if ( !strServerBindIP.isEmpty() ) + { + qWarning() + << "Option --serverbindip ignored: cannot be used on a dual-protocol IPv6/IPv4 socket. Please add --noipv6 to use IPv4 only."; + } bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available - qInfo() << "IPv6 socket created"; + qInfo() << "IPv6/IPv4 dual-protocol socket created"; } } From 05ce4291827aa9695a133b33a3d856f0af443b6a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 31 May 2026 18:33:09 +0100 Subject: [PATCH 3/6] Add some clarifications to usage message --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index db45277ad3..0be5887c04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1112,6 +1112,7 @@ QString UsageArguments ( char** argv ) " (see the Jamulus website to enable QoS on Windows)\n" " -t, --notranslation disable translation (use English language)\n" " --noipv6 disable IPv6 addressing (IPv4 is always enabled)\n" + " (recommended to leave IPv6 enabled by default)\n" "\n" "Server only:\n" " -d, --discononquit disconnect all Clients on quit\n" @@ -1133,7 +1134,8 @@ QString UsageArguments ( char** argv ) " --norecord set server not to record by default when recording is configured\n" " --noraw disable raw audio\n" " -s, --server start Server\n" - " --serverbindip IP address the Server will bind to (rather than all)\n" + " --serverbindip IPv4 address the Server will bind to (rather than all)\n" + " (only works if IPv6 is unavailable or disabled with --noipv6)\n" " -T, --multithreading use multithreading to make better use of\n" " multi-core CPUs and support more Clients\n" " -u, --numchannels maximum number of channels\n" From 74584ee95af253156d351cb92a9a81ad1b453dab Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 1 Jun 2026 21:50:10 +0100 Subject: [PATCH 4/6] Fix for re-calling Init() in iOS --- src/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.cpp b/src/socket.cpp index 9d17fcaf40..a08a8f80c7 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -455,7 +455,7 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, const CHostAddr #ifdef Q_OS_IOS // qDebug("Socket send exception - mostly happens in iOS when returning from idle"); - Init ( iPortNumber, iQosNumber, strServerBindIP ); // reinit + Init ( iPortNumber, iQosNumber, strServerBindIP, !bIPv6Available ); // reinit // loop back to retry #endif From a65ecf3b889786d19151e510ad4900b0d392d793 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 2 Jun 2026 14:43:46 +0100 Subject: [PATCH 5/6] Terminology: change dual-protocol to dual-stack --- src/socket.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index a08a8f80c7..09c851d886 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -159,7 +159,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, #endif #if !defined( Q_OS_BSD4 ) && !defined( Q_OS_WIN ) - // set the QoS for IPv4 as well, as this is a dual-protocol socket + // set the QoS for IPv4 as well, as this is a dual-stack socket if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { throw CGenErr ( "request to set ToS for IPv4 over IPv6 failed", "Network Error" ); @@ -172,18 +172,17 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, UdpPort = &UdpSocketAddr.sa6.sin6_port; // where to put the port number - // FIXME: If binding a dual-protocol interface to a specific address, does it cease to be dual-protocol? + // FIXME: If binding a dual-stack interface to a specific address, does it cease to be dual-stack? - // It is not possible to bind a dual-protocol socket to a specific address + // It is not possible to bind a dual-stack socket to a specific address if ( !strServerBindIP.isEmpty() ) { - qWarning() - << "Option --serverbindip ignored: cannot be used on a dual-protocol IPv6/IPv4 socket. Please add --noipv6 to use IPv4 only."; + qWarning() << "Option --serverbindip ignored: cannot be used on a dual-stack IPv6/IPv4 socket. Please add --noipv6 to use IPv4 only."; } bIPv6Available = true; // this is a reference to CClient::bIPv6Available or CServer::bIPv6Available - qInfo() << "IPv6/IPv4 dual-protocol socket created"; + qInfo() << "IPv6/IPv4 dual-stack socket created"; } } From 16c242b232697c40b8130b8ae44815fbe590c831 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 6 Jun 2026 15:33:00 +0100 Subject: [PATCH 6/6] Accept deprecated -6 and --enableipv6 with warning and no action --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 0be5887c04..f840503b88 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -271,6 +271,14 @@ int main ( int argc, char** argv ) continue; } + // Enable IPv6 --------------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) + { + qWarning() << "IPv6 is now enabled by default: -6 and --enableipv6 have no effect and are deprecated"; + CommandLineOptions << "--enableipv6"; + continue; + } + // Server only: // Disconnect all clients on quit --------------------------------------