Implement new classes

This commit is contained in:
Nova 2025-10-30 20:56:44 +03:30 committed by justiamtgm
parent 00155b1c9d
commit c8ac3e0096
38 changed files with 296 additions and 2597 deletions

View File

@ -256,6 +256,7 @@ set(PROJECT_SOURCES
src/configs/outbounds/anyTLS.cpp
src/configs/outbounds/http.cpp
src/configs/outbounds/hysteria.cpp
src/configs/outbounds/hysteria2.cpp
src/configs/outbounds/shadowsocks.cpp
src/configs/outbounds/socks.cpp
src/configs/outbounds/ssh.cpp
@ -266,30 +267,8 @@ set(PROJECT_SOURCES
src/configs/outbounds/vmess.cpp
src/configs/outbounds/wireguard.cpp
include/configs/generate.h
src/configs/generate.cpp
include/configs/common/utils.h
src/configs/common/utils.cpp
include/configs/outbounds/custom.h
include/configs/outbounds/chain.h
include/configs/outbounds/extracore.h
include/ui/profile/edit_socks.h
src/ui/profile/edit_socks.cpp
include/ui/profile/edit_socks.ui
include/ui/profile/edit_http.h
src/ui/profile/edit_http.cpp
include/ui/profile/edit_http.ui
include/ui/profile/edit_trojan.h
src/ui/profile/edit_trojan.cpp
include/ui/profile/edit_trojan.ui
include/ui/profile/edit_hysteria.h
src/ui/profile/edit_hysteria.cpp
include/ui/profile/edit_hysteria.ui
include/ui/profile/edit_tuic.h
src/ui/profile/edit_tuic.cpp
include/ui/profile/edit_tuic.ui
include/ui/profile/edit_advanced.h
src/ui/profile/edit_advanced.cpp
include/ui/profile/edit_advanced.ui
)
if (NOT APPLE AND Qt6_VERSION VERSION_GREATER_EQUAL 6.9.0)

View File

@ -1,7 +1,9 @@
#pragma once
#include <QJsonObject>
#include "include/global/Configs.hpp"
#include <QString>
#include "generate.h"
#include "include/global/ConfigItem.hpp"
namespace Configs
{
@ -13,13 +15,9 @@ namespace Configs
class baseConfig : public JsonStore
{
public:
virtual bool ParseFromLink(const QString& link) {
return false;
}
virtual bool ParseFromLink(const QString& link);
virtual bool ParseFromJson(const QJsonObject& object) {
return false;
}
virtual bool ParseFromJson(const QJsonObject& object);
virtual QString ExportToLink() {
return {};
@ -29,8 +27,24 @@ namespace Configs
return {};
}
virtual BuildResult Build() {
return {{}, "base class function called!"};
}
virtual BuildResult Build();
};
class outboundMeta
{
public:
virtual ~outboundMeta() = default;
void ResolveDomainToIP(const std::function<void()> &onFinished);
virtual QString DisplayAddress();
virtual QString DisplayName();
virtual QString DisplayType() { return {}; };
virtual QString DisplayTypeAndName();
virtual bool IsEndpoint() { return false; };
};
}

View File

@ -1,10 +1,5 @@
#pragma once
#include <QHostInfo>
#include <utility>
#include "DialFields.h"
#include "multiplex.h"
#include "TLS.h"
#include "transport.h"
#include "include/configs/baseConfig.h"
namespace Configs
@ -26,85 +21,6 @@ namespace Configs
_add(new configItem("dial_fields", dynamic_cast<JsonStore *>(dialFields.get()), jsonStore));
}
void ResolveDomainToIP(const std::function<void()> &onFinished) {
bool noResolve = false;
auto serverAddr = GetAddress();
if (IsIpAddress(serverAddr) || serverAddr.isEmpty()) noResolve = true;
if (noResolve) {
onFinished();
return;
}
QHostInfo::lookupHost(serverAddr, QApplication::instance(), [=, this](const QHostInfo &host) {
auto addrs = host.addresses();
if (!addrs.isEmpty()) SetAddress(addrs.first().toString());
onFinished();
});
}
virtual void SetAddress(QString newAddr) {
server = std::move(newAddr);
}
virtual QString GetAddress()
{
return server;
}
virtual void SetPort(int newPort) {
server_port = newPort;
}
virtual QString GetPort() {
return QString::number(server_port);
}
virtual QString DisplayAddress()
{
return ::DisplayAddress(server, server_port);
}
virtual QString DisplayName()
{
if (name.isEmpty()) {
return DisplayAddress();
}
return name;
}
virtual QString DisplayType() { return {}; };
QString DisplayTypeAndName()
{
return QString("[%1] %2").arg(DisplayType(), DisplayName());
}
virtual bool HasMux() { return false; }
virtual bool HasTransport() { return false; }
virtual bool HasTLS() { return false; }
virtual bool MustTLS() { return false; }
virtual std::shared_ptr<TLS> GetTLS() { return std::make_shared<TLS>(); }
virtual std::shared_ptr<Transport> GetTransport() { return std::make_shared<Transport>(); }
virtual std::shared_ptr<Multiplex> GetMux() { return std::make_shared<Multiplex>(); }
virtual bool IsEndpoint() { return false; };
QString ExportJsonLink() {
auto json = ExportToJson();
QUrl url;
url.setScheme("json");
url.setHost("throne");
url.setFragment(QJsonObject2QString(json, true)
.toUtf8()
.toBase64(QByteArray::Base64UrlEncoding));
return url.toString();
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;

View File

@ -51,21 +51,6 @@ namespace Configs
_add(new configItem("brutal", dynamic_cast<JsonStore *>(brutal.get()), jsonStore));
}
int getMuxState() {
if (enabled) return 1;
if (!unspecified) return 2;
return 0;
}
void saveMuxState(int state) {
if (state == 1) {
enabled = true;
return;
}
enabled = false;
if (state == 0) unspecified = true;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;

View File

@ -37,10 +37,6 @@ namespace Configs
_add(new configItem("service_name", &service_name, string));
}
QString getHeadersString();
static QStringList getHeaderPairs(QString rawHeader);
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;

View File

@ -7,8 +7,4 @@ namespace Configs
void mergeUrlQuery(QUrlQuery& baseQuery, const QString& strQuery);
void mergeJsonObjects(QJsonObject& baseObject, const QJsonObject& obj);
QStringList jsonObjectToQStringList(const QJsonObject& obj);
QJsonObject qStringListToJsonObject(const QStringList& list);
}

View File

