lanlinkprovider.cpp 22.3 KB
Newer Older
1
/**
2
 * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
3
 *
4
 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5
6
 */

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
7
#include "lanlinkprovider.h"
8
#include "core_debug.h"
9

Hannah von Reth's avatar
Hannah von Reth committed
10
#ifndef Q_OS_WIN
11
12
13
14
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
Hannah von Reth's avatar
Hannah von Reth committed
15
#endif
16

17
18
#include <QHostInfo>
#include <QTcpServer>
19
#include <QMetaEnum>
20
#include <QNetworkProxy>
21
#include <QUdpSocket>
22
23
#include <QNetworkSession>
#include <QNetworkConfigurationManager>
24
25
#include <QSslCipher>
#include <QSslConfiguration>
26
#include <QSslKey>
27

28
#include "daemon.h"
29
#include "landevicelink.h"
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
30
#include "lanpairinghandler.h"
31
#include "kdeconnectconfig.h"
32
#include "qtcompat_p.h"
33

34
35
#define MIN_VERSION_WITH_SSL_SUPPORT 6

36
37
38
39
LanLinkProvider::LanLinkProvider(
        bool testMode,
        quint16 udpBroadcastPort,
        quint16 udpListenPort
40
        )
41
42
43
    : m_server(new Server(this))
    , m_udpSocket(this)
    , m_tcpPort(0)
44
45
    , m_udpBroadcastPort(udpBroadcastPort)
    , m_udpListenPort(udpListenPort)
Nicolas Fella's avatar
Nicolas Fella committed
46
    , m_testMode(testMode)
47
    , m_combineBroadcastsTimer(this)
48
49
{

50
51
52
    m_combineBroadcastsTimer.setInterval(0); // increase this if waiting a single event-loop iteration is not enough
    m_combineBroadcastsTimer.setSingleShot(true);
    connect(&m_combineBroadcastsTimer, &QTimer::timeout, this, &LanLinkProvider::broadcastToNetwork);
53

54
    connect(&m_udpSocket, &QIODevice::readyRead, this, &LanLinkProvider::udpBroadcastReceived);
55

56
    m_server->setProxy(QNetworkProxy::NoProxy);
Nicolas Fella's avatar
Nicolas Fella committed
57
    connect(m_server, &QTcpServer::newConnection, this, &LanLinkProvider::newConnection);
58

59
    m_udpSocket.setProxy(QNetworkProxy::NoProxy);
60

Yuri Chornoivan's avatar
Yuri Chornoivan committed
61
    //Detect when a network interface changes status, so we announce ourselves in the new network
62
63
    QNetworkConfigurationManager* networkManager = new QNetworkConfigurationManager(this);
    connect(networkManager, &QNetworkConfigurationManager::configurationChanged, this, &LanLinkProvider::onNetworkConfigurationChanged);
64

65
66
}

67
void LanLinkProvider::onNetworkConfigurationChanged(const QNetworkConfiguration& config)
68
{
69
    if (m_lastConfig != config && config.state() == QNetworkConfiguration::Active) {
70
        m_lastConfig = config;
71
        onNetworkChange();
72
    }
73
74
}

