Compare commits
3 commits
master
...
albertvaka
Author | SHA1 | Date | |
---|---|---|---|
|
aaaa8e200a | ||
|
5737a1b95a | ||
|
96308f3237 |
7 changed files with 566 additions and 3 deletions
|
@ -37,7 +37,7 @@ if (SAILFISHOS)
|
||||||
else()
|
else()
|
||||||
set(KF5_MIN_VERSION "5.48.0")
|
set(KF5_MIN_VERSION "5.48.0")
|
||||||
set(QT_MIN_VERSION "5.10.0")
|
set(QT_MIN_VERSION "5.10.0")
|
||||||
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Kirigami2)
|
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Kirigami2 DNSSD)
|
||||||
set(KF5_OPTIONAL_COMPONENTS DocTools)
|
set(KF5_OPTIONAL_COMPONENTS DocTools)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} Runner)
|
set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} Runner)
|
||||||
|
|
|
@ -61,6 +61,7 @@ PRIVATE
|
||||||
Qt5::DBus
|
Qt5::DBus
|
||||||
KF5::I18n
|
KF5::I18n
|
||||||
KF5::ConfigCore
|
KF5::ConfigCore
|
||||||
|
KF5::DNSSD
|
||||||
)
|
)
|
||||||
|
|
||||||
if (BLUETOOTH_ENABLED)
|
if (BLUETOOTH_ENABLED)
|
||||||
|
|
|
@ -4,6 +4,7 @@ set(backends_kdeconnect_SRCS
|
||||||
|
|
||||||
backends/lan/server.cpp
|
backends/lan/server.cpp
|
||||||
backends/lan/lanlinkprovider.cpp
|
backends/lan/lanlinkprovider.cpp
|
||||||
|
backends/lan/mdnslinkprovider.cpp
|
||||||
backends/lan/landevicelink.cpp
|
backends/lan/landevicelink.cpp
|
||||||
backends/lan/lanpairinghandler.cpp
|
backends/lan/lanpairinghandler.cpp
|
||||||
backends/lan/compositeuploadjob.cpp
|
backends/lan/compositeuploadjob.cpp
|
||||||
|
|
456
core/backends/lan/mdnslinkprovider.cpp
Normal file
456
core/backends/lan/mdnslinkprovider.cpp
Normal file
|
@ -0,0 +1,456 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Albert Vaca <albertvaka@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License or (at your option) version 3 or any later version
|
||||||
|
* accepted by the membership of KDE e.V. (or its successor approved
|
||||||
|
* by the membership of KDE e.V.), which shall act as a proxy
|
||||||
|
* defined in Section 14 of version 3 of the license.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mdnslinkprovider.h"
|
||||||
|
#include "core_debug.h"
|
||||||
|
|
||||||
|
#include <KDNSSD/DNSSD/PublicService>
|
||||||
|
#include <KDNSSD/DNSSD/ServiceBrowser>
|
||||||
|
#include <KDNSSD/DNSSD/RemoteService>
|
||||||
|
|
||||||
|
#include <QHostInfo>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <QNetworkSession>
|
||||||
|
#include <QNetworkConfigurationManager>
|
||||||
|
#include <QSslCipher>
|
||||||
|
#include <QSslConfiguration>
|
||||||
|
|
||||||
|
#include "daemon.h"
|
||||||
|
#include "landevicelink.h"
|
||||||
|
#include "lanpairinghandler.h"
|
||||||
|
#include "kdeconnectconfig.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
MDNSLinkProvider::MDNSLinkProvider()
|
||||||
|
: m_server(new Server(this))
|
||||||
|
, m_tcpPort(0)
|
||||||
|
, m_publisher(nullptr)
|
||||||
|
, m_serviceBrowser(nullptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_server->setProxy(QNetworkProxy::NoProxy);
|
||||||
|
connect(m_server, &QTcpServer::newConnection, this, &MDNSLinkProvider::newConnection);
|
||||||
|
|
||||||
|
//Detect when a network interface changes status, so we announce ourselves in the new network
|
||||||
|
QNetworkConfigurationManager* networkManager = new QNetworkConfigurationManager(this);
|
||||||
|
connect(networkManager, &QNetworkConfigurationManager::configurationChanged, this, &MDNSLinkProvider::onNetworkConfigurationChanged);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MDNSLinkProvider::~MDNSLinkProvider()
|
||||||
|
{
|
||||||
|
delete m_publisher;
|
||||||
|
delete m_serviceBrowser;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::onStart()
|
||||||
|
{
|
||||||
|
m_tcpPort = MIN_TCP_PORT;
|
||||||
|
while (!m_server->listen(QHostAddress::Any, m_tcpPort)) {
|
||||||
|
m_tcpPort++;
|
||||||
|
if (m_tcpPort > MAX_TCP_PORT) { //No ports available?
|
||||||
|
qCritical(KDECONNECT_CORE) << "Error opening a port in range" << MIN_TCP_PORT << "-" << MAX_TCP_PORT;
|
||||||
|
m_tcpPort = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onNetworkChange();
|
||||||
|
qCDebug(KDECONNECT_CORE) << "MDNSLinkProvider started";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::onStop()
|
||||||
|
{
|
||||||
|
m_server->close();
|
||||||
|
qCDebug(KDECONNECT_CORE) << "MDNSLinkProvider stopped";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::onNetworkChange()
|
||||||
|
{
|
||||||
|
initializeMDNS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::onNetworkConfigurationChanged(const QNetworkConfiguration& config)
|
||||||
|
{
|
||||||
|
if (m_lastConfig != config && config.state() == QNetworkConfiguration::Active) {
|
||||||
|
m_lastConfig = config;
|
||||||
|
onNetworkChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::initializeMDNS()
|
||||||
|
{
|
||||||
|
KdeConnectConfig* config = KdeConnectConfig::instance();
|
||||||
|
|
||||||
|
std::cout << "Init PublicService" << std::endl;
|
||||||
|
|
||||||
|
// Restart the publishing.
|
||||||
|
delete m_publisher;
|
||||||
|
m_publisher = new KDNSSD::PublicService(QStringLiteral("KDEConnect on ") + config->name(), QStringLiteral("_kdeconnect._tcp"), m_tcpPort);
|
||||||
|
|
||||||
|
/*
|
||||||
|
QMap<QString, QByteArray> data;
|
||||||
|
data[QStringLiteral("protocolVersion")] = QString::number(NetworkPacket::s_protocolVersion).toUtf8();
|
||||||
|
data[QStringLiteral("type")] = config->deviceType().toUtf8();
|
||||||
|
data[QStringLiteral("name")] = config->name().toUtf8();
|
||||||
|
data[QStringLiteral("id")] = config->deviceId().toUtf8();
|
||||||
|
m_publisher->setTextData(data);
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (m_publisher->publish()) {
|
||||||
|
qCDebug(KDECONNECT_CORE) << "mDNS: published successfully";
|
||||||
|
} else {
|
||||||
|
qCDebug(KDECONNECT_CORE) << "mDNS: failed to publish";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Init ServiceBrowser" << std::endl;
|
||||||
|
|
||||||
|
delete m_serviceBrowser;
|
||||||
|
m_serviceBrowser = new KDNSSD::ServiceBrowser(QStringLiteral("_kdeconnect._tcp"), true); //TODO: Check if autoresolve can be false to save bandwidth
|
||||||
|
connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::serviceAdded, [this](KDNSSD::RemoteService::Ptr service) {
|
||||||
|
std::cout << "Discovered" << service->hostName().toStdString() << service->serviceName().toStdString() << service->port() << std::endl;
|
||||||
|
QSslSocket* socket = new QSslSocket(this);
|
||||||
|
connect(socket, &QAbstractSocket::connected, this, &MDNSLinkProvider::tcpSocketConnected);
|
||||||
|
connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &MDNSLinkProvider::connectError);
|
||||||
|
socket->setProxy(QNetworkProxy::NoProxy);
|
||||||
|
socket->connectToHost(service->hostName(), service->port());
|
||||||
|
});
|
||||||
|
connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::finished, []() {
|
||||||
|
std::cout << "Finished discovery" << std::endl;
|
||||||
|
});
|
||||||
|
m_serviceBrowser->startBrowse();
|
||||||
|
|
||||||
|
//Discovery
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::connectError(QAbstractSocket::SocketError socketError)
|
||||||
|
{
|
||||||
|
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
|
||||||
|
if (!socket) return;
|
||||||
|
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Socket error" << socketError;
|
||||||
|
|
||||||
|
//The socket we created didn't work, and we didn't manage
|
||||||
|
//to create a LanDeviceLink from it, deleting everything.
|
||||||
|
delete m_receivedIdentityPackets.take(socket).np;
|
||||||
|
delete socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
//We received a UDP packet and answered by connecting to them by TCP. This gets called on a successful connection.
|
||||||
|
void MDNSLinkProvider::tcpSocketConnected()
|
||||||
|
{
|
||||||
|
std::cout << "tcpSocketConnected" << std::endl;
|
||||||
|
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
|
||||||
|
if (!socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configureSocket(socket);
|
||||||
|
|
||||||
|
connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
|
||||||
|
|
||||||
|
std::cout << "sending our identity packet to the socket we are connecting to" << std::endl;
|
||||||
|
sendOurIdentity(socket);
|
||||||
|
std::cout << "now receiving" << std::endl;
|
||||||
|
NetworkPacket* receivedPacket = receiveTheirIdentity(socket);
|
||||||
|
if (!receivedPacket) {
|
||||||
|
qCWarning(KDECONNECT_CORE) << "Aborting tcpSocketConnected since we couldn't get their identity";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_receivedIdentityPackets[socket].np = receivedPacket;
|
||||||
|
const QString& deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
|
||||||
|
|
||||||
|
|
||||||
|
qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)";
|
||||||
|
|
||||||
|
bool isDeviceTrusted = KdeConnectConfig::instance()->trustedDevices().contains(deviceId);
|
||||||
|
configureSslSocket(socket, deviceId, isDeviceTrusted);
|
||||||
|
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)";
|
||||||
|
|
||||||
|
connect(socket, &QSslSocket::encrypted, this, &MDNSLinkProvider::encrypted);
|
||||||
|
|
||||||
|
if (isDeviceTrusted) {
|
||||||
|
connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &MDNSLinkProvider::sslErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket->startServerEncryption();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::encrypted()
|
||||||
|
{
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Socket successfully established an SSL connection";
|
||||||
|
|
||||||
|
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
|
||||||
|
if (!socket) return;
|
||||||
|
// TODO delete me?
|
||||||
|
disconnect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &MDNSLinkProvider::sslErrors);
|
||||||
|
|
||||||
|
Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode);
|
||||||
|
LanDeviceLink::ConnectionStarted connectionOrigin = (socket->mode() == QSslSocket::SslClientMode)? LanDeviceLink::Locally : LanDeviceLink::Remotely;
|
||||||
|
|
||||||
|
NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np;
|
||||||
|
const QString& deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
|
||||||
|
|
||||||
|
addLink(deviceId, socket, receivedPacket, connectionOrigin);
|
||||||
|
|
||||||
|
// Copied from tcpSocketConnected slot, now delete received packet
|
||||||
|
delete m_receivedIdentityPackets.take(socket).np;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::sslErrors(const QList<QSslError>& errors)
|
||||||
|
{
|
||||||
|
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
|
||||||
|
if (!socket) return;
|
||||||
|
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Failing due to " << errors;
|
||||||
|
Device* device = Daemon::instance()->getDevice(socket->peerVerifyName());
|
||||||
|
if (device) {
|
||||||
|
device->unpair();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_receivedIdentityPackets.take(socket).np;
|
||||||
|
// Socket disconnects itself on ssl error and will be deleted by deleteLater slot, no need to delete manually
|
||||||
|
}
|
||||||
|
|
||||||
|
//I'm the new device and this is the answer to my UDP identity packet (no data received yet). They are connecting to us through TCP, and they should send an identity.
|
||||||
|
void MDNSLinkProvider::newConnection()
|
||||||
|
{
|
||||||
|
qCDebug(KDECONNECT_CORE) << "MDNSLinkProvider newConnection";
|
||||||
|
|
||||||
|
while (m_server->hasPendingConnections()) {
|
||||||
|
QSslSocket* socket = m_server->nextPendingConnection();
|
||||||
|
configureSocket(socket);
|
||||||
|
//This socket is still managed by us (and child of the QTcpServer), if
|
||||||
|
//it disconnects before we manage to pass it to a LanDeviceLink, it's
|
||||||
|
//our responsibility to delete it. We do so with this connection.
|
||||||
|
connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
|
||||||
|
connect(socket, &QIODevice::readyRead, this, &MDNSLinkProvider::dataReceived);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//I'm the new device and this is the answer to my UDP identity packet (data received)
|
||||||
|
void MDNSLinkProvider::dataReceived()
|
||||||
|
{
|
||||||
|
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
|
||||||
|
socket->startTransaction();
|
||||||
|
|
||||||
|
std::cout << "dataReceived" << std::endl;
|
||||||
|
|
||||||
|
sendOurIdentity(socket);
|
||||||
|
NetworkPacket* theirIdentity = receiveTheirIdentity(socket);
|
||||||
|
if (!theirIdentity) {
|
||||||
|
qCWarning(KDECONNECT_CORE) << "Aborting dataReceived since we couldn't get their identity";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_receivedIdentityPackets[socket].np = theirIdentity;
|
||||||
|
|
||||||
|
const QString& deviceId = theirIdentity->get<QString>(QStringLiteral("deviceId"));
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Handshaking done (i'm the new device)";
|
||||||
|
|
||||||
|
//This socket will now be owned by the LanDeviceLink or we don't want more data to be received, forget about it
|
||||||
|
disconnect(socket, &QIODevice::readyRead, this, &MDNSLinkProvider::dataReceived);
|
||||||
|
|
||||||
|
bool isDeviceTrusted = KdeConnectConfig::instance()->trustedDevices().contains(deviceId);
|
||||||
|
configureSslSocket(socket, deviceId, isDeviceTrusted);
|
||||||
|
|
||||||
|
qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)";
|
||||||
|
|
||||||
|
connect(socket, &QSslSocket::encrypted, this, &MDNSLinkProvider::encrypted);
|
||||||
|
|
||||||
|
if (isDeviceTrusted) {
|
||||||
|
connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &MDNSLinkProvider::sslErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket->startClientEncryption();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink)
|
||||||
|
{
|
||||||
|
const QString id = destroyedDeviceLink->property("deviceId").toString();
|
||||||
|
//qCDebug(KDECONNECT_CORE) << "deviceLinkDestroyed" << id;
|
||||||
|
QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(id);
|
||||||
|
Q_ASSERT(linkIterator != m_links.end());
|
||||||
|
if (linkIterator != m_links.end()) {
|
||||||
|
Q_ASSERT(linkIterator.value() == destroyedDeviceLink);
|
||||||
|
m_links.erase(linkIterator);
|
||||||
|
auto pairingHandler = m_pairingHandlers.take(id);
|
||||||
|
if (pairingHandler) {
|
||||||
|
pairingHandler->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::configureSslSocket(QSslSocket* socket, const QString& deviceId, bool isDeviceTrusted)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
// Setting supported ciphers manually, to match those on Android (FIXME: Test if this can be left unconfigured and still works for Android 4)
|
||||||
|
QList<QSslCipher> socketCiphers;
|
||||||
|
socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-ECDSA-AES256-GCM-SHA384")));
|
||||||
|
socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-ECDSA-AES128-GCM-SHA256")));
|
||||||
|
socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-RSA-AES128-SHA")));
|
||||||
|
// Configure for ssl
|
||||||
|
QSslConfiguration sslConfig;
|
||||||
|
sslConfig.setCiphers(socketCiphers);
|
||||||
|
|
||||||
|
socket->setSslConfiguration(sslConfig);
|
||||||
|
*/
|
||||||
|
socket->setLocalCertificate(KdeConnectConfig::instance()->certificate());
|
||||||
|
socket->setPrivateKey(KdeConnectConfig::instance()->privateKeyPath());
|
||||||
|
socket->setPeerVerifyName(deviceId);
|
||||||
|
|
||||||
|
if (isDeviceTrusted) {
|
||||||
|
QString certString = KdeConnectConfig::instance()->getDeviceProperty(deviceId, QStringLiteral("certificate"), QString());
|
||||||
|
socket->addCaCertificate(QSslCertificate(certString.toLatin1()));
|
||||||
|
socket->setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||||
|
} else {
|
||||||
|
socket->setPeerVerifyMode(QSslSocket::QueryPeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Usually SSL errors are only bad for trusted devices. Uncomment this section to log errors in any case, for debugging.
|
||||||
|
//QObject::connect(socket, static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(&QSslSocket::sslErrors), [](const QList<QSslError>& errors)
|
||||||
|
//{
|
||||||
|
// Q_FOREACH (const QSslError& error, errors) {
|
||||||
|
// qCDebug(KDECONNECT_CORE) << "SSL Error:" << error.errorString();
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::configureSocket(QSslSocket* socket) {
|
||||||
|
|
||||||
|
socket->setProxy(QNetworkProxy::NoProxy);
|
||||||
|
|
||||||
|
socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
// time to start sending keepalive packets (seconds)
|
||||||
|
int maxIdle = 10;
|
||||||
|
setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
// interval between keepalive packets after the initial period (seconds)
|
||||||
|
int interval = 5;
|
||||||
|
setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
// number of missed keepalive packets before disconnecting
|
||||||
|
int count = 3;
|
||||||
|
setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, NetworkPacket* receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin)
|
||||||
|
{
|
||||||
|
// Socket disconnection will now be handled by LanDeviceLink
|
||||||
|
disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
|
||||||
|
|
||||||
|
LanDeviceLink* deviceLink;
|
||||||
|
//Do we have a link for this device already?
|
||||||
|
QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(deviceId);
|
||||||
|
if (linkIterator != m_links.end()) {
|
||||||
|
//qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId;
|
||||||
|
deviceLink = linkIterator.value();
|
||||||
|
deviceLink->reset(socket, connectionOrigin);
|
||||||
|
} else {
|
||||||
|
deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin);
|
||||||
|
connect(deviceLink, &QObject::destroyed, this, &MDNSLinkProvider::deviceLinkDestroyed);
|
||||||
|
m_links[deviceId] = deviceLink;
|
||||||
|
if (m_pairingHandlers.contains(deviceId)) {
|
||||||
|
//We shouldn't have a pairinghandler if we didn't have a link.
|
||||||
|
//Crash if debug, recover if release (by setting the new devicelink to the old pairinghandler)
|
||||||
|
Q_ASSERT(m_pairingHandlers.contains(deviceId));
|
||||||
|
m_pairingHandlers[deviceId]->setDeviceLink(deviceLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_EMIT onConnectionReceived(*receivedPacket, deviceLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
LanPairingHandler* MDNSLinkProvider::createPairingHandler(DeviceLink* link)
|
||||||
|
{
|
||||||
|
LanPairingHandler* ph = m_pairingHandlers.value(link->deviceId());
|
||||||
|
if (!ph) {
|
||||||
|
ph = new LanPairingHandler(link);
|
||||||
|
qCDebug(KDECONNECT_CORE) << "creating pairing handler for" << link->deviceId();
|
||||||
|
connect (ph, &LanPairingHandler::pairingError, link, &DeviceLink::pairingError);
|
||||||
|
m_pairingHandlers[link->deviceId()] = ph;
|
||||||
|
}
|
||||||
|
return ph;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::userRequestsPair(const QString& deviceId)
|
||||||
|
{
|
||||||
|
LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId));
|
||||||
|
ph->requestPairing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::userRequestsUnpair(const QString& deviceId)
|
||||||
|
{
|
||||||
|
LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId));
|
||||||
|
ph->unpair();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::incomingPairPacket(DeviceLink* deviceLink, const NetworkPacket& np)
|
||||||
|
{
|
||||||
|
LanPairingHandler* ph = createPairingHandler(deviceLink);
|
||||||
|
ph->packetReceived(np);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NetworkPacket* MDNSLinkProvider::receiveTheirIdentity(QSslSocket* socket)
|
||||||
|
{
|
||||||
|
|
||||||
|
const QByteArray data = socket->readLine();
|
||||||
|
qCDebug(KDECONNECT_CORE) << "MDNSLinkProvider received reply:" << data;
|
||||||
|
|
||||||
|
NetworkPacket* theirIdentity = new NetworkPacket();
|
||||||
|
bool success = NetworkPacket::unserialize(data, theirIdentity);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
delete theirIdentity;
|
||||||
|
socket->rollbackTransaction();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
socket->commitTransaction();
|
||||||
|
|
||||||
|
if (theirIdentity->type() != PACKET_TYPE_IDENTITY) {
|
||||||
|
qCWarning(KDECONNECT_CORE) << "MDNSLinkProvider/newConnection: Expected identity, received " << theirIdentity->type();
|
||||||
|
delete theirIdentity;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
|
||||||
|
return theirIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNSLinkProvider::sendOurIdentity(QSslSocket* socket)
|
||||||
|
{
|
||||||
|
NetworkPacket* ourIdentity = new NetworkPacket();
|
||||||
|
NetworkPacket::createIdentityPacket(ourIdentity);
|
||||||
|
socket->write(ourIdentity->serialize());
|
||||||
|
delete ourIdentity;
|
||||||
|
}
|
103
core/backends/lan/mdnslinkprovider.h
Normal file
103
core/backends/lan/mdnslinkprovider.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License or (at your option) version 3 or any later version
|
||||||
|
* accepted by the membership of KDE e.V. (or its successor approved
|
||||||
|
* by the membership of KDE e.V.), which shall act as a proxy
|
||||||
|
* defined in Section 14 of version 3 of the license.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MDNSLINKPROVIDER_H
|
||||||
|
#define MDNSLINKPROVIDER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QSslSocket>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QNetworkSession>
|
||||||
|
|
||||||
|
#include "kdeconnectcore_export.h"
|
||||||
|
#include "backends/linkprovider.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "landevicelink.h"
|
||||||
|
|
||||||
|
namespace KDNSSD {
|
||||||
|
class PublicService;
|
||||||
|
class ServiceBrowser;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LanPairingHandler;
|
||||||
|
class KDECONNECTCORE_EXPORT MDNSLinkProvider
|
||||||
|
: public LinkProvider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MDNSLinkProvider();
|
||||||
|
~MDNSLinkProvider() override;
|
||||||
|
|
||||||
|
QString name() override { return QStringLiteral("MDNSLinkProvider"); }
|
||||||
|
int priority() override { return PRIORITY_HIGH; }
|
||||||
|
|
||||||
|
void userRequestsPair(const QString& deviceId);
|
||||||
|
void userRequestsUnpair(const QString& deviceId);
|
||||||
|
void incomingPairPacket(DeviceLink* device, const NetworkPacket& np);
|
||||||
|
|
||||||
|
static void configureSslSocket(QSslSocket* socket, const QString& deviceId, bool isDeviceTrusted);
|
||||||
|
static void configureSocket(QSslSocket* socket);
|
||||||
|
|
||||||
|
const static quint16 MIN_TCP_PORT = 1716;
|
||||||
|
const static quint16 MAX_TCP_PORT = 1764;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void onNetworkChange() override;
|
||||||
|
void onStart() override;
|
||||||
|
void onStop() override;
|
||||||
|
void tcpSocketConnected();
|
||||||
|
void encrypted();
|
||||||
|
void connectError(QAbstractSocket::SocketError socketError);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void newConnection();
|
||||||
|
void dataReceived();
|
||||||
|
void deviceLinkDestroyed(QObject* destroyedDeviceLink);
|
||||||
|
void sslErrors(const QList<QSslError>& errors);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LanPairingHandler* createPairingHandler(DeviceLink* link);
|
||||||
|
|
||||||
|
void onNetworkConfigurationChanged(const QNetworkConfiguration& config);
|
||||||
|
void addLink(const QString& deviceId, QSslSocket* socket, NetworkPacket* receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin);
|
||||||
|
void initializeMDNS();
|
||||||
|
void sendOurIdentity(QSslSocket*);
|
||||||
|
NetworkPacket* receiveTheirIdentity(QSslSocket*);
|
||||||
|
|
||||||
|
Server* m_server;
|
||||||
|
quint16 m_tcpPort;
|
||||||
|
|
||||||
|
QMap<QString, LanDeviceLink*> m_links;
|
||||||
|
QMap<QString, LanPairingHandler*> m_pairingHandlers;
|
||||||
|
|
||||||
|
struct PendingConnect {
|
||||||
|
NetworkPacket* np;
|
||||||
|
QHostAddress sender;
|
||||||
|
};
|
||||||
|
QMap<QSslSocket*, PendingConnect> m_receivedIdentityPackets;
|
||||||
|
QNetworkConfiguration m_lastConfig;
|
||||||
|
|
||||||
|
KDNSSD::PublicService *m_publisher;
|
||||||
|
KDNSSD::ServiceBrowser *m_serviceBrowser;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -36,6 +36,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "backends/lan/lanlinkprovider.h"
|
#include "backends/lan/lanlinkprovider.h"
|
||||||
|
#include "backends/lan/mdnslinkprovider.h"
|
||||||
#include "backends/loopback/loopbacklinkprovider.h"
|
#include "backends/loopback/loopbacklinkprovider.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "backends/devicelink.h"
|
#include "backends/devicelink.h"
|
||||||
|
@ -84,7 +85,8 @@ void Daemon::init()
|
||||||
if (d->m_testMode)
|
if (d->m_testMode)
|
||||||
d->m_linkProviders.insert(new LoopbackLinkProvider());
|
d->m_linkProviders.insert(new LoopbackLinkProvider());
|
||||||
else {
|
else {
|
||||||
d->m_linkProviders.insert(new LanLinkProvider());
|
//d->m_linkProviders.insert(new LanLinkProvider());
|
||||||
|
d->m_linkProviders.insert(new MDNSLinkProvider());
|
||||||
#ifdef KDECONNECT_BLUETOOTH
|
#ifdef KDECONNECT_BLUETOOTH
|
||||||
d->m_linkProviders.insert(new BluetoothLinkProvider());
|
d->m_linkProviders.insert(new BluetoothLinkProvider());
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -88,7 +88,7 @@ private:
|
||||||
QString m_id;
|
QString m_id;
|
||||||
QString m_type;
|
QString m_type;
|
||||||
QVariantMap m_body;
|
QVariantMap m_body;
|
||||||
|
|
||||||
QSharedPointer<QIODevice> m_payload;
|
QSharedPointer<QIODevice> m_payload;
|
||||||
qint64 m_payloadSize;
|
qint64 m_payloadSize;
|
||||||
QVariantMap m_payloadTransferInfo;
|
QVariantMap m_payloadTransferInfo;
|
||||||
|
|
Loading…
Reference in a new issue