mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Merge PR #4494: FEAT(client, server): use native mDNS/DNS-SD API on Windows, if available
This commit is contained in:
commit
69dfdd2135
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#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;
|
||||
}
|
||||
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#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
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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("<tr><th align=left>%1</th><td>%2</td></tr>")
|
||||
.arg(ConnectDialog::tr("Hostname"), qsHostname.toHtmlEscaped());
|
||||
#ifdef USE_BONJOUR
|
||||
if (!qsBonjourHost.isEmpty())
|
||||
#ifdef USE_ZEROCONF
|
||||
if (!zeroconfHost.isEmpty())
|
||||
qs += QString::fromLatin1("<tr><th align=left>%1</th><td>%2</td></tr>")
|
||||
.arg(ConnectDialog::tr("Bonjour name"), qsBonjourHost.toHtmlEscaped());
|
||||
.arg(ConnectDialog::tr("Bonjour name"), zeroconfHost.toHtmlEscaped());
|
||||
#endif
|
||||
qs += QString::fromLatin1("<tr><th align=left>%1</th><td>%2</td></tr>")
|
||||
.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);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#ifdef USE_BONJOUR
|
||||
#ifdef USE_ZEROCONF
|
||||
# include "BonjourRecord.h"
|
||||
# include <dns_sd.h>
|
||||
#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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
302
src/mumble/Zeroconf.cpp
Normal file
302
src/mumble/Zeroconf.cpp
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#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<BonjourRecord>");
|
||||
#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
|
||||
78
src/mumble/Zeroconf.h
Normal file
78
src/mumble/Zeroconf.h
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#ifndef MUMBLE_MUMBLE_ZEROCONF_H_
|
||||
#define MUMBLE_MUMBLE_ZEROCONF_H_
|
||||
|
||||
#include "BonjourServiceBrowser.h"
|
||||
#include "BonjourServiceResolver.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_WIN64
|
||||
# include <windns.h>
|
||||
#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
|
||||
@ -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
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
# include <mmintrin.h>
|
||||
# endif
|
||||
|
||||
# ifdef USE_BONJOUR
|
||||
# ifdef USE_ZEROCONF
|
||||
# include <dns_sd.h>
|
||||
# endif
|
||||
|
||||
|
||||
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#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;
|
||||
}
|
||||
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#ifndef MUMBLE_MURMUR_BONJOURSERVER_H_
|
||||
#define MUMBLE_MURMUR_BONJOURSERVER_H_
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class BonjourServiceRegister;
|
||||
|
||||
class BonjourServer : public QObject {
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(BonjourServer)
|
||||
public:
|
||||
BonjourServer();
|
||||
~BonjourServer();
|
||||
|
||||
BonjourServiceRegister *bsrRegister;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
# include <winsock2.h>
|
||||
#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;
|
||||
|
||||
162
src/murmur/Zeroconf.cpp
Normal file
162
src/murmur/Zeroconf.cpp
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#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
|
||||
44
src/murmur/Zeroconf.h
Normal file
44
src/murmur/Zeroconf.h
Normal file
@ -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 <https://www.mumble.info/LICENSE>.
|
||||
|
||||
#ifndef MUMBLE_MURMUR_ZEROCONF_H_
|
||||
#define MUMBLE_MURMUR_ZEROCONF_H_
|
||||
|
||||
#include "BonjourServiceRegister.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_WIN64
|
||||
# include <windns.h>
|
||||
#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
|
||||
@ -83,7 +83,7 @@ void __cpuid(int a[4], int b);
|
||||
# include <boost/shared_ptr.hpp>
|
||||
# include <boost/weak_ptr.hpp>
|
||||
|
||||
# ifdef USE_BONJOUR
|
||||
# ifdef USE_ZEROCONF
|
||||
# include <dns_sd.h>
|
||||
# endif
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user