75
76
77
78
LanLinkProvider::~LanLinkProvider()
{
}

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
79
void LanLinkProvider::onStart()
80
{
81
    const QHostAddress bindAddress = m_testMode? QHostAddress::LocalHost : QHostAddress::Any;
82

83
    bool success = m_udpSocket.bind(bindAddress, m_udpListenPort, QUdpSocket::ShareAddress);
84
    if (!success) {
Simon Redman's avatar
Simon Redman committed
85
        QAbstractSocket::SocketError sockErr = m_udpSocket.error();
86
        // Refer to https://doc.qt.io/qt-5/qabstractsocket.html#SocketError-enum to decode socket error number
87
        QString errorMessage = QString::fromLatin1(QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey(sockErr));
Simon Redman's avatar
Simon Redman committed
88
        qCritical(KDECONNECT_CORE)
89
90
91
92
                << QLatin1String("Failed to bind UDP socket on port")
                << m_udpListenPort
                << QLatin1String("with error")
                << errorMessage;
93
    }
94
    Q_ASSERT(success);
95

96
97
98
99
    m_tcpPort = MIN_TCP_PORT;
    while (!m_server->listen(bindAddress, m_tcpPort)) {
        m_tcpPort++;
        if (m_tcpPort > MAX_TCP_PORT) { //No ports available?
100
            qCritical(KDECONNECT_CORE) << "Error opening a port in range" << MIN_TCP_PORT << "-" << MAX_TCP_PORT;
101
            m_tcpPort = 0;
102
103
            return;
        }
104
    }
105

106
    onNetworkChange();
Nicolas Fella's avatar
Nicolas Fella committed
107
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider started";
108
109
}

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
110
void LanLinkProvider::onStop()
111
{
112
113
    m_udpSocket.close();
    m_server->close();
Nicolas Fella's avatar
Nicolas Fella committed
114
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped";
115
116
}

117
void LanLinkProvider::onNetworkChange()
118
{
119
    if (m_combineBroadcastsTimer.isActive()) {
Richard Liebscher's avatar
Richard Liebscher committed
120
        qCDebug(KDECONNECT_CORE) << "Preventing duplicate broadcasts";
121
122
        return;
    }
123
    m_combineBroadcastsTimer.start();
124
125
126
127
128
}

//I'm in a new network, let's be polite and introduce myself
void LanLinkProvider::broadcastToNetwork()
{
129
    if (!m_server->isListening()) {
130
        //Not started
131
132
        return;
    }
133

134
    Q_ASSERT(m_tcpPort != 0);
135

136
    qCDebug(KDECONNECT_CORE()) << "Broadcasting identity packet";
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
137

Richard Liebscher's avatar
Richard Liebscher committed
138
    QList<QHostAddress> destinations = getBroadcastAddresses();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
139

Richard Liebscher's avatar
Richard Liebscher committed
140
    NetworkPacket np;
141
    NetworkPacket::createIdentityPacket(&np);
142
    np.set(QStringLiteral("tcpPort"), m_tcpPort);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
143
144
145
146

#ifdef Q_OS_WIN
    //On Windows we need to broadcast from every local IP address to reach all networks
    QUdpSocket sendSocket;
147
    sendSocket.setProxy(QNetworkProxy::NoProxy);
148
    for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
149
        if ( (iface.flags() & QNetworkInterface::IsUp)
150
151
             && (iface.flags() & QNetworkInterface::IsRunning)
             && (iface.flags() & QNetworkInterface::CanBroadcast)) {
152
            for (const QNetworkAddressEntry& ifaceAddress : iface.addressEntries()) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
153
154
155
                QHostAddress sourceAddress = ifaceAddress.ip();
                if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
                    qCDebug(KDECONNECT_CORE()) << "Broadcasting as" << sourceAddress;
Richard Liebscher's avatar
Richard Liebscher committed
156
                    sendBroadcasts(sendSocket, np, destinations);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
157
158
159
160
161
162
                    sendSocket.close();
                }
            }
        }
    }
#else
Richard Liebscher's avatar
Richard Liebscher committed
163
    sendBroadcasts(m_udpSocket, np, destinations);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
164
#endif
Richard Liebscher's avatar
Richard Liebscher committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
}

QList<QHostAddress> LanLinkProvider::getBroadcastAddresses()
{
    const QStringList customDevices = KdeConnectConfig::instance().customDevices();

    QList<QHostAddress> destinations;
    destinations.reserve(customDevices.length() + 1);

    // Default broadcast address
    destinations.append(m_testMode ? QHostAddress::LocalHost : QHostAddress::Broadcast);

    // Custom device addresses
    for (auto& customDevice : customDevices) {
        QHostAddress address(customDevice);
        if (address.isNull()) {
            qCWarning(KDECONNECT_CORE) << "Invalid custom device address" << customDevice;
        } else {
            destinations.append(address);
        }
    }

    return destinations;
}
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
189

