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/BonjourClient.cpp b/src/mumble/BonjourClient.cpp deleted file mode 100644 index 4f04e6c6f..000000000 --- a/src/mumble/BonjourClient.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2005-2020 The Mumble Developers. All rights reserved. -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file at the root of the -// Mumble source tree or at . - -#include "BonjourClient.h" - -BonjourClient::BonjourClient() { -#ifdef Q_OS_WIN - HMODULE hLib = LoadLibrary(L"DNSSD.DLL"); - if (!hLib) { - qWarning("Bonjour: Failed to load dnssd.dll"); - return; - } - FreeLibrary(hLib); -#endif - bsbBrowser.reset(new BonjourServiceBrowser(this)); - bsbBrowser->browseForServiceType(QLatin1String("_mumble._tcp")); - bsrResolver.reset(new BonjourServiceResolver(this)); - return; -} diff --git a/src/mumble/BonjourClient.h b/src/mumble/BonjourClient.h deleted file mode 100644 index 2a7fd57ac..000000000 --- a/src/mumble/BonjourClient.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2005-2020 The Mumble Developers. All rights reserved. -// Use of this source code is governed by a BSD-style license -// 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_ - -#include "BonjourServiceBrowser.h" -#include "BonjourServiceResolver.h" - -class BonjourClient : public QObject { -private: - Q_OBJECT - Q_DISABLE_COPY(BonjourClient) -public: - BonjourClient(); - - QScopedPointer< BonjourServiceBrowser > bsbBrowser; - QScopedPointer< BonjourServiceResolver > bsrResolver; -}; - -#endif 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..59ab0c89f 100644 --- a/src/mumble/ConnectDialog.cpp +++ b/src/mumble/ConnectDialog.cpp @@ -5,10 +5,8 @@ #include "ConnectDialog.h" -#ifdef USE_BONJOUR -# include "BonjourClient.h" -# include "BonjourServiceBrowser.h" -# include "BonjourServiceResolver.h" +#ifdef USE_ZEROCONF +# include "Zeroconf.h" #endif #include "Channel.h" @@ -102,7 +100,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 +218,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 +259,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 +272,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 +312,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 +488,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 +590,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 +942,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) { @@ -1033,22 +1031,16 @@ ConnectDialog::ConnectDialog(QWidget *p, bool autoconnect) : QDialog(p), bAutoCo startDns(si); qtwServers->siFavorite->addServerItem(si); } +#ifdef USE_ZEROCONF + 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()); -#ifdef USE_BONJOUR - // 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, - 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, - SLOT(onLanResolveError(BonjourRecord, DNSServiceErrorType))); - connect(g.bc->bsrResolver.data(), SIGNAL(bonjourRecordResolved(BonjourRecord, QString, int)), this, - SLOT(onResolved(BonjourRecord, QString, int))); - onUpdateLanList(g.bc->bsbBrowser->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; @@ -1175,9 +1173,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 +1194,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 +1353,11 @@ void ConnectDialog::initList() { WebFetch::fetch(QLatin1String("publist"), url, this, SLOT(fetched(QByteArray, QUrl, QMap< QString, QString >))); } -#ifdef USE_BONJOUR -void ConnectDialog::onResolved(BonjourRecord record, QString host, int port) { +#ifdef USE_ZEROCONF +void ConnectDialog::onResolved(const BonjourRecord record, const QString host, const uint16_t 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 +1382,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 +1391,7 @@ void ConnectDialog::onUpdateLanList(const QList< BonjourRecord > &list) { if (!found) { ServerItem *si = new ServerItem(record); qlItems << si; - g.bc->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_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->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 e04b66c70..414cc9b88 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,15 +353,14 @@ public: ConnectDialog(QWidget *parent, bool autoconnect); ~ConnectDialog(); -#ifdef USE_BONJOUR +#ifdef USE_ZEROCONF 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/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/Zeroconf.cpp b/src/mumble/Zeroconf.cpp new file mode 100644 index 000000000..a15435f09 --- /dev/null +++ b/src/mumble/Zeroconf.cpp @@ -0,0 +1,302 @@ +// Copyright 2005-2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "Zeroconf.h" + +#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; + } + + 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 new file mode 100644 index 000000000..addc99239 --- /dev/null +++ b/src/mumble/Zeroconf.h @@ -0,0 +1,78 @@ +// Copyright 2005-2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_ZEROCONF_H_ +#define MUMBLE_MUMBLE_ZEROCONF_H_ + +#include "BonjourServiceBrowser.h" +#include "BonjourServiceResolver.h" + +#include + +#ifdef Q_OS_WIN64 +# include +#endif + +class Zeroconf : public QObject { +private: + Q_OBJECT + Q_DISABLE_COPY(Zeroconf) +protected: +#ifdef Q_OS_WIN64 + struct Resolver { + Zeroconf *m_zeroconf; + BonjourRecord m_record; + DNS_SERVICE_CANCEL m_cancel; + + 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/mumble/main.cpp b/src/mumble/main.cpp index b2524ee6e..93eea4adc 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" @@ -559,9 +559,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 @@ -759,8 +759,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/BonjourServer.cpp b/src/murmur/BonjourServer.cpp deleted file mode 100644 index 9041557b3..000000000 --- a/src/murmur/BonjourServer.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2005-2020 The Mumble Developers. All rights reserved. -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file at the root of the -// Mumble source tree or at . - -#include "BonjourServer.h" - -#include "BonjourServiceRegister.h" - -BonjourServer::BonjourServer() { - bsrRegister = nullptr; -#ifdef Q_OS_WIN - static bool bDelayLoadFailed = false; - if (bDelayLoadFailed) - return; - - HMODULE hLib = LoadLibrary(L"DNSSD.DLL"); - if (!hLib) { - bDelayLoadFailed = true; - qWarning("Bonjour: Failed to load dnssd.dll"); - return; - } - FreeLibrary(hLib); -#endif - - bsrRegister = new BonjourServiceRegister(this); -} - -BonjourServer::~BonjourServer() { - delete bsrRegister; -} diff --git a/src/murmur/BonjourServer.h b/src/murmur/BonjourServer.h deleted file mode 100644 index 3258aaefb..000000000 --- a/src/murmur/BonjourServer.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2005-2020 The Mumble Developers. All rights reserved. -// Use of this source code is governed by a BSD-style license -// 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_ - -#include - -class BonjourServiceRegister; - -class BonjourServer : public QObject { -private: - Q_OBJECT - Q_DISABLE_COPY(BonjourServer) -public: - BonjourServer(); - ~BonjourServer(); - - BonjourServiceRegister *bsrRegister; -}; - -#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..4cd023b73 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -22,9 +22,8 @@ #include "User.h" #include "Version.h" -#ifdef USE_BONJOUR -# include "BonjourServer.h" -# include "BonjourServiceRegister.h" +#ifdef USE_ZEROCONF +# include "Zeroconf.h" #endif #include "Utils.h" @@ -116,8 +115,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 +272,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 +326,8 @@ void Server::stopThread() { } Server::~Server() { -#ifdef USE_BONJOUR - removeBonjour(); +#ifdef USE_ZEROCONF + removeZeroconf(); #endif stopThread(); @@ -626,11 +625,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 +664,30 @@ 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->isOk()) { + log("Registering zeroconf service..."); + zeroconf->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); + return; } + + delete zeroconf; + zeroconf = nullptr; } -void Server::removeBonjour() { - delete bsRegistration; - bsRegistration = nullptr; - log("Stopped announcing server via bonjour"); +void Server::removeZeroconf() { + if (!zeroconf) { + return; + } + + if (zeroconf->isOk()) { + log("Unregistering zeroconf service..."); + } + + delete zeroconf; + zeroconf = nullptr; } #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/Zeroconf.cpp b/src/murmur/Zeroconf.cpp new file mode 100644 index 000000000..2e1dcae84 --- /dev/null +++ b/src/murmur/Zeroconf.cpp @@ -0,0 +1,162 @@ +// Copyright 2005-2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "Zeroconf.h" + +#define SYMBOL_EXISTS(symbol) (GetProcAddress(handle, symbol)) + +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; + } + } + + 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 + resetHelper(); + + m_ok = true; +} + +Zeroconf::~Zeroconf() { + 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 new file mode 100644 index 000000000..598897735 --- /dev/null +++ b/src/murmur/Zeroconf.h @@ -0,0 +1,44 @@ +// Copyright 2005-2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MURMUR_ZEROCONF_H_ +#define MUMBLE_MURMUR_ZEROCONF_H_ + +#include "BonjourServiceRegister.h" + +#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(); +}; + +#endif 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