Add MDNS discovery

This commit is contained in:
Albert Vaca Cintora 2023-06-22 11:53:42 +02:00
parent 2a1c751ef4
commit 34e0128fe9
7 changed files with 195 additions and 9 deletions

View file

@ -65,7 +65,7 @@ else()
find_package(Qca-qt${QT_MAJOR_VERSION} ${QCA_MIN_VERSION} REQUIRED) find_package(Qca-qt${QT_MAJOR_VERSION} ${QCA_MIN_VERSION} REQUIRED)
set(Qca_LIBRARY qca-qt${QT_MAJOR_VERSION}) set(Qca_LIBRARY qca-qt${QT_MAJOR_VERSION})
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Solid Kirigami2 People WindowSystem GuiAddons) set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Solid Kirigami2 People WindowSystem GuiAddons DNSSD)
set(KF5_OPTIONAL_COMPONENTS DocTools) set(KF5_OPTIONAL_COMPONENTS DocTools)
set_package_properties(KF5Kirigami2 PROPERTIES set_package_properties(KF5Kirigami2 PROPERTIES

View file

@ -63,6 +63,7 @@ PRIVATE
Qt${QT_MAJOR_VERSION}::DBus Qt${QT_MAJOR_VERSION}::DBus
KF5::I18n KF5::I18n
KF5::ConfigCore KF5::ConfigCore
KF5::DNSSD
) )
if(WIN32) if(WIN32)

View file