@ -1,165 +1,10 @@
#pragma once
#include <QJsonArray>
#include <QJsonObject>
#include <include/dataStore/ProxyEntity.hpp>
#include <include/stats/traffic/TrafficData.hpp>
namespace Configs
{
enum OSType
{
Unknown = 0,
Linux = 1,
Windows = 2,
Darwin = 3,
};
class ExtraCoreData
{
public:
QString path;
QString args;
QString config;
QString configDir;
bool noLog;
};
class DNSDeps
{
public:
bool needDirectDnsRules = false;
QJsonArray directDomains;
QJsonArray directRuleSets;
QJsonArray directSuffixes;
QJsonArray directKeywords;
QJsonArray directRegexes;
};
class HijackDeps
{
public:
QJsonArray hijackDomains;
QJsonArray hijackDomainSuffix;
QJsonArray hijackDomainRegex;
QJsonArray hijackGeoAssets;
};
class TunDeps
{
public:
QJsonArray directIPSets;
QJsonArray directIPCIDRs;
};
class RoutingDeps
{
public:
int defaultOutboundID;
QList<int> neededOutbounds;
QStringList neededRuleSets;
std::map<int, QString> outboundMap;
};
class BuildPrerequisities
{
public:
std::shared_ptr<ExtraCoreData> extraCoreData = std::make_shared<ExtraCoreData>();
std::shared_ptr<DNSDeps> dnsDeps = std::make_shared<DNSDeps>();
std::shared_ptr<HijackDeps> hijackDeps = std::make_shared<HijackDeps>();
std::shared_ptr<TunDeps> tunDeps = std::make_shared<TunDeps>();
std::shared_ptr<RoutingDeps> routingDeps = std::make_shared<RoutingDeps>();
};
class BuildConfigResult {
public:
struct BuildResult {
QJsonObject object;
QString error;
QJsonObject coreConfig;
std::shared_ptr<ExtraCoreData> extraCoreData;
QList<std::shared_ptr<Stats::TrafficData>> outboundStats;
};
class BuildSingBoxConfigContext
{
public:
bool forTest = false;
bool forExport = false;
bool tunEnabled = false;
bool isResolvedUsed = false;
std::shared_ptr<ProxyEntity> ent = std::make_shared<ProxyEntity>(nullptr, nullptr, nullptr);
std::shared_ptr<BuildPrerequisities> buildPrerequisities = std::make_shared<BuildPrerequisities>();
OSType os;
QString error;
QStringList warnings;
QJsonArray outbounds;
QJsonArray endpoints;
std::shared_ptr<BuildConfigResult> buildConfigResult = std::make_shared<BuildConfigResult>();
};
inline QString get_jsdelivr_link(QString link)
{
if(dataStore->routing->ruleset_mirror == Mirrors::GITHUB)
return link;
if(auto url = QUrl(link); url.isValid() && url.host() == "raw.githubusercontent.com")
{
QStringList list = url.path().split('/');
QString result;
switch(dataStore->routing->ruleset_mirror) {
case Mirrors::GCORE: result = "https://gcore.jsdelivr.net/gh"; break;
case Mirrors::QUANTIL: result = "https://quantil.jsdelivr.net/gh"; break;
case Mirrors::FASTLY: result = "https://fastly.jsdelivr.net/gh"; break;
case Mirrors::CDN: result = "https://cdn.jsdelivr.net/gh"; break;
default: result = "https://testingcf.jsdelivr.net/gh";
}
int index = 0;
foreach(QString item, list)
{
if(!item.isEmpty())
{
if(index == 2)
result += "@" + item;
else
result += "/" + item;
index++;
}
}
return result;
}
return link;
}
void CalculatePrerequisities(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildLogSections(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildDNSSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx, bool useDnsObj = true);
void buildNTPSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildCertificateSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildInboundSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildOutboundsSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildRouteSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
void buildExperimentalSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
std::shared_ptr<BuildConfigResult> BuildSingBoxConfig(const std::shared_ptr<ProxyEntity> &ent);
class BuildTestConfigResult {
public:
QString error;
QMap<int, QString> fullConfigs;
QMap<QString, int> tag2entID;
QJsonObject coreConfig;
QStringList outboundTags;
};
bool IsValid(const std::shared_ptr<ProxyEntity> &ent);
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity> > &profiles);
}

View File

@ -22,18 +22,6 @@ namespace Configs
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool MustTLS() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -41,6 +29,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -23,14 +23,6 @@ namespace Configs
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -38,6 +30,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -48,18 +48,6 @@ namespace Configs
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool MustTLS() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -67,6 +55,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -27,14 +27,6 @@ namespace Configs
_add(new configItem("multiplex", dynamic_cast<JsonStore *>(multiplex.get()), jsonStore));
}
bool HasMux() override {
return true;
}
std::shared_ptr<Multiplex> GetMux() override {
return multiplex;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -42,6 +34,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -27,6 +27,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -34,6 +34,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -1,11 +1,14 @@
#pragma once
#include "include/configs/baseConfig.h"
#include "include/configs/common/Outbound.h"
namespace Configs
{
class tailscale : public outbound
class tailscale : public baseConfig, public outboundMeta
{
public:
std::shared_ptr<OutboundCommons> commons = std::make_shared<OutboundCommons>();
QString state_directory = "$HOME/.tailscale";
QString auth_key;
QString control_url = "https://controlplane.tailscale.com";
@ -18,8 +21,9 @@ namespace Configs
bool advertise_exit_node = false;
bool globalDNS = false;
tailscale() : outbound()
tailscale()
{
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
_add(new configItem("state_directory", &state_directory, itemType::string));
_add(new configItem("auth_key", &auth_key, itemType::string));
_add(new configItem("control_url", &control_url, itemType::string));
@ -40,10 +44,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
void SetAddress(QString newAddr) override;
QString GetAddress() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -22,30 +22,6 @@ namespace Configs
_add(new configItem("transport", dynamic_cast<JsonStore *>(transport.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool HasMux() override {
return true;
}
bool HasTransport() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
std::shared_ptr<Multiplex> GetMux() override {
return multiplex;
}
std::shared_ptr<Transport> GetTransport() override {
return transport;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -53,6 +29,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -32,18 +32,6 @@ namespace Configs
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool MustTLS() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -51,6 +39,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -28,30 +28,6 @@ namespace Configs
_add(new configItem("transport", dynamic_cast<JsonStore *>(transport.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool HasMux() override {
return true;
}
bool HasTransport() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
std::shared_ptr<Multiplex> GetMux() override {
return multiplex;
}
std::shared_ptr<Transport> GetTransport() override {
return transport;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -59,6 +35,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -36,30 +36,6 @@ namespace Configs
_add(new configItem("multiplex", dynamic_cast<JsonStore *>(multiplex.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool HasMux() override {
return true;
}
bool HasTransport() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
std::shared_ptr<Multiplex> GetMux() override {
return multiplex;
}
std::shared_ptr<Transport> GetTransport() override {
return transport;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
@ -67,6 +43,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "include/configs/baseConfig.h"
#include "include/configs/common/Outbound.h"
namespace Configs
@ -31,9 +32,10 @@ namespace Configs
BuildResult Build() override;
};
class wireguard : public outbound
class wireguard : public baseConfig, public outboundMeta
{
public:
std::shared_ptr<OutboundCommons> commons = std::make_shared<OutboundCommons>();
QString private_key;
std::shared_ptr<Peer> peer = std::make_shared<Peer>();
QStringList address;
@ -54,8 +56,10 @@ namespace Configs
int underload_packet_magic_header = 0;
int transport_packet_magic_header = 0;
wireguard() : outbound()
wireguard()
{
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
_add(new configItem("private_key", &private_key, itemType::string));
_add(new configItem("peer", dynamic_cast<JsonStore *>(peer.get()), jsonStore));
_add(new configItem("address", &address, itemType::stringList));
@ -83,12 +87,11 @@ namespace Configs
QJsonObject ExportToJson() override;
BuildResult Build() override;
void SetPort(int newPort) override;
QString GetPort();
void SetAddress(QString newAddr) override;
QString GetAddress() override;
// outboundMeta overrides
QString DisplayAddress() override;
QString DisplayName() override;
QString DisplayType() override;
QString DisplayTypeAndName() override;
bool IsEndpoint() override;
};
}

View File

@ -18,7 +18,7 @@ namespace Configs {
}
bool DialFields::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object == nullptr) return false;
if (object.contains("reuse_addr")) reuse_addr = object["reuse_addr"].toBool();
if (object.contains("connect_timeout")) connect_timeout = object["connect_timeout"].toString();

View File

@ -1,51 +1,11 @@
#include "include/configs/common/Outbound.h"
#include "include/configs/common/utils.h"
namespace Configs {
bool outbound::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
if (url.hasFragment()) name = url.fragment(QUrl::FullyDecoded);
server = url.host();
server_port = url.port();
dialFields->ParseFromLink(link);
return true;
}
bool outbound::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("tag")) name = object["tag"].toString();
if (object.contains("server")) server = object["server"].toString();
if (object.contains("server_port")) server_port = object["server_port"].toInt();
dialFields->ParseFromJson(object);
return true;
}
QString outbound::ExportToLink()
{
QUrlQuery query;
mergeUrlQuery(query, dialFields->ExportToLink());
return query.toString();
}
QJsonObject outbound::ExportToJson()
{
QJsonObject object;
if (!name.isEmpty()) object["tag"] = name;
if (!server.isEmpty()) object["server"] = server;
if (server_port > 0) object["server_port"] = server_port;
auto dialFieldsObj = dialFields->ExportToJson();
mergeJsonObjects(object, dialFieldsObj);
return object;
}
BuildResult outbound::Build()
{
QJsonObject object;
if (!server.isEmpty()) object["server"] = server;
if (server_port > 0) object["server_port"] = server_port;
mergeJsonObjects(object, dialFields->Build().object);
return {object, ""};
}
bool OutboundCommons::ParseFromLink(const QString& link) { return false; }
bool OutboundCommons::ParseFromJson(const QJsonObject& object) { return false; }
QString OutboundCommons::ExportToLink() { return {}; }
QJsonObject OutboundCommons::ExportToJson() { return {}; }
BuildResult OutboundCommons::Build() { return {}; }
}

View File

@ -1,312 +1,29 @@
#include "include/configs/common/TLS.h"
#include <QJsonArray>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool uTLS::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool uTLS::ParseFromLink(const QString& link) { return false; }
bool uTLS::ParseFromJson(const QJsonObject& object) { return false; }
QString uTLS::ExportToLink() { return {}; }
QJsonObject uTLS::ExportToJson() { return {}; }
BuildResult uTLS::Build() { return {}; }
// handle the common format
if (query.hasQueryItem("fp")) fingerPrint = query.queryItemValue("fp");
if (!fingerPrint.isEmpty()) enabled = true;
return true;
}
bool uTLS::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
if (object.contains("fingerprint")) fingerPrint = object["fingerprint"].toString();
return true;
}
QString uTLS::ExportToLink()
{
QUrlQuery query;
if (!enabled) return "";
if (!fingerPrint.isEmpty()) query.addQueryItem("fp", fingerPrint);
return query.toString();
}
QJsonObject uTLS::ExportToJson()
{
QJsonObject object;
if (!enabled) return object;
object["enabled"] = enabled;
if (!fingerPrint.isEmpty()) object["fingerprint"] = fingerPrint;
return object;
}
BuildResult uTLS::Build()
{
auto obj = ExportToJson();
if ((obj.isEmpty() || obj["enabled"].toBool() == false) && !dataStore->utlsFingerprint.isEmpty()) {
obj["enabled"] = true;
obj["fingerprint"] = dataStore->utlsFingerprint;
}
return {ExportToJson(), ""};
}
bool ECH::ParseFromLink(const QString& link) { return false; }
bool ECH::ParseFromJson(const QJsonObject& object) { return false; }
QString ECH::ExportToLink() { return {}; }
QJsonObject ECH::ExportToJson() { return {}; }
BuildResult ECH::Build() { return {}; }
bool ECH::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool Reality::ParseFromLink(const QString& link) { return false; }
bool Reality::ParseFromJson(const QJsonObject& object) { return false; }
QString Reality::ExportToLink() { return {}; }
QJsonObject Reality::ExportToJson() { return {}; }
BuildResult Reality::Build() { return {}; }
if (query.hasQueryItem("ech_enabled")) enabled = query.queryItemValue("ech_enabled") == "true";
if (query.hasQueryItem("ech_config")) config = query.queryItemValue("ech_config").split(",");
if (query.hasQueryItem("ech_config_path")) config_path = query.queryItemValue("ech_config_path");
return true;
}
bool ECH::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
if (object.contains("config")) {
config = QJsonArray2QListString(object["config"].toArray());
}
if (object.contains("config_path")) config_path = object["config_path"].toString();
return true;
}
QString ECH::ExportToLink()
{
QUrlQuery query;
if (!enabled) return "";
query.addQueryItem("ech_enabled", "true");
if (!config.isEmpty()) query.addQueryItem("ech_config", config.join(","));
if (!config_path.isEmpty()) query.addQueryItem("ech_config_path", config_path);
return query.toString();
}
QJsonObject ECH::ExportToJson()
{
QJsonObject object;
if (!enabled) return object;
object["enabled"] = enabled;
if (!config.isEmpty()) {
object["config"] = QListStr2QJsonArray(config);
}
if (!config_path.isEmpty()) object["config_path"] = config_path;
return object;
}
BuildResult ECH::Build()
{
return {ExportToJson(), ""};
}
bool Reality::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
// handle the common format
if (query.hasQueryItem("pbk"))
{
enabled = true;
public_key = query.queryItemValue("pbk");
short_id = query.queryItemValue("sid");
}
return true;
}
bool Reality::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
if (object.contains("public_key")) public_key = object["public_key"].toString();
if (object.contains("short_id")) short_id = object["short_id"].toString();
return true;
}
QString Reality::ExportToLink()
{
QUrlQuery query;
if (!enabled) return "";
query.addQueryItem("pbk", public_key);
query.addQueryItem("sid", short_id);
return query.toString();
}
QJsonObject Reality::ExportToJson()
{
QJsonObject object;
if (!enabled) return object;
object["enabled"] = enabled;
if (!public_key.isEmpty()) object["public_key"] = public_key;
if (!short_id.isEmpty()) object["short_id"] = short_id;
return object;
}
BuildResult Reality::Build()
{
QJsonObject object;
if (!public_key.isEmpty() || enabled) {
object["enabled"] = true;
object["public_key"] = public_key;
if (!short_id.isEmpty()) object["short_id"] = short_id;
}
return {object, ""};
}
bool TLS::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
if (query.hasQueryItem("security")) enabled = query.queryItemValue("security")
.replace("reality", "tls")
.replace("none", "")
.replace("0", "")
.replace("false", "tls")
.replace("1", "tls")
.replace("true", "tls") == "tls";
if (query.hasQueryItem("disable_sni")) disable_sni = query.queryItemValue("disable_sni").replace("1", "true") == "true";
if (query.hasQueryItem("sni")) server_name = query.queryItemValue("sni");
if (query.hasQueryItem("peer")) server_name = query.queryItemValue("peer");
if (query.hasQueryItem("allowInsecure")) insecure = query.queryItemValue("allowInsecure").replace("1", "true") == "true";
if (query.hasQueryItem("allow_insecure")) insecure = query.queryItemValue("allow_insecure").replace("1", "true") == "true";
if (query.hasQueryItem("insecure")) insecure = query.queryItemValue("insecure").replace("1", "true") == "true";
if (query.hasQueryItem("alpn")) alpn = query.queryItemValue("alpn").split(",");
if (query.hasQueryItem("tls_min_version")) min_version = query.queryItemValue("tls_min_version");
if (query.hasQueryItem("tls_max_version")) max_version = query.queryItemValue("tls_max_version");
if (query.hasQueryItem("tls_cipher_suites")) cipher_suites = query.queryItemValue("tls_cipher_suites").split(",");
if (query.hasQueryItem("tls_curve_preferences")) curve_preferences = query.queryItemValue("tls_curve_preferences").split(",");
if (query.hasQueryItem("tls_certificate")) certificate = query.queryItemValue("tls_certificate").split(",");
if (query.hasQueryItem("tls_certificate_path")) certificate_path = query.queryItemValue("tls_certificate_path");
if (query.hasQueryItem("tls_certificate_public_key_sha256")) certificate_public_key_sha256 = query.queryItemValue("tls_certificate_public_key_sha256").split(",");
if (query.hasQueryItem("tls_client_certificate")) client_certificate = query.queryItemValue("tls_client_certificate").split(",");
if (query.hasQueryItem("tls_client_certificate_path")) client_certificate_path = query.queryItemValue("tls_client_certificate_path");
if (query.hasQueryItem("tls_client_key")) client_key = query.queryItemValue("tls_client_key").split(",");
if (query.hasQueryItem("tls_client_key_path")) client_key_path = query.queryItemValue("tls_client_key_path");
if (query.hasQueryItem("tls_fragment")) fragment = query.queryItemValue("tls_fragment") == "true";
if (query.hasQueryItem("tls_fragment_fallback_delay")) fragment_fallback_delay = query.queryItemValue("tls_fragment_fallback_delay");
if (query.hasQueryItem("tls_record_fragment")) record_fragment = query.queryItemValue("tls_record_fragment") == "true";
if (!server_name.isEmpty()) enabled = true;
ech->ParseFromLink(link);
utls->ParseFromLink(link);
reality->ParseFromLink(link);
return true;
}
bool TLS::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
if (object.contains("disable_sni")) disable_sni = object["disable_sni"].toBool();
if (object.contains("server_name")) server_name = object["server_name"].toString();
if (object.contains("insecure")) insecure = object["insecure"].toBool();
if (object.contains("alpn")) {
alpn = QJsonArray2QListString(object["alpn"].toArray());
}
if (object.contains("min_version")) min_version = object["min_version"].toString();
if (object.contains("max_version")) max_version = object["max_version"].toString();
if (object.contains("cipher_suites")) {
cipher_suites = QJsonArray2QListString(object["cipher_suites"].toArray());
}
if (object.contains("curve_preferences")) {
curve_preferences = QJsonArray2QListString(object["curve_preferences"].toArray());
}
if (object.contains("certificate")) {
if (object["certificate"].isString()) {
certificate = object["certificate"].toString().split("\n", Qt::SkipEmptyParts);
} else {
certificate = QJsonArray2QListString(object["certificate"].toArray());
}
}
if (object.contains("certificate_path")) certificate_path = object["certificate_path"].toString();
if (object.contains("certificate_public_key_sha256")) {
certificate_public_key_sha256 = QJsonArray2QListString(object["certificate_public_key_sha256"].toArray());
}
if (object.contains("client_certificate")) {
client_certificate = QJsonArray2QListString(object["client_certificate"].toArray());
}
if (object.contains("client_certificate_path")) client_certificate_path = object["client_certificate_path"].toString();
if (object.contains("client_key")) {
client_key = QJsonArray2QListString(object["client_key"].toArray());
}
if (object.contains("client_key_path")) client_key_path = object["client_key_path"].toString();
if (object.contains("fragment")) fragment = object["fragment"].toBool();
if (object.contains("fragment_fallback_delay")) fragment_fallback_delay = object["fragment_fallback_delay"].toString();
if (object.contains("record_fragment")) record_fragment = object["record_fragment"].toBool();
if (object.contains("ech")) ech->ParseFromJson(object["ech"].toObject());
if (object.contains("utls")) utls->ParseFromJson(object["utls"].toObject());
if (object.contains("reality")) reality->ParseFromJson(object["reality"].toObject());
return true;
}
QString TLS::ExportToLink()
{
QUrlQuery query;
if (!enabled) return "";
query.addQueryItem("security", "tls");
if (disable_sni) query.addQueryItem("disable_sni", "true");
if (!server_name.isEmpty()) query.addQueryItem("sni", server_name);
if (insecure) query.addQueryItem("allowInsecure", "true");
if (!alpn.isEmpty()) query.addQueryItem("alpn", alpn.join(","));
if (!min_version.isEmpty()) query.addQueryItem("tls_min_version", min_version);
if (!max_version.isEmpty()) query.addQueryItem("tls_max_version", max_version);
if (!cipher_suites.isEmpty()) query.addQueryItem("tls_cipher_suites", cipher_suites.join(","));
if (!curve_preferences.isEmpty()) query.addQueryItem("tls_curve_preferences", curve_preferences.join(","));
if (!certificate.isEmpty()) query.addQueryItem("tls_certificate", certificate.join(","));
if (!certificate_path.isEmpty()) query.addQueryItem("tls_certificate_path", certificate_path);
if (!certificate_public_key_sha256.isEmpty()) query.addQueryItem("tls_certificate_public_key_sha256", certificate_public_key_sha256.join(","));
if (!client_certificate.isEmpty()) query.addQueryItem("tls_client_certificate", client_certificate.join(","));
if (!client_certificate_path.isEmpty()) query.addQueryItem("tls_client_certificate_path", client_certificate_path);
if (!client_key.isEmpty()) query.addQueryItem("tls_client_key", client_key.join(","));
if (!client_key_path.isEmpty()) query.addQueryItem("tls_client_key_path", client_key_path);
if (fragment) query.addQueryItem("tls_fragment", "true");
if (!fragment_fallback_delay.isEmpty()) query.addQueryItem("tls_fragment_fallback_delay", fragment_fallback_delay);
if (record_fragment) query.addQueryItem("tls_record_fragment", "true");
mergeUrlQuery(query, ech->ExportToLink());
mergeUrlQuery(query, utls->ExportToLink());
mergeUrlQuery(query, reality->ExportToLink());
return query.toString();
}
QJsonObject TLS::ExportToJson()
{
QJsonObject object;
if (!enabled) return object;
object["enabled"] = enabled;
if (disable_sni) object["disable_sni"] = disable_sni;
if (!server_name.isEmpty()) object["server_name"] = server_name;
if (insecure) object["insecure"] = insecure;
if (!alpn.isEmpty()) {
object["alpn"] = QListStr2QJsonArray(alpn);
}
if (!min_version.isEmpty()) object["min_version"] = min_version;
if (!max_version.isEmpty()) object["max_version"] = max_version;
if (!cipher_suites.isEmpty()) {
object["cipher_suites"] = QListStr2QJsonArray(cipher_suites);
}
if (!curve_preferences.isEmpty()) {
object["curve_preferences"] = QListStr2QJsonArray(curve_preferences);
}
if (!certificate.isEmpty()) {
object["certificate"] = QListStr2QJsonArray(certificate);
}
if (!certificate_path.isEmpty()) object["certificate_path"] = certificate_path;
if (!certificate_public_key_sha256.isEmpty()) {
object["certificate_public_key_sha256"] = QListStr2QJsonArray(certificate_public_key_sha256);
}
if (!client_certificate.isEmpty()) object["client_certificate"] = QListStr2QJsonArray(client_certificate);
if (!client_certificate_path.isEmpty()) object["client_certificate_path"] = client_certificate_path;
if (!client_key.isEmpty()) {
object["client_key"] = QListStr2QJsonArray(client_key);
}
if (!client_key_path.isEmpty()) object["client_key_path"] = client_key_path;
if (fragment) object["fragment"] = fragment;
if (!fragment_fallback_delay.isEmpty()) object["fragment_fallback_delay"] = fragment_fallback_delay;
if (record_fragment) object["record_fragment"] = record_fragment;
if (ech->enabled) object["ech"] = ech->ExportToJson();
if (utls->enabled) object["utls"] = utls->ExportToJson();
if (reality->enabled) object["reality"] = reality->ExportToJson();
return object;
}
BuildResult TLS::Build()
{
auto obj = ExportToJson();
if (!obj.isEmpty() && obj["enabled"].toBool()) {
if (dataStore->skip_cert) obj["insecure"] = true;
}
return {ExportToJson(), ""};
}
bool TLS::ParseFromLink(const QString& link) { return false; }
bool TLS::ParseFromJson(const QJsonObject& object) { return false; }
QString TLS::ExportToLink() { return {}; }
QJsonObject TLS::ExportToJson() { return {}; }
BuildResult TLS::Build() { return {}; }
}

View File

@ -1,4 +1,7 @@
#include "include/configs/common/multiplex.h"
#include <QUrlQuery>
#include "include/configs/common/utils.h"
namespace Configs {
@ -15,7 +18,7 @@ namespace Configs {
}
bool TcpBrutal::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object == nullptr) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
if (object.contains("up_mbps")) up_mbps = object["up_mbps"].toInt();
if (object.contains("down_mbps")) down_mbps = object["down_mbps"].toInt();
@ -51,8 +54,7 @@ namespace Configs {
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
if (query.hasQueryItem("mux")) enabled = query.queryItemValue("mux") == "true";
else unspecified = true;
if (query.hasQueryItem("mux_protocol")) protocol = query.queryItemValue("mux_protocol");
protocol = query.hasQueryItem("mux_protocol") ? query.queryItemValue("mux_protocol") : "smux";
if (query.hasQueryItem("mux_max_connections")) max_connections = query.queryItemValue("mux_max_connections").toInt();
if (query.hasQueryItem("mux_min_streams")) min_streams = query.queryItemValue("mux_min_streams").toInt();
if (query.hasQueryItem("mux_max_streams")) max_streams = query.queryItemValue("mux_max_streams").toInt();
@ -62,13 +64,8 @@ namespace Configs {
}
bool Multiplex::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty())
{
unspecified = true;
return false;
}
if (object == nullptr) return false;
if (object.contains("enabled")) enabled = object["enabled"].toBool();
else unspecified = true;
if (object.contains("protocol")) protocol = object["protocol"].toString();
if (object.contains("max_connections")) max_connections = object["max_connections"].toInt();
if (object.contains("min_streams")) min_streams = object["min_streams"].toInt();
@ -105,13 +102,7 @@ namespace Configs {
}
BuildResult Multiplex::Build()
{
auto obj = ExportToJson();
if (unspecified && dataStore->mux_default_on) obj["enabled"] = true;
if (!obj["enabled"].toBool()) return {{}, ""};
if (protocol.isEmpty()) obj["protocol"] = dataStore->mux_protocol;
if (max_streams == 0 && max_connections == 0 && min_streams == 0) obj["max_streams"] = dataStore->mux_concurrency;
if (dataStore->mux_padding) obj["padding"] = true;
return {obj, ""};
return {ExportToJson(), ""};
}
}

View File

@ -1,150 +1,11 @@
#include "include/configs/common/transport.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
QString Transport::getHeadersString() {
QString result;
for (int i=0;i<headers.length();i+=2) {
result += headers[i]+"=";
result += "\""+headers[i+1]+"\" ";
}
return result;
}
QStringList Transport::getHeaderPairs(QString rawHeader) {
bool inQuote = false;
QString curr;
QStringList list;
for (const auto &ch: rawHeader) {
if (inQuote) {
if (ch == '"') {
inQuote = false;
list << curr;
curr = "";
continue;
} else {
curr += ch;
continue;
}
}
if (ch == '"') {
inQuote = true;
continue;
}
if (ch == ' ') {
if (!curr.isEmpty()) {
list << curr;
curr = "";
}
continue;
}
if (ch == '=') {
if (!curr.isEmpty()) {
list << curr;
curr = "";
}
continue;
}
curr+=ch;
}
if (!curr.isEmpty()) list<<curr;
return list;
}
bool Transport::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
if (query.hasQueryItem("type"))
{
type = query.queryItemValue("type");
if ((type == "tcp" && query.queryItemValue("headerType") == "http") || type == "h2") {
type = "http";
method = "GET";
}
}
if (query.hasQueryItem("host")) host = query.queryItemValue("host");
if (query.hasQueryItem("path")) path = query.queryItemValue("path");
if (query.hasQueryItem("method")) method = query.queryItemValue("method");
if (query.hasQueryItem("headers")) headers = query.queryItemValue("headers").split(",");
if (query.hasQueryItem("idle_timeout")) idle_timeout = query.queryItemValue("idle_timeout");
if (query.hasQueryItem("ping_timeout")) ping_timeout = query.queryItemValue("ping_timeout");
if (query.hasQueryItem("max_early_data")) max_early_data = query.queryItemValue("max_early_data").toInt();
if (query.hasQueryItem("early_data_header_name")) early_data_header_name = query.queryItemValue("early_data_header_name");
if (query.hasQueryItem("serviceName")) service_name = query.queryItemValue("serviceName");
return true;
}
bool Transport::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("type")) type = object["type"].toString();
if (object.contains("host")) host = object["host"].toString();
if (object.contains("path")) path = object["path"].toString();
if (object.contains("method")) method = object["method"].toString();
if (object.contains("headers") && object["headers"].isObject()) {
headers = jsonObjectToQStringList(object["headers"].toObject());
}
if (object.contains("idle_timeout")) idle_timeout = object["idle_timeout"].toString();
if (object.contains("ping_timeout")) ping_timeout = object["ping_timeout"].toString();
if (object.contains("max_early_data")) max_early_data = object["max_early_data"].toInt();
if (object.contains("early_data_header_name")) early_data_header_name = object["early_data_header_name"].toString();
if (object.contains("service_name")) service_name = object["service_name"].toString();
return true;
}
QString Transport::ExportToLink()
{
QUrlQuery query;
if (type.isEmpty() || type == "tcp") return "";
if (!type.isEmpty()) query.addQueryItem("type", type);
if (!host.isEmpty()) query.addQueryItem("host", host);
if (!path.isEmpty()) query.addQueryItem("path", path);
if (!method.isEmpty()) query.addQueryItem("method", method);
if (!headers.isEmpty()) query.addQueryItem("headers", headers.join(","));
if (!idle_timeout.isEmpty()) query.addQueryItem("idle_timeout", idle_timeout);
if (!ping_timeout.isEmpty()) query.addQueryItem("ping_timeout", ping_timeout);
if (max_early_data > 0) query.addQueryItem("max_early_data", QString::number(max_early_data));
if (!early_data_header_name.isEmpty()) query.addQueryItem("early_data_header_name", early_data_header_name);
if (!service_name.isEmpty()) query.addQueryItem("serviceName", service_name);
return query.toString();
}
QJsonObject Transport::ExportToJson()
{
QJsonObject object;
if (type.isEmpty() || type == "tcp") return object;
if (!type.isEmpty()) object["type"] = type;
if (!path.isEmpty()) object["path"] = path;
if (!method.isEmpty()) object["method"] = method;
if (!headers.isEmpty()) {
object["headers"] = qStringListToJsonObject(headers);
}
if (!host.isEmpty()) {
if (type == "http" || type == "httpupgrade") object["host"] = host;
if (type == "ws") {
auto headersObj = object["headers"].isObject() ? object["headers"].toObject() : QJsonObject();
headersObj["host"] = host;
object["headers"] = headersObj;
}
}
if (!idle_timeout.isEmpty()) object["idle_timeout"] = idle_timeout;
if (!ping_timeout.isEmpty()) object["ping_timeout"] = ping_timeout;
if (max_early_data > 0) object["max_early_data"] = max_early_data;
if (!early_data_header_name.isEmpty()) object["early_data_header_name"] = early_data_header_name;
if (!service_name.isEmpty()) object["service_name"] = service_name;
return object;
}
BuildResult Transport::Build()
{
return {ExportToJson(), ""};
}
bool Transport::ParseFromLink(const QString& link) { return false; }
bool Transport::ParseFromJson(const QJsonObject& object) { return false; }
QString Transport::ExportToLink() { return {}; }
QJsonObject Transport::ExportToJson() { return {}; }
BuildResult Transport::Build() { return {}; }
}