Richard Liebscher's avatar
Richard Liebscher committed
190
191
192
193
194
195
196
197
void LanLinkProvider::sendBroadcasts(
        QUdpSocket& socket, const NetworkPacket& np, const QList<QHostAddress>& addresses)
{
    const QByteArray payload = np.serialize();

    for (auto& address : addresses) {
        socket.writeDatagram(payload, address, m_udpBroadcastPort);
    }
198
199
}

200
//I'm the existing device, a new device is kindly introducing itself.
201
202
//I will create a TcpSocket and try to connect. This can result in either tcpSocketConnected() or connectError().
void LanLinkProvider::udpBroadcastReceived()
203
{
204
    while (m_udpSocket.hasPendingDatagrams()) {
205

206
        QByteArray datagram;
207
        datagram.resize(m_udpSocket.pendingDatagramSize());
208
209
        QHostAddress sender;

210
        m_udpSocket.readDatagram(datagram.data(), datagram.size(), &sender);
211

212
        if (sender.isLoopback() && !m_testMode)
213
214
            continue;

215
216
        NetworkPacket* receivedPacket = new NetworkPacket(QLatin1String(""));
        bool success = NetworkPacket::unserialize(datagram, receivedPacket);
217

218
        //qCDebug(KDECONNECT_CORE) << "udp connection from " << receivedPacket->;
Albert Vaca Cintora's avatar
WIPx4    
Albert Vaca Cintora committed
219

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
220
        //qCDebug(KDECONNECT_CORE) << "Datagram " << datagram.data() ;
221

222
223
224
225
226
227
228
229
        if (!success) {
            qCDebug(KDECONNECT_CORE) << "Could not unserialize UDP packet";
            delete receivedPacket;
            continue;
        }

        if (receivedPacket->type() != PACKET_TYPE_IDENTITY) {
            qCDebug(KDECONNECT_CORE) << "Received a UDP packet of wrong type" << receivedPacket->type();
230
            delete receivedPacket;
231
            continue;
232
        }
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
233

234
        if (receivedPacket->get<QString>(QStringLiteral("deviceId")) == KdeConnectConfig::instance().deviceId()) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
235
            //qCDebug(KDECONNECT_CORE) << "Ignoring my own broadcast";
236
            delete receivedPacket;
237
            continue;
238
        }
239

240
        int tcpPort = receivedPacket->get<int>(QStringLiteral("tcpPort"));
241

242
        //qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort;
243

244
        QSslSocket* socket = new QSslSocket(this);
245
        socket->setProxy(QNetworkProxy::NoProxy);
246
247
        m_receivedIdentityPackets[socket].np = receivedPacket;
        m_receivedIdentityPackets[socket].sender = sender;
248
        connect(socket, &QAbstractSocket::connected, this, &LanLinkProvider::tcpSocketConnected);
249
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
Nicolas Fella's avatar
Nicolas Fella committed
250
        connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError);
251
252
253
#else
        connect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
#endif
254
        socket->connectToHost(sender, tcpPort);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
255
256
    }
}
257