@ -7,5 +7,7 @@ set(backends_kdeconnect_SRCS
backends/lan/landevicelink.cpp backends/lan/landevicelink.cpp
backends/lan/compositeuploadjob.cpp backends/lan/compositeuploadjob.cpp
backends/lan/uploadjob.cpp backends/lan/uploadjob.cpp
backends/lan/mdnsdiscovery.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View file

@ -46,6 +46,7 @@ LanLinkProvider::LanLinkProvider(bool testMode, quint16 udpBroadcastPort, quint1
, m_udpListenPort(udpListenPort) , m_udpListenPort(udpListenPort)
, m_testMode(testMode) , m_testMode(testMode)
, m_combineBroadcastsTimer(this) , m_combineBroadcastsTimer(this)
, m_mdnsDiscovery(this)
{ {
m_combineBroadcastsTimer.setInterval(0); // increase this if waiting a single event-loop iteration is not enough m_combineBroadcastsTimer.setInterval(0); // increase this if waiting a single event-loop iteration is not enough
m_combineBroadcastsTimer.setSingleShot(true); m_combineBroadcastsTimer.setSingleShot(true);
@ -102,12 +103,17 @@ void LanLinkProvider::onStart()
} }
} }
onNetworkChange(); broadcastUdpIdentityPacket();
m_mdnsDiscovery.startAnnouncing();
m_mdnsDiscovery.startDiscovering();
qCDebug(KDECONNECT_CORE) << "LanLinkProvider started"; qCDebug(KDECONNECT_CORE) << "LanLinkProvider started";
} }
void LanLinkProvider::onStop() void LanLinkProvider::onStop()
{ {
m_mdnsDiscovery.stopAnnouncing();
m_mdnsDiscovery.stopDiscovering();
m_udpSocket.close(); m_udpSocket.close();
m_server->close(); m_server->close();
qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped"; qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped";
@ -126,15 +132,25 @@ void LanLinkProvider::onNetworkChange()
void LanLinkProvider::broadcastToNetwork() void LanLinkProvider::broadcastToNetwork()
{ {
if (!m_server->isListening()) { if (!m_server->isListening()) {
// Not started qWarning() << "TCP server not listening, not broadcasting";
return; return;
} }
Q_ASSERT(m_tcpPort != 0); Q_ASSERT(m_tcpPort != 0);
qCDebug(KDECONNECT_CORE()) << "Broadcasting identity packet"; broadcastUdpIdentityPacket();
m_mdnsDiscovery.stopDiscovering();
m_mdnsDiscovery.startDiscovering();
}
QList<QHostAddress> destinations = getBroadcastAddresses(); void LanLinkProvider::broadcastUdpIdentityPacket()
{
sendUdpIdentityPacket(getBroadcastAddresses());
}
void LanLinkProvider::sendUdpIdentityPacket(const QList<QHostAddress> &destinations)
{
qCDebug(KDECONNECT_CORE()) << "Broadcasting identity packet";
NetworkPacket np = KdeConnectConfig::instance().deviceInfo().toIdentityPacket(); NetworkPacket np = KdeConnectConfig::instance().deviceInfo().toIdentityPacket();
np.set(QStringLiteral("tcpPort"), m_tcpPort); np.set(QStringLiteral("tcpPort"), m_tcpPort);
@ -176,14 +192,14 @@ void LanLinkProvider::broadcastToNetwork()
if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) { if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
qCDebug(KDECONNECT_CORE()) << "Broadcasting as" << sourceAddress; qCDebug(KDECONNECT_CORE()) << "Broadcasting as" << sourceAddress;
sendSocket.bind(sourceAddress); sendSocket.bind(sourceAddress);
sendBroadcasts(sendSocket, np, destinations); sendUdpPacket(sendSocket, np, destinations);
sendSocket.close(); sendSocket.close();
} }
} }
} }
} }
#else #else
sendBroadcasts(m_udpSocket, np, destinations); sendUdpPacket(m_udpSocket, np, destinations);
#endif #endif
} }
@ -210,7 +226,7 @@ QList<QHostAddress> LanLinkProvider::getBroadcastAddresses()
return destinations; return destinations;
} }
void LanLinkProvider::sendBroadcasts(QUdpSocket &socket, const NetworkPacket &np, const QList<QHostAddress> &addresses) void LanLinkProvider::sendUdpPacket(QUdpSocket &socket, const NetworkPacket &np, const QList<QHostAddress> &addresses)
{ {
const QByteArray payload = np.serialize(); const QByteArray payload = np.serialize();

View file

@ -17,6 +17,7 @@
#include "backends/linkprovider.h" #include "backends/linkprovider.h"
#include "kdeconnectcore_export.h" #include "kdeconnectcore_export.h"
#include "landevicelink.h" #include "landevicelink.h"
#include "mdnsdiscovery.h"
#include "server.h" #include "server.h"
class KDECONNECTCORE_EXPORT LanLinkProvider : public LinkProvider class KDECONNECTCORE_EXPORT LanLinkProvider : public LinkProvider
@ -37,6 +38,8 @@ public:
return QStringLiteral("LanLinkProvider"); return QStringLiteral("LanLinkProvider");
} }
void sendUdpIdentityPacket(const QList<QHostAddress> &addresses);
static void configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted); static void configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted);
static void configureSocket(QSslSocket *socket); static void configureSocket(QSslSocket *socket);
@ -67,7 +70,8 @@ private:
void onNetworkConfigurationChanged(const QNetworkConfiguration &config); void onNetworkConfigurationChanged(const QNetworkConfiguration &config);
void addLink(QSslSocket *socket, const DeviceInfo &deviceInfo); void addLink(QSslSocket *socket, const DeviceInfo &deviceInfo);
QList<QHostAddress> getBroadcastAddresses(); QList<QHostAddress> getBroadcastAddresses();
void sendBroadcasts(QUdpSocket &socket, const NetworkPacket &np, const QList<QHostAddress> &addresses); void sendUdpPacket(QUdpSocket &socket, const NetworkPacket &np, const QList<QHostAddress> &addresses);
void broadcastUdpIdentityPacket();
Server *m_server; Server *m_server;
QUdpSocket m_udpSocket; QUdpSocket m_udpSocket;
@ -86,6 +90,8 @@ private:
QNetworkConfiguration m_lastConfig; QNetworkConfiguration m_lastConfig;
const bool m_testMode; const bool m_testMode;
QTimer m_combineBroadcastsTimer; QTimer m_combineBroadcastsTimer;
MdnsDiscovery m_mdnsDiscovery;
}; };
#endif #endif

View file