View File

@ -18,30 +18,4 @@ namespace Configs
baseObject[key] = obj[key];
}
}
QStringList jsonObjectToQStringList(const QJsonObject& obj)
{
auto result = QStringList();
for (const auto& key : obj.keys())
{
result << key << obj[key].toString();
}
return result;
}
QJsonObject qStringListToJsonObject(const QStringList& list)
{
auto result = QJsonObject();
if (list.count() %2 != 0)
{
qDebug() << "QStringList of odd length in qStringListToJsonObject:" << list;
return result;
}
for (int i=0;i<list.size();i+=2)
{
result[list[i]] = list[i+1];
}
return result;
}
}

View File

@ -1,93 +1,17 @@
#include "include/configs/outbounds/anyTLS.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool anyTLS::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool anyTLS::ParseFromLink(const QString& link) { return false; }
bool anyTLS::ParseFromJson(const QJsonObject& object) { return false; }
QString anyTLS::ExportToLink() { return {}; }
QJsonObject anyTLS::ExportToJson() { return {}; }
BuildResult anyTLS::Build() { return {}; }
outbound::ParseFromLink(link);
password = url.userName();
if (server_port == 0) server_port = 443;
if (query.hasQueryItem("idle_session_check_interval")) idle_session_check_interval = query.queryItemValue("idle_session_check_interval");
if (query.hasQueryItem("idle_session_timeout")) idle_session_timeout = query.queryItemValue("idle_session_timeout");
if (query.hasQueryItem("min_idle_session")) min_idle_session = query.queryItemValue("min_idle_session").toInt();
tls->ParseFromLink(link);
tls->enabled = true; // anyTLS always uses tls
return true;
}
bool anyTLS::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "anytls") return false;
outbound::ParseFromJson(object);
if (object.contains("password")) password = object["password"].toString();
if (object.contains("idle_session_check_interval")) idle_session_check_interval = object["idle_session_check_interval"].toString();
if (object.contains("idle_session_timeout")) idle_session_timeout = object["idle_session_timeout"].toString();
if (object.contains("min_idle_session")) min_idle_session = object["min_idle_session"].toInt();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
return true;
}
QString anyTLS::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("anytls");
url.setUserName(password);
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!idle_session_check_interval.isEmpty()) query.addQueryItem("idle_session_check_interval", idle_session_check_interval);
if (!idle_session_timeout.isEmpty()) query.addQueryItem("idle_session_timeout", idle_session_timeout);
if (min_idle_session > 0) query.addQueryItem("min_idle_session", QString::number(min_idle_session));
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject anyTLS::ExportToJson()
{
QJsonObject object;
object["type"] = "anytls";
mergeJsonObjects(object, outbound::ExportToJson());
if (!password.isEmpty()) object["password"] = password;
if (!idle_session_check_interval.isEmpty()) object["idle_session_check_interval"] = idle_session_check_interval;
if (!idle_session_timeout.isEmpty()) object["idle_session_timeout"] = idle_session_timeout;
if (min_idle_session > 0) object["min_idle_session"] = min_idle_session;
object["tls"] = tls->ExportToJson();
return object;
}
BuildResult anyTLS::Build()
{
QJsonObject object;
object["type"] = "anytls";
mergeJsonObjects(object, outbound::Build().object);
if (!password.isEmpty()) object["password"] = password;
if (!idle_session_check_interval.isEmpty()) object["idle_session_check_interval"] = idle_session_check_interval;
if (!idle_session_timeout.isEmpty()) object["idle_session_timeout"] = idle_session_timeout;
if (min_idle_session > 0) object["min_idle_session"] = min_idle_session;
object["tls"] = tls->Build().object;
return {object, ""};
}
QString anyTLS::DisplayType()
{
return "AnyTLS";
}
QString anyTLS::DisplayAddress() { return {}; }
QString anyTLS::DisplayName() { return {}; }
QString anyTLS::DisplayType() { return {}; }
QString anyTLS::DisplayTypeAndName() { return {}; }
bool anyTLS::IsEndpoint() { return false; }
}