258
void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError)
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
259
{
260
    QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
261
    if (!socket) return;
262

263
    qCDebug(KDECONNECT_CORE) << "Socket error" << socketError;
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
264
    qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString();
265
266
    NetworkPacket np(QLatin1String(""));
    NetworkPacket::createIdentityPacket(&np);
267
    np.set(QStringLiteral("tcpPort"), m_tcpPort);
268
    m_udpSocket.writeDatagram(np.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
269

270
271
    //The socket we created didn't work, and we didn't manage
    //to create a LanDeviceLink from it, deleting everything.
272
    delete m_receivedIdentityPackets.take(socket).np;
273
    delete socket;
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
274
275
}

Nicolas Fella's avatar
Nicolas Fella committed
276
//We received a UDP packet and answered by connecting to them by TCP. This gets called on a successful connection.
277
void LanLinkProvider::tcpSocketConnected()
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
278
{
279
    QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
280

281
    if (!socket) return;
Nicolas Fella's avatar
Nicolas Fella committed
282
    // TODO Delete me?
283
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
Nicolas Fella's avatar
Nicolas Fella committed
284
    disconnect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError);
285
286
287
#else
    disconnect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
#endif
Nicolas Fella's avatar
Nicolas Fella committed
288

289
290
    configureSocket(socket);

Tobias Fella's avatar
Tobias Fella committed
291
    // If socket disconnects due to any reason after connection, link on ssl failure
292
    connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
293

294
295
    NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np;
    const QString& deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
296
    //qCDebug(KDECONNECT_CORE) << "tcpSocketConnected" << socket->isWritable();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
297

298
    // If network is on ssl, do not believe when they are connected, believe when handshake is completed
299
300
    NetworkPacket np2(QLatin1String(""));
    NetworkPacket::createIdentityPacket(&np2);
301
302
    socket->write(np2.serialize());
    bool success = socket->waitForBytesWritten();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
303
304

    if (success) {
305

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
306
        qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)";
307
308

        // if ssl supported
309
        if (receivedPacket->get<int>(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) {
310

311
            bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
312
313
            configureSslSocket(socket, deviceId, isDeviceTrusted);

Albert Vaca Cintora's avatar
Cleanup    
Albert Vaca Cintora committed
314
            qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)";
315

316
            connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted);
317

318
            connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
319

320
            socket->startServerEncryption();
321

322
            return; // Return statement prevents from deleting received packet, needed in slot "encrypted"
323
        } else {
324
325
            qWarning() << receivedPacket->get<QString>(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work";
            //addLink(deviceId, socket, receivedPacket, LanDeviceLink::Remotely);
326
327
        }

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
328
    } else {
329
330
        //I think this will never happen, but if it happens the deviceLink
        //(or the socket that is now inside it) might not be valid. Delete them.
331
        qCDebug(KDECONNECT_CORE) << "Fallback (2), try reverse connection (send udp packet)";
332
        m_udpSocket.writeDatagram(np2.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort);
333
334
    }

335
    delete m_receivedIdentityPackets.take(socket).np;
336
    //We don't delete the socket because now it's owned by the LanDeviceLink
337
338
}

Vineet Garg's avatar
Vineet Garg committed
339
340
void LanLinkProvider::encrypted()
{
Nicholas D Steeves's avatar
Nicholas D Steeves committed
341
    qCDebug(KDECONNECT_CORE) << "Socket successfully established an SSL connection";
Vineet Garg's avatar
Vineet Garg committed
342

343
    QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
344
    if (!socket) return;
345

346
347
348
    Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode);
    LanDeviceLink::ConnectionStarted connectionOrigin = (socket->mode() == QSslSocket::SslClientMode)? LanDeviceLink::Locally : LanDeviceLink::Remotely;

349
350
    NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np;
    const QString& deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
351

352
    addLink(deviceId, socket, receivedPacket, connectionOrigin);
353

354
    // Copied from tcpSocketConnected slot, now delete received packet
355
    delete m_receivedIdentityPackets.take(socket).np;
356
357
}

Vineet Garg's avatar
Vineet Garg committed
358
void LanLinkProvider::sslErrors(const QList<QSslError>& errors)
359
360
{
    QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
361
    if (!socket) return;
362

363
364
365
366
367
368
369
370
    bool fatal = false;
    for (const QSslError& error : errors) {
        if (error.error() != QSslError::SelfSignedCertificate) {
            qCCritical(KDECONNECT_CORE) << "Disconnecting due to fatal SSL Error: " << error;
            fatal = true;
        } else {
            qCDebug(KDECONNECT_CORE) << "Ignoring self-signed cert error";
        }
371
372
    }

373
374
375
376
    if (fatal) {
        socket->disconnectFromHost();
        delete m_receivedIdentityPackets.take(socket).np;
    }
377
378
}

