diff --git a/docs/JSON-RPC.md b/docs/JSON-RPC.md index 3501fbdc9d..2c181cc83f 100644 --- a/docs/JSON-RPC.md +++ b/docs/JSON-RPC.md @@ -376,6 +376,34 @@ Results: | result | string | Always "ok". | +### jamulusdirectory/getServerList + +Returns the list of registered servers along with details about them. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params | object | No parameters (empty object). | + +Results: + +| Name | Type | Description | +| --- | --- | --- | +| result.numservers | number | The number of registered servers. | +| result.servers | array | The list of registered servers. | +| result.servers[*].id | number | The server's index. | +| result.servers[*].name | string | The server's name. | +| result.servers[*].countryName | string | The text name of the country specified by the user for this channel (see QLocale::Country). | +| result.servers[*].city | string | The city name provided by the operator of this server. | +| result.servers[*].maxClients | number | The max number of clients supported by the server. | +| result.servers[*].permanent | bool | The permanent status of the server. | +| result.servers[*].ipv4addr | string | The IPv4 address the server registered from (ip:port) | +| result.servers[*].ipv4addrLocal | string | The local IPv4 address provided by the server (ip:port) | +| result.servers[*].ipv6addr | string | The IPv6 address the server registered from ([ipv6]:port) | +| result.servers[*].ipv6addrLocal | string | The local IPv6 address provided by the server ([ipv6]:port) | + + ### jamulusserver/broadcastChatMessage Sends a message (as the server) to all connected clients. This can be used to broadcast messages from external sources (e.g. scripts or monitoring tools). diff --git a/src/clientrpc.cpp b/src/clientrpc.cpp index 0f376d10b4..1d0439e953 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -137,14 +137,14 @@ CClientRpc::CClientRpc ( CClient* pClient, CClientSettings* pSettings, CRpcServe for ( const auto& serverInfo : vecServerInfo ) { QJsonObject objServerInfo{ - { "address", serverInfo.HostAddr.toString() }, + { "address", serverInfo.HostAddr4.toString() }, { "name", serverInfo.strName }, { "countryId", serverInfo.eCountry }, { "country", QLocale::countryToString ( serverInfo.eCountry ) }, { "city", serverInfo.strCity }, }; arrServerInfo.append ( objServerInfo ); - pClient->CreateCLServerListPingMes ( serverInfo.HostAddr ); + pClient->CreateCLServerListPingMes ( serverInfo.HostAddr4 ); } pRpcServer->BroadcastNotification ( "jamulusclient/serverListReceived", QJsonObject{ @@ -189,8 +189,8 @@ CClientRpc::CClientRpc ( CClient* pClient, CClientSettings* pSettings, CRpcServe CHostAddress haDirectoryAddress; - // Allow IPv4 only for communicating with Directories - if ( !NetworkUtil::ParseNetworkAddress ( jsonDirectoryIp.toString(), haDirectoryAddress, false ) ) + // Allow IPv4 and IPv6 for communicating with Directories + if ( !NetworkUtil::ParseNetworkAddress ( jsonDirectoryIp.toString(), haDirectoryAddress, pClient->IsIPv6Available() ) ) { response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: directory is not a valid socket address" ); diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 5c3913100a..a28511b77f 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -319,7 +319,7 @@ void CConnectDlg::RequestServerList() if ( NetworkUtil::ParseNetworkAddress ( NetworkUtil::GetDirectoryAddress ( pSettings->eDirectoryType, pSettings->vstrDirectoryAddress[pSettings->iCustomDirectoryIndex] ), haDirectoryAddress, - false ) ) + pClient->IsIPv6Available() ) ) { // send the request for the server list emit ReqServerListQuery ( haDirectoryAddress ); @@ -419,7 +419,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector 0 ) { - CurHostAddress = vecServerInfo[iIdx].HostAddr; + CurHostAddress = vecServerInfo[iIdx].HostAddr4; } else { @@ -447,7 +447,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVectorsetText ( LVC_NAME, CurHostAddress.toString ( CHostAddress::SM_IP_NO_LAST_BYTE ) ); diff --git a/src/global.h b/src/global.h index 5414a58c41..bfc0448c1e 100644 --- a/src/global.h +++ b/src/global.h @@ -285,7 +285,7 @@ LED bar: lbr #define MAX_LEN_CHAT_TEXT 1600 #define MAX_LEN_CHAT_TEXT_PLUS_HTML 1800 #define MAX_LEN_SERVER_NAME 20 -#define MAX_LEN_IP_ADDRESS 15 +#define MAX_LEN_IP_ADDRESS 39 // 15 for IPv4, 39 for IPv6 #define MAX_LEN_SERVER_CITY 20 #define MAX_LEN_VERSION_TEXT 50 diff --git a/src/protocol.cpp b/src/protocol.cpp index 3030c2dca1..1203ec9e69 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2109,11 +2109,11 @@ void CProtocol::CreateCLServerListMes ( const CHostAddress& InetAddr, const CVec // IP address (4 bytes) // note the Server List manager has put the internal details in HostAddr where required - PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr.InetAddr.toIPv4Address() ), 4 ); + PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr4.InetAddr.toIPv4Address() ), 4 ); // port number (2 bytes) // note the Server List manager has put the internal details in HostAddr where required - PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr.iPort ), 2 ); + PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr4.iPort ), 2 ); // country (2 bytes) PutCountryOnStream ( vecData, iPos, vecServerInfo[i].eCountry ); @@ -2232,11 +2232,11 @@ void CProtocol::CreateCLRedServerListMes ( const CHostAddress& InetAddr, const C // IP address (4 bytes) // note the Server List manager has put the internal details in HostAddr where required - PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr.InetAddr.toIPv4Address() ), 4 ); + PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr4.InetAddr.toIPv4Address() ), 4 ); // port number (2 bytes) // note the Server List manager has put the internal details in HostAddr where required - PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr.iPort ), 2 ); + PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr4.iPort ), 2 ); // name (note that the string length indicator is 1 in this special case) PutStringUTF8OnStream ( vecData, iPos, strUTF8Name, 1 ); diff --git a/src/server.h b/src/server.h index c54c3083cd..846eb1f47f 100644 --- a/src/server.h +++ b/src/server.h @@ -166,6 +166,8 @@ class CServer : public QObject, public CServerSlots void SetServerCountry ( const QLocale::Country eNewCountry ) { ServerListManager.SetServerCountry ( eNewCountry ); } QLocale::Country GetServerCountry() { return ServerListManager.GetServerCountry(); } + bool GetDirectoryServerList ( CVector& vecServerInfo ) { return ServerListManager.GetDirectoryServerList ( vecServerInfo ); } + bool GetRecorderInitialised() { return JamController.GetRecorderInitialised(); } void SetEnableRecording ( bool bNewEnableRecording ); bool GetDisableRecording() { return bDisableRecording; } diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 0dafa73863..79cdcb2628 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -130,8 +130,8 @@ QString CServerListEntry::toCSV() { QStringList sl; - sl.append ( this->HostAddr.toString() ); - sl.append ( this->LHostAddr.toString() ); + sl.append ( this->HostAddr4.toString() ); + sl.append ( this->LHostAddr4.toString() ); sl.append ( ToBase64 ( this->strName ) ); sl.append ( ToBase64 ( this->strCity ) ); sl.append ( QString::number ( this->eCountry ) ); @@ -543,7 +543,7 @@ void CServerListManager::OnTimerPingServerInList() for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) { // send empty message to keep NAT port open at registered server - pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr ); + pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr4 ); } } @@ -560,7 +560,7 @@ void CServerListManager::OnTimerPollList() if ( ServerList[iIdx].RegisterTime.elapsed() > ( SERVLIST_TIME_OUT_MINUTES * 60000 ) ) { // remove this list entry - vecRemovedHostAddr.Add ( ServerList[iIdx].HostAddr ); + vecRemovedHostAddr.Add ( ServerList[iIdx].HostAddr4 ); ServerList.removeAt ( iIdx ); } } @@ -637,7 +637,7 @@ void CServerListManager::Append ( const CHostAddress& InetAddr, else { // update all data and call update registration function - ServerList[iSelIdx].LHostAddr = LInetAddr; + ServerList[iSelIdx].LHostAddr4 = LInetAddr; ServerList[iSelIdx].strName = ServerInfo.strName; ServerList[iSelIdx].eCountry = ServerInfo.eCountry; ServerList[iSelIdx].strCity = ServerInfo.strCity; @@ -695,12 +695,12 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) bool clientIsInternal = NetworkUtil::IsPrivateNetworkIP ( InetAddr.InetAddr ); CHostAddress clientPublicAddr = InetAddr; - if ( clientIsInternal && CHostAddress().InetAddr != ServerList[0].LHostAddr.InetAddr && - !NetworkUtil::IsPrivateNetworkIP ( ServerList[0].LHostAddr.InetAddr ) ) + if ( clientIsInternal && CHostAddress().InetAddr != ServerList[0].LHostAddr4.InetAddr && + !NetworkUtil::IsPrivateNetworkIP ( ServerList[0].LHostAddr4.InetAddr ) ) { // client and directory on same LAN, directory has public IP set, that should be suitable for the // client, too (i.e. same router with same public IP will be used for both), so use it for client public IP - clientPublicAddr.InetAddr = ServerList[0].LHostAddr.InetAddr; + clientPublicAddr.InetAddr = ServerList[0].LHostAddr4.InetAddr; } const ushort iCurServerListSize = static_cast ( ServerList.size() ); @@ -709,8 +709,8 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) CVector vecServerInfo ( iCurServerListSize ); // copy list item for the directory and just let the protocol sort out the actual details - vecServerInfo[0] = ServerList[0]; - vecServerInfo[0].HostAddr = CHostAddress(); + vecServerInfo[0] = ServerList[0]; + vecServerInfo[0].HostAddr4 = CHostAddress(); // copy the list (we have to copy it since the message requires a vector but the list is actually stored in a QList object // and not in a vector object) @@ -719,25 +719,25 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) // copy list item CServerInfo& siCurListEntry = vecServerInfo[iIdx] = ServerList[iIdx]; - bool serverIsInternal = NetworkUtil::IsPrivateNetworkIP ( siCurListEntry.HostAddr.InetAddr ); + bool serverIsInternal = NetworkUtil::IsPrivateNetworkIP ( siCurListEntry.HostAddr4.InetAddr ); - bool wantHostAddr = clientIsInternal /* HostAddr is local IP if local server else external IP, so do not replace */ || + bool wantHostAddr = clientIsInternal /* HostAddr4 is local IP if local server else external IP, so do not replace */ || ( !serverIsInternal && - InetAddr.InetAddr != siCurListEntry.HostAddr.InetAddr /* external server and client have different public IPs */ ); + InetAddr.InetAddr != siCurListEntry.HostAddr4.InetAddr /* external server and client have different public IPs */ ); if ( !wantHostAddr ) { - vecServerInfo[iIdx].HostAddr = siCurListEntry.LHostAddr; + vecServerInfo[iIdx].HostAddr4 = siCurListEntry.LHostAddr4; } // do not send a "ping" to a server local to the directory (no need) if ( !serverIsInternal ) { // create "send empty message" for all other registered servers - // this causes the server (vecServerInfo[iIdx].HostAddr) + // this causes the server (vecServerInfo[iIdx].HostAddr4) // to send a "reply" to the client (InetAddr or best guess public IP address if internal to directory) // - with the intent of opening the server firewall for the client - pConnLessProtocol->CreateCLSendEmptyMesMes ( siCurListEntry.HostAddr, clientPublicAddr ); + pConnLessProtocol->CreateCLSendEmptyMesMes ( siCurListEntry.HostAddr4, clientPublicAddr ); } } @@ -749,6 +749,31 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) } } +// fetch copy of registered server list for use by JSON-RPC +// return false if not a directory +bool CServerListManager::GetDirectoryServerList ( CVector& vecServerInfo ) +{ + QMutexLocker locker ( &Mutex ); + + if ( !bIsDirectory ) + { + return false; + } + + const ushort iCurServerListSize = static_cast ( ServerList.size() ); + + // allocate memory for the entire list + vecServerInfo.Init ( iCurServerListSize ); + + // copy the list + for ( int iIdx = 0; iIdx < iCurServerListSize; iIdx++ ) + { + vecServerInfo[iIdx] = ServerList[iIdx]; + } + + return true; +} + int CServerListManager::IndexOf ( const CHostAddress& haSearchTerm ) { // Called with lock set. @@ -758,7 +783,7 @@ int CServerListManager::IndexOf ( const CHostAddress& haSearchTerm ) // (i.e., this server). for ( int iIdx = ServerList.size() - 1; iIdx > 0; iIdx-- ) { - if ( ServerList[iIdx].HostAddr == haSearchTerm ) + if ( ServerList[iIdx].HostAddr4 == haSearchTerm ) { return iIdx; } @@ -847,15 +872,15 @@ bool CServerListManager::Load() pServer->IsIPv6Available() ); // We expect servers to have addresses... - if ( ( CHostAddress() == serverListEntry.HostAddr ) ) + if ( ( CHostAddress() == serverListEntry.HostAddr4 ) ) { qWarning() << qUtf8Printable ( QString ( "Could not parse '%1' successfully - invalid host" ).arg ( line ) ); continue; } qInfo() << qUtf8Printable ( QString ( "Loading registration for %1 (%2): %3" ) - .arg ( serverListEntry.HostAddr.toString() ) - .arg ( serverListEntry.LHostAddr.toString() ) + .arg ( serverListEntry.HostAddr4.toString() ) + .arg ( serverListEntry.LHostAddr4.toString() ) .arg ( serverListEntry.strName ) ); ServerList.append ( serverListEntry ); } @@ -889,8 +914,8 @@ void CServerListManager::Save() for ( int iIdx = ServerList.size() - 1; iIdx > 0; iIdx-- ) { qInfo() << qUtf8Printable ( QString ( tr ( "Saving registration for %1 (%2): %3" ) ) - .arg ( ServerList[iIdx].HostAddr.toString() ) - .arg ( ServerList[iIdx].LHostAddr.toString() ) + .arg ( ServerList[iIdx].HostAddr4.toString() ) + .arg ( ServerList[iIdx].LHostAddr4.toString() ) .arg ( ServerList[iIdx].strName ) ); out << ServerList[iIdx].toCSV() << '\n'; } @@ -1012,7 +1037,7 @@ void CServerListManager::SetRegistered ( const bool bIsRegister ) // Allow IPv4 only for communicating with Directories // Use SRV DNS discovery for directory connections, fallback to A/AAAA if none. const QString strNetworkAddress = NetworkUtil::GetDirectoryAddress ( DirectoryType, strDirectoryAddress ); - const bool bDirectoryAddressValid = NetworkUtil::ParseNetworkAddress ( strNetworkAddress, DirectoryAddress, false ); + const bool bDirectoryAddressValid = NetworkUtil::ParseNetworkAddress ( strNetworkAddress, DirectoryAddress, pServer->IsIPv6Available() ); // lock the mutex again now that the address has been resolved. locker.relock(); @@ -1027,7 +1052,18 @@ void CServerListManager::SetRegistered ( const bool bIsRegister ) // For a registered server, the server properties are stored in the // very first item in the server list (which is actually no server list // but just one item long for the registered server). - pConnLessProtocol->CreateCLRegisterServerExMes ( DirectoryAddress, ServerList[0].LHostAddr, ServerList[0] ); + if ( DirectoryAddress.InetAddr.protocol() == QAbstractSocket::IPv4Protocol ) + { + pConnLessProtocol->CreateCLRegisterServerExMes ( DirectoryAddress, ServerPublicIP, ServerList[0] ); + } + else if ( DirectoryAddress.InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) + { + pConnLessProtocol->CreateCLRegisterServerExMes ( DirectoryAddress, ServerPublicIP6, ServerList[0] ); + } + else + { + SetSvrRegStatus ( SRS_BAD_ADDRESS ); + } } else { diff --git a/src/serverlist.h b/src/serverlist.h index f18c40678d..b9aa607b5a 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -139,7 +139,7 @@ class CServerListEntry : public CServerInfo QString strCountry, QString strNumClients, bool isPermanent, - bool bEnableIPv6 ); + bool bIPv6Available ); QString toCSV(); // time on which the entry was registered @@ -195,6 +195,8 @@ class CServerListManager : public QObject void Remove ( const CHostAddress& InetAddr ); void RetrieveAll ( const CHostAddress& InetAddr ); + bool GetDirectoryServerList ( CVector& vecServerInfo ); + void StoreRegistrationResult ( ESvrRegResult eStatus ); QString GetServerListFileName() { return ServerListFileName; } diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 05bb54be62..b7aba05fc1 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -373,6 +373,92 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare response["result"] = "acknowledged"; Q_UNUSED ( params ); } ); + + /// @rpc_method jamulusdirectory/getServerList + /// @brief Returns the list of registered servers along with details about them. + /// @param {object} params - No parameters (empty object). + /// @result {number} result.numservers - The number of registered servers. + /// @result {array} result.servers - The list of registered servers. + /// @result {number} result.servers[*].id - The server's index. + /// @result {string} result.servers[*].name - The server's name. + /// @result {string} result.servers[*].countryName - The text name of the country specified by the user for this channel (see + /// QLocale::Country). + /// @result {string} result.servers[*].city - The city name provided by the operator of this server. + /// @result {number} result.servers[*].maxClients - The max number of clients supported by the server. + /// @result {bool} result.servers[*].permanent - The permanent status of the server. + /// @result {string} result.servers[*].ipv4addr - The IPv4 address the server registered from (ip:port) + /// @result {string} result.servers[*].ipv4addrLocal - The local IPv4 address provided by the server (ip:port) + /// @result {string} result.servers[*].ipv6addr - The IPv6 address the server registered from ([ipv6]:port) + /// @result {string} result.servers[*].ipv6addrLocal - The local IPv6 address provided by the server ([ipv6]:port) + pRpcServer->HandleMethod ( "jamulusdirectory/getServerList", [=] ( const QJsonObject& params, QJsonObject& response ) { + QJsonArray servers; + CVector vecServerInfo; + + int isDirectory = pServer->GetDirectoryServerList ( vecServerInfo ); + + if ( !isDirectory ) + { + response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Server is not a directory" ); + return; + } + + const int iNumServers = vecServerInfo.Size(); + + // fill list with connected clients + for ( int i = 0; i < iNumServers; i++ ) + { + + // name of the server + // QString strName; + + // country in which the server is located + // QLocale::Country eCountry; + + // city in which the server is located + // QString strCity; + + // maximum number of clients which can connect to the server at the same + // time + // int iMaxNumClients; + + // is the server permanently online or not (flag) + // bool bPermanentOnline; + + // IPv4 address of the server + // CHostAddress HostAddr4; + + // IPv4 internal address of the server + // CHostAddress LHostAddr4; + + // IPv6 address of the server + // CHostAddress HostAddr6; + + // IPv6 internal address of the server + // CHostAddress LHostAddr6; + + QJsonObject server{ + { "id", i }, + { "name", vecServerInfo[i].strName }, + { "countryName", QLocale::countryToString ( vecServerInfo[i].eCountry ) }, + { "city", vecServerInfo[i].strCity }, + { "maxClients", vecServerInfo[i].iMaxNumClients }, + { "permanent", vecServerInfo[i].bPermanentOnline }, + { "ipv4addr", vecServerInfo[i].HostAddr4.toString() }, + { "ipv4addrLocal", vecServerInfo[i].LHostAddr4.toString() }, + { "ipv6addr", vecServerInfo[i].HostAddr6.toString() }, + { "ipv6addrLocal", vecServerInfo[i].LHostAddr6.toString() }, + }; + servers.append ( server ); + } + + // create result object + QJsonObject result{ + { "numservers", iNumServers }, + { "servers", servers }, + }; + response["result"] = result; + Q_UNUSED ( params ); + } ); } #if defined( Q_OS_MACOS ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) diff --git a/src/testbench.h b/src/testbench.h index 233acd6351..97f5db8c0a 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -234,8 +234,8 @@ public slots: case 19: // PROTMESSID_CLM_SERVER_LIST vecServerInfo[0].bPermanentOnline = static_cast ( GenRandomIntInRange ( 0, 1 ) ); vecServerInfo[0].eCountry = static_cast ( GenRandomIntInRange ( 0, 100 ) ); - vecServerInfo[0].HostAddr = CurHostAddress; - vecServerInfo[0].LHostAddr = CurLocalAddress; + vecServerInfo[0].HostAddr4 = CurHostAddress; + vecServerInfo[0].LHostAddr4 = CurLocalAddress; vecServerInfo[0].iMaxNumClients = GenRandomIntInRange ( -2, 10000 ); vecServerInfo[0].strCity = GenRandomString(); vecServerInfo[0].strName = GenRandomString(); diff --git a/src/util.cpp b/src/util.cpp index 39efa10a16..891c9cfdfc 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -922,7 +922,7 @@ bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& Hos // need further testing to confirm. // End processing here (= return true), but pass back an // invalid HostAddress to let the connect logic fail properly. - HostAddress = CHostAddress ( QHostAddress ( "." ), 0 ); + HostAddress = CHostAddress ( QHostAddress(), 0 ); return true; } qDebug() << qUtf8Printable ( @@ -1187,35 +1187,39 @@ QString CHostAddress::toString ( const EStringMode eStringMode ) const { QString strReturn = InetAddr.toString(); - // special case: for local host address, we do not replace the last byte - if ( ( ( eStringMode == SM_IP_NO_LAST_BYTE ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) && - ( InetAddr != QHostAddress ( QHostAddress::LocalHost ) ) && ( InetAddr != QHostAddress ( QHostAddress::LocalHostIPv6 ) ) ) + switch ( InetAddr.protocol() ) { - // replace last part by an "x" - if ( strReturn.contains ( "." ) ) + case QAbstractSocket::IPv4Protocol: + if ( ( ( eStringMode == SM_IP_NO_LAST_BYTE ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) && + InetAddr != QHostAddress ( QHostAddress::LocalHost ) ) { - // IPv4 or IPv4-mapped: + // replace last part by an "x" strReturn = strReturn.section ( ".", 0, -2 ) + ".x"; } - else + if ( ( eStringMode == SM_IP_PORT ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) { - // IPv6 - strReturn = strReturn.section ( ":", 0, -2 ) + ":x"; + // add port + strReturn = QString ( "%1:%2" ).arg ( strReturn ).arg ( iPort ); } - } + break; - if ( ( eStringMode == SM_IP_PORT ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) - { - // add port number after a colon - if ( strReturn.contains ( "." ) ) + case QAbstractSocket::IPv6Protocol: + if ( ( ( eStringMode == SM_IP_NO_LAST_BYTE ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) && + InetAddr != QHostAddress ( QHostAddress::LocalHostIPv6 ) ) { - strReturn += ":" + QString().setNum ( iPort ); + // replace last part by an "x" + strReturn = strReturn.section ( ":", 0, -2 ) + ":x"; } - else + if ( ( eStringMode == SM_IP_PORT ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) { // enclose pure IPv6 address in [ ] before adding port, to avoid ambiguity - strReturn = "[" + strReturn + "]:" + QString().setNum ( iPort ); + strReturn = QString ( "[%1]:%2" ).arg ( strReturn ).arg ( iPort ); } + break; + + default: + // do not append port to an invalid address + break; } return strReturn; diff --git a/src/util.h b/src/util.h index 08e45166cb..7d4783ff26 100644 --- a/src/util.h +++ b/src/util.h @@ -804,7 +804,7 @@ class CHostAddress SM_IP_NO_LAST_BYTE_PORT }; - CHostAddress() : InetAddr ( static_cast ( 0 ) ), iPort ( 0 ) {} + CHostAddress() : InetAddr(), iPort ( 0 ) {} CHostAddress ( const QHostAddress& NInetAddr, const quint16 iNPort ) : InetAddr ( NInetAddr ), iPort ( iNPort ) {} @@ -1038,7 +1038,7 @@ class CServerCoreInfo class CServerInfo : public CServerCoreInfo { public: - CServerInfo() : HostAddr ( CHostAddress() ), LHostAddr ( CHostAddress() ) {} + CServerInfo() : HostAddr4 ( CHostAddress() ), LHostAddr4 ( CHostAddress() ), HostAddr6 ( CHostAddress() ), LHostAddr6 ( CHostAddress() ) {} CServerInfo ( const CHostAddress& NHAddr, const CHostAddress& NLAddr, @@ -1047,16 +1047,40 @@ class CServerInfo : public CServerCoreInfo const QString& NsCity, const int NiMaxNumClients, const bool NbPermOnline ) : - CServerCoreInfo ( NsName, NeCountry, NsCity, NiMaxNumClients, NbPermOnline ), - HostAddr ( NHAddr ), - LHostAddr ( NLAddr ) - {} + CServerCoreInfo ( NsName, NeCountry, NsCity, NiMaxNumClients, NbPermOnline ) + { + if ( NHAddr.InetAddr.protocol() == QAbstractSocket::IPv4Protocol ) + { + HostAddr4 = NHAddr; + } + + if ( NLAddr.InetAddr.protocol() == QAbstractSocket::IPv4Protocol ) + { + LHostAddr4 = NLAddr; + } + + if ( NHAddr.InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) + { + HostAddr6 = NHAddr; + } + + if ( NLAddr.InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) + { + LHostAddr6 = NLAddr; + } + } + + // IPv4 address of the server + CHostAddress HostAddr4; + + // IPv4 internal address of the server + CHostAddress LHostAddr4; - // internet address of the server - CHostAddress HostAddr; + // IPv6 address of the server + CHostAddress HostAddr6; - // server internal address - CHostAddress LHostAddr; + // IPv6 internal address of the server + CHostAddress LHostAddr6; }; // Network transport properties ------------------------------------------------