View File

@ -1,87 +1,17 @@
#include "include/configs/outbounds/http.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool http::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool http::ParseFromLink(const QString& link) { return false; }
bool http::ParseFromJson(const QJsonObject& object) { return false; }
QString http::ExportToLink() { return {}; }
QJsonObject http::ExportToJson() { return {}; }
BuildResult http::Build() { return {}; }
outbound::ParseFromLink(link);
username = url.userName();
password = url.password();
if (query.hasQueryItem("path")) path = query.queryItemValue("path");
if (query.hasQueryItem("headers")) headers = query.queryItemValue("headers").split(",");
if (url.scheme() == "https" || query.queryItemValue("security") == "tls")
{
tls->ParseFromLink(link);
tls->enabled = true; // force enable in case scheme is https and no security queryValue is set
}
if (server_port == 0) server_port = 443;
return true;
}
bool http::ParseFromJson(const QJsonObject& object)
{
if (object.empty() || object["type"] != "http") return false;
outbound::ParseFromJson(object);
if (object.contains("username")) username = object["username"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("path")) path = object["path"].toString();
if (object.contains("headers") && object["headers"].isObject()) headers = jsonObjectToQStringList(object["headers"].toObject());
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
return true;
}
QString http::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme(tls->enabled ? "https" : "http");
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!username.isEmpty()) url.setUserName(username);
if (!password.isEmpty()) url.setPassword(password);
if (!path.isEmpty()) query.addQueryItem("path", path);
if (!headers.empty()) query.addQueryItem("headers", headers.join(","));
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject http::ExportToJson()
{
QJsonObject object;
object["type"] = "http";
mergeJsonObjects(object, outbound::ExportToJson());
if (!username.isEmpty()) object["username"] = username;
if (!password.isEmpty()) object["password"] = password;
if (!path.isEmpty()) object["path"] = path;
if (auto headerObj = qStringListToJsonObject(headers); !headerObj.isEmpty()) object["headers"] = headerObj;
if (auto tlsObj = tls->ExportToJson(); !tlsObj.isEmpty()) object["tls"] = tlsObj;
return object;
}
BuildResult http::Build()
{
QJsonObject object;
object["type"] = "http";
mergeJsonObjects(object, outbound::Build().object);
if (!username.isEmpty()) object["username"] = username;
if (!password.isEmpty()) object["password"] = password;
if (!path.isEmpty()) object["path"] = path;
if (auto headerObj = qStringListToJsonObject(headers); !headerObj.isEmpty()) object["headers"] = headerObj;
if (auto tlsObj = tls->Build().object; !tlsObj.isEmpty()) object["tls"] = tlsObj;
return {object, ""};
}
QString http::DisplayType()
{
return "HTTP";
}
QString http::DisplayAddress() { return {}; }
QString http::DisplayName() { return {}; }
QString http::DisplayType() { return {}; }
QString http::DisplayTypeAndName() { return {}; }
bool http::IsEndpoint() { return false; }
}

View File

@ -1,235 +1,17 @@
#include "include/configs/outbounds/hysteria.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <3rdparty/URLParser/url_parser.h>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool hysteria::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid())
{
if(!url.errorString().startsWith("Invalid port"))
return false;
server_port = 0;
server_ports = QString::fromStdString(URLParser::Parse((link.split("?")[0] + "/").toStdString()).port).split(",");
for (auto & serverPort : server_ports) {
serverPort.replace("-", ":");
}
}
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool hysteria::ParseFromLink(const QString& link) { return false; }
bool hysteria::ParseFromJson(const QJsonObject& object) { return false; }
QString hysteria::ExportToLink() { return {}; }
QJsonObject hysteria::ExportToJson() { return {}; }
BuildResult hysteria::Build() { return {}; }
outbound::ParseFromLink(link);
if (url.scheme() == "hysteria") {
protocol_version = "1";
if (query.hasQueryItem("obfsParam")) obfs = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8());
if (query.hasQueryItem("auth")) {
auth = query.queryItemValue("auth");
auth_type = "STRING";
}
if (query.hasQueryItem("recv_window_conn")) recv_window_conn = query.queryItemValue("recv_window_conn").toInt();
if (query.hasQueryItem("recv_window")) recv_window = query.queryItemValue("recv_window").toInt();
if (query.hasQueryItem("disable_mtu_discovery")) disable_mtu_discovery = query.queryItemValue("disable_mtu_discovery") == "true";
} else {
protocol_version = "2";
if (url.password().isEmpty()) {
password = url.userName();
} else {
password = url.userName() + ":" + url.password();
}
if (query.hasQueryItem("obfs-password")) {
obfs = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8());
}
}
if (query.hasQueryItem("upmbps")) up_mbps = query.queryItemValue("upmbps").toInt();
if (query.hasQueryItem("downmbps")) down_mbps = query.queryItemValue("downmbps").toInt();
if (query.hasQueryItem("hop_interval")) hop_interval = query.queryItemValue("hop_interval");
if (query.hasQueryItem("mport")) {
server_ports = query.queryItemValue("mport").split(",");
for (auto & server_port : server_ports) {
server_port.replace("-", ":");
}
}
tls->ParseFromLink(link);
if (server_port == 0 && server_ports.isEmpty()) server_port = 443;
return true;
}
bool hysteria::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object["type"].toString() == "hysteria") {
protocol_version = "1";
} else if (object["type"].toString() == "hysteria2") {
protocol_version = "2";
} else {
return false;
}
outbound::ParseFromJson(object);
if (object.contains("server_ports")) {
server_ports = QJsonArray2QListString(object["server_ports"].toArray());
}
if (object.contains("hop_interval")) hop_interval = object["hop_interval"].toString();
if (object.contains("up_mbps")) up_mbps = object["up_mbps"].toInt();
if (object.contains("down_mbps")) down_mbps = object["down_mbps"].toInt();
if (protocol_version == "1") {
if (object.contains("obfs")) obfs = object["obfs"].toString();
if (object.contains("auth")) {
auth = object["auth"].toString();
auth_type = "BASE64";
}
if (object.contains("auth_str")) {
auth = object["auth_str"].toString();
auth_type = "STRING";
}
if (object.contains("recv_window_conn")) recv_window_conn = object["recv_window_conn"].toInt();
if (object.contains("recv_window")) recv_window = object["recv_window"].toInt();
if (object.contains("disable_mtu_discovery")) disable_mtu_discovery = object["disable_mtu_discovery"].toBool();
} else {
if (object.contains("obfs")) {
auto obfsObj = object["obfs"].toObject();
if (obfsObj.contains("password")) obfs = obfsObj["password"].toString();
}
if (object.contains("obfsPassword")) obfs = object["obfsPassword"].toString();
if (object.contains("password")) password = object["password"].toString();
}
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
return true;
}
QString hysteria::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme(protocol_version == "1" ? "hysteria" : "hysteria2");
url.setHost(server);
url.setPort(0);
if (!server_ports.isEmpty()) {
QStringList portList;
for (const auto& port : server_ports) {
QString modified = port;
modified.replace(":", "-");
portList.append(modified);
}
url.setPort(0);
query.addQueryItem("mport", portList.join(","));
} else {
url.setPort(server_port);
}
if (!name.isEmpty()) url.setFragment(name);
if (protocol_version == "1") {
if (!obfs.isEmpty()) {
query.addQueryItem("obfsParam", QUrl::toPercentEncoding(obfs));
}
if (auth_type == "STRING" && !auth.isEmpty()) query.addQueryItem("auth", auth);
if (recv_window_conn > 0) query.addQueryItem("recv_window_conn", QString::number(recv_window_conn));
if (recv_window > 0) query.addQueryItem("recv_window", QString::number(recv_window));
if (disable_mtu_discovery) query.addQueryItem("disable_mtu_discovery", "true");
} else {
if (password.contains(":")) {
url.setUserName(SubStrBefore(password, ":"));
url.setPassword(SubStrAfter(password, ":"));
} else {
url.setUserName(password);
}
if (!obfs.isEmpty()) {
query.addQueryItem("obfs-password", QUrl::toPercentEncoding(obfs));
}
}
if (up_mbps > 0) query.addQueryItem("upmbps", QString::number(up_mbps));
if (down_mbps > 0) query.addQueryItem("downmbps", QString::number(down_mbps));
if (!hop_interval.isEmpty()) query.addQueryItem("hop_interval", hop_interval);
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
QString result = url.toString();
if (!server_ports.isEmpty()) {
result = result.replace(":0?", ":" + query.queryItemValue("mport") + "?");
}
return result;
}
QJsonObject hysteria::ExportToJson()
{
QJsonObject object;
object["type"] = protocol_version == "1" ? "hysteria" : "hysteria2";
mergeJsonObjects(object, outbound::ExportToJson());
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (protocol_version == "1") {
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) {
if (auth_type == "BASE64") object["auth"] = auth;
if (auth_type == "STRING") object["auth_str"] = auth;
}
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
} else {
if (!obfs.isEmpty()) {
object["obfs"] = QJsonObject{
{"type", "salamander"},
{"password", obfs},
};
}
if (!password.isEmpty()) object["password"] = password;
}
if (tls->enabled) object["tls"] = tls->ExportToJson();
return object;
}
BuildResult hysteria::Build()
{
QJsonObject object;
object["type"] = protocol_version == "1" ? "hysteria" : "hysteria2";
mergeJsonObjects(object, outbound::Build().object);
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (protocol_version == "1") {
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) {
if (auth_type == "BASE64") object["auth"] = auth;
if (auth_type == "STRING") object["auth_str"] = auth;
}
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
} else {
if (!obfs.isEmpty()) {
object["obfs"] = QJsonObject{
{"type", "salamander"},
{"password", obfs},
};
}
if (!password.isEmpty()) object["password"] = password;
}
if (tls->enabled) object["tls"] = tls->Build().object;
return {object, ""};
}
QString hysteria::DisplayType()
{
return "Hysteria";
}
QString hysteria::DisplayAddress() { return {}; }
QString hysteria::DisplayName() { return {}; }
QString hysteria::DisplayType() { return {}; }
QString hysteria::DisplayTypeAndName() { return {}; }
bool hysteria::IsEndpoint() { return false; }
}

View File

