AusweisApp2/src/remote_device/RemoteClientImpl.cpp

287 lines
8.0 KiB
C++

/*!
* \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany
*/
#include "RemoteClientImpl.h"
#include "AppSettings.h"
#include "Env.h"
#include "messages/RemoteMessageParser.h"
#include "RemoteConnectorImpl.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(remote_device)
using namespace governikus;
namespace governikus
{
template<> RemoteClient* createNewObject<RemoteClient*>()
{
return new RemoteClientImpl;
}
}
RemoteClientImpl::RemoteClientImpl()
: mDatagramHandler()
, mRemoteDeviceList(Env::create<RemoteDeviceList*>())
, mErrorCounter()
, mRemoteConnectorThread()
, mRemoteConnector()
, mRemoteConnectorPending()
{
connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceAppeared, this, &RemoteClient::fireDeviceAppeared);
connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceVanished, this, &RemoteClient::fireDeviceVanished);
bootstrapRemoteConnectorThread();
}
RemoteClientImpl::~RemoteClientImpl()
{
shutdownRemoteConnectorThread();
}
void RemoteClientImpl::bootstrapRemoteConnectorThread()
{
Q_ASSERT(!mRemoteConnectorThread.isRunning());
Q_ASSERT(mRemoteConnector.isNull());
mRemoteConnectorThread.setObjectName(QStringLiteral("RemoteConnectorThread"));
mRemoteConnector = Env::create<RemoteConnector*>();
mRemoteConnector->moveToThread(&mRemoteConnectorThread);
connect(&mRemoteConnectorThread, &QThread::finished, mRemoteConnector.data(), &QObject::deleteLater);
connect(mRemoteConnector.data(), &RemoteConnector::fireRemoteDispatcherCreated, this, &RemoteClientImpl::onRemoteDispatcherCreated);
connect(mRemoteConnector.data(), &RemoteConnector::fireRemoteDispatcherError, this, &RemoteClientImpl::onRemoteDispatcherError);
mRemoteConnectorThread.start();
}
void RemoteClientImpl::shutdownRemoteConnectorThread()
{
if (mRemoteConnectorThread.isRunning() && !mRemoteConnectorThread.isInterruptionRequested())
{
qCDebug(remote_device) << "Shutdown RemoteConnector...";
mRemoteConnectorThread.requestInterruption(); // do not try to stop AGAIN from dtor
mRemoteConnectorThread.quit();
mRemoteConnectorThread.wait(2500);
qCDebug(remote_device) << "RemoteConnector:" << (mRemoteConnectorThread.isRunning() ? "still running" : "stopped");
Q_ASSERT(!mRemoteConnectorThread.isRunning());
}
}
QSharedPointer<RemoteDeviceListEntry> RemoteClientImpl::mapToAndTakeRemoteConnectorPending(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor)
{
QMutableVectorIterator<QSharedPointer<RemoteDeviceListEntry> > i(mRemoteConnectorPending);
while (i.hasNext())
{
QSharedPointer<RemoteDeviceListEntry> entry = i.next();
if (entry->contains(pRemoteDeviceDescriptor))
{
i.remove();
return entry;
}
}
Q_ASSERT(false);
return QSharedPointer<RemoteDeviceListEntry>();
}
void RemoteClientImpl::onNewMessage(const QJsonDocument& pData, const QHostAddress& pAddress)
{
bool isIPv4;
pAddress.toIPv4Address(&isIPv4);
if (!isIPv4)
{
static int timesLogged = 0;
if (timesLogged < 20)
{
qCCritical(remote_device) << "IPv6 not supported" << pAddress;
timesLogged++;
}
return;
}
const QSharedPointer<const Discovery>& discovery = RemoteMessageParser().parseDiscovery(pData);
if (discovery.isNull())
{
static int timesLogged = 0;
if (timesLogged < 20)
{
qCDebug(remote_device) << "Discarding unparsable message";
timesLogged++;
}
return;
}
const RemoteDeviceDescriptor remoteDeviceDescriptor(discovery, pAddress);
mRemoteDeviceList->update(remoteDeviceDescriptor);
}
void RemoteClientImpl::onRemoteDispatcherCreated(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, const QSharedPointer<RemoteDispatcher>& pDispatcher)
{
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
const QSharedPointer<RemoteDeviceListEntry>& entry = mapToAndTakeRemoteConnectorPending(pRemoteDeviceDescriptor);
if (entry.isNull())
{
qCDebug(remote_device) << "Failed to map RemoteConnector response:" << pRemoteDeviceDescriptor;
Q_ASSERT(false);
return;
}
mConnectedDeviceIds.append(entry->getRemoteDeviceDescriptor().getIfdId());
connect(pDispatcher.data(), &RemoteDispatcher::fireClosed, this, &RemoteClientImpl::onDispatcherDestroyed);
Q_EMIT fireEstablishConnectionDone(entry, GlobalStatus::Code::No_Error);
Q_EMIT fireNewRemoteDispatcher(pDispatcher);
}
void RemoteClientImpl::onRemoteDispatcherError(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor, RemoteErrorCode pErrorCode)
{
const QSharedPointer<RemoteDeviceListEntry>& entry = mapToAndTakeRemoteConnectorPending(pRemoteDeviceDescriptor);
if (entry.isNull())
{
qCDebug(remote_device) << "Failed to map RemoteConnector response:" << pRemoteDeviceDescriptor;
Q_ASSERT(false);
return;
}
if (pErrorCode == RemoteErrorCode::REMOTE_HOST_REFUSED_CONNECTION || pErrorCode == RemoteErrorCode::NO_SUPPORTED_API_LEVEL)
{
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] += 1;
if (mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] >= 7)
{
qCCritical(remote_device) << "Remote device refused connection seven times, removing certificate with fingerprint:" << pRemoteDeviceDescriptor.getIfdId();
RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings();
QString deviceName;
const auto& infos = settings.getRemoteInfos();
for (const auto& remoteInfo : infos)
{
if (remoteInfo.getFingerprint() == pRemoteDeviceDescriptor.getIfdId())
{
deviceName = remoteInfo.getName();
}
}
settings.removeTrustedCertificate(pRemoteDeviceDescriptor.getIfdId());
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
if (!deviceName.isEmpty())
{
Q_EMIT fireCertificateRemoved(deviceName);
}
}
}
else
{
mErrorCounter[pRemoteDeviceDescriptor.getIfdId()] = 0;
}
Q_EMIT fireEstablishConnectionDone(entry, RemoteConnector::errorToGlobalStatus(pErrorCode));
}
void RemoteClientImpl::startDetection()
{
if (!mDatagramHandler.isNull())
{
qCDebug(remote_device) << "Detection already started";
return;
}
mDatagramHandler.reset(Env::create<DatagramHandler*>());
connect(mDatagramHandler.data(), &DatagramHandler::fireNewMessage, this, &RemoteClientImpl::onNewMessage);
Q_EMIT fireDetectionChanged();
}
void RemoteClientImpl::stopDetection()
{
mDatagramHandler.reset();
mRemoteDeviceList->clear();
Q_EMIT fireDetectionChanged();
}
bool RemoteClientImpl::isDetecting()
{
return !mDatagramHandler.isNull();
}
void RemoteClientImpl::establishConnection(const QSharedPointer<RemoteDeviceListEntry>& pEntry, const QString& pPsk)
{
if (mRemoteConnector.isNull())
{
qCCritical(remote_device) << "There is no RemoteConnector!";
Q_ASSERT(false);
}
if (!pEntry)
{
qCWarning(remote_device) << "Cannot connect to given device.";
Q_EMIT fireEstablishConnectionDone(pEntry, GlobalStatus::Code::RemoteConnector_ConnectionError);
return;
}
mRemoteConnectorPending += pEntry;
QMetaObject::invokeMethod(mRemoteConnector.data(), "onConnectRequest", Qt::QueuedConnection,
Q_ARG(RemoteDeviceDescriptor, pEntry->getRemoteDeviceDescriptor()),
Q_ARG(QString, pPsk));
}
QVector<QSharedPointer<RemoteDeviceListEntry> > RemoteClientImpl::getRemoteDevices() const
{
return mRemoteDeviceList->getRemoteDevices();
}
void RemoteClientImpl::requestRemoteDevices()
{
Q_EMIT fireRemoteDevicesInfo(mRemoteDeviceList->getRemoteDevices());
}
QVector<RemoteServiceSettings::RemoteInfo> RemoteClientImpl::getConnectedDeviceInfos()
{
RemoteServiceSettings& settings = AppSettings::getInstance().getRemoteServiceSettings();
auto remoteInfos = settings.getRemoteInfos();
QVector<RemoteServiceSettings::RemoteInfo> result;
for (const auto& info : remoteInfos)
{
for (const auto& id : qAsConst(mConnectedDeviceIds))
{
if (info.getFingerprint() == id)
{
result.append(info);
}
}
}
return result;
}
void RemoteClientImpl::onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QSharedPointer<RemoteDispatcher>& pRemoteDispatcher)
{
if (pRemoteDispatcher)
{
mConnectedDeviceIds.removeAll(pRemoteDispatcher->getId());
}
Q_EMIT fireDispatcherDestroyed(pCloseCode, pRemoteDispatcher);
}