From 6a59ae2443b25942e8460980290e7cdfaf13c87e Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Mon, 21 Sep 2020 03:30:05 +0200 Subject: [PATCH 1/2] REFAC(client, server): replace "Bonjour" with "Zeroconf", except for 3rdparty This should make it more clear that we don't include Bonjour-related stuff in our project. We use external libraries depending on the OS. For compatibility, the server option is still called "bonjour". We should probably add a new option called "zeroconf" and then handle the old one if present in the configuration file. --- .ci/travis-ci/script.bash | 2 +- src/CMakeLists.txt | 2 +- src/mumble/CMakeLists.txt | 11 +- src/mumble/ConnectDialog.cpp | 112 +++++++++--------- src/mumble/ConnectDialog.h | 18 +-- src/mumble/Global.cpp | 6 +- src/mumble/Global.h | 4 +- .../{BonjourClient.cpp => Zeroconf.cpp} | 4 +- src/mumble/{BonjourClient.h => Zeroconf.h} | 10 +- src/mumble/main.cpp | 14 +-- src/mumble/mumble_pch.hpp | 2 +- src/murmur/CMakeLists.txt | 11 +- src/murmur/Server.cpp | 47 ++++---- src/murmur/Server.h | 12 +- .../{BonjourServer.cpp => Zeroconf.cpp} | 6 +- src/murmur/{BonjourServer.h => Zeroconf.h} | 12 +- src/murmur/murmur_pch.h | 2 +- 17 files changed, 139 insertions(+), 136 deletions(-) rename src/mumble/{BonjourClient.cpp => Zeroconf.cpp} (90%) rename src/mumble/{BonjourClient.h => Zeroconf.h} (74%) rename src/murmur/{BonjourServer.cpp => Zeroconf.cpp} (87%) rename src/murmur/{BonjourServer.h => Zeroconf.h} (67%) diff --git a/.ci/travis-ci/script.bash b/.ci/travis-ci/script.bash index 78664c0c3..34af33f6a 100755 --- a/.ci/travis-ci/script.bash +++ b/.ci/travis-ci/script.bash @@ -39,7 +39,7 @@ if [ "${TRAVIS_OS_NAME}" == "linux" ]; then PATH=$PATH:/usr/lib/mxe/usr/bin ${MUMBLE_HOST}.static-cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -Dtests=ON -Dversion=$VER -Dstatic=ON -Dsymbols=ON -Dasio=ON \ - -Dbonjour=OFF -Dice=OFF -Doverlay=OFF -Donline-tests=ON .. + -Dzeroconf=OFF -Dice=OFF -Doverlay=OFF -Donline-tests=ON .. cmake --build . # TODO: investigate why tests fail. #ctest diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c450a9f8c..df1aa8de8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,7 @@ option(server "Build the server (Murmur)" ON) option(qssldiffiehellmanparameters "Build support for custom Diffie-Hellman parameters." ON) -option(bonjour "Build support for Bonjour." ON) +option(zeroconf "Build support for zeroconf (mDNS/DNS-SD)." ON) option(dbus "Build support for DBus." ON) diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 01f7d48b5..8694aee50 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -814,7 +814,7 @@ if(g15) endif() endif() -if(bonjour) +if(zeroconf) if(NOT APPLE) find_pkg(avahi-compat-libdns_sd QUIET) if(avahi-compat-libdns_sd_FOUND) @@ -831,9 +831,10 @@ if(bonjour) target_sources(mumble PRIVATE - "BonjourClient.cpp" - "BonjourClient.h" - + "Zeroconf.cpp" + "Zeroconf.h" + # Unlike what the name implies, this 3rdparty helper is not actually related to Bonjour. + # It just uses the API provided by mDNSResponder, making it compatible with Avahi too. "${3RDPARTY_DIR}/qqbonjour/BonjourRecord.h" "${3RDPARTY_DIR}/qqbonjour/BonjourServiceBrowser.cpp" "${3RDPARTY_DIR}/qqbonjour/BonjourServiceBrowser.h" @@ -841,7 +842,7 @@ if(bonjour) "${3RDPARTY_DIR}/qqbonjour/BonjourServiceResolver.h" ) - target_compile_definitions(mumble PRIVATE "USE_BONJOUR") + target_compile_definitions(mumble PRIVATE "USE_ZEROCONF") target_include_directories(mumble PRIVATE "${3RDPARTY_DIR}/qqbonjour") endif() diff --git a/src/mumble/ConnectDialog.cpp b/src/mumble/ConnectDialog.cpp index 941b130f2..52a129267 100644 --- a/src/mumble/ConnectDialog.cpp +++ b/src/mumble/ConnectDialog.cpp @@ -5,8 +5,8 @@ #include "ConnectDialog.h" -#ifdef USE_BONJOUR -# include "BonjourClient.h" +#ifdef USE_ZEROCONF +# include "Zeroconf.h" # include "BonjourServiceBrowser.h" # include "BonjourServiceResolver.h" #endif @@ -102,7 +102,7 @@ ServerView::ServerView(QWidget *p) : QTreeWidget(p) { siFavorite->setExpanded(true); siFavorite->setHidden(true); -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF siLAN = new ServerItem(tr("LAN"), ServerItem::LANType); addTopLevelItem(siLAN); siLAN->setExpanded(true); @@ -220,10 +220,10 @@ ServerItem::ServerItem(const FavoriteServer &fs) : QTreeWidgetItem(QTreeWidgetIt qsUrl = fs.qsUrl; bCA = false; -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF if (fs.qsHostname.startsWith(QLatin1Char('@'))) { - qsBonjourHost = fs.qsHostname.mid(1); - brRecord = BonjourRecord(qsBonjourHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); + zeroconfHost = fs.qsHostname.mid(1); + zeroconfRecord = BonjourRecord(zeroconfHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); } else { qsHostname = fs.qsHostname; } @@ -261,10 +261,10 @@ ServerItem::ServerItem(const QString &name, const QString &host, unsigned short qsPassword = password; bCA = false; -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF if (host.startsWith(QLatin1Char('@'))) { - qsBonjourHost = host.mid(1); - brRecord = BonjourRecord(qsBonjourHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); + zeroconfHost = host.mid(1); + zeroconfRecord = BonjourRecord(zeroconfHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); } else { qsHostname = host; } @@ -274,16 +274,16 @@ ServerItem::ServerItem(const QString &name, const QString &host, unsigned short init(); } -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF ServerItem::ServerItem(const BonjourRecord &br) : QTreeWidgetItem(QTreeWidgetItem::UserType) { - siParent = nullptr; - bParent = false; - itType = LANType; - qsName = br.serviceName; - qsBonjourHost = qsName; - brRecord = br; - usPort = 0; - bCA = false; + siParent = nullptr; + bParent = false; + itType = LANType; + qsName = br.serviceName; + zeroconfHost = qsName; + zeroconfRecord = br; + usPort = 0; + bCA = false; init(); } @@ -314,9 +314,9 @@ ServerItem::ServerItem(const ServerItem *si) { qsCountryCode = si->qsCountryCode; qsContinentCode = si->qsContinentCode; qsUrl = si->qsUrl; -#ifdef USE_BONJOUR - qsBonjourHost = si->qsBonjourHost; - brRecord = si->brRecord; +#ifdef USE_ZEROCONF + zeroconfHost = si->zeroconfHost; + zeroconfRecord = si->zeroconfRecord; #endif qlAddresses = si->qlAddresses; bCA = si->bCA; @@ -490,10 +490,10 @@ QVariant ServerItem::data(int column, int role) const { .arg(ConnectDialog::tr("Servername"), qsName.toHtmlEscaped()) + QString::fromLatin1("%1%2") .arg(ConnectDialog::tr("Hostname"), qsHostname.toHtmlEscaped()); -#ifdef USE_BONJOUR - if (!qsBonjourHost.isEmpty()) +#ifdef USE_ZEROCONF + if (!zeroconfHost.isEmpty()) qs += QString::fromLatin1("%1%2") - .arg(ConnectDialog::tr("Bonjour name"), qsBonjourHost.toHtmlEscaped()); + .arg(ConnectDialog::tr("Bonjour name"), zeroconfHost.toHtmlEscaped()); #endif qs += QString::fromLatin1("%1%2") .arg(ConnectDialog::tr("Port")) @@ -592,9 +592,9 @@ void ServerItem::setDatas(double elapsed, quint32 users, quint32 maxusers) { FavoriteServer ServerItem::toFavoriteServer() const { FavoriteServer fs; fs.qsName = qsName; -#ifdef USE_BONJOUR - if (!qsBonjourHost.isEmpty()) - fs.qsHostname = QLatin1Char('@') + qsBonjourHost; +#ifdef USE_ZEROCONF + if (!zeroconfHost.isEmpty()) + fs.qsHostname = QLatin1Char('@') + zeroconfHost; else fs.qsHostname = qsHostname; #else @@ -944,7 +944,7 @@ ConnectDialog::ConnectDialog(QWidget *p, bool autoconnect) : QDialog(p), bAutoCo bAllowPing = g.s.ptProxyType == Settings::NoProxy; bAllowHostLookup = g.s.ptProxyType == Settings::NoProxy; - bAllowBonjour = g.s.ptProxyType == Settings::NoProxy; + bAllowZeroconf = g.s.ptProxyType == Settings::NoProxy; bAllowFilters = g.s.ptProxyType == Settings::NoProxy; if (tPublicServers.elapsed() >= 60 * 24 * 1000000ULL) { @@ -1034,18 +1034,18 @@ ConnectDialog::ConnectDialog(QWidget *p, bool autoconnect) : QDialog(p), bAutoCo qtwServers->siFavorite->addServerItem(si); } -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF // Make sure the we got the objects we need, then wire them up - if (bAllowBonjour && g.bc->bsbBrowser && g.bc->bsrResolver) { - connect(g.bc->bsbBrowser.data(), SIGNAL(error(DNSServiceErrorType)), this, + if (bAllowZeroconf && g.zeroconf->bsbBrowser && g.zeroconf->bsrResolver) { + connect(g.zeroconf->bsbBrowser.data(), SIGNAL(error(DNSServiceErrorType)), this, SLOT(onLanBrowseError(DNSServiceErrorType))); - connect(g.bc->bsbBrowser.data(), SIGNAL(currentBonjourRecordsChanged(const QList< BonjourRecord > &)), this, - SLOT(onUpdateLanList(const QList< BonjourRecord > &))); - connect(g.bc->bsrResolver.data(), SIGNAL(error(BonjourRecord, DNSServiceErrorType)), this, + connect(g.zeroconf->bsbBrowser.data(), SIGNAL(currentBonjourRecordsChanged(const QList< BonjourRecord > &)), + this, SLOT(onUpdateLanList(const QList< BonjourRecord > &))); + connect(g.zeroconf->bsrResolver.data(), SIGNAL(error(BonjourRecord, DNSServiceErrorType)), this, SLOT(onLanResolveError(BonjourRecord, DNSServiceErrorType))); - connect(g.bc->bsrResolver.data(), SIGNAL(bonjourRecordResolved(BonjourRecord, QString, int)), this, + connect(g.zeroconf->bsrResolver.data(), SIGNAL(bonjourRecordResolved(BonjourRecord, QString, int)), this, SLOT(onResolved(BonjourRecord, QString, int))); - onUpdateLanList(g.bc->bsbBrowser->currentRecords()); + onUpdateLanList(g.zeroconf->bsbBrowser->currentRecords()); } #endif @@ -1175,9 +1175,9 @@ void ConnectDialog::on_qaFavoriteEdit_triggered() { return; QString host; -#ifdef USE_BONJOUR - if (!si->qsBonjourHost.isEmpty()) - host = QLatin1Char('@') + si->qsBonjourHost; +#ifdef USE_ZEROCONF + if (!si->zeroconfHost.isEmpty()) + host = QLatin1Char('@') + si->zeroconfHost; else host = si->qsHostname; #else @@ -1196,16 +1196,16 @@ void ConnectDialog::on_qaFavoriteEdit_triggered() { si->reset(); si->usPort = cde->usPort; -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF if (cde->qsHostname.startsWith(QLatin1Char('@'))) { - si->qsHostname = QString(); - si->qsBonjourHost = cde->qsHostname.mid(1); - si->brRecord = - BonjourRecord(si->qsBonjourHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); + si->qsHostname = QString(); + si->zeroconfHost = cde->qsHostname.mid(1); + si->zeroconfRecord = + BonjourRecord(si->zeroconfHost, QLatin1String("_mumble._tcp."), QLatin1String("local.")); } else { - si->qsHostname = cde->qsHostname; - si->qsBonjourHost = QString(); - si->brRecord = BonjourRecord(); + si->qsHostname = cde->qsHostname; + si->zeroconfHost = QString(); + si->zeroconfRecord = BonjourRecord(); } #else si->qsHostname = cde->qsHostname; @@ -1355,11 +1355,11 @@ void ConnectDialog::initList() { WebFetch::fetch(QLatin1String("publist"), url, this, SLOT(fetched(QByteArray, QUrl, QMap< QString, QString >))); } -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF void ConnectDialog::onResolved(BonjourRecord record, QString host, int port) { qlBonjourActive.removeAll(record); foreach (ServerItem *si, qlItems) { - if (si->brRecord == record) { + if (si->zeroconfRecord == record) { unsigned short usport = static_cast< unsigned short >(port); if ((host != si->qsHostname) || (usport != si->usPort)) { stopDns(si); @@ -1384,7 +1384,7 @@ void ConnectDialog::onUpdateLanList(const QList< BonjourRecord > &list) { foreach (const BonjourRecord &record, list) { bool found = false; foreach (ServerItem *si, old) { - if (si->brRecord == record) { + if (si->zeroconfRecord == record) { items.insert(si); found = true; break; @@ -1393,7 +1393,7 @@ void ConnectDialog::onUpdateLanList(const QList< BonjourRecord > &list) { if (!found) { ServerItem *si = new ServerItem(record); qlItems << si; - g.bc->bsrResolver->resolveBonjourRecord(record); + g.zeroconf->bsrResolver->resolveBonjourRecord(record); startDns(si); qtwServers->siLAN->addServerItem(si); } @@ -1628,11 +1628,11 @@ void ConnectDialog::startDns(ServerItem *si) { return; } -#ifdef USE_BONJOUR - if (bAllowBonjour && si->qsHostname.isEmpty() && !si->brRecord.serviceName.isEmpty()) { - if (!qlBonjourActive.contains(si->brRecord)) { - g.bc->bsrResolver->resolveBonjourRecord(si->brRecord); - qlBonjourActive.append(si->brRecord); +#ifdef USE_ZEROCONF + if (bAllowZeroconf && si->qsHostname.isEmpty() && !si->zeroconfRecord.serviceName.isEmpty()) { + if (!qlBonjourActive.contains(si->zeroconfRecord)) { + g.zeroconf->bsrResolver->resolveBonjourRecord(si->zeroconfRecord); + qlBonjourActive.append(si->zeroconfRecord); } return; } diff --git a/src/mumble/ConnectDialog.h b/src/mumble/ConnectDialog.h index e04b66c70..ffa5c94ce 100644 --- a/src/mumble/ConnectDialog.h +++ b/src/mumble/ConnectDialog.h @@ -20,7 +20,7 @@ #include -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF # include "BonjourRecord.h" # include #endif @@ -136,9 +136,9 @@ public: QString qsContinentCode; QString qsUrl; -#ifdef USE_BONJOUR - QString qsBonjourHost; - BonjourRecord brRecord; +#ifdef USE_ZEROCONF + QString zeroconfHost; + BonjourRecord zeroconfRecord; #endif /// Contains the resolved addresses for /// this ServerItem. @@ -150,7 +150,7 @@ public: ServerItem(const PublicInfo &pi); ServerItem(const QString &name, const QString &host, unsigned short port, const QString &uname, const QString &password = QString()); -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF ServerItem(const BonjourRecord &br); #endif ServerItem(const QString &name, ItemType itype); @@ -279,9 +279,9 @@ protected: /// bAllowHostLookup determines whether ConnectDialog can /// resolve hosts via DNS, Bonjour, and so on. bool bAllowHostLookup; - /// bAllowBonjour determines whether ConfigDialog can use - /// Bonjour to find nearby servers on the local network. - bool bAllowBonjour; + /// bAllowZeroconf determines whether ConfigDialog can use + /// zeroconf to find nearby servers on the local network. + bool bAllowZeroconf; /// bAllowFilters determines whether filters are available /// in the ConfigDialog. If this option is diabled, the /// 'Show All' filter is forced, and no other filter can @@ -353,7 +353,7 @@ public: ConnectDialog(QWidget *parent, bool autoconnect); ~ConnectDialog(); -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF protected: QList< BonjourRecord > qlBonjourActive; public slots: diff --git a/src/mumble/Global.cpp b/src/mumble/Global.cpp index c042c7f49..fcb0dc8dc 100644 --- a/src/mumble/Global.cpp +++ b/src/mumble/Global.cpp @@ -105,9 +105,9 @@ Global::Global(const QString &qsConfigPath) { qs = nullptr; - bc = nullptr; - lcd = nullptr; - l = nullptr; + zeroconf = nullptr; + lcd = nullptr; + l = nullptr; #ifdef USE_OVERLAY ocIntercept = nullptr; diff --git a/src/mumble/Global.h b/src/mumble/Global.h index 74d901007..e3094014c 100644 --- a/src/mumble/Global.h +++ b/src/mumble/Global.h @@ -26,7 +26,7 @@ class Plugins; class QSettings; class Overlay; class LCD; -class BonjourClient; +class Zeroconf; class OverlayClient; class CELTCodec; class OpusCodec; @@ -57,7 +57,7 @@ public: Overlay *o; #endif LCD *lcd; - BonjourClient *bc; + Zeroconf *zeroconf; QNetworkAccessManager *nam; QSharedPointer< LogEmitter > le; DeveloperConsole *c; diff --git a/src/mumble/BonjourClient.cpp b/src/mumble/Zeroconf.cpp similarity index 90% rename from src/mumble/BonjourClient.cpp rename to src/mumble/Zeroconf.cpp index 4f04e6c6f..3d3af6f15 100644 --- a/src/mumble/BonjourClient.cpp +++ b/src/mumble/Zeroconf.cpp @@ -3,9 +3,9 @@ // that can be found in the LICENSE file at the root of the // Mumble source tree or at . -#include "BonjourClient.h" +#include "Zeroconf.h" -BonjourClient::BonjourClient() { +Zeroconf::Zeroconf() { #ifdef Q_OS_WIN HMODULE hLib = LoadLibrary(L"DNSSD.DLL"); if (!hLib) { diff --git a/src/mumble/BonjourClient.h b/src/mumble/Zeroconf.h similarity index 74% rename from src/mumble/BonjourClient.h rename to src/mumble/Zeroconf.h index 2a7fd57ac..9c0cdd4a7 100644 --- a/src/mumble/BonjourClient.h +++ b/src/mumble/Zeroconf.h @@ -3,18 +3,18 @@ // that can be found in the LICENSE file at the root of the // Mumble source tree or at . -#ifndef MUMBLE_MUMBLE_BONJOURCLIENT_H_ -#define MUMBLE_MUMBLE_BONJOURCLIENT_H_ +#ifndef MUMBLE_MUMBLE_ZEROCONF_H_ +#define MUMBLE_MUMBLE_ZEROCONF_H_ #include "BonjourServiceBrowser.h" #include "BonjourServiceResolver.h" -class BonjourClient : public QObject { +class Zeroconf : public QObject { private: Q_OBJECT - Q_DISABLE_COPY(BonjourClient) + Q_DISABLE_COPY(Zeroconf) public: - BonjourClient(); + Zeroconf(); QScopedPointer< BonjourServiceBrowser > bsbBrowser; QScopedPointer< BonjourServiceResolver > bsrResolver; diff --git a/src/mumble/main.cpp b/src/mumble/main.cpp index 093dac7ad..ad687986d 100644 --- a/src/mumble/main.cpp +++ b/src/mumble/main.cpp @@ -18,8 +18,8 @@ #include "MainWindow.h" #include "Plugins.h" #include "ServerHandler.h" -#ifdef USE_BONJOUR -# include "BonjourClient.h" +#ifdef USE_ZEROCONF +# include "Zeroconf.h" #endif #ifdef USE_DBUS # include "DBus.h" @@ -496,9 +496,9 @@ int main(int argc, char **argv) { // Initialize database g.db = new Database(QLatin1String("main")); -#ifdef USE_BONJOUR - // Initialize bonjour - g.bc = new BonjourClient(); +#ifdef USE_ZEROCONF + // Initialize zeroconf + g.zeroconf = new Zeroconf(); #endif #ifdef USE_OVERLAY @@ -701,8 +701,8 @@ int main(int argc, char **argv) { delete g.p; delete g.l; -#ifdef USE_BONJOUR - delete g.bc; +#ifdef USE_ZEROCONF + delete g.zeroconf; #endif #ifdef USE_OVERLAY diff --git a/src/mumble/mumble_pch.hpp b/src/mumble/mumble_pch.hpp index 76dab5fea..874e6d50a 100644 --- a/src/mumble/mumble_pch.hpp +++ b/src/mumble/mumble_pch.hpp @@ -98,7 +98,7 @@ # include # endif -# ifdef USE_BONJOUR +# ifdef USE_ZEROCONF # include # endif diff --git a/src/murmur/CMakeLists.txt b/src/murmur/CMakeLists.txt index 45733927c..87b61d272 100644 --- a/src/murmur/CMakeLists.txt +++ b/src/murmur/CMakeLists.txt @@ -145,7 +145,7 @@ else() set_target_properties(murmur PROPERTIES OUTPUT_NAME "murmurd") endif() -if(bonjour) +if(zeroconf) if(NOT APPLE) find_pkg(avahi-compat-libdns_sd QUIET) if(avahi-compat-libdns_sd_FOUND) @@ -160,15 +160,16 @@ if(bonjour) endif() endif() - target_compile_definitions(murmur PRIVATE "USE_BONJOUR") + target_compile_definitions(murmur PRIVATE "USE_ZEROCONF") target_include_directories(murmur PRIVATE "${3RDPARTY_DIR}/qqbonjour") target_sources(murmur PRIVATE - "BonjourServer.cpp" - "BonjourServer.h" - + "Zeroconf.cpp" + "Zeroconf.h" + # Unlike what the name implies, this 3rdparty helper is not actually related to Bonjour. + # It just uses the API provided by mDNSResponder, making it compatible with Avahi too. "${3RDPARTY_DIR}/qqbonjour/BonjourRecord.h" "${3RDPARTY_DIR}/qqbonjour/BonjourServiceRegister.cpp" "${3RDPARTY_DIR}/qqbonjour/BonjourServiceRegister.h" diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index 7d48b7267..d002a0ed7 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -22,8 +22,8 @@ #include "User.h" #include "Version.h" -#ifdef USE_BONJOUR -# include "BonjourServer.h" +#ifdef USE_ZEROCONF +# include "Zeroconf.h" # include "BonjourServiceRegister.h" #endif @@ -116,8 +116,8 @@ QSslSocket *SslServer::nextPendingSSLConnection() { Server::Server(int snum, QObject *p) : QThread(p) { bValid = true; iServerNum = snum; -#ifdef USE_BONJOUR - bsRegistration = nullptr; +#ifdef USE_ZEROCONF + zeroconf = nullptr; #endif bUsingMetaCert = false; @@ -273,9 +273,9 @@ Server::Server(int snum, QObject *p) : QThread(p) { uiVersionBlob = qToBigEndian(static_cast< quint32 >((major << 16) | (minor << 8) | patch)); if (bValid) { -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF if (bBonjour) - initBonjour(); + initZeroconf(); #endif initRegister(); } @@ -327,8 +327,8 @@ void Server::stopThread() { } Server::~Server() { -#ifdef USE_BONJOUR - removeBonjour(); +#ifdef USE_ZEROCONF + removeZeroconf(); #endif stopThread(); @@ -626,11 +626,12 @@ void Server::setLiveConf(const QString &key, const QString &value) { bForceExternalAuth = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bForceExternalAuth; else if (key == "bonjour") { bBonjour = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bBonjour; -#ifdef USE_BONJOUR - if (bBonjour && !bsRegistration) - initBonjour(); - else if (!bBonjour && bsRegistration) - removeBonjour(); +#ifdef USE_ZEROCONF + if (bBonjour && !zeroconf) { + initZeroconf(); + } else if (!bBonjour && zeroconf) { + removeZeroconf(); + } #endif } else if (key == "allowping") bAllowPing = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bAllowPing; @@ -664,19 +665,19 @@ void Server::setLiveConf(const QString &key, const QString &value) { } } -#ifdef USE_BONJOUR -void Server::initBonjour() { - bsRegistration = new BonjourServer(); - if (bsRegistration->bsrRegister) { - log("Announcing server via bonjour"); - bsRegistration->bsrRegister->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); +#ifdef USE_ZEROCONF +void Server::initZeroconf() { + zeroconf = new Zeroconf(); + if (zeroconf->bsrRegister) { + log("Announcing server via zeroconf"); + zeroconf->bsrRegister->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); } } -void Server::removeBonjour() { - delete bsRegistration; - bsRegistration = nullptr; - log("Stopped announcing server via bonjour"); +void Server::removeZeroconf() { + delete zeroconf; + zeroconf = nullptr; + log("Stopped announcing server via zeroconf"); } #endif diff --git a/src/murmur/Server.h b/src/murmur/Server.h index a104a9c5e..802866f3f 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -45,7 +45,7 @@ # include #endif -class BonjourServer; +class Zeroconf; class Channel; class PacketDataStream; class ServerUser; @@ -98,8 +98,8 @@ protected: QNetworkAccessManager *qnamNetwork; -#ifdef USE_BONJOUR - BonjourServer *bsRegistration; +#ifdef USE_ZEROCONF + Zeroconf *zeroconf; #endif void startThread(); void stopThread(); @@ -173,9 +173,9 @@ public: bool bOpus; void recheckCodecVersions(ServerUser *connectingUser = 0); -#ifdef USE_BONJOUR - void initBonjour(); - void removeBonjour(); +#ifdef USE_ZEROCONF + void initZeroconf(); + void removeZeroconf(); #endif // Registration, implementation in Register.cpp QTimer qtTick; diff --git a/src/murmur/BonjourServer.cpp b/src/murmur/Zeroconf.cpp similarity index 87% rename from src/murmur/BonjourServer.cpp rename to src/murmur/Zeroconf.cpp index 9041557b3..4c19a6596 100644 --- a/src/murmur/BonjourServer.cpp +++ b/src/murmur/Zeroconf.cpp @@ -3,11 +3,11 @@ // that can be found in the LICENSE file at the root of the // Mumble source tree or at . -#include "BonjourServer.h" +#include "Zeroconf.h" #include "BonjourServiceRegister.h" -BonjourServer::BonjourServer() { +Zeroconf::Zeroconf() { bsrRegister = nullptr; #ifdef Q_OS_WIN static bool bDelayLoadFailed = false; @@ -26,6 +26,6 @@ BonjourServer::BonjourServer() { bsrRegister = new BonjourServiceRegister(this); } -BonjourServer::~BonjourServer() { +Zeroconf::~Zeroconf() { delete bsrRegister; } diff --git a/src/murmur/BonjourServer.h b/src/murmur/Zeroconf.h similarity index 67% rename from src/murmur/BonjourServer.h rename to src/murmur/Zeroconf.h index 3258aaefb..cc92f1443 100644 --- a/src/murmur/BonjourServer.h +++ b/src/murmur/Zeroconf.h @@ -3,20 +3,20 @@ // that can be found in the LICENSE file at the root of the // Mumble source tree or at . -#ifndef MUMBLE_MURMUR_BONJOURSERVER_H_ -#define MUMBLE_MURMUR_BONJOURSERVER_H_ +#ifndef MUMBLE_MURMUR_ZEROCONF_H_ +#define MUMBLE_MURMUR_ZEROCONF_H_ #include class BonjourServiceRegister; -class BonjourServer : public QObject { +class Zeroconf : public QObject { private: Q_OBJECT - Q_DISABLE_COPY(BonjourServer) + Q_DISABLE_COPY(Zeroconf) public: - BonjourServer(); - ~BonjourServer(); + Zeroconf(); + ~Zeroconf(); BonjourServiceRegister *bsrRegister; }; diff --git a/src/murmur/murmur_pch.h b/src/murmur/murmur_pch.h index d6f5ca8dd..8eab1b459 100644 --- a/src/murmur/murmur_pch.h +++ b/src/murmur/murmur_pch.h @@ -83,7 +83,7 @@ void __cpuid(int a[4], int b); # include # include -# ifdef USE_BONJOUR +# ifdef USE_ZEROCONF # include # endif From ee731f8405caf42d22a92c8784809f0bc4bb1ad3 Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Tue, 22 Sep 2020 07:36:43 +0200 Subject: [PATCH 2/2] FEAT(client, server): use native mDNS/DNS-SD API on Windows, if available This allows: - The client to find servers advertized via zeroconf without the need for Bonjour to be installed. - The server to advertize itself via zeroconf without the need for Bonjour to be installed. The Win32 API was introduced in the version 10.0.18362.0 (1903/19H1) of Windows SDK. Before that, only the UWP interface was available (introduced in Windows 10 1507). This commit was successfully tested on Windows 10 1809, which probably means that the API can be used on previous versions as well. Even if that isn't the case, it's not a problem: if the code fails to load the required symbols, it falls back to Bonjour. "Q_OS_WIN64" is used instead of "Q_OS_WIN" because of an issue that appears when certain DNS functions are used in an x86 (32 bit) build: https://developercommunity.visualstudio.com/content/problem/1191345/some-dns-api-functions-cause-lnk2019-errors-in-32.html This means that until the issue is fixed we can safely use the native mDNS-DNS-SD API only on x86_64 (64 bit). --- src/mumble/ConnectDialog.cpp | 45 +++--- src/mumble/ConnectDialog.h | 5 +- src/mumble/Zeroconf.cpp | 303 +++++++++++++++++++++++++++++++++-- src/mumble/Zeroconf.h | 63 +++++++- src/murmur/Server.cpp | 20 ++- src/murmur/Zeroconf.cpp | 163 +++++++++++++++++-- src/murmur/Zeroconf.h | 28 +++- 7 files changed, 557 insertions(+), 70 deletions(-) diff --git a/src/mumble/ConnectDialog.cpp b/src/mumble/ConnectDialog.cpp index 52a129267..59ab0c89f 100644 --- a/src/mumble/ConnectDialog.cpp +++ b/src/mumble/ConnectDialog.cpp @@ -7,8 +7,6 @@ #ifdef USE_ZEROCONF # include "Zeroconf.h" -# include "BonjourServiceBrowser.h" -# include "BonjourServiceResolver.h" #endif #include "Channel.h" @@ -1033,22 +1031,16 @@ ConnectDialog::ConnectDialog(QWidget *p, bool autoconnect) : QDialog(p), bAutoCo startDns(si); qtwServers->siFavorite->addServerItem(si); } - #ifdef USE_ZEROCONF - // Make sure the we got the objects we need, then wire them up - if (bAllowZeroconf && g.zeroconf->bsbBrowser && g.zeroconf->bsrResolver) { - connect(g.zeroconf->bsbBrowser.data(), SIGNAL(error(DNSServiceErrorType)), this, - SLOT(onLanBrowseError(DNSServiceErrorType))); - connect(g.zeroconf->bsbBrowser.data(), SIGNAL(currentBonjourRecordsChanged(const QList< BonjourRecord > &)), - this, SLOT(onUpdateLanList(const QList< BonjourRecord > &))); - connect(g.zeroconf->bsrResolver.data(), SIGNAL(error(BonjourRecord, DNSServiceErrorType)), this, - SLOT(onLanResolveError(BonjourRecord, DNSServiceErrorType))); - connect(g.zeroconf->bsrResolver.data(), SIGNAL(bonjourRecordResolved(BonjourRecord, QString, int)), this, - SLOT(onResolved(BonjourRecord, QString, int))); - onUpdateLanList(g.zeroconf->bsbBrowser->currentRecords()); + if (bAllowZeroconf && g.zeroconf && g.zeroconf->isOk()) { + connect(g.zeroconf, &Zeroconf::recordsChanged, this, &ConnectDialog::onUpdateLanList); + connect(g.zeroconf, &Zeroconf::recordResolved, this, &ConnectDialog::onResolved); + connect(g.zeroconf, &Zeroconf::resolveError, this, &ConnectDialog::onLanResolveError); + onUpdateLanList(g.zeroconf->currentRecords()); + + g.zeroconf->startBrowser(QLatin1String("_mumble._tcp")); } #endif - qtPingTick = new QTimer(this); connect(qtPingTick, SIGNAL(timeout()), this, SLOT(timeTick())); @@ -1082,6 +1074,12 @@ ConnectDialog::ConnectDialog(QWidget *p, bool autoconnect) : QDialog(p), bAutoCo } ConnectDialog::~ConnectDialog() { +#ifdef USE_ZEROCONF + if (bAllowZeroconf && g.zeroconf && g.zeroconf->isOk()) { + g.zeroconf->stopBrowser(); + g.zeroconf->cleanupResolvers(); + } +#endif ServerItem::qmIcons.clear(); QList< FavoriteServer > ql; @@ -1356,7 +1354,7 @@ void ConnectDialog::initList() { } #ifdef USE_ZEROCONF -void ConnectDialog::onResolved(BonjourRecord record, QString host, int port) { +void ConnectDialog::onResolved(const BonjourRecord record, const QString host, const uint16_t port) { qlBonjourActive.removeAll(record); foreach (ServerItem *si, qlItems) { if (si->zeroconfRecord == record) { @@ -1393,7 +1391,7 @@ void ConnectDialog::onUpdateLanList(const QList< BonjourRecord > &list) { if (!found) { ServerItem *si = new ServerItem(record); qlItems << si; - g.zeroconf->bsrResolver->resolveBonjourRecord(record); + g.zeroconf->startResolver(record); startDns(si); qtwServers->siLAN->addServerItem(si); } @@ -1406,13 +1404,8 @@ void ConnectDialog::onUpdateLanList(const QList< BonjourRecord > &list) { } } -void ConnectDialog::onLanBrowseError(DNSServiceErrorType err) { - qWarning() << "Bonjour reported browser error " << err; -} - -void ConnectDialog::onLanResolveError(BonjourRecord br, DNSServiceErrorType err) { - qlBonjourActive.removeAll(br); - qWarning() << "Bonjour reported resolver error " << err; +void ConnectDialog::onLanResolveError(const BonjourRecord record) { + qlBonjourActive.removeAll(record); } #endif @@ -1627,17 +1620,15 @@ void ConnectDialog::startDns(ServerItem *si) { foreach (const ServerAddress &addr, si->qlAddresses) { qhPings[addr].insert(si); } return; } - #ifdef USE_ZEROCONF if (bAllowZeroconf && si->qsHostname.isEmpty() && !si->zeroconfRecord.serviceName.isEmpty()) { if (!qlBonjourActive.contains(si->zeroconfRecord)) { - g.zeroconf->bsrResolver->resolveBonjourRecord(si->zeroconfRecord); + g.zeroconf->startResolver(si->zeroconfRecord); qlBonjourActive.append(si->zeroconfRecord); } return; } #endif - if (!qhDNSWait.contains(unresolved)) { if (si->itType == ServerItem::PublicType) qlDNSLookup.append(unresolved); diff --git a/src/mumble/ConnectDialog.h b/src/mumble/ConnectDialog.h index ffa5c94ce..414cc9b88 100644 --- a/src/mumble/ConnectDialog.h +++ b/src/mumble/ConnectDialog.h @@ -358,10 +358,9 @@ protected: QList< BonjourRecord > qlBonjourActive; public slots: void onUpdateLanList(const QList< BonjourRecord > &); - void onLanBrowseError(DNSServiceErrorType); - void onResolved(BonjourRecord, QString, int); - void onLanResolveError(BonjourRecord, DNSServiceErrorType); + void onResolved(const BonjourRecord, const QString, const uint16_t); + void onLanResolveError(const BonjourRecord); #endif private slots: void on_qleSearchServername_textChanged(const QString &searchServername); diff --git a/src/mumble/Zeroconf.cpp b/src/mumble/Zeroconf.cpp index 3d3af6f15..a15435f09 100644 --- a/src/mumble/Zeroconf.cpp +++ b/src/mumble/Zeroconf.cpp @@ -5,17 +5,298 @@ #include "Zeroconf.h" -Zeroconf::Zeroconf() { -#ifdef Q_OS_WIN - HMODULE hLib = LoadLibrary(L"DNSSD.DLL"); - if (!hLib) { - qWarning("Bonjour: Failed to load dnssd.dll"); +#define SYMBOL_EXISTS(symbol) (GetProcAddress(handle, symbol)) + +Zeroconf::Zeroconf() : m_ok(false) { + qRegisterMetaType< uint16_t >("uint16_t"); + qRegisterMetaType< BonjourRecord >("BonjourRecord"); + qRegisterMetaType< QList< BonjourRecord > >("QList"); +#ifdef Q_OS_WIN64 + static bool winDnsUnavailable = false; + if (!winDnsUnavailable) { + auto handle = GetModuleHandle(L"dnsapi.dll"); + if (handle) { + if (SYMBOL_EXISTS("DnsServiceBrowse") && SYMBOL_EXISTS("DnsServiceBrowseCancel") + && SYMBOL_EXISTS("DnsServiceResolve") && SYMBOL_EXISTS("DnsServiceResolveCancel") + && SYMBOL_EXISTS("DnsServiceFreeInstance")) { + m_ok = true; + return; + } + } + + winDnsUnavailable = true; + qWarning("Zeroconf: Native mDNS/DNS-SD API not available, falling back to third-party API"); + } + + static bool dnssdLoadFailed = false; + if (!dnssdLoadFailed) { + auto handle = LoadLibrary(L"dnssd.DLL"); + if (!handle) { + dnssdLoadFailed = true; + qWarning("Zeroconf: Failed to load dnssd.dll, assuming third-party API is not available"); + return; + } + FreeLibrary(handle); + } +#endif + resetHelperBrowser(); + resetHelperResolver(); + + m_ok = true; +} + +Zeroconf::~Zeroconf() { + if (!m_helperBrowser) { + stopBrowser(); + } + + if (!m_helperResolver) { + cleanupResolvers(); + } +} + +void Zeroconf::resetHelperBrowser() { + m_helperBrowser.reset(new BonjourServiceBrowser(this)); + connect(m_helperBrowser.get(), &BonjourServiceBrowser::currentBonjourRecordsChanged, this, + &Zeroconf::helperBrowserRecordsChanged); + connect(m_helperBrowser.get(), &BonjourServiceBrowser::error, this, &Zeroconf::helperBrowserError); +} + +void Zeroconf::resetHelperResolver() { + m_helperResolver.reset(new BonjourServiceResolver(this)); + connect(m_helperResolver.get(), &BonjourServiceResolver::bonjourRecordResolved, this, + &Zeroconf::helperResolverRecordResolved); + connect(m_helperResolver.get(), &BonjourServiceResolver::error, this, &Zeroconf::helperResolverError); +} + +bool Zeroconf::startBrowser(const QString &serviceType) { + if (!m_ok) { + return false; + } + + stopBrowser(); + + if (m_helperBrowser) { + m_helperBrowser->browseForServiceType(serviceType); + return true; + } +#ifdef Q_OS_WIN64 + const QString queryName = serviceType + QLatin1String(".local"); + + DNS_SERVICE_BROWSE_REQUEST request{}; + request.Version = DNS_QUERY_REQUEST_VERSION1; + request.QueryName = reinterpret_cast< LPCWSTR >(queryName.utf16()); + request.pBrowseCallback = callbackBrowseComplete; + request.pQueryContext = this; + + m_cancelBrowser.reset(new DNS_SERVICE_CANCEL{}); + const auto ret = DnsServiceBrowse(&request, m_cancelBrowser.get()); + if (ret == DNS_REQUEST_PENDING) { + return true; + } + + m_cancelBrowser.reset(); + qWarning("Zeroconf: DnsServiceBrowse() failed with error %u!", ret); +#endif + return false; +} + +bool Zeroconf::stopBrowser() { + if (!m_ok) { + return false; + } + + if (m_helperBrowser) { + resetHelperBrowser(); + return true; + } +#ifdef Q_OS_WIN64 + if (m_cancelBrowser) { + const auto ret = DnsServiceBrowseCancel(m_cancelBrowser.get()); + if (ret == ERROR_SUCCESS || ret == ERROR_CANCELLED) { + m_cancelBrowser.reset(); + return true; + } + + qWarning("Zeroconf: DnsServiceBrowseCancel() failed with error %u!", ret); + return false; + } +#endif + return true; +} + +bool Zeroconf::startResolver(const BonjourRecord &record) { + if (!m_ok) { + return false; + } + + if (m_helperResolver) { + m_helperResolver->resolveBonjourRecord(record); + return true; + } +#ifdef Q_OS_WIN64 + stopResolver(record); + + auto qualifiedHostname = record.serviceName.toStdWString() + L"." + record.registeredType.toStdWString() + + record.replyDomain.toStdWString(); + + m_resolvers.append(Resolver(this, record)); + auto context = &m_resolvers.last(); + + DNS_SERVICE_RESOLVE_REQUEST request{}; + request.Version = DNS_QUERY_REQUEST_VERSION1; + request.QueryName = &qualifiedHostname[0]; + request.pResolveCompletionCallback = &Zeroconf::callbackResolveComplete; + request.pQueryContext = context; + + const auto ret = DnsServiceResolve(&request, &context->m_cancel); + if (ret == DNS_REQUEST_PENDING) { + return true; + } + + m_resolvers.removeLast(); + + qWarning("Zeroconf: DnsServiceResolve() failed with error %u!", ret); +#endif + return false; +} +#ifdef Q_OS_WIN64 +bool Zeroconf::stopResolver(const BonjourRecord &record) { + if (!m_ok) { + return false; + } + + Resolver tmp(this, record); + if (!m_resolvers.contains(tmp)) { + return true; + } + + auto resolver = m_resolvers[m_resolvers.indexOf(tmp)]; + return stopResolver(resolver); +} + +bool Zeroconf::stopResolver(Resolver &resolver) { + if (!m_ok) { + return false; + } + + const auto ret = DnsServiceResolveCancel(&resolver.m_cancel); + if (ret == ERROR_SUCCESS || ret == ERROR_CANCELLED) { + return true; + } + + qWarning("Zeroconf: DnsServiceResolveCancel() failed with error %u!", ret); + return false; +} +#endif +bool Zeroconf::cleanupResolvers() { + if (!m_ok) { + return false; + } + + if (m_helperResolver) { + resetHelperResolver(); + return true; + } + + auto result = true; +#ifdef Q_OS_WIN64 + for (auto i = 0; i < m_resolvers.size(); ++i) { + if (!stopResolver(m_resolvers[i])) { + result = false; + } + } + + m_resolvers.clear(); +#endif + return result; +} + +void Zeroconf::helperBrowserRecordsChanged(const QList< BonjourRecord > &records) { + emit recordsChanged(records); +} + +void Zeroconf::helperResolverRecordResolved(const BonjourRecord record, const QString hostname, const int port) { + emit recordResolved(record, hostname, port); +} + +void Zeroconf::helperBrowserError(const DNSServiceErrorType error) const { + qWarning("Zeroconf: Third-party browser API reports error %d", error); +} + +void Zeroconf::helperResolverError(const BonjourRecord record, const DNSServiceErrorType error) { + qWarning("Zeroconf: Third-party resolver API reports error %d", error); + emit resolveError(record); +} +#ifdef Q_OS_WIN64 +void WINAPI Zeroconf::callbackBrowseComplete(const DWORD status, void *context, DNS_RECORD *records) { + auto zeroconf = static_cast< Zeroconf * >(context); + zeroconf->m_cancelBrowser.reset(); + + if (status != ERROR_SUCCESS) { + if (records) { + DnsRecordListFree(records, DnsFreeRecordList); + } + + qWarning("Zeroconf: DnsServiceBrowse() reports status code %u, ignoring results", status); return; } - FreeLibrary(hLib); -#endif - bsbBrowser.reset(new BonjourServiceBrowser(this)); - bsbBrowser->browseForServiceType(QLatin1String("_mumble._tcp")); - bsrResolver.reset(new BonjourServiceResolver(this)); - return; + + if (!records) { + return; + } + + bool changed = false; + + for (auto cur = records; cur; cur = cur->pNext) { + if (cur->wType != DNS_TYPE_PTR) { + continue; + } + + // Example: "_mumble._tcp.local". + const auto domain = QString::fromWCharArray(cur->pName); + // Example: "Test._mumble._tcp.local". + const auto hostname = QString::fromWCharArray(cur->Data.PTR.pNameHost); + + BonjourRecord record; + record.serviceName = hostname.left(hostname.lastIndexOf(domain)); + // Trim trailing ".". + record.serviceName.resize(record.serviceName.size() - 1); + + record.replyDomain = domain.mid(domain.lastIndexOf('.')); + record.registeredType = domain.left(domain.lastIndexOf(record.replyDomain)); + + if (!zeroconf->m_records.contains(record)) { + zeroconf->m_records.append(record); + changed = true; + } + } + + DnsRecordListFree(records, DnsFreeRecordList); + + if (changed) { + emit zeroconf->recordsChanged(zeroconf->m_records); + } } + +void WINAPI Zeroconf::callbackResolveComplete(const DWORD status, void *context, DNS_SERVICE_INSTANCE *instance) { + if (status != ERROR_SUCCESS) { + if (instance) { + DnsServiceFreeInstance(instance); + } + + qWarning("Zeroconf: DnsServiceResolve() reports status code %u, ignoring result", status); + return; + } + + if (!instance) { + return; + } + + auto resolverContext = static_cast< Resolver * >(context); + emit resolverContext->m_zeroconf->recordResolved(resolverContext->m_record, + QString::fromWCharArray(instance->pszHostName), instance->wPort); + + DnsServiceFreeInstance(instance); +} +#endif diff --git a/src/mumble/Zeroconf.h b/src/mumble/Zeroconf.h index 9c0cdd4a7..addc99239 100644 --- a/src/mumble/Zeroconf.h +++ b/src/mumble/Zeroconf.h @@ -9,15 +9,70 @@ #include "BonjourServiceBrowser.h" #include "BonjourServiceResolver.h" +#include + +#ifdef Q_OS_WIN64 +# include +#endif + class Zeroconf : public QObject { private: Q_OBJECT Q_DISABLE_COPY(Zeroconf) -public: - Zeroconf(); +protected: +#ifdef Q_OS_WIN64 + struct Resolver { + Zeroconf *m_zeroconf; + BonjourRecord m_record; + DNS_SERVICE_CANCEL m_cancel; - QScopedPointer< BonjourServiceBrowser > bsbBrowser; - QScopedPointer< BonjourServiceResolver > bsrResolver; + bool operator==(const Resolver &other) const { return m_record == other.m_record; } + + Resolver(Zeroconf *zeroconf, const BonjourRecord &record) : m_zeroconf(zeroconf), m_record(record){}; + }; +#endif + bool m_ok; + QList< BonjourRecord > m_records; + std::unique_ptr< BonjourServiceBrowser > m_helperBrowser; + std::unique_ptr< BonjourServiceResolver > m_helperResolver; +#ifdef Q_OS_WIN64 + QList< Resolver > m_resolvers; + std::unique_ptr< DNS_SERVICE_CANCEL > m_cancelBrowser; + + bool stopResolver(Resolver &resolver); + + static void WINAPI callbackBrowseComplete(const DWORD status, void *context, DNS_RECORD *records); + static void WINAPI callbackResolveComplete(const DWORD status, void *context, DNS_SERVICE_INSTANCE *instance); +#endif + void resetHelperBrowser(); + void resetHelperResolver(); + + void helperBrowserRecordsChanged(const QList< BonjourRecord > &records); + void helperResolverRecordResolved(const BonjourRecord record, const QString hostname, const int port); + void helperBrowserError(const DNSServiceErrorType error) const; + void helperResolverError(const BonjourRecord record, const DNSServiceErrorType error); + +public: + inline bool isOk() const { return m_ok; } + inline QList< BonjourRecord > currentRecords() const { + return m_helperBrowser ? m_helperBrowser->currentRecords() : m_records; + } + + bool startBrowser(const QString &serviceType); + bool stopBrowser(); + + bool startResolver(const BonjourRecord &record); +#ifdef Q_OS_WIN64 + bool stopResolver(const BonjourRecord &record); +#endif + bool cleanupResolvers(); + + Zeroconf(); + ~Zeroconf(); +signals: + void recordsChanged(const QList< BonjourRecord > &records); + void recordResolved(const BonjourRecord record, const QString hostname, const uint16_t port); + void resolveError(const BonjourRecord record); }; #endif diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index d002a0ed7..4cd023b73 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -24,7 +24,6 @@ #ifdef USE_ZEROCONF # include "Zeroconf.h" -# include "BonjourServiceRegister.h" #endif #include "Utils.h" @@ -668,16 +667,27 @@ void Server::setLiveConf(const QString &key, const QString &value) { #ifdef USE_ZEROCONF void Server::initZeroconf() { zeroconf = new Zeroconf(); - if (zeroconf->bsrRegister) { - log("Announcing server via zeroconf"); - zeroconf->bsrRegister->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); + if (zeroconf->isOk()) { + log("Registering zeroconf service..."); + zeroconf->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); + return; } + + delete zeroconf; + zeroconf = nullptr; } void Server::removeZeroconf() { + if (!zeroconf) { + return; + } + + if (zeroconf->isOk()) { + log("Unregistering zeroconf service..."); + } + delete zeroconf; zeroconf = nullptr; - log("Stopped announcing server via zeroconf"); } #endif diff --git a/src/murmur/Zeroconf.cpp b/src/murmur/Zeroconf.cpp index 4c19a6596..2e1dcae84 100644 --- a/src/murmur/Zeroconf.cpp +++ b/src/murmur/Zeroconf.cpp @@ -5,27 +5,158 @@ #include "Zeroconf.h" -#include "BonjourServiceRegister.h" +#define SYMBOL_EXISTS(symbol) (GetProcAddress(handle, symbol)) -Zeroconf::Zeroconf() { - bsrRegister = nullptr; -#ifdef Q_OS_WIN - static bool bDelayLoadFailed = false; - if (bDelayLoadFailed) - return; +Zeroconf::Zeroconf() : m_ok(false) { +#ifdef Q_OS_WIN64 + static bool winDnsUnavailable = false; + if (!winDnsUnavailable) { + auto handle = GetModuleHandle(L"dnsapi.dll"); + if (handle) { + if (SYMBOL_EXISTS("DnsServiceConstructInstance") && SYMBOL_EXISTS("DnsServiceFreeInstance") + && SYMBOL_EXISTS("DnsServiceRegister") && SYMBOL_EXISTS("DnsServiceDeRegister") + && SYMBOL_EXISTS("DnsServiceRegisterCancel")) { + m_ok = true; + return; + } + } - HMODULE hLib = LoadLibrary(L"DNSSD.DLL"); - if (!hLib) { - bDelayLoadFailed = true; - qWarning("Bonjour: Failed to load dnssd.dll"); - return; + winDnsUnavailable = true; + qWarning("Zeroconf: Native mDNS/DNS-SD API not available, falling back to third-party API"); } - FreeLibrary(hLib); -#endif - bsrRegister = new BonjourServiceRegister(this); + static bool dnssdLoadFailed = false; + if (!dnssdLoadFailed) { + auto handle = LoadLibrary(L"dnssd.DLL"); + if (!handle) { + dnssdLoadFailed = true; + qWarning("Zeroconf: Failed to load dnssd.dll, assuming third-party API is not available"); + return; + } + + FreeLibrary(handle); + } +#endif + resetHelper(); + + m_ok = true; } Zeroconf::~Zeroconf() { - delete bsrRegister; + if (!m_helper) { + unregisterService(); + } } + +void Zeroconf::resetHelper() { + m_helper.reset(new BonjourServiceRegister(this)); + connect(m_helper.get(), &BonjourServiceRegister::error, this, &Zeroconf::helperError); +} + +bool Zeroconf::registerService(const BonjourRecord &record, const uint16_t port) { + if (!m_ok) { + return false; + } + + unregisterService(); + + if (m_helper) { + m_helper->registerService(record, port); + return true; + } +#ifdef Q_OS_WIN64 + DWORD size = 0; + GetComputerNameEx(ComputerNameDnsHostname, nullptr, &size); + std::vector< wchar_t > hostname(size); + if (!GetComputerNameEx(ComputerNameDnsHostname, &hostname[0], &size)) { + qWarning("Zeroconf: GetComputerNameEx() failed with error %u!", GetLastError()); + return false; + } + + const auto domain = record.replyDomain.isEmpty() ? L".local" : L"." + record.replyDomain.toStdWString(); + const auto qualifiedHostname = &hostname[0] + domain; + + auto service = record.serviceName.isEmpty() ? &hostname[0] : record.serviceName.toStdWString(); + service += L"."; + service += record.registeredType.toStdWString(); + service += domain; + + auto instance = DnsServiceConstructInstance(service.c_str(), qualifiedHostname.c_str(), nullptr, nullptr, port, 0, + 0, 0, nullptr, nullptr); + if (!instance) { + qWarning("Zeroconf: DnsServiceConstructInstance() returned nullptr!"); + return false; + } + + m_request.reset(new DNS_SERVICE_REGISTER_REQUEST{}); + m_request->Version = DNS_QUERY_REQUEST_VERSION1; + m_request->pServiceInstance = instance; + m_request->pRegisterCompletionCallback = callbackRegisterComplete; + m_request->pQueryContext = this; + + m_cancel.reset(new DNS_SERVICE_CANCEL{}); + const auto ret = DnsServiceRegister(m_request.get(), m_cancel.get()); + DnsServiceFreeInstance(instance); + + if (ret == DNS_REQUEST_PENDING) { + return true; + } + + qWarning("Zeroconf: DnsServiceRegister() failed with error %u!", ret); + m_request.reset(); + m_cancel.reset(); +#endif + return false; +} + +bool Zeroconf::unregisterService() { + if (!m_ok) { + return false; + } + + if (m_helper) { + resetHelper(); + return true; + } +#ifdef Q_OS_WIN64 + if (m_cancel) { + const auto ret = DnsServiceRegisterCancel(m_cancel.get()); + if (ret == ERROR_SUCCESS || ret == ERROR_CANCELLED) { + return true; + } + + m_cancel.reset(); + qWarning("Zeroconf: DnsServiceRegisterCancel() failed with error %u!", ret); + } else if (m_request) { + const auto ret = DnsServiceDeRegister(m_request.get(), nullptr); + if (ret == DNS_REQUEST_PENDING) { + return true; + } + + qWarning("Zeroconf: DnsServiceDeRegister() failed with error %u!", ret); + } +#endif + return false; +} + +void Zeroconf::helperError(const DNSServiceErrorType error) { + qWarning("Zeroconf: Third-party API reports error %d, service registration probably failed", error); +} +#ifdef Q_OS_WIN64 +void WINAPI Zeroconf::callbackRegisterComplete(const DWORD status, void *context, DNS_SERVICE_INSTANCE *instance) { + if (instance) { + DnsServiceFreeInstance(instance); + } + + if (status == ERROR_CANCELLED) { + return; + } + + auto zeroconf = static_cast< Zeroconf * >(context); + zeroconf->m_cancel.reset(); + + if (status != ERROR_SUCCESS) { + qWarning("Zeroconf: DnsServiceRegister() reports status code %u, service registration probably failed", status); + } +} +#endif diff --git a/src/murmur/Zeroconf.h b/src/murmur/Zeroconf.h index cc92f1443..598897735 100644 --- a/src/murmur/Zeroconf.h +++ b/src/murmur/Zeroconf.h @@ -6,19 +6,39 @@ #ifndef MUMBLE_MURMUR_ZEROCONF_H_ #define MUMBLE_MURMUR_ZEROCONF_H_ -#include +#include "BonjourServiceRegister.h" -class BonjourServiceRegister; +#include + +#ifdef Q_OS_WIN64 +# include +#endif class Zeroconf : public QObject { private: Q_OBJECT Q_DISABLE_COPY(Zeroconf) +protected: + bool m_ok; + std::unique_ptr< BonjourServiceRegister > m_helper; +#ifdef Q_OS_WIN64 + std::unique_ptr< DNS_SERVICE_CANCEL > m_cancel; + std::unique_ptr< DNS_SERVICE_REGISTER_REQUEST > m_request; + + static void WINAPI callbackRegisterComplete(const DWORD status, void *context, DNS_SERVICE_INSTANCE *instance); +#endif + void helperError(const DNSServiceErrorType error); + public: + inline bool isOk() const { return m_ok; } + + void resetHelper(); + + bool registerService(const BonjourRecord &record, const uint16_t port); + bool unregisterService(); + Zeroconf(); ~Zeroconf(); - - BonjourServiceRegister *bsrRegister; }; #endif