Skip to content

Commit ed18ad6

Browse files
committed
Client: Add SRV-based virtual hosting support
When connecting to host names without port (e.g. example.org), the client will now perform an SRV lookup to _jamulus._udp.example.org. If this lookup returns exactly one result, the result is used to select the actual target address/port. If the lookup does not return a usable result, the regular connect logic kicks in (A lookup, default port). Related: https://github.com/orgs/jamulussoftware/discussions/1772
1 parent 24adc20 commit ed18ad6

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

src/client.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,11 @@ void CClient::StartDelayTimer()
477477
bool CClient::SetServerAddr ( QString strNAddr )
478478
{
479479
CHostAddress HostAddress;
480+
#ifdef CLIENT_NO_SRV_CONNECT
480481
if ( NetworkUtil().ParseNetworkAddress ( strNAddr, HostAddress, bEnableIPv6 ) )
482+
#else
483+
if ( NetworkUtil().ParseNetworkAddressWithSrvDiscovery ( strNAddr, HostAddress, bEnableIPv6 ) )
484+
#endif
481485
{
482486
// apply address to the channel
483487
Channel.SetAddress ( HostAddress );

src/util.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,75 @@ bool NetworkUtil::ParseNetworkAddressString ( QString strAddress, QHostAddress&
914914
return false;
915915
}
916916

917+
#ifndef CLIENT_NO_SRV_CONNECT
918+
bool NetworkUtil::ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
919+
{
920+
// init requested host address with invalid address first
921+
HostAddress = CHostAddress();
922+
923+
QRegularExpression plainHostRegex ( "^([^\\[:0-9.][^:]*)$" );
924+
if ( plainHostRegex.match ( strAddress ).capturedStart() != 0 )
925+
{
926+
// not a plain hostname? then don't attempt SRV lookup and fail
927+
// immediately.
928+
return false;
929+
}
930+
931+
QDnsLookup* dns = new QDnsLookup();
932+
dns->setType ( QDnsLookup::SRV );
933+
dns->setName ( QString ( "_jamulus._udp.%1" ).arg ( strAddress ) );
934+
dns->lookup();
935+
// QDnsLookup::lookup() works asynchronously. Therefore, wait for
936+
// it to complete here by resuming the main loop here.
937+
// This is not nice and blocks the UI, but is similar to what
938+
// the regular resolve function does as well.
939+
QTime dieTime = QTime::currentTime().addMSecs ( DNS_SRV_RESOLVE_TIMEOUT_MS );
940+
while ( QTime::currentTime() < dieTime && !dns->isFinished() )
941+
{
942+
QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 );
943+
}
944+
QList<QDnsServiceRecord> records = dns->serviceRecords();
945+
dns->deleteLater();
946+
if ( records.length() != 1 )
947+
{
948+
return false;
949+
}
950+
QDnsServiceRecord record = records.first();
951+
if ( record.target() == "." || record.target() == "" )
952+
{
953+
// RFC2782 says that "." indicates that the service is not available.
954+
// Qt strips the trailing dot, which is why we check for empty string
955+
// as well. Therefore, the "." part might be redundant, but this would
956+
// need further testing to confirm.
957+
// End processing here (= return true), but pass back an
958+
// invalid HostAddress to let the connect logic fail properly.
959+
HostAddress = CHostAddress ( QHostAddress ( "." ), 0 );
960+
return true;
961+
}
962+
qDebug() << qUtf8Printable (
963+
QString ( "resolved %1 to a single SRV record: %2:%3" ).arg ( strAddress ).arg ( record.target() ).arg ( record.port() ) );
964+
965+
QHostAddress InetAddr;
966+
if ( ParseNetworkAddressString ( record.target(), InetAddr, bEnableIPv6 ) )
967+
{
968+
HostAddress = CHostAddress ( InetAddr, record.port() );
969+
return true;
970+
}
971+
return false;
972+
}
973+
974+
bool NetworkUtil::ParseNetworkAddressWithSrvDiscovery ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
975+
{
976+
// Try SRV-based discovery first:
977+
if ( ParseNetworkAddressSrv ( strAddress, HostAddress, bEnableIPv6 ) )
978+
{
979+
return true;
980+
}
981+
// Try regular connect via plain IP or host name lookup (A/AAAA):
982+
return ParseNetworkAddress ( strAddress, HostAddress, bEnableIPv6 );
983+
}
984+
#endif
985+
917986
bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
918987
{
919988
QHostAddress InetAddr;

src/util.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
#include <QElapsedTimer>
5454
#include <QTextBoundaryFinder>
5555
#include <QTimer>
56+
#ifndef CLIENT_NO_SRV_CONNECT
57+
# include <QDnsLookup>
58+
#endif
5659
#ifndef _WIN32
5760
# include <QThread>
5861
#endif
@@ -79,8 +82,9 @@ class CClient; // forward declaration of CClient
7982
#endif
8083

8184
/* Definitions ****************************************************************/
82-
#define METER_FLY_BACK 2
83-
#define INVALID_MIDI_CH -1 // invalid MIDI channel definition
85+
#define METER_FLY_BACK 2
86+
#define INVALID_MIDI_CH -1 // invalid MIDI channel definition
87+
#define DNS_SRV_RESOLVE_TIMEOUT_MS 500
8488

8589
/* Global functions ***********************************************************/
8690
// converting float to short
@@ -1039,6 +1043,11 @@ class NetworkUtil
10391043
{
10401044
public:
10411045
static bool ParseNetworkAddressString ( QString strAddress, QHostAddress& InetAddr, bool bEnableIPv6 );
1046+
1047+
#ifndef CLIENT_NO_SRV_CONNECT
1048+
static bool ParseNetworkAddressSrv ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
1049+
static bool ParseNetworkAddressWithSrvDiscovery ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
1050+
#endif
10421051
static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
10431052

10441053
static QString FixAddress ( const QString& strAddress );

0 commit comments

Comments
 (0)