[OpenDocString] kdeconnect-kde (cpp)
lanlinkprovider.cpp
LanLinkProvider::LanLinkProvider(bool testMode, quint16 udpBroadcastPort, quint16 udpListenPort)
    : m_server(new Server(this))
    , m_udpSocket(this)
    , m_tcpPort(0)
    , m_udpBroadcastPort(udpBroadcastPort)
    , m_udpListenPort(udpListenPort)
    , m_testMode(testMode)
    , m_combineBroadcastsTimer(this)
{
    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);

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

    m_server->setProxy(QNetworkProxy::NoProxy);
    connect(m_server, &QTcpServer::newConnection, this, &LanLinkProvider::newConnection);

    m_udpSocket.setProxy(QNetworkProxy::NoProxy);

    // 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, &LanLinkProvider::onNetworkConfigurationChanged);
}
This constructor builds a LAN link provider object and its internal state. It takes the given ports for udp broadcast and udp listen port, and connects the server and network connections to the network layer. It also connects the UDP socket to the UDP port, sets a single shot interval for the first iteration. It also connects the UDP broadcast to the TCP port, sets a proxy on the QNetworkConfigurationManager object to track the changes in the status.
void LanLinkProvider::onNetworkConfigurationChanged(const QNetworkConfiguration &config)
{
    if (m_lastConfig != config && config.state() == QNetworkConfiguration::Active) {
        m_lastConfig = config;
        onNetworkChange();
    }
}
This method is called when the network configuration has changed. It saves the last config object in the m_lastConfig variable and calls the onNetworkChange method if the config is active.
LanLinkProvider::~LanLinkProvider()
{
}
This implements the no - op implementation of the LAN link provider.
void LanLinkProvider::onStart()
{
    const QHostAddress bindAddress = m_testMode ? QHostAddress::LocalHost : QHostAddress::Any;

    bool success = m_udpSocket.bind(bindAddress, m_udpListenPort, QUdpSocket::ShareAddress);
    if (!success) {
        QAbstractSocket::SocketError sockErr = m_udpSocket.error();
        // Refer to https://doc.qt.io/qt-5/qabstractsocket.html#SocketError-enum to decode socket error number
        QString errorMessage = QString::fromLatin1(QMetaEnum::fromType().valueToKey(sockErr));
        qCritical(KDECONNECT_CORE) << QLatin1String("Failed to bind UDP socket on port") << m_udpListenPort << QLatin1String("with error") << errorMessage;
    }
    Q_ASSERT(success);

    m_tcpPort = MIN_TCP_PORT;
    while (!m_server->listen(bindAddress, 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) << "LanLinkProvider started";
}
This binds the UDP socket and listens for connections. It checks that the socket is still open. If it is not, it creates a TCP port number and listens for connections. It also starts listening for changes in the network.
void LanLinkProvider::onStop()
{
    m_udpSocket.close();
    m_server->close();
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped";
}
This closes the UDP socket and the server.
void LanLinkProvider::onNetworkChange()
{
    if (m_combineBroadcastsTimer.isActive()) {
        qCDebug(KDECONNECT_CORE) << "Preventing duplicate broadcasts";
        return;
    }
    m_combineBroadcastsTimer.start();
}
This starts the timer that allows to prevent multiple broadcasts when the network is changed. It makes sure the timer is not already active.
void LanLinkProvider::broadcastToNetwork()
{
    if (!m_server->isListening()) {
        // Not started
        return;
    }

    Q_ASSERT(m_tcpPort != 0);

    qCDebug(KDECONNECT_CORE()) << "Broadcasting identity packet";

    QList destinations = getBroadcastAddresses();

    NetworkPacket np;
    NetworkPacket::createIdentityPacket(&np);
    np.set(QStringLiteral("tcpPort"), m_tcpPort);
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
    // On macOS and FreeBSD, the too large UDP packet (larger than MTU) causes
    // incomplete transmission.
    // We remove the capacitilities to reduce the discovery packet to the min
    // MTU of the interfaces with broadcast feature.
    int mtu = 1500;
    for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
        if ((iface.flags() & QNetworkInterface::IsUp) && (iface.flags() & QNetworkInterface::IsRunning) && (iface.flags() & QNetworkInterface::CanBroadcast)) {
            int ifaceMtu = iface.maximumTransmissionUnit();
            if (ifaceMtu < mtu && ifaceMtu > 0) {
                mtu = ifaceMtu;
            }
        }
    }
    QByteArray payload = np.serialize();
    if (payload.length() > mtu) {
        // First try to drop the less important outgoing capabilities
        np.set(QStringLiteral("outgoingCapabilities"), QStringList());
        payload = np.serialize();
    }
    if (payload.length() > mtu) {
        // If still too large, drop the incoming capabilities
        np.set(QStringLiteral("incomingCapabilities"), QStringList());
        payload = np.serialize();
    }
#endif

#if defined(Q_OS_WIN) || defined(Q_OS_FREEBSD)
    // On Windows and FreeBSD we need to broadcast from every local IP address to reach all networks
    QUdpSocket sendSocket;
    sendSocket.setProxy(QNetworkProxy::NoProxy);
    for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
        if ((iface.flags() & QNetworkInterface::IsUp) && (iface.flags() & QNetworkInterface::IsRunning) && (iface.flags() & QNetworkInterface::CanBroadcast)) {
            for (const QNetworkAddressEntry &ifaceAddress : iface.addressEntries()) {
                QHostAddress sourceAddress = ifaceAddress.ip();
                if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
                    qCDebug(KDECONNECT_CORE()) << "Broadcasting as" << sourceAddress;
                    sendSocket.bind(sourceAddress);
                    sendBroadcasts(sendSocket, np, destinations);
                    sendSocket.close();
                }
            }
        }
    }
#else
    sendBroadcasts(m_udpSocket, np, destinations);
#endif
}
This sends an identity packet to all interfaces and broadcasts to all destinations.
QList LanLinkProvider::getBroadcastAddresses()
{
    const QStringList customDevices = KdeConnectConfig::instance().customDevices();

    QList 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;
}
This method returns a list of broadcast addresses. It first reserves the number of addresses, and adds the local host address to the list, and custom devices if any.
void LanLinkProvider::sendBroadcasts(QUdpSocket &socket, const NetworkPacket &np, const QList &addresses)
{
    const QByteArray payload = np.serialize();

    for (auto &address : addresses) {
        socket.writeDatagram(payload, address, m_udpBroadcastPort);
    }
}
This sends a network packet to all addresses in the list.
void LanLinkProvider::udpBroadcastReceived()
{
    while (m_udpSocket.hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(m_udpSocket.pendingDatagramSize());
        QHostAddress sender;

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

        if (sender.isLoopback() && !m_testMode)
            continue;

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

        // qCDebug(KDECONNECT_CORE) << "udp connection from " << receivedPacket->;

        // qCDebug(KDECONNECT_CORE) << "Datagram " << datagram.data() ;

        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();
            delete receivedPacket;
            continue;
        }

        if (receivedPacket->get(QStringLiteral("deviceId")) == KdeConnectConfig::instance().deviceId()) {
            // qCDebug(KDECONNECT_CORE) << "Ignoring my own broadcast";
            delete receivedPacket;
            continue;
        }

        int tcpPort = receivedPacket->get(QStringLiteral("tcpPort"));
        if (tcpPort < MIN_TCP_PORT || tcpPort > MAX_TCP_PORT) {
            qCDebug(KDECONNECT_CORE) << "TCP port outside of kdeconnect's range";
            delete receivedPacket;
            continue;
        }

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

        if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
            qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << receivedPacket->get(QStringLiteral("deviceId"))
                                       << "received via UDP";
            delete receivedPacket;
            continue;
        }

        QSslSocket *socket = new QSslSocket(this);
        socket->setProxy(QNetworkProxy::NoProxy);
        m_receivedIdentityPackets[socket].np = receivedPacket;
        m_receivedIdentityPackets[socket].sender = sender;
        connect(socket, &QAbstractSocket::connected, this, &LanLinkProvider::tcpSocketConnected);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
        connect(socket, QOverload::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError);
#else
        connect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