379
//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.
380
void LanLinkProvider::newConnection()
381
{
Adam Pigg's avatar
Adam Pigg committed
382
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider newConnection";
383

384
385
    while (m_server->hasPendingConnections()) {
        QSslSocket* socket = m_server->nextPendingConnection();
386
387
388
        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
Boris Egorov's avatar
Boris Egorov committed
389
        //our responsibility to delete it. We do so with this connection.
390
391
392
393
        connect(socket, &QAbstractSocket::disconnected,
                socket, &QObject::deleteLater);
        connect(socket, &QIODevice::readyRead,
                this, &LanLinkProvider::dataReceived);
394

395
    }
396
397
}

398
//I'm the new device and this is the answer to my UDP identity packet (data received)
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
399
void LanLinkProvider::dataReceived()
400
{
401
    QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
402
403
404
405
#if QT_VERSION < QT_VERSION_CHECK(5,7,0)
    if (!socket->canReadLine())
        return;
#else
406
    socket->startTransaction();
407
#endif
408

409
    const QByteArray data = socket->readLine();
410

Adam Pigg's avatar
Adam Pigg committed
411
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider received reply:" << data;
412

413
414
    NetworkPacket* np = new NetworkPacket(QLatin1String(""));
    bool success = NetworkPacket::unserialize(data, np);
Vineet Garg's avatar
Vineet Garg committed
415

416
417
418
419
420
421
#if QT_VERSION < QT_VERSION_CHECK(5,7,0)
    if (!success) {
        delete np;
        return;
    }
#else
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
422
423
    if (!success) {
        delete np;
424
        socket->rollbackTransaction();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
425
426
        return;
    }
427
    socket->commitTransaction();
428
#endif
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
429

430
    if (np->type() != PACKET_TYPE_IDENTITY) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
431
432
        qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type();
        delete np;
433
434
        return;
    }
435

436
    // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
437
    m_receivedIdentityPackets[socket].np = np;
Vineet Garg's avatar
Vineet Garg committed
438

439
    const QString& deviceId = np->get<QString>(QStringLiteral("deviceId"));
Àlex Fiestas's avatar
Àlex Fiestas committed
440
    //qCDebug(KDECONNECT_CORE) << "Handshaking done (i'm the new device)";
441

442
    //This socket will now be owned by the LanDeviceLink or we don't want more data to be received, forget about it
443
    disconnect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived);
444

445
    if (np->get<int>(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) {
446

447
        bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
448
        configureSslSocket(socket, deviceId, isDeviceTrusted);
449

Albert Vaca Cintora's avatar
Cleanup    
Albert Vaca Cintora committed
450
        qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)";
451

452
        connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted);
453

454
        if (isDeviceTrusted) {
Nicolas Fella's avatar
Nicolas Fella committed
455
            connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
456
457
458
        }

        socket->startClientEncryption();
459

460
    } else {
461
        qWarning() << np->get<QString>(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work";
462
        //addLink(deviceId, socket, np, LanDeviceLink::Locally);
463
        delete m_receivedIdentityPackets.take(socket).np;
464
    }
465
466
}

467
void LanLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink)
468
{
469
    const QString id = destroyedDeviceLink->property("deviceId").toString();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
470
    //qCDebug(KDECONNECT_CORE) << "deviceLinkDestroyed" << id;
471
    QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(id);
472
    Q_ASSERT(linkIterator != m_links.end());
473
    if (linkIterator != m_links.end()) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
474
        Q_ASSERT(linkIterator.value() == destroyedDeviceLink);
475
        m_links.erase(linkIterator);
476
477
478
479
        auto pairingHandler = m_pairingHandlers.take(id);
        if (pairingHandler) {
            pairingHandler->deleteLater();
        }
480
    }
481

482
483
}

