File indexing completed on 2024-05-12 04:58:51

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "lanlinkprovider.h"
0008 #include "core_debug.h"
0009 
0010 #ifndef Q_OS_WIN
0011 #include <netdb.h>
0012 #include <netinet/in.h>
0013 #include <netinet/tcp.h>
0014 #include <sys/socket.h>
0015 #else
0016 #include <winsock2.h>
0017 // Winsock2 needs to be included before any other header
0018 #include <mstcpip.h>
0019 #endif
0020 
0021 #if defined(Q_OS_WIN) || defined(Q_OS_FREEBSD)
0022 #include <QNetworkInterface>
0023 #endif
0024 
0025 #include <QHostInfo>
0026 #include <QMetaEnum>
0027 #include <QNetworkProxy>
0028 #include <QSslCipher>
0029 #include <QSslConfiguration>
0030 #include <QSslKey>
0031 #include <QStringList>
0032 #include <QTcpServer>
0033 #include <QUdpSocket>
0034 
0035 #include "daemon.h"
0036 #include "dbushelper.h"
0037 #include "kdeconnectconfig.h"
0038 #include "landevicelink.h"
0039 
0040 static const int MAX_UNPAIRED_CONNECTIONS = 42;
0041 static const int MAX_REMEMBERED_IDENTITY_PACKETS = 42;
0042 
0043 static const long MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE = 500;
0044 
0045 LanLinkProvider::LanLinkProvider(bool testMode)
0046     : m_server(new Server(this))
0047     , m_udpSocket(this)
0048     , m_tcpPort(0)
0049     , m_testMode(testMode)
0050     , m_combineNetworkChangeTimer(this)
0051 #ifdef KDECONNECT_MDNS
0052     , m_mdnsDiscovery(this)
0053 #endif
0054 {
0055     m_combineNetworkChangeTimer.setInterval(0); // increase this if waiting a single event-loop iteration is not enough
0056     m_combineNetworkChangeTimer.setSingleShot(true);
0057     connect(&m_combineNetworkChangeTimer, &QTimer::timeout, this, &LanLinkProvider::combinedOnNetworkChange);
0058 
0059     connect(&m_udpSocket, &QIODevice::readyRead, this, &LanLinkProvider::udpBroadcastReceived);
0060 
0061     m_server->setProxy(QNetworkProxy::NoProxy);
0062     connect(m_server, &QTcpServer::newConnection, this, &LanLinkProvider::newConnection);
0063 
0064     m_udpSocket.setProxy(QNetworkProxy::NoProxy);
0065 
0066     connect(&m_udpSocket, &QAbstractSocket::errorOccurred, [](QAbstractSocket::SocketError socketError) {
0067         qWarning() << "Error sending UDP packet:" << socketError;
0068     });
0069 
0070 #if QT_VERSION_MAJOR < 6
0071     QNetworkConfigurationManager *networkManager = new QNetworkConfigurationManager(this);
0072     connect(networkManager, &QNetworkConfigurationManager::configurationChanged, this, [this](QNetworkConfiguration config) {
0073         if (config.state() == QNetworkConfiguration::Active) {
0074             onNetworkChange();
0075         }
0076     });
0077 #else
0078     const auto checkNetworkChange = [this]() {
0079         if (QNetworkInformation::instance()->reachability() == QNetworkInformation::Reachability::Online) {
0080             onNetworkChange();
0081         }
0082     };
0083     // Detect when a network interface changes status, so we announce ourselves in the new network
0084     QNetworkInformation::instance()->loadBackendByFeatures(QNetworkInformation::Feature::Reachability);
0085 
0086     // We want to know if our current network reachability has changed, or if we change from one network to another
0087     connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, checkNetworkChange);
0088     connect(QNetworkInformation::instance(), &QNetworkInformation::transportMediumChanged, this, checkNetworkChange);
0089 #endif
0090 }
0091 
0092 LanLinkProvider::~LanLinkProvider()
0093 {
0094 }
0095 
0096 void LanLinkProvider::onStart()
0097 {
0098     const QHostAddress bindAddress = m_testMode ? QHostAddress::LocalHost : QHostAddress::Any;
0099 
0100     bool success = m_udpSocket.bind(bindAddress, UDP_PORT, QUdpSocket::ShareAddress);
0101     if (!success) {
0102         QAbstractSocket::SocketError sockErr = m_udpSocket.error();
0103         // Refer to https://doc.qt.io/qt-5/qabstractsocket.html#SocketError-enum to decode socket error number
0104         QString errorMessage = QString::fromLatin1(QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey(sockErr));
0105         qCritical(KDECONNECT_CORE) << "Failed to bind UDP socket on port" << UDP_PORT << "with error" << errorMessage;
0106     }
0107     Q_ASSERT(success);
0108 
0109     m_tcpPort = MIN_TCP_PORT;
0110     while (!m_server->listen(bindAddress, m_tcpPort)) {
0111         m_tcpPort++;
0112         if (m_tcpPort > MAX_TCP_PORT) { // No ports available?
0113             qCritical(KDECONNECT_CORE) << "Error opening a port in range" << MIN_TCP_PORT << "-" << MAX_TCP_PORT;
0114             m_tcpPort = 0;
0115             return;
0116         }
0117     }
0118 
0119     broadcastUdpIdentityPacket();
0120 
0121 #ifdef KDECONNECT_MDNS
0122     m_mdnsDiscovery.onStart();
0123 #endif
0124 
0125     qCDebug(KDECONNECT_CORE) << "LanLinkProvider started";
0126 }
0127 
0128 void LanLinkProvider::onStop()
0129 {
0130 #ifdef KDECONNECT_MDNS
0131     m_mdnsDiscovery.onStop();
0132 #endif
0133     m_udpSocket.close();
0134     m_server->close();
0135     qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped";
0136 }
0137 
0138 void LanLinkProvider::onNetworkChange()
0139 {
0140     if (m_combineNetworkChangeTimer.isActive()) {
0141         qCDebug(KDECONNECT_CORE) << "Device discovery triggered too fast, ignoring";
0142         return;
0143     }
0144     m_combineNetworkChangeTimer.start();
0145 }
0146 
0147 // I'm in a new network, let's be polite and introduce myself
0148 void LanLinkProvider::combinedOnNetworkChange()
0149 {
0150     if (!m_server->isListening()) {
0151         qWarning() << "TCP server not listening, not broadcasting";
0152         return;
0153     }
0154 
0155     Q_ASSERT(m_tcpPort != 0);
0156 
0157     broadcastUdpIdentityPacket();
0158 #ifdef KDECONNECT_MDNS
0159     m_mdnsDiscovery.onNetworkChange();
0160 #endif
0161 }
0162 
0163 void LanLinkProvider::broadcastUdpIdentityPacket()
0164 {
0165     if (qEnvironmentVariableIsSet("KDECONNECT_DISABLE_UDP_BROADCAST")) {
0166         qWarning() << "Not broadcasting UDP because KDECONNECT_DISABLE_UDP_BROADCAST is set";
0167         return;
0168     }
0169     qCDebug(KDECONNECT_CORE) << "Broadcasting identity packet";
0170 
0171     QList<QHostAddress> addresses = getBroadcastAddresses();
0172 
0173 #if defined(Q_OS_WIN) || defined(Q_OS_FREEBSD)
0174     // On Windows and FreeBSD we need to broadcast from every local IP address to reach all networks
0175     QUdpSocket sendSocket;
0176     sendSocket.setProxy(QNetworkProxy::NoProxy);
0177     for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
0178         if ((iface.flags() & QNetworkInterface::IsUp) && (iface.flags() & QNetworkInterface::IsRunning) && (iface.flags() & QNetworkInterface::CanBroadcast)) {
0179             for (const QNetworkAddressEntry &ifaceAddress : iface.addressEntries()) {
0180                 QHostAddress sourceAddress = ifaceAddress.ip();
0181                 if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
0182                     qCDebug(KDECONNECT_CORE) << "Broadcasting as" << sourceAddress;
0183                     sendSocket.bind(sourceAddress);
0184                     sendUdpIdentityPacket(sendSocket, addresses);
0185                     sendSocket.close();
0186                 }
0187             }
0188         }
0189     }
0190 #else
0191     sendUdpIdentityPacket(addresses);
0192 #endif
0193 }
0194 
0195 QList<QHostAddress> LanLinkProvider::getBroadcastAddresses()
0196 {
0197     const QStringList customDevices = KdeConnectConfig::instance().customDevices();
0198 
0199     QList<QHostAddress> destinations;
0200     destinations.reserve(customDevices.length() + 1);
0201 
0202     // Default broadcast address
0203     destinations.append(m_testMode ? QHostAddress::LocalHost : QHostAddress::Broadcast);
0204 
0205     // Custom device addresses
0206     for (auto &customDevice : customDevices) {
0207         QHostAddress address(customDevice);
0208         if (address.isNull()) {
0209             qCWarning(KDECONNECT_CORE) << "Invalid custom device address" << customDevice;
0210         } else {
0211             destinations.append(address);
0212         }
0213     }
0214 
0215     return destinations;
0216 }
0217 
0218 void LanLinkProvider::sendUdpIdentityPacket(const QList<QHostAddress> &addresses)
0219 {
0220     sendUdpIdentityPacket(m_udpSocket, addresses);
0221 }
0222 
0223 void LanLinkProvider::sendUdpIdentityPacket(QUdpSocket &socket, const QList<QHostAddress> &addresses)
0224 {
0225     DeviceInfo myDeviceInfo = KdeConnectConfig::instance().deviceInfo();
0226     NetworkPacket identityPacket = myDeviceInfo.toIdentityPacket();
0227     identityPacket.set(QStringLiteral("tcpPort"), m_tcpPort);
0228     const QByteArray payload = identityPacket.serialize();
0229 
0230     for (auto &address : addresses) {
0231         qint64 bytes = socket.writeDatagram(payload, address, UDP_PORT);
0232         if (bytes == -1 && socket.error() == QAbstractSocket::DatagramTooLargeError) {
0233             // On macOS and FreeBSD, UDP broadcasts larger than MTU get dropped. See:
0234             // https://opensource.apple.com/source/xnu/xnu-3789.1.32/bsd/netinet/ip_output.c.auto.html#:~:text=/*%20don%27t%20allow%20broadcast%20messages%20to%20be%20fragmented%20*/
0235             // We remove the capabilities to reduce the size of the packet.
0236             // This should only happen for broadcasts, so UDP packets sent from MDNS discoveries should still work.
0237             qWarning() << "Identity packet to" << address << "got rejected because it was too large. Retrying without including the capabilities";
0238             identityPacket.set(QStringLiteral("outgoingCapabilities"), QStringList());
0239             identityPacket.set(QStringLiteral("incomingCapabilities"), QStringList());
0240             const QByteArray smallPayload = identityPacket.serialize();
0241             socket.writeDatagram(smallPayload, address, UDP_PORT);
0242         }
0243     }
0244 }
0245 
0246 // I'm the existing device, a new device is kindly introducing itself.
0247 // I will create a TcpSocket and try to connect. This can result in either tcpSocketConnected() or connectError().
0248 void LanLinkProvider::udpBroadcastReceived()
0249 {
0250     while (m_udpSocket.hasPendingDatagrams()) {
0251         QByteArray datagram;
0252         datagram.resize(m_udpSocket.pendingDatagramSize());
0253         QHostAddress sender;
0254 
0255         m_udpSocket.readDatagram(datagram.data(), datagram.size(), &sender);
0256 
0257         if (sender.isLoopback() && !m_testMode)
0258             continue;
0259 
0260         NetworkPacket *receivedPacket = new NetworkPacket();
0261         bool success = NetworkPacket::unserialize(datagram, receivedPacket);
0262 
0263         // qCDebug(KDECONNECT_CORE) << "Datagram " << datagram.data() ;
0264 
0265         if (!success) {
0266             qCDebug(KDECONNECT_CORE) << "Could not unserialize UDP packet";
0267             delete receivedPacket;
0268             continue;
0269         }
0270 
0271         if (receivedPacket->type() != PACKET_TYPE_IDENTITY) {
0272             qCDebug(KDECONNECT_CORE) << "Received a UDP packet of wrong type" << receivedPacket->type();
0273             delete receivedPacket;
0274             continue;
0275         }
0276 
0277         QString deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
0278 
0279         if (deviceId == KdeConnectConfig::instance().deviceId()) {
0280             // qCDebug(KDECONNECT_CORE) << "Ignoring my own broadcast";
0281             delete receivedPacket;
0282             continue;
0283         }
0284 
0285         qint64 now = QDateTime::currentMSecsSinceEpoch();
0286         if (m_lastConnectionTime[deviceId] + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE > now) {
0287             qCDebug(KDECONNECT_CORE) << "Discarding second UPD packet from the same device" << deviceId << "received too quickly";
0288             delete receivedPacket;
0289             return;
0290         }
0291         m_lastConnectionTime[deviceId] = now;
0292 
0293         int tcpPort = receivedPacket->get<int>(QStringLiteral("tcpPort"));
0294         if (tcpPort < MIN_TCP_PORT || tcpPort > MAX_TCP_PORT) {
0295             qCDebug(KDECONNECT_CORE) << "TCP port outside of kdeconnect's range";
0296             delete receivedPacket;
0297             continue;
0298         }
0299 
0300         // qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort;
0301 
0302         if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
0303             qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << receivedPacket->get<QString>(QStringLiteral("deviceId"))
0304                                        << "received via UDP";
0305             delete receivedPacket;
0306             continue;
0307         }
0308 
0309         QSslSocket *socket = new QSslSocket(this);
0310         socket->setProxy(QNetworkProxy::NoProxy);
0311         m_receivedIdentityPackets[socket].np = receivedPacket;
0312         m_receivedIdentityPackets[socket].sender = sender;
0313         connect(socket, &QAbstractSocket::connected, this, &LanLinkProvider::tcpSocketConnected);
0314         connect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
0315         connect(socket, &QObject::destroyed, this, [this, socket]() {
0316             delete m_receivedIdentityPackets.take(socket).np;
0317         });
0318         socket->connectToHost(sender, tcpPort);
0319     }
0320 }
0321 
0322 void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError)
0323 {
0324     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
0325     if (!socket)
0326         return;
0327 
0328     qCDebug(KDECONNECT_CORE) << "Socket error" << socketError;
0329     qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString();
0330     NetworkPacket np = KdeConnectConfig::instance().deviceInfo().toIdentityPacket();
0331     np.set(QStringLiteral("tcpPort"), m_tcpPort);
0332     m_udpSocket.writeDatagram(np.serialize(), m_receivedIdentityPackets[socket].sender, UDP_PORT);
0333 
0334     // The socket we created didn't work, and we didn't manage
0335     // to create a LanDeviceLink from it, deleting everything.
0336     socket->deleteLater();
0337 }
0338 
0339 // We received a UDP packet and answered by connecting to them by TCP. This gets called on a successful connection.
0340 void LanLinkProvider::tcpSocketConnected()
0341 {
0342     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
0343 
0344     if (!socket) {
0345         return;
0346     }
0347 
0348     disconnect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
0349 
0350     configureSocket(socket);
0351 
0352     // If socket disconnects due to any reason after connection, link on ssl failure
0353     connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
0354 
0355     NetworkPacket *receivedPacket = m_receivedIdentityPackets[socket].np;
0356     const QString &deviceId = receivedPacket->get<QString>(QStringLiteral("deviceId"));
0357     // qCDebug(KDECONNECT_CORE) << "tcpSocketConnected" << socket->isWritable();
0358 
0359     // If network is on ssl, do not believe when they are connected, believe when handshake is completed
0360     NetworkPacket np2 = KdeConnectConfig::instance().deviceInfo().toIdentityPacket();
0361     socket->write(np2.serialize());
0362     bool success = socket->waitForBytesWritten();
0363 
0364     if (success) {
0365         qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)";
0366 
0367         // if ssl supported
0368         bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
0369         configureSslSocket(socket, deviceId, isDeviceTrusted);
0370 
0371         qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)";
0372 
0373         connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted);
0374 
0375         connect(socket, &QSslSocket::sslErrors, this, &LanLinkProvider::sslErrors);
0376 
0377         socket->startServerEncryption();
0378     } else {
0379         // The socket doesn't seem to work, so we can't create the connection.
0380 
0381         qCDebug(KDECONNECT_CORE) << "Fallback (2), try reverse connection (send udp packet)";
0382         m_udpSocket.writeDatagram(np2.serialize(), m_receivedIdentityPackets[socket].sender, UDP_PORT);
0383 
0384         // Disconnect should trigger deleteLater, which should remove the socket from m_receivedIdentityPackets
0385         socket->disconnectFromHost();
0386     }
0387 }
0388 
0389 void LanLinkProvider::encrypted()
0390 {
0391     qCDebug(KDECONNECT_CORE) << "Socket successfully established an SSL connection";
0392 
0393     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
0394     if (!socket)
0395         return;
0396 
0397     Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode);
0398 
0399     NetworkPacket *identityPacket = m_receivedIdentityPackets[socket].np;
0400 
0401     DeviceInfo deviceInfo = DeviceInfo::FromIdentityPacketAndCert(*identityPacket, socket->peerCertificate());
0402 
0403     // We don't delete the socket because now it's owned by the LanDeviceLink
0404     disconnect(socket, &QObject::destroyed, nullptr, nullptr);
0405     delete m_receivedIdentityPackets.take(socket).np;
0406 
0407     addLink(socket, deviceInfo);
0408 }
0409 
0410 void LanLinkProvider::sslErrors(const QList<QSslError> &errors)
0411 {
0412     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
0413     if (!socket)
0414         return;
0415 
0416     bool fatal = false;
0417     for (const QSslError &error : errors) {
0418         if (error.error() != QSslError::SelfSignedCertificate) {
0419             qCCritical(KDECONNECT_CORE) << "Disconnecting due to fatal SSL Error: " << error;
0420             fatal = true;
0421         } else {
0422             qCDebug(KDECONNECT_CORE) << "Ignoring self-signed cert error";
0423         }
0424     }
0425 
0426     if (fatal) {
0427         // Disconnect should trigger deleteLater, which should remove the socket from m_receivedIdentityPackets
0428         socket->disconnectFromHost();
0429     }
0430 }
0431 
0432 // 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
0433 // identity.
0434 void LanLinkProvider::newConnection()
0435 {
0436     qCDebug(KDECONNECT_CORE) << "LanLinkProvider newConnection";
0437 
0438     while (m_server->hasPendingConnections()) {
0439         QSslSocket *socket = m_server->nextPendingConnection();
0440         configureSocket(socket);
0441         // This socket is still managed by us (and child of the QTcpServer), if
0442         // it disconnects before we manage to pass it to a LanDeviceLink, it's
0443         // our responsibility to delete it. We do so with this connection.
0444         connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
0445         connect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived);
0446 
0447         QTimer *timer = new QTimer(socket);
0448         timer->setSingleShot(true);
0449         timer->setInterval(1000);
0450         connect(socket, &QSslSocket::encrypted, timer, &QObject::deleteLater);
0451         connect(timer, &QTimer::timeout, socket, [socket] {
0452             qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Host timed out without sending any identity." << socket->peerAddress();
0453             socket->disconnectFromHost();
0454         });
0455         timer->start();
0456     }
0457 }
0458 
0459 // I'm the new device and this is the TCP response to my UDP identity packet
0460 void LanLinkProvider::dataReceived()
0461 {
0462     QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
0463     // the size here is arbitrary and is now at 8192 bytes. It needs to be considerably long as it includes the capabilities but there needs to be a limit
0464     // Tested between my systems and I get around 2000 per identity package.
0465     if (socket->bytesAvailable() > 8192) {
0466         qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Suspiciously long identity package received. Closing connection." << socket->peerAddress()
0467                                    << socket->bytesAvailable();
0468         socket->disconnectFromHost();
0469         return;
0470     }
0471 
0472     if (!socket->canReadLine()) {
0473         // This can happen if the packet is large enough to be split in two chunks
0474         return;
0475     }
0476 
0477     const QByteArray data = socket->readLine();
0478 
0479     qCDebug(KDECONNECT_CORE) << "LanLinkProvider received reply:" << data;
0480 
0481     NetworkPacket *np = new NetworkPacket();
0482     bool success = NetworkPacket::unserialize(data, np);
0483 
0484     if (!success) {
0485         delete np;
0486         return;
0487     }
0488 
0489     if (np->type() != PACKET_TYPE_IDENTITY) {
0490         qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type();
0491         delete np;
0492         return;
0493     }
0494 
0495     if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
0496         qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << np->get<QString>(QStringLiteral("deviceId")) << "received via TCP";
0497         delete np;
0498         return;
0499     }
0500 
0501     // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
0502     m_receivedIdentityPackets[socket].np = np;
0503     connect(socket, &QObject::destroyed, this, [this, socket]() {
0504         delete m_receivedIdentityPackets.take(socket).np;
0505     });
0506 
0507     const QString &deviceId = np->get<QString>(QStringLiteral("deviceId"));
0508     // qCDebug(KDECONNECT_CORE) << "Handshaking done (i'm the new device)";
0509 
0510     // This socket will now be owned by the LanDeviceLink or we don't want more data to be received, forget about it
0511     disconnect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived);
0512 
0513     bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
0514     configureSslSocket(socket, deviceId, isDeviceTrusted);
0515 
0516     qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)";
0517 
0518     connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted);
0519 
0520     if (isDeviceTrusted) {
0521         connect(socket, &QSslSocket::sslErrors, this, &LanLinkProvider::sslErrors);
0522     }
0523 
0524     socket->startClientEncryption();
0525 }
0526 
0527 void LanLinkProvider::onLinkDestroyed(const QString &deviceId, DeviceLink *oldPtr)
0528 {
0529     qCDebug(KDECONNECT_CORE) << "LanLinkProvider deviceLinkDestroyed" << deviceId;
0530     DeviceLink *link = m_links.take(deviceId);
0531     Q_ASSERT(link == oldPtr);
0532 }
0533 
0534 void LanLinkProvider::configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted)
0535 {
0536     // Configure for ssl
0537     QSslConfiguration sslConfig;
0538     sslConfig.setLocalCertificate(KdeConnectConfig::instance().certificate());
0539 
0540     QFile privateKeyFile(KdeConnectConfig::instance().privateKeyPath());
0541     QSslKey privateKey;
0542     if (privateKeyFile.open(QIODevice::ReadOnly)) {
0543         privateKey = QSslKey(privateKeyFile.readAll(), QSsl::Rsa);
0544     }
0545     privateKeyFile.close();
0546     sslConfig.setPrivateKey(privateKey);
0547 
0548     if (isDeviceTrusted) {
0549         QSslCertificate certificate = KdeConnectConfig::instance().getTrustedDeviceCertificate(deviceId);
0550         sslConfig.setCaCertificates({certificate});
0551         sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
0552     } else {
0553         sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer);
0554     }
0555     socket->setSslConfiguration(sslConfig);
0556     socket->setPeerVerifyName(deviceId);
0557 
0558     // Usually SSL errors are only bad for trusted devices. Uncomment this section to log errors in any case, for debugging.
0559     // QObject::connect(socket, static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(&QSslSocket::sslErrors), [](const QList<QSslError>& errors)
0560     //{
0561     //     Q_FOREACH (const QSslError& error, errors) {
0562     //         qCDebug(KDECONNECT_CORE) << "SSL Error:" << error.errorString();
0563     //     }
0564     // });
0565 }
0566 
0567 void LanLinkProvider::configureSocket(QSslSocket *socket)
0568 {
0569     socket->setProxy(QNetworkProxy::NoProxy);
0570 
0571     socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
0572 
0573 #ifdef TCP_KEEPIDLE
0574     // time to start sending keepalive packets (seconds)
0575     int maxIdle = 10;
0576     setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle));
0577 #endif
0578 
0579 #ifdef TCP_KEEPINTVL
0580     // interval between keepalive packets after the initial period (seconds)
0581     int interval = 5;
0582     setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
0583 #endif
0584 
0585 #ifdef TCP_KEEPCNT
0586     // number of missed keepalive packets before disconnecting
0587     int count = 3;
0588     setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
0589 #endif
0590 
0591 #if defined(Q_OS_WIN)
0592     int maxIdle = 5 * 60 * 1000; // 5 minutes of idle before sending keep-alive
0593     int interval = 5 * 1000; // 5 seconds interval between probes after 5 minute delay
0594     DWORD nop;
0595 
0596     // see https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals
0597     struct tcp_keepalive keepalive = {1 /* true */, maxIdle, interval};
0598 
0599     int rv = WSAIoctl(socket->socketDescriptor(), SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0, &nop, nullptr, nullptr);
0600     if (!rv) {
0601         int error = WSAGetLastError();
0602         qCDebug(KDECONNECT_CORE) << "Could not enable TCP Keep-Alive: " << error;
0603     }
0604 #endif
0605 }
0606 
0607 void LanLinkProvider::addLink(QSslSocket *socket, const DeviceInfo &deviceInfo)
0608 {
0609     QString certDeviceId = socket->peerCertificate().subjectDisplayName();
0610     DBusHelper::filterNonExportableCharacters(certDeviceId);
0611     if (deviceInfo.id != certDeviceId) {
0612         socket->disconnectFromHost();
0613         qCWarning(KDECONNECT_CORE) << "DeviceID in cert doesn't match deviceID in identity packet. " << deviceInfo.id << " vs " << certDeviceId;
0614         return;
0615     }
0616 
0617     // Socket disconnection will now be handled by LanDeviceLink
0618     disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
0619 
0620     LanDeviceLink *deviceLink;
0621     // Do we have a link for this device already?
0622     QMap<QString, LanDeviceLink *>::iterator linkIterator = m_links.find(deviceInfo.id);
0623     if (linkIterator != m_links.end()) {
0624         deviceLink = linkIterator.value();
0625         if (deviceLink->deviceInfo().certificate != deviceInfo.certificate) {
0626             qWarning() << "LanLink was asked to replace a socket but the certificate doesn't match, aborting";
0627             return;
0628         }
0629         // qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId;
0630         deviceLink->reset(socket);
0631     } else {
0632         deviceLink = new LanDeviceLink(deviceInfo, this, socket);
0633         // Socket disconnection will now be handled by LanDeviceLink
0634         disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
0635         bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceInfo.id);
0636         if (!isDeviceTrusted && m_links.size() > MAX_UNPAIRED_CONNECTIONS) {
0637             qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceInfo.id;
0638             socket->disconnectFromHost();
0639             socket->deleteLater();
0640             return;
0641         }
0642         m_links[deviceInfo.id] = deviceLink;
0643     }
0644     Q_EMIT onConnectionReceived(deviceLink);
0645 }
0646 
0647 #include "moc_lanlinkprovider.cpp"