@ -0,0 +1,17 @@
#include "include/configs/outbounds/hysteria2.h"
namespace Configs {
bool hysteria2::ParseFromLink(const QString& link) { return false; }
bool hysteria2::ParseFromJson(const QJsonObject& object) { return false; }
QString hysteria2::ExportToLink() { return {}; }
QJsonObject hysteria2::ExportToJson() { return {}; }
BuildResult hysteria2::Build() { return {}; }
QString hysteria2::DisplayAddress() { return {}; }
QString hysteria2::DisplayName() { return {}; }
QString hysteria2::DisplayType() { return {}; }
QString hysteria2::DisplayTypeAndName() { return {}; }
bool hysteria2::IsEndpoint() { return false; }
}

View File

@ -1,128 +1,17 @@
#include "include/configs/outbounds/shadowsocks.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool shadowsocks::ParseFromLink(const QString& link)
{
QUrl url;
if (SubStrBefore(link, "#").contains("@")) {
url = QUrl(link);
} else {
// v2rayN format: base64 encoded full URL
QString linkN = DecodeB64IfValid(SubStrBefore(SubStrAfter(link, "://"), "#"), QByteArray::Base64Option::Base64UrlEncoding);
if (linkN.isEmpty()) return false;
if (link.contains("#")) linkN += "#" + SubStrAfter(link, "#");
url = QUrl("https://" + linkN);
}
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
outbound::ParseFromLink(url.toString());
bool shadowsocks::ParseFromLink(const QString& link) { return false; }
bool shadowsocks::ParseFromJson(const QJsonObject& object) { return false; }
QString shadowsocks::ExportToLink() { return {}; }
QJsonObject shadowsocks::ExportToJson() { return {}; }
BuildResult shadowsocks::Build() { return {}; }
// Traditional SS format
if (url.password().isEmpty()) {
// Traditional format: method:password base64 encoded in username
auto method_password = DecodeB64IfValid(url.userName(), QByteArray::Base64Option::Base64UrlEncoding);
if (method_password.isEmpty()) return false;
method = SubStrBefore(method_password, ":");
password = SubStrAfter(method_password, ":");
} else {
// 2022 format: method in username, password in password
method = url.userName();
password = url.password();
}
plugin = query.queryItemValue("plugin").replace("simple-obfs;", "obfs-local;");
plugin_opts = SubStrAfter(plugin, ";");
plugin = SubStrBefore(plugin, ";");
if (query.hasQueryItem("plugin-opts")) plugin_opts = query.queryItemValue("plugin-opts");
if (query.hasQueryItem("uot")) uot = query.queryItemValue("uot") == "true" || query.queryItemValue("uot").toInt() > 0;
multiplex->ParseFromLink(link);
return !(server.isEmpty() || method.isEmpty() || password.isEmpty());
}
bool shadowsocks::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "shadowsocks") return false;
outbound::ParseFromJson(object);
if (object.contains("method")) method = object["method"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("plugin")) plugin = object["plugin"].toString();
if (object.contains("plugin_opts")) plugin_opts = object["plugin_opts"].toString();
if (object.contains("uot"))
{
if (object["uot"].isBool()) uot = object["uot"].toBool();
if (object["uot"].isObject()) uot = object["uot"].toObject()["enabled"].toBool();
}
if (object.contains("multiplex")) multiplex->ParseFromJson(object["multiplex"].toObject());
return true;
}
QString shadowsocks::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("ss");
if (method.startsWith("2022-")) {
// 2022 format: method:password directly
url.setUserName(method);
url.setPassword(password);
} else {
// Traditional format: base64 encode method:password
auto method_password = method + ":" + password;
url.setUserName(method_password.toUtf8().toBase64(QByteArray::Base64Option::Base64UrlEncoding));
}
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!plugin.isEmpty()) query.addQueryItem("plugin", plugin);
if (!plugin_opts.isEmpty()) query.addQueryItem("plugin-opts", plugin_opts);
if (uot) query.addQueryItem("uot", "true");
mergeUrlQuery(query, multiplex->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject shadowsocks::ExportToJson()
{
QJsonObject object;
object["type"] = "shadowsocks";
mergeJsonObjects(object, outbound::ExportToJson());
if (!method.isEmpty()) object["method"] = method;
if (!password.isEmpty()) object["password"] = password;
if (!plugin.isEmpty()) object["plugin"] = plugin;
if (!plugin_opts.isEmpty()) object["plugin_opts"] = plugin_opts;
if (uot) object["uot"] = uot;
if (multiplex->enabled) object["multiplex"] = multiplex->ExportToJson();
return object;
}
BuildResult shadowsocks::Build()
{
QJsonObject object;
object["type"] = "shadowsocks";
mergeJsonObjects(object, outbound::Build().object);
if (!method.isEmpty()) object["method"] = method;
if (!password.isEmpty()) object["password"] = password;
if (!plugin.isEmpty()) object["plugin"] = plugin;
if (!plugin_opts.isEmpty()) object["plugin_opts"] = plugin_opts;
if (uot) object["uot"] = uot;
if (auto obj = multiplex->Build().object; !obj.isEmpty()) object["multiplex"] = obj;
return {object, ""};
}
QString shadowsocks::DisplayType()
{
return "Shadowsocks";
}
QString shadowsocks::DisplayAddress() { return {}; }
QString shadowsocks::DisplayName() { return {}; }
QString shadowsocks::DisplayType() { return {}; }
QString shadowsocks::DisplayTypeAndName() { return {}; }
bool shadowsocks::IsEndpoint() { return false; }
}

View File

@ -1,112 +1,17 @@
#include "include/configs/outbounds/socks.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool socks::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool socks::ParseFromLink(const QString& link) { return false; }
bool socks::ParseFromJson(const QJsonObject& object) { return false; }
QString socks::ExportToLink() { return {}; }
QJsonObject socks::ExportToJson() { return {}; }
BuildResult socks::Build() { return {}; }
outbound::ParseFromLink(link);
if (query.hasQueryItem("version"))
{
version = query.queryItemValue("version").toInt();
} else
{
if (url.scheme() == "socks4") version = 4;
if (url.scheme() == "socks5") version = 5;
}
// Handle v2rayN format (base64 encoded username)
if (!url.password().isEmpty() || !url.userName().isEmpty()) {
username = url.userName();
password = url.password();
// Check if username is base64 encoded (v2rayN format)
if (password.isEmpty() && !username.isEmpty()) {
QString decoded = DecodeB64IfValid(username);
if (!decoded.isEmpty()) {
username = SubStrBefore(decoded, ":");
password = SubStrAfter(decoded, ":");
}
}
}
if (query.hasQueryItem("uot")) uot = query.queryItemValue("uot") == "true" || query.queryItemValue("uot").toInt() > 0;
// Default port
if (server_port == 0) server_port = 1080;
return !server.isEmpty();
}
bool socks::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "socks") return false;
outbound::ParseFromJson(object);
if (object.contains("username")) username = object["username"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("version")) version = object["version"].toInt();
if (object.contains("uot")) uot = object["uot"].toBool();
return true;
}
QString socks::ExportToLink()
{
QUrl url;
QUrlQuery query;
// Determine scheme based on SOCKS version (default to socks5)
QString scheme = "socks5";
if (version == 4) {
scheme = "socks4";
}
url.setScheme(scheme);
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!username.isEmpty()) url.setUserName(username);
if (!password.isEmpty()) url.setPassword(password);
if (uot) query.addQueryItem("uot", "1");
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject socks::ExportToJson()
{
QJsonObject object;
object["type"] = "socks";
mergeJsonObjects(object, outbound::ExportToJson());
if (!username.isEmpty()) object["username"] = username;
if (!password.isEmpty()) object["password"] = password;
if (version == 4) object["version"] = "4";
if (uot) object["uot"] = uot;
return object;
}
BuildResult socks::Build()
{
QJsonObject object;
object["type"] = "socks";
mergeJsonObjects(object, outbound::Build().object);
if (!username.isEmpty()) object["username"] = username;
if (!password.isEmpty()) object["password"] = password;
if (version == 4) object["version"] = "4";
if (uot) object["uot"] = uot;
return {object, ""};
}
QString socks::DisplayType()
{
return "Socks";
}
QString socks::DisplayAddress() { return {}; }
QString socks::DisplayName() { return {}; }
QString socks::DisplayType() { return {}; }
QString socks::DisplayTypeAndName() { return {}; }
bool socks::IsEndpoint() { return false; }
}

View File

@ -1,145 +1,17 @@
#include "include/configs/outbounds/ssh.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool ssh::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool ssh::ParseFromLink(const QString& link) { return false; }
bool ssh::ParseFromJson(const QJsonObject& object) { return false; }
QString ssh::ExportToLink() { return {}; }
QJsonObject ssh::ExportToJson() { return {}; }
BuildResult ssh::Build() { return {}; }
outbound::ParseFromLink(link);
if (query.hasQueryItem("user")) user = query.queryItemValue("user");
if (query.hasQueryItem("password")) password = query.queryItemValue("password");
QString privateKeyB64 = query.queryItemValue("private_key");
if (!privateKeyB64.isEmpty()) {
private_key = QByteArray::fromBase64(privateKeyB64.toUtf8(), QByteArray::OmitTrailingEquals);
}
if (query.hasQueryItem("private_key_path")) private_key_path = query.queryItemValue("private_key_path");
if (query.hasQueryItem("private_key_passphrase")) private_key_passphrase = query.queryItemValue("private_key_passphrase");
QString hostKeysRaw = query.queryItemValue("host_key");
if (!hostKeysRaw.isEmpty()) {
for (const auto& item : hostKeysRaw.split("-")) {
auto b64hostKey = QByteArray::fromBase64(item.toUtf8(), QByteArray::OmitTrailingEquals);
if (!b64hostKey.isEmpty()) host_key.append(QString(b64hostKey));
}
}
QString hostKeyAlgsRaw = query.queryItemValue("host_key_algorithms");
if (!hostKeyAlgsRaw.isEmpty()) {
for (const auto& item : hostKeyAlgsRaw.split("-")) {
auto b64hostKeyAlg = QByteArray::fromBase64(item.toUtf8(), QByteArray::OmitTrailingEquals);
if (!b64hostKeyAlg.isEmpty()) host_key_algorithms.append(QString(b64hostKeyAlg));
}
}
if (query.hasQueryItem("client_version")) client_version = query.queryItemValue("client_version");
return !server.isEmpty();
}
bool ssh::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "ssh") return false;
outbound::ParseFromJson(object);
if (object.contains("user")) user = object["user"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("private_key")) private_key = object["private_key"].toString();
if (object.contains("private_key_path")) private_key_path = object["private_key_path"].toString();
if (object.contains("private_key_passphrase")) private_key_passphrase = object["private_key_passphrase"].toString();
if (object.contains("host_key")) {
host_key = QJsonArray2QListString(object["host_key"].toArray());
}
if (object.contains("host_key_algorithms")) {
host_key_algorithms = QJsonArray2QListString(object["host_key_algorithms"].toArray());
}
if (object.contains("client_version")) client_version = object["client_version"].toString();
return true;
}
QString ssh::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("ssh");
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!user.isEmpty()) query.addQueryItem("user", user);
if (!password.isEmpty()) query.addQueryItem("password", password);
if (!private_key.isEmpty()) {
query.addQueryItem("private_key", private_key.toUtf8().toBase64(QByteArray::OmitTrailingEquals));
}
if (!private_key_path.isEmpty()) query.addQueryItem("private_key_path", private_key_path);
if (!private_key_passphrase.isEmpty()) query.addQueryItem("private_key_passphrase", private_key_passphrase);
if (!host_key.isEmpty()) {
QStringList b64HostKeys;
for (const auto& item : host_key) {
b64HostKeys.append(item.toUtf8().toBase64(QByteArray::OmitTrailingEquals));
}
query.addQueryItem("host_key", b64HostKeys.join("-"));
}
if (!host_key_algorithms.isEmpty()) {
QStringList b64HostKeyAlgs;
for (const auto& item : host_key_algorithms) {
b64HostKeyAlgs.append(item.toUtf8().toBase64(QByteArray::OmitTrailingEquals));
}
query.addQueryItem("host_key_algorithms", b64HostKeyAlgs.join("-"));
}
if (!client_version.isEmpty()) query.addQueryItem("client_version", client_version);
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject ssh::ExportToJson()
{
QJsonObject object;
object["type"] = "ssh";
mergeJsonObjects(object, outbound::ExportToJson());
if (!user.isEmpty()) object["user"] = user;
if (!password.isEmpty()) object["password"] = password;
if (!private_key.isEmpty()) object["private_key"] = private_key;
if (!private_key_path.isEmpty()) object["private_key_path"] = private_key_path;
if (!private_key_passphrase.isEmpty()) object["private_key_passphrase"] = private_key_passphrase;
if (!host_key.isEmpty()) object["host_key"] = QListStr2QJsonArray(host_key);
if (!host_key_algorithms.isEmpty()) object["host_key_algorithms"] = QListStr2QJsonArray(host_key_algorithms);
if (!client_version.isEmpty()) object["client_version"] = client_version;
return object;
}
BuildResult ssh::Build()
{
QJsonObject object;
object["type"] = "ssh";
mergeJsonObjects(object, outbound::Build().object);
if (!user.isEmpty()) object["user"] = user;
if (!password.isEmpty()) object["password"] = password;
if (!private_key.isEmpty()) object["private_key"] = private_key;
if (!private_key_path.isEmpty()) object["private_key_path"] = private_key_path;
if (!private_key_passphrase.isEmpty()) object["private_key_passphrase"] = private_key_passphrase;
if (!host_key.isEmpty()) object["host_key"] = QListStr2QJsonArray(host_key);
if (!host_key_algorithms.isEmpty()) object["host_key_algorithms"] = QListStr2QJsonArray(host_key_algorithms);
if (!client_version.isEmpty()) object["client_version"] = client_version;
return {object, ""};
}
QString ssh::DisplayType()
{
return "SSH";
}
QString ssh::DisplayAddress() { return {}; }
QString ssh::DisplayName() { return {}; }
QString ssh::DisplayType() { return {}; }
QString ssh::DisplayTypeAndName() { return {}; }
bool ssh::IsEndpoint() { return false; }
}