484
void LanLinkProvider::configureSslSocket(QSslSocket* socket, const QString& deviceId, bool isDeviceTrusted)
485
{
486
    // Setting supported ciphers manually, to match those on Android (FIXME: Test if this can be left unconfigured and still works for Android 4)
487
    QList<QSslCipher> socketCiphers;
488
489
490
    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")));
491

492
    // Configure for ssl
493
494
    QSslConfiguration sslConfig;
    sslConfig.setCiphers(socketCiphers);
495
    sslConfig.setLocalCertificate(KdeConnectConfig::instance().certificate());
496

497
498
499
500
501
502
503
    QFile privateKeyFile(KdeConnectConfig::instance().privateKeyPath());
    QSslKey privateKey;
    if (privateKeyFile.open(QIODevice::ReadOnly)) {
        privateKey = QSslKey(privateKeyFile.readAll(), QSsl::Rsa);
    }
    privateKeyFile.close();
    sslConfig.setPrivateKey(privateKey);
504
505

    if (isDeviceTrusted) {
506
        QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId, QStringLiteral("certificate"), QString());
507
508
        sslConfig.setCaCertificates({QSslCertificate(certString.toLatin1())});
        sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
509
    } else {
510
        sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer);
511
    }
512
513
514
    socket->setSslConfiguration(sslConfig);
    socket->setPeerVerifyName(deviceId);

515

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
516
517
518
    //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)
    //{
519
    //    Q_FOREACH (const QSslError& error, errors) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
520
521
522
    //        qCDebug(KDECONNECT_CORE) << "SSL Error:" << error.errorString();
    //    }
    //});
523
524
525
}

void LanLinkProvider::configureSocket(QSslSocket* socket) {
526

527
528
    socket->setProxy(QNetworkProxy::NoProxy);

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
529
    socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
530

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
#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
548
549

}
550

551
void LanLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, NetworkPacket* receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin)
Albert Vaca Cintora's avatar
WIP    
Albert Vaca Cintora committed
552
{
553
    // Socket disconnection will now be handled by LanDeviceLink
554
    disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
555

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
556
    LanDeviceLink* deviceLink;
557
    //Do we have a link for this device already?
558
559
    QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(deviceId);
    if (linkIterator != m_links.end()) {
560
        //qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId;
561
562
563
564
        deviceLink = linkIterator.value();
        deviceLink->reset(socket, connectionOrigin);
    } else {
        deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin);
565
        connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed);
566
567
        m_links[deviceId] = deviceLink;
        if (m_pairingHandlers.contains(deviceId)) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
568
569
            //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)
570
571
            Q_ASSERT(m_pairingHandlers.contains(deviceId));
            m_pairingHandlers[deviceId]->setDeviceLink(deviceLink);
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
572
        }
573
    }
574
    Q_EMIT onConnectionReceived(*receivedPacket, deviceLink);
575
}
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
576

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
577
LanPairingHandler* LanLinkProvider::createPairingHandler(DeviceLink* link)
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
578
{
579
    LanPairingHandler* ph = m_pairingHandlers.value(link->deviceId());
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
580
    if (!ph) {
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
581
        ph = new LanPairingHandler(link);
582
        qCDebug(KDECONNECT_CORE) << "creating pairing handler for" << link->deviceId();
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
583
        connect (ph, &LanPairingHandler::pairingError, link, &DeviceLink::pairingError);
584
        m_pairingHandlers[link->deviceId()] = ph;
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
585
    }
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
586
587
    return ph;
}
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
588

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
589
590
void LanLinkProvider::userRequestsPair(const QString& deviceId)
{
591
    LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId));
Albert Vaca Cintora's avatar
WIPx3!    
Albert Vaca Cintora committed
592
593
    ph->requestPairing();
}
Albert Vaca Cintora's avatar
WIPx4    
Albert Vaca Cintora committed
594

595
596
void LanLinkProvider::userRequestsUnpair(const QString& deviceId)
{
597
    LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId));
598
599
600
    ph->unpair();
}

601
void LanLinkProvider::incomingPairPacket(DeviceLink* deviceLink, const NetworkPacket& np)
602
{
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
603
    LanPairingHandler* ph = createPairingHandler(deviceLink);
604
    ph->packetReceived(np);
605
}
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
606