Compare commits

..

11 Commits

Author SHA1 Message Date
Vladyslav Miachkov
3e5e98d916 Do not allow to add loopback/multicast/broadcast ips to split tunnel list 2024-06-28 15:25:53 +03:00
pokamest
ef712b7054 Merge pull request #841 from amnezia-vpn/bugfix/api-awg-settings-display
fixed display of awg config settings received from api
2024-06-10 12:36:08 +01:00
Nethius
c22f9ff08a added ui for proxy container (#762)
Added proxy container
2024-06-10 12:35:24 +01:00
vladimir.kuznetsov
04fb1825d5 fixed display of awg config settings received from api 2024-06-05 22:19:23 +02:00
pokamest
4f8f873682 Merge pull request #828 from amnezia-vpn/fix/hindi_file_extensions 2024-06-03 08:50:32 +01:00
pokamest
9fe75c6120 Merge pull request #831 from amnezia-vpn/bugfix/wg-show-possible-crash-fix 2024-06-03 08:49:26 +01:00
pokamest
bb7e8f46cb Merge pull request #835 from amnezia-vpn/bugfix/has-split-tunneling 2024-06-03 08:44:04 +01:00
vladimir.kuznetsov
5db0c281ee fixed isDefaultServerDefaultContainerHasSplitTunneling() 2024-05-30 12:42:53 +02:00
Vladyslav Miachkov
aac9bfcea6 Possible wg show crash fix 2024-05-27 18:58:36 +03:00
Mykola Baibuz
e6ee9085a2 Connection string support for XRay protocol (#777)
* Connection string support for XRay protocol
2024-05-27 16:15:55 +01:00
lunardunno
d62ade58a5 update Hindi translation
Fixed handling of file extensions in Hindi translation.
2024-05-27 12:05:53 +04:00
27 changed files with 710 additions and 27 deletions

View File

@@ -351,6 +351,9 @@ void AmneziaApplication::initModels()
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),

View File

@@ -41,6 +41,7 @@
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/appSplitTunnelingModel.h"
@@ -114,6 +115,7 @@ private:
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;

View File

@@ -69,6 +69,8 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
case DockerContainer::Sftp: return { Proto::Sftp };
case DockerContainer::Socks5Proxy: return { Proto::Socks5Proxy };
default: return { defaultProtocol(container) };
}
}
@@ -98,7 +100,8 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
@@ -131,7 +134,9 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
{ DockerContainer::Dns,
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp,
QObject::tr("Create a file vault on your server to securely store and transfer files.") } };
QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy,
QObject::tr("") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
@@ -240,7 +245,8 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
QObject::tr("After installation, Amnezia will create a\n\n file storage on your server. "
"You will be able to access it using\n FileZilla or other SFTP clients, "
"as well as mount the disk on your device to access\n it directly from your device.\n\n"
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") }
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") }
};
}
@@ -265,6 +271,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
case DockerContainer::TorWebSite: return Proto::TorWebSite;
case DockerContainer::Dns: return Proto::Dns;
case DockerContainer::Sftp: return Proto::Sftp;
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
default: return Proto::Any;
}
}
@@ -367,6 +374,7 @@ bool ContainerProps::isShareable(DockerContainer container)
case DockerContainer::TorWebSite: return false;
case DockerContainer::Dns: return false;
case DockerContainer::Sftp: return false;
case DockerContainer::Socks5Proxy: return false;
default: return true;
}
}

View File

@@ -28,7 +28,8 @@ namespace amnezia
// non-vpn
TorWebSite,
Dns,
Sftp
Sftp,
Socks5Proxy
};
Q_ENUM_NS(DockerContainer)
} // namespace ContainerEnumNS

View File

@@ -40,6 +40,28 @@ void ApiController::processApiConfig(const QString &protocol, const ApiControlle
return;
} else if (protocol == configKey::awg) {
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto serverConfig = QJsonDocument::fromJson(config.toUtf8()).object();
auto containers = serverConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
return;
}
auto container = containers.at(0).toObject();
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
auto containerConfig = container.value(containerName).toObject();
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig;
containers.replace(0, container);
serverConfig[config_key::containers] = containers;
config = QString(QJsonDocument(serverConfig).toJson());
}
return;
}

View File

@@ -106,7 +106,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
if (e)
return e;
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
QString runner = QString("sudo docker exec -i $CONTAINER_NAME sh %1 ").arg(fileName);
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
@@ -376,6 +376,10 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
return true;
}
if (container == DockerContainer::Socks5Proxy) {
return true;
}
return false;
}
@@ -516,6 +520,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
Vars vars;
@@ -613,6 +618,14 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
// Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key:: userName).toString();
auto password = socks5ProxyConfig.value(config_key::password).toString();
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({ { "$SOCKS5_USER", socks5user } });
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
if (!serverIp.isEmpty()) {
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });

View File

@@ -18,6 +18,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
case DockerContainer::Dns: return QLatin1String("dns");
case DockerContainer::Sftp: return QLatin1String("sftp");
case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy");
default: return QString();
}
}

View File

@@ -77,7 +77,8 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
{ Proto::TorWebSite, "Website in Tor network" },
{ Proto::Dns, "DNS Service" },
{ Proto::Sftp, QObject::tr("Sftp service") } };
{ Proto::Sftp, QObject::tr("Sftp service") },
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
}
QMap<amnezia::Proto, QString> ProtocolProps::protocolDescriptions()
@@ -102,6 +103,7 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
case Proto::TorWebSite: return ServiceType::Other;
case Proto::Dns: return ServiceType::Other;
case Proto::Sftp: return ServiceType::Other;
case Proto::Socks5Proxy: return ServiceType::Other;
default: return ServiceType::Other;
}
}
@@ -113,6 +115,7 @@ int ProtocolProps::getPortForInstall(Proto p)
case WireGuard:
case ShadowSocks:
case OpenVpn:
case Socks5Proxy:
return QRandomGenerator::global()->bounded(30000, 50000);
default:
return defaultPort(p);
@@ -135,6 +138,7 @@ int ProtocolProps::defaultPort(Proto p)
case Proto::TorWebSite: return -1;
case Proto::Dns: return 53;
case Proto::Sftp: return 222;
case Proto::Socks5Proxy: return 38080;
default: return -1;
}
}
@@ -154,6 +158,7 @@ bool ProtocolProps::defaultPortChangeable(Proto p)
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::Sftp: return true;
case Proto::Socks5Proxy: return true;
default: return false;
}
}
@@ -175,6 +180,7 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
case Proto::TorWebSite: return TransportProto::Tcp;
case Proto::Dns: return TransportProto::Udp;
case Proto::Sftp: return TransportProto::Tcp;
case Proto::Socks5Proxy: return TransportProto::Tcp;
}
}
@@ -195,6 +201,7 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::Sftp: return false;
case Proto::Socks5Proxy: return false;
default: return false;
}
return false;

View File

@@ -84,6 +84,7 @@ namespace amnezia
constexpr char awg[] = "awg";
constexpr char xray[] = "xray";
constexpr char ssxray[] = "ssxray";
constexpr char socks5proxy[] = "socks5proxy";
constexpr char configVersion[] = "config_version";
@@ -216,6 +217,14 @@ namespace amnezia
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
}
namespace socks5Proxy
{
constexpr char defaultUserName[] = "proxy_user";
constexpr char defaultPort[] = "38080";
constexpr char proxyConfigPath[] = "/usr/local/3proxy/conf/3proxy.cfg";
}
} // namespace protocols
namespace ProtocolEnumNS
@@ -244,7 +253,8 @@ namespace amnezia
// non-vpn
TorWebSite,
Dns,
Sftp
Sftp,
Socks5Proxy
};
Q_ENUM_NS(Proto)

View File

@@ -198,7 +198,7 @@
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
@@ -239,5 +239,10 @@
<file>images/controls/alert-circle.svg</file>
<file>images/controls/file-check-2.svg</file>
<file>ui/qml/Controls2/WarningType.qml</file>
<file>ui/qml/Pages2/PageServiceSocksProxySettings.qml</file>
<file>server_scripts/socks5_proxy/run_container.sh</file>
<file>server_scripts/socks5_proxy/Dockerfile</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file>
<file>server_scripts/socks5_proxy/start.sh</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,10 @@
FROM 3proxy/3proxy:latest
LABEL maintainer="AmneziaVPN"
RUN mkdir -p /opt/amnezia
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
RUN chmod a+x /opt/amnezia/start.sh
ENTRYPOINT [ "/bin/sh", "/opt/amnezia/start.sh" ]
CMD [ "" ]

View File

