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 
7 #include "landevicelink.h"
8 
9 #include <KLocalizedString>
10 
11 #include "core_debug.h"
12 #include "kdeconnectconfig.h"
13 #include "backends/linkprovider.h"
14 #include "socketlinereader.h"
15 #include "lanlinkprovider.h"
16 #include "plugins/share/shareplugin.h"
17 
LanDeviceLink(const QString & deviceId,LinkProvider * parent,QSslSocket * socket,ConnectionStarted connectionSource)18 LanDeviceLink::LanDeviceLink(const QString& deviceId, LinkProvider* parent, QSslSocket* socket, ConnectionStarted connectionSource)
19     : DeviceLink(deviceId, parent)
20     , m_socketLineReader(nullptr)
21 {
22     reset(socket, connectionSource);
23 }
24 
reset(QSslSocket * socket,ConnectionStarted connectionSource)25 void LanDeviceLink::reset(QSslSocket* socket, ConnectionStarted connectionSource)
26 {
27     if (m_socketLineReader) {
28         disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
29         delete m_socketLineReader;
30     }
31 
32     m_socketLineReader = new SocketLineReader(socket, this);
33 
34     connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
35     connect(m_socketLineReader, &SocketLineReader::readyRead, this, &LanDeviceLink::dataReceived);
36 
37     //We take ownership of the socket.
38     //When the link provider destroys us,
39     //the socket (and the reader) will be
40     //destroyed as well
41     socket->setParent(m_socketLineReader);
42 
43     m_connectionSource = connectionSource;
44 
45     QString certString = KdeConnectConfig::instance().getDeviceProperty(deviceId(), QStringLiteral("certificate"));
46     DeviceLink::setPairStatus(certString.isEmpty()? PairStatus::NotPaired : PairStatus::Paired);
47 }
48 
hostAddress() const49 QHostAddress LanDeviceLink::hostAddress() const
50 {
51     if (!m_socketLineReader) {
52         return QHostAddress::Null;
53     }
54     QHostAddress addr = m_socketLineReader->m_socket->peerAddress();
55     if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
56         bool success;
57         QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success));
58         if (success) {
59             qCDebug(KDECONNECT_CORE) << "Converting IPv6" << addr << "to IPv4" << convertedAddr;
60             addr = convertedAddr;
61         }
62     }
63     return addr;
64 }
65 
name()66 QString LanDeviceLink::name()
67 {
68     return QStringLiteral("LanLink"); // Should be same in both android and kde version
69 }
70 
sendPacket(NetworkPacket & np)71 bool LanDeviceLink::sendPacket(NetworkPacket& np)
72 {
73     if (np.payload()) {
74         if (np.type() == PACKET_TYPE_SHARE_REQUEST && np.payloadSize() >= 0) {
75             if (!m_compositeUploadJob || !m_compositeUploadJob->isRunning()) {
76                 m_compositeUploadJob = new CompositeUploadJob(deviceId(), true);
77             }
78 
79             m_compositeUploadJob->addSubjob(new UploadJob(np));
80 
81             if (!m_compositeUploadJob->isRunning()) {
82                 m_compositeUploadJob->start();
83             }
84         } else { //Infinite stream
85             CompositeUploadJob* fireAndForgetJob = new CompositeUploadJob(deviceId(), false);
86             fireAndForgetJob->addSubjob(new UploadJob(np));
87             fireAndForgetJob->start();
88         }
89 
90         return true;
91     } else {
92         int written = m_socketLineReader->write(np.serialize());
93 
94         //Actually we can't detect if a packet is received or not. We keep TCP
95         //"ESTABLISHED" connections that look legit (return true when we use them),
96         //but that are actually broken (until keepalive detects that they are down).
97         return (written != -1);
98     }
99 }
100 
dataReceived()101 void LanDeviceLink::dataReceived()
102 {
103     if (!m_socketLineReader->hasPacketsAvailable()) return;
104 
105     const QByteArray serializedPacket = m_socketLineReader->readLine();
106     NetworkPacket packet((QString()));
107     NetworkPacket::unserialize(serializedPacket, &packet);
108 
109     //qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket;
110 
111     if (packet.type() == PACKET_TYPE_PAIR) {
112         //TODO: Handle pair/unpair requests and forward them (to the pairing handler?)
113         qobject_cast<LanLinkProvider*>(provider())->incomingPairPacket(this, packet);
114         return;
115     }
116 
117     if (packet.hasPayloadTransferInfo()) {
118         //qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo";
119         const QVariantMap transferInfo = packet.payloadTransferInfo();
120 
121         QSharedPointer<QSslSocket> socket(new QSslSocket);
122 
123         LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true);
124 
125         // emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket.
126         // Needs investigation and upstreaming of the fix. QTBUG-62257
127         connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished);
128 
129         const QString address = m_socketLineReader->peerAddress().toString();
130         const quint16 port = transferInfo[QStringLiteral("port")].toInt();
131         socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite);
132         packet.setPayload(socket, packet.payloadSize());
133     }
134 
135     Q_EMIT receivedPacket(packet);
136 
137     if (m_socketLineReader->hasPacketsAvailable()) {
138         QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
139     }
140 
141 }
142 
userRequestsPair()143 void LanDeviceLink::userRequestsPair()
144 {
145     if (m_socketLineReader->peerCertificate().isNull()) {
146         Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
147     } else {
148         qobject_cast<LanLinkProvider*>(provider())->userRequestsPair(deviceId());
149     }
150 }
151 
userRequestsUnpair()152 void LanDeviceLink::userRequestsUnpair()
153 {
154     qobject_cast<LanLinkProvider*>(provider())->userRequestsUnpair(deviceId());
155 }
156 
setPairStatus(PairStatus status)157 void LanDeviceLink::setPairStatus(PairStatus status)
158 {
159     if (status == Paired && m_socketLineReader->peerCertificate().isNull()) {
160         Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect."));
161         return;
162     }
163 
164     DeviceLink::setPairStatus(status);
165     if (status == Paired) {
166         Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
167         KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
168     }
169 }
170 
linkShouldBeKeptAlive()171 bool LanDeviceLink::linkShouldBeKeptAlive() {
172 
173     return true;     //FIXME: Current implementation is broken, so for now we will keep links always established
174 
175     //We keep the remotely initiated connections, since the remotes require them if they want to request
176     //pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
177     //return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired);
178 
179 }
180 
certificate() const181 QSslCertificate LanDeviceLink::certificate() const
182 {
183     return m_socketLineReader->peerCertificate();
184 }
185