#endif
        socket->connectToHost(sender, tcpPort);
    }
}
This code receives a UDP packet from the network and processes it. It receives the packet from the udp socket, which is a QHostAddress object, and connects it to the tcp port using the UDP protocol. If the sender is loopback, it checks that the packet is of the correct type, since it will only receive packets of the correct type, since it will only receive packets of the correct type, since it will only receive packets of the correct type, since it will only ever ever ever connects to the tcp port if it is not in the range of the kdeconnect's range. If the device id is not in kdeconnect's range, a warning message is printed to the console.
void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError)
{
    QSslSocket *socket = qobject_cast(sender());
    if (!socket)
        return;

    qCDebug(KDECONNECT_CORE) << "Socket error" << socketError;
    qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString();
    NetworkPacket np(QLatin1String(""));
    NetworkPacket::createIdentityPacket(&np);
    np.set(QStringLiteral("tcpPort"), m_tcpPort);
    m_udpSocket.writeDatagram(np.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort);

    // 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;
    socket->deleteLater();
}
This sends an identity packet to the tcp port and writes it to the udp socket with the broadcast port number set to tcpPort, and deletes the received identity packets.
void LanLinkProvider::tcpSocketConnected()
{
    QSslSocket *socket = qobject_cast(sender());

    if (!socket) {
        return;
    }

    // TODO Delete me?
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
    disconnect(socket, QOverload::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError);
#else
    disconnect(socket, &QAbstractSocket::errorOccurred, this, &LanLinkProvider::connectError);
#endif

    configureSocket(socket);

    // If socket disconnects due to any reason after connection, link on ssl failure
    connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);

    NetworkPacket *receivedPacket = m_receivedIdentityPackets[socket].np;
    const QString &deviceId = receivedPacket->get(QStringLiteral("deviceId"));
    // qCDebug(KDECONNECT_CORE) << "tcpSocketConnected" << socket->isWritable();

    // If network is on ssl, do not believe when they are connected, believe when handshake is completed
    NetworkPacket np2(QLatin1String(""));
    NetworkPacket::createIdentityPacket(&np2);
    socket->write(np2.serialize());
    bool success = socket->waitForBytesWritten();

    if (success) {
        qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)";

        // if ssl supported
        if (receivedPacket->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) {
            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, &LanLinkProvider::encrypted);

            connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);

            socket->startServerEncryption();

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

    } else {
        // 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.
        qCDebug(KDECONNECT_CORE) << "Fallback (2), try reverse connection (send udp packet)";
        m_udpSocket.writeDatagram(np2.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort);
    }

    delete m_receivedIdentityPackets.take(socket).np;
    // We don't delete the socket because now it's owned by the LanDeviceLink
}
This creates a socket object and writes the received identity packet to it. It first checks that the socket is still open, if it is not connected, it connects to the socket and starts the server encryption procedure. Then it checks that the device is trusted. Then it sends the packet to the udp socket and removes the socket from the list.
void LanLinkProvider::encrypted()
{
    qCDebug(KDECONNECT_CORE) << "Socket successfully established an SSL connection";

    QSslSocket *socket = qobject_cast(sender());
    if (!socket)
        return;

    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 = socket->peerCertificate().subjectDisplayName();

    if (m_links.contains(deviceId) && m_links[deviceId]->certificate() != socket->peerCertificate()) {
        socket->disconnectFromHost();
        qCWarning(KDECONNECT_CORE) << "Got connection for the same deviceId but certificates don't match. Ignoring " << deviceId;
        return;
    }

    addLink(deviceId, socket, receivedPacket, connectionOrigin);

    // Copied from tcpSocketConnected slot, now delete received packet
    delete m_receivedIdentityPackets.take(socket).np;
}
This creates a socket object from the sender of the current connection, and adds it to the list of links. It checks that the socket is not in unencrypted mode, as those are handled separately. If the socket is in client mode, the connection origin is locally remote, and remotely is remotely remote. Then it removes the packet from the list, and marks the packet as encrypted. If the socket is in client mode, the packet is sent to the host. If the device is not connected, the packet is sent to the peer of the current device, the socket is disconnected from the host. If the socket is in client mode, the packet is sent to the peer's device, the packet is sent to the peer's peer certificate list. If the socket is in client mode, the packet is sent to the peer's list of links, and a signal is emitted.
void LanLinkProvider::sslErrors(const QList &errors)
{
    QSslSocket *socket = qobject_cast(sender());
    if (!socket)
        return;

    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";
        }
    }

    if (fatal) {
        socket->disconnectFromHost();
        delete m_receivedIdentityPackets.take(socket).np;
    }
}
This removes the received identity packets from the list of ssl errors, if any are fatal.
void LanLinkProvider::newConnection()
{
    qCDebug(KDECONNECT_CORE) << "LanLinkProvider 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, &LanLinkProvider::dataReceived);

        QTimer *timer = new QTimer(socket);
        timer->setSingleShot(true);
        timer->setInterval(1000);
        connect(socket, &QSslSocket::encrypted, timer, &QObject::deleteLater);
        connect(timer, &QTimer::timeout, socket, [socket] {
            qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Host timed out without sending any identity." << socket->peerAddress();
            socket->disconnectFromHost();
        });
        timer->start();
    }
}
This creates a new connection from the internal list of sockets. It connects each socket in the list, it sets up a QAbstractSocket object, the socket is connected to, the QIODevice object is ready read, and a QTimer object is started to send identity messages to the host. If the host times out, it logs a warning.
void LanLinkProvider::dataReceived()
{
    QSslSocket *socket = qobject_cast(sender());
    // 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
    // Tested between my systems and I get around 2000 per identity package.
    if (socket->bytesAvailable() > 8192) {
        qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Suspiciously long identity package received. Closing connection." << socket->peerAddress()
                                   << socket->bytesAvailable();
        socket->disconnectFromHost();
        return;
    }

#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
    if (!socket->canReadLine())
        return;
#else
    socket->startTransaction();
#endif

    const QByteArray data = socket->readLine();

    qCDebug(KDECONNECT_CORE) << "LanLinkProvider received reply:" << data;

    NetworkPacket *np = new NetworkPacket(QLatin1String(""));
    bool success = NetworkPacket::unserialize(data, np);

#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
    if (!success) {
        delete np;
        return;
    }
#else
    if (!success) {
        delete np;
        socket->rollbackTransaction();
        return;
    }
    socket->commitTransaction();
#endif

    if (np->type() != PACKET_TYPE_IDENTITY) {
        qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type();
        delete np;
        return;
    }

    if (m_receivedIdentityPackets.size() > MAX_REMEMBERED_IDENTITY_PACKETS) {
        qCWarning(KDECONNECT_CORE) << "Too many remembered identities, ignoring" << np->get(QStringLiteral("deviceId")) << "received via TCP";
        delete np;
        return;
    }

    // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected"
    m_receivedIdentityPackets[socket].np = np;

    const QString &deviceId = np->get(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, &LanLinkProvider::dataReceived);

    if (np->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) {
        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, &LanLinkProvider::encrypted);

        if (isDeviceTrusted) {
            connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors);
        }

        socket->startClientEncryption();

    } else {
        qWarning() << np->get(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work";
        // addLink(deviceId, socket, np, LanDeviceLink::Locally);
        delete m_receivedIdentityPackets.take(socket).np;
    }
}
This reads a single network packet from the socket. It first checks that the socket is ready to receive a packet, if it is able. Then it creates a NetworkPacket object, loads it from the buffer, creates it from the buffer, and connects it to the device using the ssl interface. It also checks that the packet is valid, if it is a valid identity packet, and commits the transaction. If the packet is not valid, it logs a warning.
void LanLinkProvider::deviceLinkDestroyed(QObject *destroyedDeviceLink)
{
    const QString id = destroyedDeviceLink->property("deviceId").toString();
    // qCDebug(KDECONNECT_CORE) << "deviceLinkDestroyed" << id;
    QMap::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();
        }
    }
}
This removes the device link object from the internal list of links. The function first gets the device id of the link object, and if it exists, it removes the link object from the list of links, and marks the device link as destroyed. If the pairing handler is present, the device link is deleted immediately.
void LanLinkProvider::configureSslSocket(QSslSocket *socket, const QString &deviceId, bool isDeviceTrusted)
{
    // Configure for ssl
    QSslConfiguration sslConfig;
    sslConfig.setLocalCertificate(KdeConnectConfig::instance().certificate());

    QFile privateKeyFile(KdeConnectConfig::instance().privateKeyPath());
    QSslKey privateKey;
    if (privateKeyFile.open(QIODevice::ReadOnly)) {
        privateKey = QSslKey(privateKeyFile.readAll(), QSsl::Rsa);
    }
    privateKeyFile.close();
    sslConfig.setPrivateKey(privateKey);

    if (isDeviceTrusted) {
        QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId, QStringLiteral("certificate"), QString());
        sslConfig.setCaCertificates({QSslCertificate(certString.toLatin1())});
        sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
    } else {
        sslConfig.setPeerVerifyMode(QSslSocket::QueryPeer);
    }
    socket->setSslConfiguration(sslConfig);
    socket->setPeerVerifyName(deviceId);

    // 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&)>(&QSslSocket::sslErrors), [](const QList& errors)
    //{
    //     Q_FOREACH (const QSslError& error, errors) {
    //         qCDebug(KDECONNECT_CORE) << "SSL Error:" << error.errorString();
    //     }
    // });
}
This configures an ssl socket based on its current configuration. It takes the certificate from the KdeConnectConfig singleton instance and sets the private key for the ssl config. It also sets the certificate verify mode for the trusted devices, if it isDeviceTrusted is true.
void LanLinkProvider::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