@@ -0,0 +1,12 @@
#!/bin/sh
echo -e "#!/bin/3proxy" > /usr/local/3proxy/conf/3proxy.cfg
echo -e "config /usr/local/3proxy/conf/3proxy.cfg" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "timeouts 1 5 30 60 180 1800 15 60" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "$SOCKS5_USER" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "log /usr/local/3proxy/logs/3proxy.log" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "logformat \"-\\\"\"+_G{\"\"time_unix\"\":%t, \"\"proxy\"\":{\"\"type:\"\":\"\"%N\"\", \"\"port\"\":%p}, \"\"error\"\":{\"\"code\"\":\"\"%E\"\"}, \"\"auth\"\":{\"\"user\"\":\"\"%U\"\"}, \"\"client\"\":{\"\"ip\"\":\"\"%C\"\", \"\"port\"\":%c}, \"\"server\"\":{\"\"ip\"\":\"\"%R\"\", \"\"port\"\":%r}, \"\"bytes\"\":{\"\"sent\"\":%O, \"\"received\"\":%I}, \"\"request\"\":{\"\"hostname\"\":\"\"%n\"\"}, \"\"message\"\":\"\"%T\"\"}\"" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "auth $SOCKS5_AUTH_TYPE" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "socks -p$SOCKS5_PROXY_PORT" >> /usr/local/3proxy/conf/3proxy.cfg

View File

@@ -0,0 +1,5 @@
sudo docker run -d \
--restart always \
-p $SOCKS5_PROXY_PORT:$SOCKS5_PROXY_PORT/tcp \
--name $CONTAINER_NAME \
$CONTAINER_NAME

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
echo "Container startup"
/bin/3proxy /usr/local/3proxy/conf/3proxy.cfg

View File

@@ -9,6 +9,18 @@
#include "containers/containers_defs.h"
#include "logger.h"
namespace {
// true if invalid address or ip matches either of localhost/multicast/broadcast
bool isIpAddressReserved(const QString &ipStr)
{
QHostAddress ip(ipStr);
return ip.isLoopback() || ip.isMulticast() || ip.isBroadcast();
}
}
const char Settings::cloudFlareNs1[] = "1.1.1.1";
const char Settings::cloudFlareNs2[] = "1.0.0.1";
@@ -272,6 +284,11 @@ bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip
if (sites.contains(site) && ip.isEmpty())
return false;
if (isIpAddressReserved(site))
{
return false;
}
sites.insert(site, ip);
setVpnSites(mode, sites);
return true;
@@ -284,6 +301,11 @@ void Settings::addVpnSites(RouteMode mode, const QMap<QString, QString> &sites)
const QString &site = i.key();
const QString &ip = i.value();
if (isIpAddressReserved(site))
{
continue;
}
if (allSites.contains(site) && allSites.value(site) == ip)
continue;

View File

@@ -1144,7 +1144,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/>
<source>Executable file (*.*)</source>
<translation>ि (**)</translation>
<translation>ि (*.*)</translation>
</message>
</context>
<context>
@@ -1276,7 +1276,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="102"/>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="134"/>
<source>Backup files (*.backup)</source>
<translation> (*.)</translation>
<translation> (*.backup)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="111"/>
@@ -1480,7 +1480,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="143"/>
<source>Logs files (*.log)</source>
<translation> (*.)</translation>
<translation> (*.log)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="152"/>

View File

@@ -123,6 +123,9 @@ void InstallController::install(DockerContainer container, int port, TransportPr
} else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
} else if (container == DockerContainer::Socks5Proxy) {
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
}
config.insert(config_key::container, ContainerProps::containerToString(container));
@@ -362,7 +365,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
if (containerInfo.isEmpty()) {
continue;
}
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z0-9]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
if (containerAndPortMatch.hasMatch()) {
QString name = containerAndPortMatch.captured(1);
@@ -427,6 +430,20 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
containerConfig.insert(config_key::userName, userName);
containerConfig.insert(config_key::password, password);
} else if (protocol == Proto::Socks5Proxy) {
QString proxyConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::socks5Proxy::proxyConfigPath, errorCode);
const static QRegularExpression usernameAndPasswordRegExp("users (\\w+):CL:(\\w+)");
QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig);
if (usernameAndPasswordMatch.hasMatch()) {
QString userName = usernameAndPasswordMatch.captured(1);
QString password = usernameAndPasswordMatch.captured(2);
containerConfig.insert(config_key::userName, userName);
containerConfig.insert(config_key::password, password);
}
}
config.insert(config_key::container, ContainerProps::containerToString(container));
@@ -603,6 +620,10 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
int serverIndex = m_serversModel->getProcessedServerIndex();
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
if (ContainerProps::containerService(container) == ServiceType::Other) {
return;
}
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));

View File

@@ -35,6 +35,7 @@ namespace PageLoader
PageServiceSftpSettings,
PageServiceTorWebsiteSettings,
PageServiceDnsSettings,
PageServiceSocksProxySettings,
PageSetupWizardStart,
PageSetupWizardCredentials,

View File