View File

@ -1,139 +1,17 @@
#include "include/configs/outbounds/tailscale.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool tailscale::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool tailscale::ParseFromLink(const QString& link) { return false; }
bool tailscale::ParseFromJson(const QJsonObject& object) { return false; }
QString tailscale::ExportToLink() { return {}; }
QJsonObject tailscale::ExportToJson() { return {}; }
BuildResult tailscale::Build() { return {}; }
outbound::ParseFromLink(link);
if (query.hasQueryItem("state_directory")) state_directory = QUrl::fromPercentEncoding(query.queryItemValue("state_directory").toUtf8());
if (query.hasQueryItem("auth_key")) auth_key = QUrl::fromPercentEncoding(query.queryItemValue("auth_key").toUtf8());
if (query.hasQueryItem("control_url")) control_url = QUrl::fromPercentEncoding(query.queryItemValue("control_url").toUtf8());
if (query.hasQueryItem("ephemeral")) ephemeral = query.queryItemValue("ephemeral") == "true";
if (query.hasQueryItem("hostname")) hostname = QUrl::fromPercentEncoding(query.queryItemValue("hostname").toUtf8());
if (query.hasQueryItem("accept_routes")) accept_routes = query.queryItemValue("accept_routes") == "true";
if (query.hasQueryItem("exit_node")) exit_node = query.queryItemValue("exit_node");
if (query.hasQueryItem("exit_node_allow_lan_access")) exit_node_allow_lan_access = query.queryItemValue("exit_node_allow_lan_access") == "true";
if (query.hasQueryItem("advertise_routes")) {
advertise_routes = QUrl::fromPercentEncoding(query.queryItemValue("advertise_routes").toUtf8()).split(",");
}
if (query.hasQueryItem("advertise_exit_node")) advertise_exit_node = query.queryItemValue("advertise_exit_node") == "true";
if (query.hasQueryItem("globalDNS")) globalDNS = query.queryItemValue("globalDNS") == "true";
if (query.hasQueryItem("global_dns")) globalDNS = query.queryItemValue("global_dns") == "true";
return true;
}
bool tailscale::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "tailscale") return false;
outbound::ParseFromJson(object);
if (object.contains("state_directory")) state_directory = object["state_directory"].toString();
if (object.contains("auth_key")) auth_key = object["auth_key"].toString();
if (object.contains("control_url")) control_url = object["control_url"].toString();
if (object.contains("ephemeral")) ephemeral = object["ephemeral"].toBool();
if (object.contains("hostname")) hostname = object["hostname"].toString();
if (object.contains("accept_routes")) accept_routes = object["accept_routes"].toBool();
if (object.contains("exit_node")) exit_node = object["exit_node"].toString();
if (object.contains("exit_node_allow_lan_access")) exit_node_allow_lan_access = object["exit_node_allow_lan_access"].toBool();
if (object.contains("advertise_routes")) {
advertise_routes = QJsonArray2QListString(object["advertise_routes"].toArray());
}
if (object.contains("advertise_exit_node")) advertise_exit_node = object["advertise_exit_node"].toBool();
if (object.contains("globalDNS")) globalDNS = object["globalDNS"].toBool();
return true;
}
QString tailscale::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("ts");
url.setHost("tailscale");
if (!name.isEmpty()) url.setFragment(name);
if (!state_directory.isEmpty()) query.addQueryItem("state_directory", QUrl::toPercentEncoding(state_directory));
if (!auth_key.isEmpty()) query.addQueryItem("auth_key", QUrl::toPercentEncoding(auth_key));
if (!control_url.isEmpty()) query.addQueryItem("control_url", QUrl::toPercentEncoding(control_url));
if (ephemeral) query.addQueryItem("ephemeral", "true");
if (!hostname.isEmpty()) query.addQueryItem("hostname", QUrl::toPercentEncoding(hostname));
if (accept_routes) query.addQueryItem("accept_routes", "true");
if (!exit_node.isEmpty()) query.addQueryItem("exit_node", exit_node);
if (exit_node_allow_lan_access) query.addQueryItem("exit_node_allow_lan_access", "true");
if (!advertise_routes.isEmpty()) query.addQueryItem("advertise_routes", QUrl::toPercentEncoding(advertise_routes.join(",")));
if (advertise_exit_node) query.addQueryItem("advertise_exit_node", "true");
if (globalDNS) query.addQueryItem("global_dns", "true");
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject tailscale::ExportToJson()
{
QJsonObject object;
object["type"] = "tailscale";
object["tag"] = name;
if (!state_directory.isEmpty()) object["state_directory"] = state_directory;
if (!auth_key.isEmpty()) object["auth_key"] = auth_key;
if (!control_url.isEmpty()) object["control_url"] = control_url;
if (ephemeral) object["ephemeral"] = ephemeral;
if (!hostname.isEmpty()) object["hostname"] = hostname;
if (accept_routes) object["accept_routes"] = accept_routes;
if (!exit_node.isEmpty()) object["exit_node"] = exit_node;
if (exit_node_allow_lan_access) object["exit_node_allow_lan_access"] = exit_node_allow_lan_access;
if (!advertise_routes.isEmpty()) object["advertise_routes"] = QListStr2QJsonArray(advertise_routes);
if (advertise_exit_node) object["advertise_exit_node"] = advertise_exit_node;
if (globalDNS) object["globalDNS"] = globalDNS;
return object;
}
BuildResult tailscale::Build()
{
QJsonObject object;
object["type"] = "tailscale";
if (!state_directory.isEmpty()) object["state_directory"] = state_directory;
if (!auth_key.isEmpty()) object["auth_key"] = auth_key;
if (!control_url.isEmpty()) object["control_url"] = control_url;
if (ephemeral) object["ephemeral"] = ephemeral;
if (!hostname.isEmpty()) object["hostname"] = hostname;
if (accept_routes) object["accept_routes"] = accept_routes;
if (!exit_node.isEmpty()) object["exit_node"] = exit_node;
if (exit_node_allow_lan_access) object["exit_node_allow_lan_access"] = exit_node_allow_lan_access;
if (!advertise_routes.isEmpty()) object["advertise_routes"] = QListStr2QJsonArray(advertise_routes);
if (advertise_exit_node) object["advertise_exit_node"] = advertise_exit_node;
if (globalDNS) object["globalDNS"] = globalDNS;
return {object, ""};
}
void tailscale::SetAddress(QString newAddr) {
control_url = newAddr;
}
QString tailscale::GetAddress() {
return control_url;
}
QString tailscale::DisplayAddress()
{
return control_url;
}
QString tailscale::DisplayType()
{
return "Tailscale";
}
bool tailscale::IsEndpoint()
{
return true;
}
QString tailscale::DisplayAddress() { return {}; }
QString tailscale::DisplayName() { return {}; }
QString tailscale::DisplayType() { return {}; }
QString tailscale::DisplayTypeAndName() { return {}; }
bool tailscale::IsEndpoint() { return false; }
}

View File

@ -1,75 +1,17 @@
#include "include/configs/outbounds/trojan.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool Trojan::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
bool Trojan::ParseFromLink(const QString& link) { return false; }
bool Trojan::ParseFromJson(const QJsonObject& object) { return false; }
QString Trojan::ExportToLink() { return {}; }
QJsonObject Trojan::ExportToJson() { return {}; }
BuildResult Trojan::Build() { return {}; }
outbound::ParseFromLink(link);
password = url.userName();
tls->ParseFromLink(link);
transport->ParseFromLink(link);
multiplex->ParseFromLink(link);
return true;
}
bool Trojan::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "trojan") return false;
outbound::ParseFromJson(object);
if (object.contains("password")) password = object["password"].toString();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
if (object.contains("transport")) transport->ParseFromJson(object["transport"].toObject());
if (object.contains("multiplex")) multiplex->ParseFromJson(object["multiplex"].toObject());
return true;
}
QString Trojan::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setHost(server);
url.setPort(server_port);
url.setScheme("trojan");
if (!name.isEmpty()) url.setFragment(name);
url.setUserName(password);
if (tls->enabled) mergeUrlQuery(query, tls->ExportToLink());
if (!transport->type.isEmpty()) mergeUrlQuery(query, transport->ExportToLink());
if (multiplex->enabled) mergeUrlQuery(query, multiplex->ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject Trojan::ExportToJson()
{
QJsonObject object;
object["type"] = "trojan";
mergeJsonObjects(object, outbound::ExportToJson());
if (!password.isEmpty()) object["password"] = password;
if (tls->enabled) object["tls"] = tls->ExportToJson();
if (!transport->type.isEmpty()) object["transport"] = transport->ExportToJson();
if (multiplex->enabled) object["multiplex"] = multiplex->ExportToJson();
return object;
}
BuildResult Trojan::Build()
{
QJsonObject object;
object["type"] = "trojan";
mergeJsonObjects(object, outbound::Build().object);
if (!password.isEmpty()) object["password"] = password;
if (tls->enabled) object["tls"] = tls->Build().object;
if (!transport->type.isEmpty()) object["transport"] = transport->Build().object;
if (auto obj = multiplex->Build().object; !obj.isEmpty()) object["multiplex"] = obj;
return {object, ""};
}
QString Trojan::DisplayType()
{
return "Trojan";
}
QString Trojan::DisplayAddress() { return {}; }
QString Trojan::DisplayName() { return {}; }
QString Trojan::DisplayType() { return {}; }
QString Trojan::DisplayTypeAndName() { return {}; }
bool Trojan::IsEndpoint() { return false; }
}

View File

@ -1,107 +1,17 @@
#include "include/configs/outbounds/tuic.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool tuic::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool tuic::ParseFromLink(const QString& link) { return false; }
bool tuic::ParseFromJson(const QJsonObject& object) { return false; }
QString tuic::ExportToLink() { return {}; }
QJsonObject tuic::ExportToJson() { return {}; }
BuildResult tuic::Build() { return {}; }
outbound::ParseFromLink(link);
uuid = url.userName();
password = url.password();
if (query.hasQueryItem("congestion_control")) congestion_control = query.queryItemValue("congestion_control");
if (query.hasQueryItem("udp_relay_mode")) udp_relay_mode = query.queryItemValue("udp_relay_mode");
if (query.hasQueryItem("udp_over_stream")) udp_over_stream = query.queryItemValue("udp_over_stream") == "true";
if (query.hasQueryItem("zero_rtt_handshake")) zero_rtt_handshake = query.queryItemValue("zero_rtt_handshake") == "true";
if (query.hasQueryItem("heartbeat")) heartbeat = query.queryItemValue("heartbeat");
tls->ParseFromLink(link);
if (server_port == 0) server_port = 443;
return !(uuid.isEmpty() || password.isEmpty() || server.isEmpty());
}
bool tuic::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "tuic") return false;
outbound::ParseFromJson(object);
if (object.contains("uuid")) uuid = object["uuid"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("congestion_control")) congestion_control = object["congestion_control"].toString();
if (object.contains("udp_relay_mode")) udp_relay_mode = object["udp_relay_mode"].toString();
if (object.contains("udp_over_stream")) udp_over_stream = object["udp_over_stream"].toBool();
if (object.contains("zero_rtt_handshake")) zero_rtt_handshake = object["zero_rtt_handshake"].toBool();
if (object.contains("heartbeat")) heartbeat = object["heartbeat"].toString();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
return true;
}
QString tuic::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("tuic");
url.setUserName(uuid);
url.setPassword(password);
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (!congestion_control.isEmpty()) query.addQueryItem("congestion_control", congestion_control);
if (!udp_relay_mode.isEmpty()) query.addQueryItem("udp_relay_mode", udp_relay_mode);
if (udp_over_stream) query.addQueryItem("udp_over_stream", "true");
if (zero_rtt_handshake) query.addQueryItem("zero_rtt_handshake", "true");
if (!heartbeat.isEmpty()) query.addQueryItem("heartbeat", heartbeat);
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject tuic::ExportToJson()
{
QJsonObject object;
object["type"] = "tuic";
mergeJsonObjects(object, outbound::ExportToJson());
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (!password.isEmpty()) object["password"] = password;
if (!congestion_control.isEmpty()) object["congestion_control"] = congestion_control;
if (!udp_relay_mode.isEmpty()) object["udp_relay_mode"] = udp_relay_mode;
if (udp_over_stream) object["udp_over_stream"] = udp_over_stream;
if (zero_rtt_handshake) object["zero_rtt_handshake"] = zero_rtt_handshake;
if (!heartbeat.isEmpty()) object["heartbeat"] = heartbeat;
if (tls->enabled) object["tls"] = tls->ExportToJson();
return object;
}
BuildResult tuic::Build()
{
QJsonObject object;
object["type"] = "tuic";
mergeJsonObjects(object, outbound::Build().object);
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (!password.isEmpty()) object["password"] = password;
if (!congestion_control.isEmpty()) object["congestion_control"] = congestion_control;
if (!udp_relay_mode.isEmpty()) object["udp_relay_mode"] = udp_relay_mode;
if (udp_over_stream) object["udp_over_stream"] = udp_over_stream;
if (zero_rtt_handshake) object["zero_rtt_handshake"] = zero_rtt_handshake;
if (!heartbeat.isEmpty()) object["heartbeat"] = heartbeat;
if (tls->enabled) object["tls"] = tls->Build().object;
return {object, ""};
}
QString tuic::DisplayType()
{
return "TUIC";
}
QString tuic::DisplayAddress() { return {}; }
QString tuic::DisplayName() { return {}; }
QString tuic::DisplayType() { return {}; }
QString tuic::DisplayTypeAndName() { return {}; }
bool tuic::IsEndpoint() { return false; }
}