#if defined(Q_OS_WIN)
    int maxIdle = 5 * 60 * 1000; // 5 minutes of idle before sending keep-alives
    int interval = 5 * 1000; // 5 seconds interval between probes after 5 minute delay
    DWORD nop;

    // see https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals
    struct tcp_keepalive keepalive = {
        1 /* true */,
        maxIdle,
        interval
    };

    int rv = WSAIoctl(socket->socketDescriptor(), SIO_KEEPALIVE_VALS, &keepalive,
                        sizeof(keepalive), nullptr, 0, &nop,
                        nullptr, nullptr);
    if (!rv) {
        int error = WSAGetLastError();
        qCDebug(KDECONNECT_CORE) << "Could not enable TCP Keep-Alive: " << error;
    }
#endif
}
This configures a socket object for sending keepalive packets. It allows the socket to be used for proxying, as well as for checking for a connection error.
void LanLinkProvider::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::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);
        // Socket disconnection will now be handled by LanDeviceLink
        disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);
        bool isDeviceTrusted = KdeConnectConfig::instance().trustedDevices().contains(deviceId);
        if (!isDeviceTrusted && m_links.size() > MAX_UNPAIRED_CONNECTIONS) {
            qCWarning(KDECONNECT_CORE) << "Too many unpaired devices to remember them all. Ignoring " << deviceId;
            socket->disconnectFromHost();
            socket->deleteLater();
            return;
        }
        connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::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);
}
This adds a device link to the list of links. It first checks if the device link for the device exists, if it doesn't, it creates one, sets the link on the socket, and sets the connection origin to the given connectionOrigin. If the device link is already in the list, the function returns early. Otherwise, the link is added to the list, the socket is connected to the host, and a number of connections are made between the link and the connection origin. If there are unpaired devices, the link is added to the list, the list is reset, the socket is deleted, and the connection is emitted. If the device is trusted and there is a number of unpaired devices, the socket is connected to the host, the connectionOrigin is ignored. If this device is paired with the list, the connection is lost, and a warning message is printed to the console.
LanPairingHandler *LanLinkProvider::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;
}
This creates a pairing handler for the given device link. It creates it if it doesn't exist. It connects the link to the pairing error signal and returns it.
void LanLinkProvider::userRequestsPair(const QString &deviceId)
{
    LanPairingHandler *ph = createPairingHandler(m_links.value(deviceId));
    ph->requestPairing();
}
This requests that the device be paired.
void LanLinkProvider::userRequestsUnpair(const QString &deviceId)
{
    LanPairingHandler *ph = createPairingHandler(m_links.value(deviceId));
    ph->unpair();
}
This removes the pairing process for the given device id.
void LanLinkProvider::incomingPairPacket(DeviceLink *deviceLink, const NetworkPacket &np)
{
    LanPairingHandler *ph = createPairingHandler(deviceLink);
    ph->packetReceived(np);
}
This sends a pairing packet to the device link.