diff --git a/ChangeLog b/ChangeLog index a543adceb2..0ea8a77b27 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,9 @@ - remove ConsoleWriterFactory (#926) +- add new --serverpublicip option to support central servers behind NAT, + coded by hoffie (#954) + TODO fix crash if settings are changed in ASIO4All during a connection (contained in #796) diff --git a/src/main.cpp b/src/main.cpp index 9f59fda66b..f47b790150 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,6 +83,7 @@ int main ( int argc, char** argv ) QString strRecordingDirName = ""; QString strCentralServer = ""; QString strServerInfo = ""; + QString strServerPublicIP = ""; QString strServerListFilter = ""; QString strWelcomeMessage = ""; QString strClientName = ""; @@ -397,6 +398,20 @@ int main ( int argc, char** argv ) } + // Server Public IP -------------------------------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--serverpublicip", // no short form + "--serverpublicip", + strArgument ) ) + { + strServerPublicIP = strArgument; + CommandLineOptions << "--serverpublicip"; + continue; + } + + // Server info --------------------------------------------------------- if ( GetStringArgument ( argc, argv, @@ -696,6 +711,7 @@ int main ( int argc, char** argv ) strHTMLStatusFileName, strCentralServer, strServerInfo, + strServerPublicIP, strServerListFilter, strWelcomeMessage, strRecordingDirName, @@ -814,6 +830,9 @@ QString UsageArguments ( char **argv ) " -u, --numchannels maximum number of channels\n" " -w, --welcomemessage welcome message on connect\n" " -z, --startminimized start minimizied\n" + " --serverpublicip specify your public IP address when\n" + " running a slave and your own central server\n" + " behind the same NAT\n" "\nClient only:\n" " -M, --mutestream starts the application in muted state\n" " --mutemyown mute me in my personal mix (headless only)\n" diff --git a/src/protocol.cpp b/src/protocol.cpp index 0e94b99c75..de027c2b5c 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -303,6 +303,9 @@ CONNECTION LESS MESSAGES NOTE: In the PROTMESSID_CLM_SERVER_LIST list, this field will be empty as only the initial IP address should be used by the client. Where necessary, that value will contain the server internal address. + When running a central server and a slave server behind the same NAT, + this field is used the other way round: It will contain the public + IP in this case which will be served to clients from the Internet. - PROTMESSID_CLM_REGISTER_SERVER_EX: Register a server, providing extended server diff --git a/src/server.cpp b/src/server.cpp index e80df8e020..9ef505efb5 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -226,6 +226,7 @@ CServer::CServer ( const int iNewMaxNumChan, const QString& strCentralServer, const QString& strServerInfo, const QString& strServerListFilter, + const QString& strServerPublicIP, const QString& strNewWelcomeMessage, const QString& strRecordingDirName, const bool bNDisconnectAllClientsOnQuit, @@ -245,6 +246,7 @@ CServer::CServer ( const int iNewMaxNumChan, ServerListManager ( iPortNumber, strCentralServer, strServerInfo, + strServerPublicIP, strServerListFilter, iNewMaxNumChan, &ConnLessProtocol ), diff --git a/src/server.h b/src/server.h index 119ef0e94f..b4e78b4462 100755 --- a/src/server.h +++ b/src/server.h @@ -176,6 +176,7 @@ class CServer : const QString& strCentralServer, const QString& strServerInfo, const QString& strServerListFilter, + const QString& strServerPublicIP, const QString& strNewWelcomeMessage, const QString& strRecordingDirName, const bool bNDisconnectAllClientsOnQuit, diff --git a/src/serverlist.cpp b/src/serverlist.cpp index e835fcd80f..79d0c4768d 100755 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -29,6 +29,7 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, const QString& sNCentServAddr, const QString& strServerInfo, const QString& strServerListFilter, + const QString& strServerPublicIP, const int iNumChannels, CProtocol* pNConLProt ) : eCentralServerAddressType ( AT_CUSTOM ), // must be AT_CUSTOM for the "no GUI" case @@ -41,7 +42,18 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, SetCentralServerAddress ( sNCentServAddr ); // set the server internal address, including internal port number - SlaveCurLocalHostAddress = CHostAddress( NetworkUtil::GetLocalAddress().InetAddr, iNPortNum ); + QHostAddress qhaServerPublicIP; + if ( strServerPublicIP == "" ) + { + // No user-supplied override via --serverpublicip -> use auto-detection + qhaServerPublicIP = NetworkUtil::GetLocalAddress().InetAddr; + } + else + { + // User-supplied --serverpublicip + qhaServerPublicIP = QHostAddress ( strServerPublicIP ); + } + SlaveCurLocalHostAddress = CHostAddress ( qhaServerPublicIP, iNPortNum ); // prepare the server info information QStringList slServInfoSeparateParams; @@ -443,6 +455,20 @@ void CServerListManager::CentralServerQueryServerList ( const CHostAddress& Inet { vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr; } + else if ( !NetworkUtil::IsPrivateNetworkIP ( InetAddr.InetAddr ) && + NetworkUtil::IsPrivateNetworkIP ( vecServerInfo[iIdx].HostAddr.InetAddr ) && + !NetworkUtil::IsPrivateNetworkIP ( ServerList[iIdx].LHostAddr.InetAddr ) ) + { + // We've got a request from a public client, the server + // list's entry's primary address is a private address, + // but it supplied an additional public address using + // --serverpublicip. + // In this case, use the latter. + // This is common when running a central server with slave + // servers behind a NAT and dealing with external, public + // clients. + vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr; + } else { // create "send empty message" for all registered servers diff --git a/src/serverlist.h b/src/serverlist.h index 51a494c56d..6fa1df276e 100755 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -128,6 +128,7 @@ class CServerListManager : public QObject const QString& sNCentServAddr, const QString& strServerInfo, const QString& strServerListFilter, + const QString& strServerPublicIP, const int iNumChannels, CProtocol* pNConLProt ); diff --git a/src/util.cpp b/src/util.cpp index 5df6d9d29e..2790bc4185 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -1059,6 +1059,28 @@ QString NetworkUtil::FixAddress ( const QString& strAddress ) return strAddress.simplified().replace ( " ", "" ); } +// Return whether the given HostAdress is within a private IP range +// as per RFC 1918 & RFC 5735. +bool NetworkUtil::IsPrivateNetworkIP ( const QHostAddress &qhAddr ) +{ + // https://www.rfc-editor.org/rfc/rfc1918 + // https://www.rfc-editor.org/rfc/rfc5735 + static QList> addresses = + { + QPair ( QHostAddress ( "10.0.0.0" ), 8 ), + QPair ( QHostAddress ( "127.0.0.0" ), 8 ), + QPair ( QHostAddress ( "172.16.0.0" ), 12 ), + QPair ( QHostAddress ( "192.168.0.0" ), 16 ), + }; + + foreach ( auto item, addresses ) { + if ( qhAddr.isInSubnet ( item ) ) { + return true; + } + } + return false; +} + // Instrument picture data base ------------------------------------------------ CVector& CInstPictures::GetTable ( const bool bReGenerateTable ) diff --git a/src/util.h b/src/util.h index 7dadac409b..3c8d766de7 100755 --- a/src/util.h +++ b/src/util.h @@ -1104,6 +1104,7 @@ class NetworkUtil static CHostAddress GetLocalAddress(); static QString GetCentralServerAddress ( const ECSAddType eCentralServerAddressType, const QString& strCentralServerAddress ); + static bool IsPrivateNetworkIP ( const QHostAddress& qhAddr ); };