@@ -35,7 +35,12 @@ void SitesController::addSite(QString hostname)
}
const auto &processSite = [this](const QString &hostname, const QString &ip) {
m_sitesModel->addSite(hostname, ip);
bool isAdded = m_sitesModel->addSite(hostname, ip);
if (!isAdded)
{
return false;
}
if (!ip.isEmpty()) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
@@ -45,6 +50,8 @@ void SitesController::addSite(QString hostname)
Q_ARG(QStringList, QStringList() << hostname));
}
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
return true;
};
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
@@ -57,14 +64,20 @@ void SitesController::addSite(QString hostname)
}
};
bool isSiteAdded = false;
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
processSite(hostname, "");
isSiteAdded = processSite(hostname, "");
} else {
processSite(hostname, "");
isSiteAdded = processSite(hostname, "");
QHostInfo::lookupHost(hostname, this, resolveCallback);
}
emit finished(tr("New site added: %1").arg(hostname));
if (isSiteAdded) {
emit finished(tr("New site added: %1").arg(hostname));
} else
{
emit finished(tr("Invalid address or ip matches either of localhost/multicast/broadcast: %1").arg(hostname));
}
}
void SitesController::removeSite(int index)

View File

@@ -281,7 +281,8 @@ ErrorCode ClientManagementModel::wgShow(const DockerContainer container, const S
}
};
for (int i = 0; i < peerList.size() && i < transferredDataList.size(); ++i) {
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size(); ++i) {
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
auto latestHandshake = getStrValue(latestHandshakeList[i]);
auto serverBytesReceived = transferredData.front().trimmed();

View File

@@ -86,6 +86,7 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
case Proto::Sftp: return PageLoader::PageEnum::PageServiceSftpSettings;
case Proto::Socks5Proxy: return PageLoader::PageEnum::PageServiceSocksProxySettings;
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
}
}

View File

@@ -548,6 +548,8 @@ QStringList ServersModel::getAllInstalledServicesName(const int serverIndex)
servicesName.append("SFTP");
} else if (container == DockerContainer::TorWebSite) {
servicesName.append("TOR");
} else if (container == DockerContainer::Socks5Proxy) {
servicesName.append("SOCKS5");
}
}
}
@@ -615,15 +617,18 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
{
auto server = m_servers.at(m_defaultServerIndex).toObject();
auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
auto containerConfig = server.value(config_key::containers).toArray().at(defaultContainer).toObject();
auto protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
return !(protocolConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|| defaultContainer == DockerContainer::ShadowSocks) {
return !(protocolConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
auto containers = server.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|| defaultContainer == DockerContainer::ShadowSocks) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
}
}
return false;
}

View File

@@ -0,0 +1,80 @@
#include "socks5ProxyConfigModel.h"
#include "protocols/protocols_defs.h"
Socks5ProxyConfigModel::Socks5ProxyConfigModel(QObject *parent) : QAbstractListModel(parent)
{
}
int Socks5ProxyConfigModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
bool Socks5ProxyConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
return false;
}
switch (role) {
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
case Roles::UserNameRole: m_protocolConfig.insert(config_key::userName, value.toString()); break;
case Roles::PasswordRole: m_protocolConfig.insert(config_key::password, value.toString()); break;
}
emit dataChanged(index, index, QList { role });
return true;
}
QVariant Socks5ProxyConfigModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return false;
}
switch (role) {
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString();
case Roles::UserNameRole:
return m_protocolConfig.value(config_key::userName).toString();
case Roles::PasswordRole: return m_protocolConfig.value(config_key::password).toString();
}
return QVariant();
}
void Socks5ProxyConfigModel::updateModel(const QJsonObject &config)
{
beginResetModel();
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config;
QJsonObject protocolConfig = config.value(config_key::socks5proxy).toObject();
m_protocolConfig.insert(config_key::userName,
protocolConfig.value(config_key::userName).toString());
m_protocolConfig.insert(config_key::password, protocolConfig.value(config_key::password).toString());
m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString());
endResetModel();
}
QJsonObject Socks5ProxyConfigModel::getConfig()
{
m_fullConfig.insert(config_key::socks5proxy, m_protocolConfig);
return m_fullConfig;
}
QHash<int, QByteArray> Socks5ProxyConfigModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[PortRole] = "port";
roles[UserNameRole] = "username";
roles[PasswordRole] = "password";
return roles;
}

View File

@@ -0,0 +1,40 @@
#ifndef SOCKS5PROXYCONFIGMODEL_H
#define SOCKS5PROXYCONFIGMODEL_H
#include <QAbstractListModel>
#include <QJsonObject>
#include "containers/containers_defs.h"
class Socks5ProxyConfigModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
PortRole = Qt::UserRole + 1,
UserNameRole,
PasswordRole
};
explicit Socks5ProxyConfigModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateModel(const QJsonObject &config);
QJsonObject getConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
DockerContainer m_container;
QJsonObject m_protocolConfig;
QJsonObject m_fullConfig;
};
#endif // SOCKS5PROXYCONFIGMODEL_H

