Skip to content

Commit

Permalink
Merge pull request #2930 from hoffie/client-srv-support
Browse files Browse the repository at this point in the history
Client: Add SRV-based virtual hosting support
  • Loading branch information
ann0see authored Nov 15, 2022
2 parents d070ff8 + ed18ad6 commit 26701d9
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 27 deletions.
4 changes: 4 additions & 0 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,11 @@ void CClient::StartDelayTimer()
bool CClient::SetServerAddr ( QString strNAddr )
{
CHostAddress HostAddress;
#ifdef CLIENT_NO_SRV_CONNECT
if ( NetworkUtil().ParseNetworkAddress ( strNAddr, HostAddress, bEnableIPv6 ) )
#else
if ( NetworkUtil().ParseNetworkAddressWithSrvDiscovery ( strNAddr, HostAddress, bEnableIPv6 ) )
#endif
{
// apply address to the channel
Channel.SetAddress ( HostAddress );
Expand Down
120 changes: 95 additions & 25 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,100 @@ QSize CMinimumStackedLayout::sizeHint() const
* Other Classes *
\******************************************************************************/
// Network utility functions ---------------------------------------------------
bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, bool bEnableIPv6 )
{
// try to get host by name, assuming
// that the string contains a valid host name string or IP address
const QHostInfo HostInfo = QHostInfo::fromName ( strAddress );

if ( HostInfo.error() != QHostInfo::NoError )
{
// qInfo() << qUtf8Printable ( QString ( "Invalid hostname" ) );
return false; // invalid address
}

foreach ( const QHostAddress HostAddr, HostInfo.addresses() )
{
// 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 ) )
{
InetAddr = HostAddr;
return true;
}
}
return false;
}

#ifndef CLIENT_NO_SRV_CONNECT
bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
{
// init requested host address with invalid address first
HostAddress = CHostAddress();

QRegularExpression plainHostRegex ( "^([^\\[:0-9.][^:]*)$" );
if ( plainHostRegex.match ( strAddress ).capturedStart() != 0 )
{
// not a plain hostname? then don't attempt SRV lookup and fail
// immediately.
return false;
}

QDnsLookup* dns = new QDnsLookup();
dns->setType ( QDnsLookup::SRV );
dns->setName ( QString ( "_jamulus._udp.%1" ).arg ( strAddress ) );
dns->lookup();
// QDnsLookup::lookup() works asynchronously. Therefore, wait for
// it to complete here by resuming the main loop here.
// This is not nice and blocks the UI, but is similar to what
// the regular resolve function does as well.
QTime dieTime = QTime::currentTime().addMSecs ( DNS_SRV_RESOLVE_TIMEOUT_MS );
while ( QTime::currentTime() < dieTime && !dns->isFinished() )
{
QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 );
}
QList<QDnsServiceRecord> records = dns->serviceRecords();
dns->deleteLater();
if ( records.length() != 1 )
{
return false;
}
QDnsServiceRecord record = records.first();
if ( record.target() == "." || record.target() == "" )
{
// RFC2782 says that "." indicates that the service is not available.
// Qt strips the trailing dot, which is why we check for empty string
// as well. Therefore, the "." part might be redundant, but this would
// 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 );
return true;
}
qDebug() << qUtf8Printable (
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 ) )
{
HostAddress = CHostAddress ( InetAddr, record.port() );
return true;
}
return false;
}

bool NetworkUtil::ParseNetworkAddressWithSrvDiscovery ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
{
// Try SRV-based discovery first:
if ( ParseNetworkAddressSrv ( strAddress, HostAddress, bEnableIPv6 ) )
{
return true;
}
// Try regular connect via plain IP or host name lookup (A/AAAA):
return ParseNetworkAddress ( strAddress, HostAddress, bEnableIPv6 );
}
#endif

bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
{
QHostAddress InetAddr;
Expand Down Expand Up @@ -971,31 +1065,7 @@ bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAd
return false; // invalid address
}

// try to get host by name, assuming
// that the string contains a valid host name string
const QHostInfo HostInfo = QHostInfo::fromName ( strAddress );

if ( HostInfo.error() != QHostInfo::NoError )
{
// qInfo() << qUtf8Printable ( QString ( "Invalid hostname" ) );
return false; // invalid address
}

bool bFoundAddr = false;

foreach ( const QHostAddress HostAddr, HostInfo.addresses() )
{
// 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 ) )
{
InetAddr = HostAddr;
bFoundAddr = true;
break;
}
}

if ( !bFoundAddr )
if ( !ParseNetworkAddressString ( strAddress, InetAddr, bEnableIPv6 ) )
{
// no valid address found
// qInfo() << qUtf8Printable ( QString ( "No IP address found for hostname" ) );
Expand Down
14 changes: 12 additions & 2 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
#include <QElapsedTimer>
#include <QTextBoundaryFinder>
#include <QTimer>
#ifndef CLIENT_NO_SRV_CONNECT
# include <QDnsLookup>
#endif
#ifndef _WIN32
# include <QThread>
#endif
Expand All @@ -79,8 +82,9 @@ class CClient; // forward declaration of CClient
#endif

/* Definitions ****************************************************************/
#define METER_FLY_BACK 2
#define INVALID_MIDI_CH -1 // invalid MIDI channel definition
#define METER_FLY_BACK 2
#define INVALID_MIDI_CH -1 // invalid MIDI channel definition
#define DNS_SRV_RESOLVE_TIMEOUT_MS 500

/* Global functions ***********************************************************/
// converting float to short
Expand Down Expand Up @@ -1039,6 +1043,12 @@ class CNetworkTransportProps
class NetworkUtil
{
public:
static bool ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, bool bEnableIPv6 );

#ifndef CLIENT_NO_SRV_CONNECT
static bool ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
static bool ParseNetworkAddressWithSrvDiscovery ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
#endif
static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );

static QString FixAddress ( const QString& strAddress );
Expand Down

0 comments on commit 26701d9

Please sign in to comment.