View File

@ -1,102 +1,17 @@
#include "include/configs/outbounds/vless.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool vless::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool vless::ParseFromLink(const QString& link) { return false; }
bool vless::ParseFromJson(const QJsonObject& object) { return false; }
QString vless::ExportToLink() { return {}; }
QJsonObject vless::ExportToJson() { return {}; }
BuildResult vless::Build() { return {}; }
outbound::ParseFromLink(link);
uuid = url.userName();
if (server_port == 0) server_port = 443;
flow = GetQueryValue(query, "flow", "");
transport->ParseFromLink(link);
tls->ParseFromLink(link);
if (!tls->server_name.isEmpty()) {
tls->enabled = true;
}
packet_encoding = GetQueryValue(query, "packetEncoding", "xudp");
multiplex->ParseFromLink(link);
return !(uuid.isEmpty() || server.isEmpty());
}
bool vless::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "vless") return false;
outbound::ParseFromJson(object);
if (object.contains("uuid")) uuid = object["uuid"].toString();
if (object.contains("flow")) flow = object["flow"].toString();
if (object.contains("packet_encoding")) packet_encoding = object["packet_encoding"].toString();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
if (object.contains("transport")) transport->ParseFromJson(object["transport"].toObject());
if (object.contains("multiplex")) multiplex->ParseFromJson(object["multiplex"].toObject());
return true;
}
QString vless::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("vless");
url.setUserName(uuid);
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
query.addQueryItem("encryption", "none");
if (!flow.isEmpty()) query.addQueryItem("flow", flow);
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, transport->ExportToLink());
mergeUrlQuery(query, multiplex->ExportToLink());
if (!packet_encoding.isEmpty()) query.addQueryItem("packetEncoding", packet_encoding);
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject vless::ExportToJson()
{
QJsonObject object;
object["type"] = "vless";
mergeJsonObjects(object, outbound::ExportToJson());
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (!flow.isEmpty()) object["flow"] = flow;
if (!packet_encoding.isEmpty()) object["packet_encoding"] = packet_encoding;
if (tls->enabled) object["tls"] = tls->ExportToJson();
if (!transport->type.isEmpty()) object["transport"] = transport->ExportToJson();
if (multiplex->enabled) object["multiplex"] = multiplex->ExportToJson();
return object;
}
BuildResult vless::Build()
{
QJsonObject object;
object["type"] = "vless";
mergeJsonObjects(object, outbound::Build().object);
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (!flow.isEmpty()) object["flow"] = flow;
if (!packet_encoding.isEmpty()) object["packet_encoding"] = packet_encoding;
if (tls->enabled) object["tls"] = tls->Build().object;
if (!transport->type.isEmpty()) object["transport"] = transport->Build().object;
if (auto obj = multiplex->Build().object; !obj.isEmpty()) object["multiplex"] = obj;
return {object, ""};
}
QString vless::DisplayType()
{
return "VLESS";
}
QString vless::DisplayAddress() { return {}; }
QString vless::DisplayName() { return {}; }
QString vless::DisplayType() { return {}; }
QString vless::DisplayTypeAndName() { return {}; }
bool vless::IsEndpoint() { return false; }
}

View File

@ -1,150 +1,17 @@
#include "include/configs/outbounds/vmess.h"
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool vmess::ParseFromLink(const QString& link)
{
// Try V2RayN format first (base64 encoded JSON)
QString linkN = DecodeB64IfValid(SubStrAfter(link, "vmess://"));
if (!linkN.isEmpty()) {
auto objN = QString2QJsonObject(linkN);
if (!objN.isEmpty()) {
uuid = objN["id"].toString();
server = objN["add"].toString();
server_port = objN["port"].toVariant().toInt();
name = objN["ps"].toString();
alter_id = objN["aid"].toVariant().toInt();
QString net = objN["net"].toString();
if (net == "h2") net = "http";
transport->type = net;
transport->host = objN["host"].toString();
transport->path = objN["path"].toString();
QString scy = objN["scy"].toString();
if (!scy.isEmpty()) security = scy;
QString tlsStr = objN["tls"].toString();
if (tlsStr == "tls") {
tls->enabled = true;
tls->server_name = objN["sni"].toString();
}
return !(uuid.isEmpty() || server.isEmpty());
}
}
// Standard VMess URL format
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool vmess::ParseFromLink(const QString& link) { return false; }
bool vmess::ParseFromJson(const QJsonObject& object) { return false; }
QString vmess::ExportToLink() { return {}; }
QJsonObject vmess::ExportToJson() { return {}; }
BuildResult vmess::Build() { return {}; }
outbound::ParseFromLink(link);
uuid = url.userName();
if (server_port == 0) server_port = 443;
security = GetQueryValue(query, "encryption", "auto");
transport->ParseFromLink(link);
tls->ParseFromLink(link);
if (!tls->server_name.isEmpty()) {
tls->enabled = true;
}
multiplex->ParseFromLink(link);
if (query.hasQueryItem("alterId")) alter_id = query.queryItemValue("alterId").toInt();
if (query.hasQueryItem("globalPadding")) global_padding = query.queryItemValue("globalPadding") == "true";
if (query.hasQueryItem("authenticatedLength")) authenticated_length = query.queryItemValue("authenticatedLength") == "true";
if (query.hasQueryItem("packetEncoding")) packet_encoding = query.queryItemValue("packetEncoding");
return !(uuid.isEmpty() || server.isEmpty());
}
bool vmess::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "vmess") return false;
outbound::ParseFromJson(object);
if (object.contains("uuid")) uuid = object["uuid"].toString();
if (object.contains("security")) security = object["security"].toString();
if (object.contains("alter_id")) alter_id = object["alter_id"].toInt();
if (object.contains("alter-id")) alter_id = object["alter-id"].toInt();
if (object.contains("global_padding")) global_padding = object["global_padding"].toBool();
if (object.contains("global-padding")) global_padding = object["global-padding"].toBool();
if (object.contains("authenticated_length")) authenticated_length = object["authenticated_length"].toBool();
if (object.contains("packet_encoding")) packet_encoding = object["packet_encoding"].toString();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
if (object.contains("transport")) transport->ParseFromJson(object["transport"].toObject());
if (object.contains("multiplex")) multiplex->ParseFromJson(object["multiplex"].toObject());
return true;
}
QString vmess::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("vmess");
url.setUserName(uuid);
url.setHost(server);
url.setPort(server_port);
if (!name.isEmpty()) url.setFragment(name);
if (security != "auto") query.addQueryItem("encryption", security);
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, transport->ExportToLink());
mergeUrlQuery(query, multiplex->ExportToLink());
if (alter_id > 0) query.addQueryItem("alterId", QString::number(alter_id));
if (global_padding) query.addQueryItem("globalPadding", "true");
if (authenticated_length) query.addQueryItem("authenticatedLength", "true");
if (!packet_encoding.isEmpty()) query.addQueryItem("packetEncoding", packet_encoding);
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject vmess::ExportToJson()
{
QJsonObject object;
object["type"] = "vmess";
mergeJsonObjects(object, outbound::ExportToJson());
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (security != "auto") object["security"] = security;
if (alter_id > 0) object["alter_id"] = alter_id;
if (global_padding) object["global_padding"] = global_padding;
if (authenticated_length) object["authenticated_length"] = authenticated_length;
if (!packet_encoding.isEmpty()) object["packet_encoding"] = packet_encoding;
if (tls->enabled) object["tls"] = tls->ExportToJson();
if (!transport->type.isEmpty()) object["transport"] = transport->ExportToJson();
if (multiplex->enabled) object["multiplex"] = multiplex->ExportToJson();
return object;
}
BuildResult vmess::Build()
{
QJsonObject object;
object["type"] = "vmess";
mergeJsonObjects(object, outbound::Build().object);
if (!uuid.isEmpty()) object["uuid"] = uuid;
if (security != "auto") object["security"] = security;
if (alter_id > 0) object["alter_id"] = alter_id;
if (global_padding) object["global_padding"] = global_padding;
if (authenticated_length) object["authenticated_length"] = authenticated_length;
if (!packet_encoding.isEmpty()) object["packet_encoding"] = packet_encoding;
if (tls->enabled) object["tls"] = tls->Build().object;
if (!transport->type.isEmpty()) object["transport"] = transport->Build().object;
if (auto obj = multiplex->Build().object; !obj.isEmpty()) object["multiplex"] = obj;
return {object, ""};
}
QString vmess::DisplayType()
{
return "VMess";
}
QString vmess::DisplayAddress() { return {}; }
QString vmess::DisplayName() { return {}; }
QString vmess::DisplayType() { return {}; }
QString vmess::DisplayTypeAndName() { return {}; }
bool vmess::IsEndpoint() { return false; }
}

View File