@ -0,0 +1,120 @@
/**
* SPDX-FileCopyrightText: 2023 Albert Vaca <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "mdnsdiscovery.h"
#include "core_debug.h"
#include "kdeconnectconfig.h"
#include "lanlinkprovider.h"
#include <KDNSSD/DNSSD/PublicService>
#include <KDNSSD/DNSSD/RemoteService>
#include <KDNSSD/DNSSD/ServiceBrowser>
const QString kServiceName = QStringLiteral("_kdeconnect._udp");
MdnsDiscovery::MdnsDiscovery(LanLinkProvider *lanLinkProvider)
: lanLinkProvider(lanLinkProvider)
{
switch (KDNSSD::ServiceBrowser::isAvailable()) {
case KDNSSD::ServiceBrowser::Stopped:
qWarning() << "mDNS or Avahi daemons are not running, mDNS discovery not available";
break;
case KDNSSD::ServiceBrowser::Working:
qCDebug(KDECONNECT_CORE) << "mDNS discovery is available";
break;
case KDNSSD::ServiceBrowser::Unsupported:
qWarning() << "mDNS discovery not available (library built without DNS-SD support)";
break;
}
}
MdnsDiscovery::~MdnsDiscovery()
{
stopAnnouncing();
stopDiscovering();
}
void MdnsDiscovery::startAnnouncing()
{
if (m_publisher != nullptr) {
qCDebug(KDECONNECT_CORE) << "MDNS already announcing";
return;
}
qCDebug(KDECONNECT_CORE) << "MDNS start announcing";
KdeConnectConfig &config = KdeConnectConfig::instance();
m_publisher = new KDNSSD::PublicService(config.deviceId(), kServiceName, LanLinkProvider::UDP_PORT, QStringLiteral("local"));
m_publisher->setParent(this);
// We can't fit the device certificate in this field, so this is not enough info to create a Device and won't be used.
QMap<QString, QByteArray> data;
data[QStringLiteral("id")] = config.deviceId().toUtf8();
data[QStringLiteral("name")] = config.name().toUtf8();
data[QStringLiteral("type")] = config.deviceType().toString().toUtf8();
data[QStringLiteral("protocol")] = QString::number(NetworkPacket::s_protocolVersion).toUtf8();
m_publisher->setTextData(data);
connect(m_publisher, &KDNSSD::PublicService::published, [](bool successful) {
if (successful) {
qCDebug(KDECONNECT_CORE) << "MDNS published successfully";
} else {
qWarning() << "MDNS failed to publish";
}
});
m_publisher->publishAsync();
}
void MdnsDiscovery::stopAnnouncing()
{
if (m_publisher != nullptr) {
qCDebug(KDECONNECT_CORE) << "MDNS stop announcing";
delete m_publisher;
m_publisher = nullptr;
}
}
void MdnsDiscovery::startDiscovering()
{
if (m_serviceBrowser != nullptr) {
qCDebug(KDECONNECT_CORE) << "MDNS already discovering";
return;
}
qCDebug(KDECONNECT_CORE) << "MDNS start discovering";
m_serviceBrowser = new KDNSSD::ServiceBrowser(kServiceName, true);
connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::serviceAdded, [this](KDNSSD::RemoteService::Ptr service) {
if (KdeConnectConfig::instance().deviceId() == service->serviceName()) {
qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring";
return;
}
qCDebug(KDECONNECT_CORE) << "Discovered " << service->serviceName() << " at " << service->hostName();
QHostAddress address(service->hostName());
lanLinkProvider->sendUdpIdentityPacket(QList<QHostAddress>{address});
});
connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::serviceRemoved, [](KDNSSD::RemoteService::Ptr service) {
qCDebug(KDECONNECT_CORE) << "Lost " << service->serviceName();
});
connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::finished, []() {
qCDebug(KDECONNECT_CORE) << "Finished discovery";
});
m_serviceBrowser->startBrowse();
}
void MdnsDiscovery::stopDiscovering()
{
if (m_serviceBrowser != nullptr) {
qCDebug(KDECONNECT_CORE) << "MDNS stop discovering";
delete m_serviceBrowser;
m_serviceBrowser = nullptr;
}
}

View file

@ -0,0 +1,41 @@
/**
* SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#ifndef KDECONNECT_MDNS_DISCOVERY_H
#define KDECONNECT_MDNS_DISCOVERY_H
#include <QObject>
#include "kdeconnectcore_export.h"
class LanLinkProvider;
namespace KDNSSD
{
class PublicService;
class ServiceBrowser;
};
class KDECONNECTCORE_EXPORT MdnsDiscovery : public QObject
{
Q_OBJECT
public:
explicit MdnsDiscovery(LanLinkProvider *parent);
~MdnsDiscovery();
void startDiscovering();
void stopDiscovering();
void stopAnnouncing();
void startAnnouncing();
private:
LanLinkProvider *lanLinkProvider = nullptr;
KDNSSD::PublicService *m_publisher = nullptr;
KDNSSD::ServiceBrowser *m_serviceBrowser = nullptr;
};
#endif // KDECONNECT_SERVER_H