View File

@@ -0,0 +1,385 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview
Connections {
target: InstallController
function onUpdateContainerFinished() {
PageController.showNotificationMessage(qsTr("Settings updated successfully"))
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: listview.implicitHeight
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: Socks5ProxyConfigModel
onFocusChanged: {
if (focus) {
listview.currentItem.focusItemId.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: listview.width
implicitHeight: content.implicitHeight
property alias focusItemId: hostLabel.rightButton
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SOCKS5 settings")
}
LabelWithButtonType {
id: hostLabel
Layout.fillWidth: true
Layout.topMargin: 32
parentFlickable: fl
KeyNavigation.tab: portLabel.rightButton
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: portLabel
Layout.fillWidth: true
text: qsTr("Port")
descriptionText: port
descriptionOnTop: true
parentFlickable: fl
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: usernameLabel
Layout.fillWidth: true
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
parentFlickable: fl
KeyNavigation.tab: passwordLabel.eyeButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: passwordLabel
Layout.fillWidth: true
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
parentFlickable: fl
eyeButton.KeyNavigation.tab: passwordLabel.rightButton
rightButton.KeyNavigation.tab: changeSettingsButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
DrawerType2 {
id: changeSettingsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.9
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
property string tempPort: port
property string tempUsername: username
property string tempPassword: password
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
Connections {
target: changeSettingsDrawer
function onOpened() {
if (!GC.isMobile()) {
drawerFocusItem.forceActiveFocus()
}
tempPort = port
tempUsername = username
tempPassword = password
}
function onClosed() {
port = tempPort
username = tempUsername
password = tempPassword
portTextField.textFieldText = port
usernameTextField.textFieldText = username
passwordTextField.textFieldText = password
}
}
Item {
id: drawerFocusItem
KeyNavigation.tab: portTextField.textField
}
HeaderType {
Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
headerText: qsTr("Port")
textFieldText: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
if (textFieldText !== port) {
port = textFieldText
}
}
KeyNavigation.tab: usernameTextField.textField
}
TextFieldWithHeaderType {
id: usernameTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Username")
textFieldPlaceholderText: "username"
textFieldText: username
textField.maximumLength: 32
textField.onEditingFinished: {
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
if (textFieldText !== username) {
username = textFieldText
}
}
KeyNavigation.tab: passwordTextField.textField
}
TextFieldWithHeaderType {
id: passwordTextField
property bool hidePassword: true
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Password")
textFieldPlaceholderText: "password"
textFieldText: password
textField.maximumLength: 32
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
clickedFunc: function() {
hidePassword = !hidePassword
}
textField.onFocusChanged: {
textFieldText = textField.text.replace(/^\s+|\s+$/g, '')
if (textFieldText !== password) {
password = textFieldText
}
}
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Change connection settings")
Keys.onTabPressed: lastItemTabClicked(drawerFocusItem)
clickedFunc: function() {
forceActiveFocus()
if (!portTextField.textField.acceptableInput) {
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
if (usernameTextField.textFieldText && passwordTextField.textFieldText === "") {
passwordTextField.errorText = qsTr("Password cannot be empty")
return
} else if (usernameTextField.textFieldText === "" && passwordTextField.textFieldText) {
usernameTextField.errorText = qsTr("Username cannot be empty")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
tempPort = portTextField.textFieldText
tempUsername = usernameTextField.textFieldText
tempPassword = passwordTextField.textFieldText
changeSettingsDrawer.close()
}
}
}
}
BasicButtonType {
id: changeSettingsButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Change connection settings")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()
changeSettingsDrawer.open()
}
}
}
}
}
}
}

View File

@@ -18,6 +18,8 @@ import "../Components"
PageType {
id: root
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
defaultActiveFocusItem: focusItem
Item {
@@ -103,6 +105,7 @@ PageType {
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(protocolPage);
}
@@ -124,7 +127,7 @@ PageType {
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: root.isClearCacheVisible
KeyNavigation.tab: removeButton
text: qsTr("Clear %1 profile").arg(ContainersModel.getProcessedContainerName())
@@ -167,7 +170,7 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: root.isClearCacheVisible
}
LabelWithButtonType {

View File

@@ -261,6 +261,11 @@ PageType {
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
if (!port.textField.acceptableInput) {
port.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textFieldText, transportProtoSelector.currentIndex)
}