Merge pull request #1194 from amnezia-vpn/feature/proxy-bypass-checks
feature/proxy bypass checks
This commit is contained in:
commit
92bc1a6f09
1 changed files with 50 additions and 15 deletions
|
|
@ -1,5 +1,8 @@
|
||||||
#include "apiController.h"
|
#include "apiController.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
|
@ -11,8 +14,8 @@
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "configurators/wireguard_configurator.h"
|
#include "configurators/wireguard_configurator.h"
|
||||||
#include "core/enums/apiEnums.h"
|
#include "core/enums/apiEnums.h"
|
||||||
#include "version.h"
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +68,28 @@ namespace
|
||||||
return ErrorCode::ApiConfigDownloadError;
|
return ErrorCode::ApiConfigDownloadError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
|
||||||
|
const QByteArray &iv = "", const QByteArray &salt = "")
|
||||||
|
{
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
qDebug() << "Timeout occurred";
|
||||||
|
return true;
|
||||||
|
} else if (responseBody.contains("html")) {
|
||||||
|
qDebug() << "The response contains an html tag";
|
||||||
|
return true;
|
||||||
|
} else if (checkEncryption) {
|
||||||
|
try {
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||||
|
} catch (...) {
|
||||||
|
qDebug() << "Failed to decrypt the data";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
||||||
|
|
@ -320,24 +345,30 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
responseBody = reply->readAll();
|
||||||
|
|
||||||
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
||||||
m_proxyUrls = getProxyUrls();
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
for (const QString &proxyUrl : m_proxyUrls) {
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||||
|
reply->deleteLater(); // delete the previous reply
|
||||||
reply = amnApp->manager()->get(request);
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
|
||||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
responseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseBody = reply->readAll();
|
|
||||||
auto errorCode = checkErrors(sslErrors, reply);
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
return errorCode;
|
return errorCode;
|
||||||
|
|
@ -419,32 +450,36 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
auto encryptedResponseBody = reply->readAll();
|
||||||
if (m_proxyUrls.isEmpty()) {
|
|
||||||
m_proxyUrls = getProxyUrls();
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true)) {
|
||||||
}
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
for (const QString &proxyUrl : m_proxyUrls) {
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||||
|
reply->deleteLater(); // delete the previous reply
|
||||||
reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
|
||||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
encryptedResponseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto errorCode = checkErrors(sslErrors, reply);
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
reply->deleteLater();
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto encryptedResponseBody = reply->readAll();
|
|
||||||
reply->deleteLater();
|
|
||||||
try {
|
try {
|
||||||
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||||
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue