AusweisApp2/src/network/NetworkManager.cpp

366 lines
9.5 KiB
C++

/*
* \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany
*/
#include "NetworkManager.h"
#include "NetworkReplyError.h"
#include "NetworkReplyTimeout.h"
#include "SecureStorage.h"
#include "SingletonHelper.h"
#include "VersionInfo.h"
#include <http_parser.h>
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QNetworkProxyFactory>
#include <QRegularExpression>
#include <QStringBuilder>
using namespace governikus;
defineSingleton(NetworkManager)
Q_DECLARE_LOGGING_CATEGORY(network)
bool NetworkManager::mLockProxy = false;
NetworkManager::NetworkManager()
: QObject()
, mApplicationExitInProgress(false)
, mTrackedConnectionsMutex()
, mTrackedConnections()
, mNetAccessManager(new QNetworkAccessManager())
{
#ifndef QT_NO_NETWORKPROXY
connect(mNetAccessManager.data(), &QNetworkAccessManager::proxyAuthenticationRequired, this, &NetworkManager::fireProxyAuthenticationRequired);
#endif
}
NetworkManager::~NetworkManager()
{
}
NetworkManager& NetworkManager::getInstance()
{
return *Instance;
}
int NetworkManager::getOpenConnectionCount()
{
QMutexLocker locker(&mTrackedConnectionsMutex);
return mTrackedConnections.size();
}
void NetworkManager::clearConnections()
{
mNetAccessManager->clearConnectionCache();
}
QNetworkReply* NetworkManager::paos(QNetworkRequest& pRequest,
const QByteArray& pNamespace,
const QByteArray& pData,
bool pUsePsk,
const QByteArray& pSslSession,
int pTimeoutInMilliSeconds)
{
if (mApplicationExitInProgress)
{
return new NetworkReplyError(pRequest);
}
pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader());
pRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/vnd.paos+xml; charset=UTF-8"));
pRequest.setHeader(QNetworkRequest::ContentLengthHeader, QString::number(pData.size()));
pRequest.setRawHeader("Connection", "keep-alive");
pRequest.setRawHeader("Accept", "text/html; application/vnd.paos+xml");
pRequest.setRawHeader("PAOS", QByteArray("ver=\"%1\"").replace(QByteArray("%1"), pNamespace));
QNetworkReply* response;
SecureStorage::TlsSuite tlsSuite = pUsePsk ? SecureStorage::TlsSuite::PSK : SecureStorage::TlsSuite::DEFAULT;
auto cfg = Env::getSingleton<SecureStorage>()->getTlsConfig(tlsSuite).getConfiguration();
cfg.setSessionTicket(pSslSession);
pRequest.setSslConfiguration(cfg);
response = mNetAccessManager->post(pRequest, pData);
trackConnection(response, pTimeoutInMilliSeconds);
return response;
}
QNetworkReply* NetworkManager::get(QNetworkRequest& pRequest,
const QByteArray& pSslSession,
int pTimeoutInMilliSeconds)
{
if (mApplicationExitInProgress)
{
return new NetworkReplyError(pRequest);
}
pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader());
auto cfg = Env::getSingleton<SecureStorage>()->getTlsConfig().getConfiguration();
cfg.setSessionTicket(pSslSession);
pRequest.setSslConfiguration(cfg);
QNetworkReply* response = mNetAccessManager->get(pRequest);
trackConnection(response, pTimeoutInMilliSeconds);
return response;
}
QNetworkReply* NetworkManager::post(QNetworkRequest& pRequest,
const QByteArray& pData,
int pTimeoutInMilliSeconds)
{
if (mApplicationExitInProgress)
{
return new NetworkReplyError(pRequest);
}
pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader());
pRequest.setHeader(QNetworkRequest::ContentLengthHeader, QString::number(pData.size()));
auto cfg = Env::getSingleton<SecureStorage>()->getTlsConfig(SecureStorage::TlsSuite::DEFAULT).getConfiguration();
pRequest.setSslConfiguration(cfg);
QNetworkReply* response = mNetAccessManager->post(pRequest, pData);
trackConnection(response, pTimeoutInMilliSeconds);
return response;
}
bool NetworkManager::checkUpdateServerCertificate(const QNetworkReply& pReply)
{
const QVector<QSslCertificate>& trustedCertificates = Env::getSingleton<SecureStorage>()->getUpdateCertificates();
const auto& cert = pReply.sslConfiguration().peerCertificate();
return !cert.isNull() && trustedCertificates.contains(cert);
}
QString NetworkManager::getUserAgentHeader() const
{
const auto& info = VersionInfo::getInstance();
const QString spec = QStringLiteral(" (%1/%2)").arg(info.getSpecificationTitle(), info.getSpecificationVersion());
return QCoreApplication::applicationName() % QLatin1Char('/') % QCoreApplication::applicationVersion() % spec;
}
void NetworkManager::onShutdown()
{
mApplicationExitInProgress = true;
Q_EMIT fireShutdown();
}
NetworkManager::NetworkError NetworkManager::toNetworkError(const QNetworkReply* const pNetworkReply)
{
qCDebug(network) << "Select error message for:" << pNetworkReply->error();
switch (pNetworkReply->error())
{
case QNetworkReply::TimeoutError:
return NetworkError::TimeOut;
case QNetworkReply::ServiceUnavailableError:
return NetworkError::ServiceUnavailable;
case QNetworkReply::ProxyAuthenticationRequiredError:
case QNetworkReply::ProxyConnectionClosedError:
case QNetworkReply::ProxyConnectionRefusedError:
case QNetworkReply::ProxyNotFoundError:
case QNetworkReply::ProxyTimeoutError:
case QNetworkReply::UnknownProxyError:
return NetworkError::ProxyError;
case QNetworkReply::SslHandshakeFailedError:
return NetworkError::SecurityError;
default:
qCCritical(network) << "Network error opening URL" << pNetworkReply->request().url().toString();
qCCritical(network) << "Network error description" << pNetworkReply->errorString();
return NetworkError::OtherError;
}
}
GlobalStatus NetworkManager::toTrustedChannelStatus(const QNetworkReply* const pNetworkReply)
{
switch (toNetworkError(pNetworkReply))
{
case NetworkManager::NetworkError::ServiceUnavailable:
return GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable;
case NetworkManager::NetworkError::TimeOut:
return GlobalStatus::Code::Workflow_TrustedChannel_TimeOut;
case NetworkManager::NetworkError::ProxyError:
return GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error;
case NetworkManager::NetworkError::SecurityError:
return GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error;
case NetworkManager::NetworkError::OtherError:
return GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error;
}
return GlobalStatus::Code::Unknown_Error;
}
GlobalStatus NetworkManager::toStatus(const QNetworkReply* const pNetworkReply)
{
switch (toNetworkError(pNetworkReply))
{
case NetworkManager::NetworkError::ServiceUnavailable:
return GlobalStatus::Code::Network_ServiceUnavailable;
case NetworkManager::NetworkError::TimeOut:
return GlobalStatus::Code::Network_TimeOut;
case NetworkManager::NetworkError::ProxyError:
return GlobalStatus::Code::Network_Proxy_Error;
case NetworkManager::NetworkError::SecurityError:
return GlobalStatus::Code::Network_Ssl_Establishment_Error;
case NetworkManager::NetworkError::OtherError:
return GlobalStatus::Code::Network_Other_Error;
}
return GlobalStatus::Code::Unknown_Error;
}
void NetworkManager::trackConnection(QNetworkReply* pResponse, const int pTimeoutInMilliSeconds)
{
Q_ASSERT(pResponse);
addTrackedConnection(pResponse);
connect(pResponse, &QObject::destroyed, this, [ = ] {
this->removeTrackedConnection(pResponse);
});
NetworkReplyTimeout::setTimeout(pResponse, pTimeoutInMilliSeconds);
}
void NetworkManager::addTrackedConnection(QNetworkReply* pResponse)
{
QMutexLocker locker(&mTrackedConnectionsMutex);
mTrackedConnections.insert(pResponse);
}
void NetworkManager::removeTrackedConnection(QNetworkReply* pResponse)
{
QMutexLocker locker(&mTrackedConnectionsMutex);
mTrackedConnections.remove(pResponse);
}
QByteArray NetworkManager::getStatusMessage(int pStatus)
{
switch (pStatus)
{
#define XX(num, name, string) case num:\
return QByteArrayLiteral(#string);
HTTP_STATUS_MAP(XX)
#undef XX
}
return QByteArray();
}
int NetworkManager::getLoggedStatusCode(const QNetworkReply* const pReply, const QMessageLogger& pLogger)
{
const int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
pLogger.debug() << "Status Code:" << statusCode << getStatusMessage(statusCode);
for (const auto& header : pReply->rawHeaderPairs())
{
pLogger.debug().nospace().noquote() << "Header | " << header.first << ": " << header.second;
}
return statusCode;
}
#ifndef QT_NO_NETWORKPROXY
namespace
{
class NoProxyFactory
: public QNetworkProxyFactory
{
private:
QList<QNetworkProxy> mProxies;
public:
NoProxyFactory()
: QNetworkProxyFactory()
, mProxies()
{
mProxies << QNetworkProxy::NoProxy;
}
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery& pInputQuery = QNetworkProxyQuery()) override
{
qCDebug(network) << pInputQuery;
qCDebug(network) << "Found proxies" << mProxies;
return mProxies;
}
};
class SystemProxyFactory
: public QNetworkProxyFactory
{
public:
SystemProxyFactory()
: QNetworkProxyFactory()
{
}
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery& pInputQuery = QNetworkProxyQuery()) override
{
qCDebug(network) << pInputQuery;
QList<QNetworkProxy> proxies = systemProxyForQuery(pInputQuery);
qCDebug(network) << "Found proxies" << proxies;
return proxies;
}
};
} // namespace
void NetworkManager::setApplicationProxyFactory()
{
if (Q_UNLIKELY(mLockProxy))
{
qCWarning(network) << "Won't use system proxy because user locked it!";
qCDebug(network) << "proxy -> none";
QNetworkProxyFactory::setApplicationProxyFactory(new NoProxyFactory());
}
else
{
qCDebug(network) << "proxy -> system";
QNetworkProxyFactory::setApplicationProxyFactory(new SystemProxyFactory());
}
}
#endif