@ -1,323 +1,23 @@
#include "include/configs/outbounds/wireguard.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool Peer::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
bool Peer::ParseFromLink(const QString& link) { return false; }
bool Peer::ParseFromJson(const QJsonObject& object) { return false; }
QString Peer::ExportToLink() { return {}; }
QJsonObject Peer::ExportToJson() { return {}; }
BuildResult Peer::Build() { return {}; }
address = url.host();
port = url.port();
if (query.hasQueryItem("public_key")) public_key = query.queryItemValue("public_key");
if (query.hasQueryItem("peer_public_key")) public_key = query.queryItemValue("peer_public_key");
if (query.hasQueryItem("pre_shared_key")) pre_shared_key = query.queryItemValue("pre_shared_key");
if (query.hasQueryItem("reserved")) {
QString rawReserved = query.queryItemValue("reserved");
if (!rawReserved.isEmpty()) {
for (const auto& item : rawReserved.split("-")) {
int val = item.toInt();
if (val > 0) reserved.append(val);
}
}
}
if (query.hasQueryItem("persistent_keepalive_interval")) persistent_keepalive = query.queryItemValue("persistent_keepalive").toInt();
return true;
}
bool wireguard::ParseFromLink(const QString& link) { return false; }
bool wireguard::ParseFromJson(const QJsonObject& object) { return false; }
QString wireguard::ExportToLink() { return {}; }
QJsonObject wireguard::ExportToJson() { return {}; }
BuildResult wireguard::Build() { return {}; }
bool Peer::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty()) return false;
if (object.contains("address")) address = object["address"].toString();
if (object.contains("port")) port = object["port"].toInt();
if (object.contains("public_key")) public_key = object["public_key"].toString();
if (object.contains("pre_shared_key")) pre_shared_key = object["pre_shared_key"].toString();
if (object.contains("reserved")) {
reserved = QJsonArray2QListInt(object["reserved"].toArray());
}
if (object.contains("persistent_keepalive_interval")) persistent_keepalive = object["persistent_keepalive"].toInt();
return true;
}
QString Peer::ExportToLink()
{
QUrlQuery query;
if (!public_key.isEmpty()) query.addQueryItem("public_key", public_key);
if (!pre_shared_key.isEmpty()) query.addQueryItem("pre_shared_key", pre_shared_key);
if (!reserved.isEmpty()) {
QStringList reservedStr;
for (auto val : reserved) {
reservedStr.append(QString::number(val));
}
query.addQueryItem("reserved", reservedStr.join("-"));
}
if (persistent_keepalive > 0) query.addQueryItem("persistent_keepalive_interval", QString::number(persistent_keepalive));
return query.toString();
}
QJsonObject Peer::ExportToJson()
{
QJsonObject object;
if (!address.isEmpty()) object["address"] = address;
if (port > 0) object["port"] = port;
if (!public_key.isEmpty()) object["public_key"] = public_key;
if (!pre_shared_key.isEmpty()) object["pre_shared_key"] = pre_shared_key;
if (!reserved.isEmpty()) object["reserved"] = QListInt2QJsonArray(reserved);
if (persistent_keepalive > 0) object["persistent_keepalive_interval"] = persistent_keepalive;
return object;
}
BuildResult Peer::Build()
{
QJsonObject object;
if (!address.isEmpty()) object["address"] = address;
if (port > 0) object["port"] = port;
if (!public_key.isEmpty()) object["public_key"] = public_key;
if (!pre_shared_key.isEmpty()) object["pre_shared_key"] = pre_shared_key;
if (!reserved.isEmpty()) object["reserved"] = QListInt2QJsonArray(reserved);
if (persistent_keepalive > 0) object["persistent_keepalive_interval"] = persistent_keepalive;
object["allowed_ips"] = QListStr2QJsonArray({"0.0.0.0/0", "::/0"});
return {object, ""};
}
bool wireguard::ParseFromLink(const QString& link)
{
// Try WireGuard config file format first
if (link.contains("[Interface]") && link.contains("[Peer]")) {
auto lines = link.split("\n");
for (const auto& line : lines) {
QString trimmed = line.trimmed();
if (trimmed.isEmpty()) continue;
if (trimmed == "[Peer]" || trimmed == "[Interface]") {
continue;
}
if (!trimmed.contains("=")) continue;
auto eqIdx = trimmed.indexOf("=");
QString key = trimmed.left(eqIdx).trimmed();
QString value = trimmed.mid(eqIdx + 1).trimmed();
if (key == "PrivateKey") private_key = value;
if (key == "Address") address = value.replace(" ", "").split(",");
if (key == "MTU") mtu = value.toInt();
if (key == "PublicKey") peer->public_key = value;
if (key == "PresharedKey") peer->pre_shared_key = value;
if (key == "PersistentKeepalive") peer->persistent_keepalive = value.toInt();
if (key == "Endpoint") {
QStringList parts = value.split(":");
if (parts.size() >= 2) {
peer->address = parts[0].trimmed();
peer->port = parts.last().trimmed().toInt();
server = peer->address;
server_port = peer->port;
}
}
if (key == "S1") enable_amnezia = true, init_packet_junk_size = value.toInt();
if (key == "S2") enable_amnezia = true, response_packet_junk_size = value.toInt();
if (key == "Jc") enable_amnezia = true, junk_packet_count = value.toInt();
if (key == "Jmin") enable_amnezia = true, junk_packet_min_size = value.toInt();
if (key == "Jmax") enable_amnezia = true, junk_packet_max_size = value.toInt();
if (key == "H1") enable_amnezia = true, init_packet_magic_header = value.toInt();
if (key == "H2") enable_amnezia = true, response_packet_magic_header = value.toInt();
if (key == "H3") enable_amnezia = true, underload_packet_magic_header = value.toInt();
if (key == "H4") enable_amnezia = true, transport_packet_magic_header = value.toInt();
}
return !private_key.isEmpty() && !peer->public_key.isEmpty();
}
// Standard wg:// URL format
auto url = QUrl(link);
if (!url.isValid()) return false;
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
outbound::ParseFromLink(link);
if (query.hasQueryItem("private_key")) private_key = query.queryItemValue("private_key");
peer->ParseFromLink(link);
QString rawLocalAddr = query.queryItemValue("local_address");
if (!rawLocalAddr.isEmpty()) {
address = rawLocalAddr.split("-");
}
if (query.hasQueryItem("mtu")) mtu = query.queryItemValue("mtu").toInt();
if (query.hasQueryItem("use_system_interface")) system = query.queryItemValue("use_system_interface") == "true";
if (query.hasQueryItem("workers")) worker_count = query.queryItemValue("workers").toInt();
if (query.hasQueryItem("udp_timeout")) udp_timeout = query.queryItemValue("udp_timeout");
enable_amnezia = query.queryItemValue("enable_amnezia") == "true";
if (enable_amnezia) {
if (query.hasQueryItem("junk_packet_count")) junk_packet_count = query.queryItemValue("junk_packet_count").toInt();
if (query.hasQueryItem("junk_packet_min_size")) junk_packet_min_size = query.queryItemValue("junk_packet_min_size").toInt();
if (query.hasQueryItem("junk_packet_max_size")) junk_packet_max_size = query.queryItemValue("junk_packet_max_size").toInt();
if (query.hasQueryItem("init_packet_junk_size")) init_packet_junk_size = query.queryItemValue("init_packet_junk_size").toInt();
if (query.hasQueryItem("response_packet_junk_size")) response_packet_junk_size = query.queryItemValue("response_packet_junk_size").toInt();
if (query.hasQueryItem("init_packet_magic_header")) init_packet_magic_header = query.queryItemValue("init_packet_magic_header").toInt();
if (query.hasQueryItem("response_packet_magic_header")) response_packet_magic_header = query.queryItemValue("response_packet_magic_header").toInt();
if (query.hasQueryItem("underload_packet_magic_header")) underload_packet_magic_header = query.queryItemValue("underload_packet_magic_header").toInt();
if (query.hasQueryItem("transport_packet_magic_header")) transport_packet_magic_header = query.queryItemValue("transport_packet_magic_header").toInt();
}
return !(private_key.isEmpty() || peer->public_key.isEmpty() || server.isEmpty());
}
bool wireguard::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "wireguard") return false;
outbound::ParseFromJson(object);
if (object.contains("private_key")) private_key = object["private_key"].toString();
if (object.contains("peers") && object["peers"].isArray() && !object["peers"].toArray().empty()) peer->ParseFromJson(object["peers"].toArray()[0].toObject());
if (object.contains("address")) address = QJsonArray2QListString(object["address"].toArray());
if (object.contains("mtu")) mtu = object["mtu"].toInt();
if (object.contains("system")) system = object["system"].toBool();
if (object.contains("worker_count")) worker_count = object["worker_count"].toInt();
if (object.contains("udp_timeout")) udp_timeout = object["udp_timeout"].toString();
if (object.contains("junk_packet_count")) junk_packet_count = object["junk_packet_count"].toInt(), enable_amnezia = true;
if (object.contains("junk_packet_min_size")) junk_packet_min_size = object["junk_packet_min_size"].toInt(), enable_amnezia = true;
if (object.contains("junk_packet_max_size")) junk_packet_max_size = object["junk_packet_max_size"].toInt(), enable_amnezia = true;
if (object.contains("init_packet_junk_size")) init_packet_junk_size = object["init_packet_junk_size"].toInt(), enable_amnezia = true;
if (object.contains("response_packet_junk_size")) response_packet_junk_size = object["response_packet_junk_size"].toInt(), enable_amnezia = true;
if (object.contains("init_packet_magic_header")) init_packet_magic_header = object["init_packet_magic_header"].toInt(), enable_amnezia = true;
if (object.contains("response_packet_magic_header")) response_packet_magic_header = object["response_packet_magic_header"].toInt(), enable_amnezia = true;
if (object.contains("underload_packet_magic_header")) underload_packet_magic_header = object["underload_packet_magic_header"].toInt(), enable_amnezia = true;
if (object.contains("transport_packet_magic_header")) transport_packet_magic_header = object["transport_packet_magic_header"].toInt(), enable_amnezia = true;
return true;
}
QString wireguard::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("wg");
url.setHost(peer->address);
url.setPort(peer->port);
if (!name.isEmpty()) url.setFragment(name);
if (!private_key.isEmpty()) query.addQueryItem("private_key", private_key);
if (!address.isEmpty()) query.addQueryItem("local_address", address.join("-"));
if (mtu > 0 && mtu != 1420) query.addQueryItem("mtu", QString::number(mtu));
if (system) query.addQueryItem("use_system_interface", "true");
if (worker_count > 0) query.addQueryItem("workers", QString::number(worker_count));
if (!udp_timeout.isEmpty()) query.addQueryItem("udp_timeout", udp_timeout);
if (enable_amnezia) {
query.addQueryItem("enable_amnezia", "true");
if (junk_packet_count > 0) query.addQueryItem("junk_packet_count", QString::number(junk_packet_count));
if (junk_packet_min_size > 0) query.addQueryItem("junk_packet_min_size", QString::number(junk_packet_min_size));
if (junk_packet_max_size > 0) query.addQueryItem("junk_packet_max_size", QString::number(junk_packet_max_size));
if (init_packet_junk_size > 0) query.addQueryItem("init_packet_junk_size", QString::number(init_packet_junk_size));
if (response_packet_junk_size > 0) query.addQueryItem("response_packet_junk_size", QString::number(response_packet_junk_size));
if (init_packet_magic_header > 0) query.addQueryItem("init_packet_magic_header", QString::number(init_packet_magic_header));
if (response_packet_magic_header > 0) query.addQueryItem("response_packet_magic_header", QString::number(response_packet_magic_header));
if (underload_packet_magic_header > 0) query.addQueryItem("underload_packet_magic_header", QString::number(underload_packet_magic_header));
if (transport_packet_magic_header > 0) query.addQueryItem("transport_packet_magic_header", QString::number(transport_packet_magic_header));
}
mergeUrlQuery(query, outbound::ExportToLink());
mergeUrlQuery(query, peer->ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
return url.toString();
}
QJsonObject wireguard::ExportToJson()
{
QJsonObject object;
object["type"] = "wireguard";
if (!name.isEmpty()) object["tag"] = name;
mergeJsonObjects(object, dialFields->ExportToJson());
if (!private_key.isEmpty()) object["private_key"] = private_key;
if (!address.isEmpty()) object["address"] = QListStr2QJsonArray(address);
if (mtu > 0) object["mtu"] = mtu;
if (system) object["system"] = system;
if (worker_count > 0) object["worker_count"] = worker_count;
if (!udp_timeout.isEmpty()) object["udp_timeout"] = udp_timeout;
if (enable_amnezia) {
if (junk_packet_count > 0) object["junk_packet_count"] = junk_packet_count;
if (junk_packet_min_size > 0) object["junk_packet_min_size"] = junk_packet_min_size;
if (junk_packet_max_size > 0) object["junk_packet_max_size"] = junk_packet_max_size;
if (init_packet_junk_size > 0) object["init_packet_junk_size"] = init_packet_junk_size;
if (response_packet_junk_size > 0) object["response_packet_junk_size"] = response_packet_junk_size;
if (init_packet_magic_header > 0) object["init_packet_magic_header"] = init_packet_magic_header;
if (response_packet_magic_header > 0) object["response_packet_magic_header"] = response_packet_magic_header;
if (underload_packet_magic_header > 0) object["underload_packet_magic_header"] = underload_packet_magic_header;
if (transport_packet_magic_header > 0) object["transport_packet_magic_header"] = transport_packet_magic_header;
}
auto peerObj = peer->ExportToJson();
if (!peerObj.isEmpty()) {
object["peers"] = QJsonArray({peerObj});
}
return object;
}
BuildResult wireguard::Build()
{
QJsonObject object;
object["type"] = "wireguard";
if (!name.isEmpty()) object["tag"] = name;
mergeJsonObjects(object, dialFields->Build().object);
if (!private_key.isEmpty()) object["private_key"] = private_key;
if (!address.isEmpty()) object["address"] = QListStr2QJsonArray(address);
if (mtu > 0) object["mtu"] = mtu;
if (system) object["system"] = system;
if (worker_count > 0) object["worker_count"] = worker_count;
if (!udp_timeout.isEmpty()) object["udp_timeout"] = udp_timeout;
if (enable_amnezia) {
if (junk_packet_count > 0) object["junk_packet_count"] = junk_packet_count;
if (junk_packet_min_size > 0) object["junk_packet_min_size"] = junk_packet_min_size;
if (junk_packet_max_size > 0) object["junk_packet_max_size"] = junk_packet_max_size;
if (init_packet_junk_size > 0) object["init_packet_junk_size"] = init_packet_junk_size;
if (response_packet_junk_size > 0) object["response_packet_junk_size"] = response_packet_junk_size;
if (init_packet_magic_header > 0) object["init_packet_magic_header"] = init_packet_magic_header;
if (response_packet_magic_header > 0) object["response_packet_magic_header"] = response_packet_magic_header;
if (underload_packet_magic_header > 0) object["underload_packet_magic_header"] = underload_packet_magic_header;
if (transport_packet_magic_header > 0) object["transport_packet_magic_header"] = transport_packet_magic_header;
}
auto peerObj = peer->Build().object;
if (!peerObj.isEmpty()) {
object["peers"] = QJsonArray({peerObj});
}
return {object, ""};
}
void wireguard::SetAddress(QString newAddr) {
peer->address = newAddr;
}
QString wireguard::GetAddress() {
return peer->address;
}
void wireguard::SetPort(int newPort) {
peer->port = newPort;
}
QString wireguard::GetPort() {
return QString::number(peer->port);
}
QString wireguard::DisplayAddress()
{
return ::DisplayAddress(peer->address, peer->port);
}
QString wireguard::DisplayType()
{
return "WireGuard";
}
bool wireguard::IsEndpoint()
{
return true;
}
QString wireguard::DisplayAddress() { return {}; }
QString wireguard::DisplayName() { return {}; }
QString wireguard::DisplayType() { return {}; }
QString wireguard::DisplayTypeAndName() { return {}; }
bool wireguard::IsEndpoint() { return false; }
}