mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 13:42:51 +08:00
Migrate from bean to outbound
This commit is contained in:
parent
56d6394e9c
commit
45d9c5a875
@ -97,7 +97,6 @@ set(PROJECT_SOURCES
|
||||
src/dataStore/Database.cpp
|
||||
src/stats/traffic/TrafficLooper.cpp
|
||||
src/dataStore/ProfileFilter.cpp
|
||||
src/configs/ConfigBuilder.cpp
|
||||
|
||||
src/configs/proxy/AbstractBean.cpp
|
||||
src/configs/proxy/Bean2CoreObj_box.cpp
|
||||
|
||||
@ -1,126 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "include/dataStore/ProxyEntity.hpp"
|
||||
#include "include/sys/Process.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 BuildConfigResult {
|
||||
public:
|
||||
QString error;
|
||||
QJsonObject coreConfig;
|
||||
std::shared_ptr<ExtraCoreData> extraCoreData;
|
||||
|
||||
QList<std::shared_ptr<Stats::TrafficData>> outboundStats; // all, but not including "bypass" "block"
|
||||
};
|
||||
|
||||
class BuildTestConfigResult {
|
||||
public:
|
||||
QString error;
|
||||
QMap<int, QString> fullConfigs;
|
||||
QMap<QString, int> tag2entID;
|
||||
QJsonObject coreConfig;
|
||||
QStringList outboundTags;
|
||||
};
|
||||
|
||||
class BuildConfigStatus {
|
||||
public:
|
||||
std::shared_ptr<BuildConfigResult> result;
|
||||
std::shared_ptr<ProxyEntity> ent;
|
||||
int chainID = 0;
|
||||
bool forTest;
|
||||
bool forExport;
|
||||
|
||||
// xxList is V2Ray format string list
|
||||
|
||||
QStringList domainListDNSDirect;
|
||||
|
||||
// config format
|
||||
|
||||
QJsonArray routingRules;
|
||||
QJsonArray inbounds;
|
||||
QJsonArray outbounds;
|
||||
QJsonArray endpoints;
|
||||
};
|
||||
|
||||
inline OSType getOS()
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
return Darwin;
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
return Linux;
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
return Windows;
|
||||
#endif
|
||||
return Unknown;
|
||||
};
|
||||
|
||||
bool IsValid(const std::shared_ptr<ProxyEntity> &ent);
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles);
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID = 0);
|
||||
|
||||
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status);
|
||||
|
||||
QJsonObject BuildDnsObject(QString address, bool tunEnabled);
|
||||
|
||||
QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status);
|
||||
|
||||
QString BuildChainInternal(int chainId, const QList<std::shared_ptr<ProxyEntity>> &ents,
|
||||
const std::shared_ptr<BuildConfigStatus> &status);
|
||||
|
||||
void BuildOutbound(const std::shared_ptr<ProxyEntity> &ent, const std::shared_ptr<BuildConfigStatus> &status, QJsonObject& outbound, const QString& tag);
|
||||
|
||||
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;
|
||||
}
|
||||
} // namespace Configs
|
||||
@ -13,14 +13,24 @@ namespace Configs
|
||||
class baseConfig : public JsonStore
|
||||
{
|
||||
public:
|
||||
virtual bool ParseFromLink(const QString& link);
|
||||
virtual bool ParseFromLink(const QString& link) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ParseFromJson(const QJsonObject& object);
|
||||
virtual bool ParseFromJson(const QJsonObject& object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual QString ExportToLink();
|
||||
virtual QString ExportToLink() {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual QJsonObject ExportToJson();
|
||||
virtual QJsonObject ExportToJson() {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual BuildResult Build();
|
||||
virtual BuildResult Build() {
|
||||
return {{}, "base class function called!"};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ namespace Configs
|
||||
bool forExport = false;
|
||||
bool tunEnabled = false;
|
||||
bool isResolvedUsed = false;
|
||||
std::shared_ptr<ProxyEntity> ent = std::make_shared<ProxyEntity>(nullptr, nullptr);
|
||||
std::shared_ptr<ProxyEntity> ent = std::make_shared<ProxyEntity>(nullptr, nullptr, nullptr);
|
||||
std::shared_ptr<BuildPrerequisities> buildPrerequisities = std::make_shared<BuildPrerequisities>();
|
||||
OSType os;
|
||||
|
||||
@ -161,5 +161,5 @@ namespace Configs
|
||||
|
||||
bool IsValid(const std::shared_ptr<ProxyEntity> &ent);
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles);
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity> > &profiles);
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ namespace Configs
|
||||
int min_idle_session = 5;
|
||||
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
|
||||
|
||||
anyTLS()
|
||||
anyTLS() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("password", &password, string));
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Configs
|
||||
public:
|
||||
QList<int> list; // from in to out
|
||||
|
||||
chain()
|
||||
chain() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("list", &list, integerList));
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
class custom : public outbound
|
||||
class Custom : public outbound
|
||||
{
|
||||
public:
|
||||
QString config;
|
||||
QString type; // one of outbound or fullconfig
|
||||
QString type; // one of outbound or fullconfig TODO use consts
|
||||
|
||||
custom()
|
||||
Custom() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("config", &config, string));
|
||||
|
||||
@ -12,7 +12,7 @@ namespace Configs
|
||||
QString extraCoreConf;
|
||||
bool noLogs = false;
|
||||
|
||||
extracore() {
|
||||
extracore() : outbound() {
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("socks_address", &socksAddress, itemType::string));
|
||||
_add(new configItem("socks_port", &socksPort, itemType::integer));
|
||||
|
||||
@ -14,7 +14,7 @@ namespace Configs
|
||||
QStringList headers;
|
||||
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
|
||||
|
||||
http()
|
||||
http() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("username", &username, string));
|
||||
|
||||
@ -19,7 +19,7 @@ namespace Configs
|
||||
bool disable_mtu_discovery = false;
|
||||
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
|
||||
|
||||
hysteria()
|
||||
hysteria() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("server_ports", &server_ports, stringList));
|
||||
|
||||
@ -16,7 +16,7 @@ namespace Configs
|
||||
QString password;
|
||||
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
|
||||
|
||||
hysteria2()
|
||||
hysteria2() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("server_ports", &server_ports, stringList));
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Configs
|
||||
bool uot = false;
|
||||
std::shared_ptr<Multiplex> multiplex = std::make_shared<Multiplex>();
|
||||
|
||||
shadowsocks()
|
||||
shadowsocks() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("method", &method, string));
|
||||
|
||||
@ -12,7 +12,7 @@ namespace Configs
|
||||
int version = 5;
|
||||
bool uot = false;
|
||||
|
||||
socks()
|
||||
socks() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("username", &username, string));
|
||||
|
||||
@ -15,7 +15,7 @@ namespace Configs
|
||||
QStringList host_key_algorithms;
|
||||
QString client_version;
|
||||
|
||||
ssh()
|
||||
ssh() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("user", &user, string));
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Configs
|
||||
bool advertise_exit_node = false;
|
||||
bool globalDNS = false;
|
||||
|
||||
tailscale()
|
||||
tailscale() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("state_directory", &state_directory, itemType::string));
|
||||
|
||||
@ -14,7 +14,7 @@ namespace Configs
|
||||
std::shared_ptr<Multiplex> multiplex = std::make_shared<Multiplex>();
|
||||
std::shared_ptr<Transport> transport = std::make_shared<Transport>();
|
||||
|
||||
Trojan()
|
||||
Trojan() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("password", &password, string));
|
||||
|
||||
@ -20,7 +20,7 @@ namespace Configs
|
||||
QString heartbeat;
|
||||
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
|
||||
|
||||
tuic()
|
||||
tuic() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("uuid", &uuid, string));
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Configs
|
||||
std::shared_ptr<Multiplex> multiplex = std::make_shared<Multiplex>();
|
||||
std::shared_ptr<Transport> transport = std::make_shared<Transport>();
|
||||
|
||||
vless()
|
||||
vless() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("uuid", &uuid, string));
|
||||
|
||||
@ -23,7 +23,7 @@ namespace Configs
|
||||
std::shared_ptr<Transport> transport = std::make_shared<Transport>();
|
||||
std::shared_ptr<Multiplex> multiplex = std::make_shared<Multiplex>();
|
||||
|
||||
vmess()
|
||||
vmess() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("uuid", &uuid, string));
|
||||
|
||||
@ -54,7 +54,7 @@ namespace Configs
|
||||
int underload_packet_magic_header = 0;
|
||||
int transport_packet_magic_header = 0;
|
||||
|
||||
wireguard()
|
||||
wireguard() : outbound()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
|
||||
|
||||
@ -25,32 +25,6 @@
|
||||
#include "include/configs/proxy/AbstractBean.hpp"
|
||||
#include "include/configs/proxy/ExtraCore.h"
|
||||
|
||||
namespace Configs {
|
||||
class SocksHttpBean;
|
||||
|
||||
class ShadowSocksBean;
|
||||
|
||||
class VMessBean;
|
||||
|
||||
class TrojanVLESSBean;
|
||||
|
||||
class NaiveBean;
|
||||
|
||||
class QUICBean;
|
||||
|
||||
class AnyTLSBean;
|
||||
|
||||
class WireguardBean;
|
||||
|
||||
class TailscaleBean;
|
||||
|
||||
class SSHBean;
|
||||
|
||||
class CustomBean;
|
||||
|
||||
class ChainBean;
|
||||
}; // namespace Configs
|
||||
|
||||
namespace Configs {
|
||||
class ProxyEntity : public JsonStore {
|
||||
public:
|
||||
@ -68,68 +42,12 @@ namespace Configs {
|
||||
|
||||
QString full_test_report;
|
||||
|
||||
ProxyEntity(Configs::AbstractBean *bean, const QString &type_);
|
||||
|
||||
ProxyEntity(Configs::outbound *outbound, const QString &type_);
|
||||
ProxyEntity(Configs::outbound *outbound, Configs::AbstractBean *bean, const QString &type_);
|
||||
|
||||
[[nodiscard]] QString DisplayTestResult() const;
|
||||
|
||||
[[nodiscard]] QColor DisplayLatencyColor() const;
|
||||
|
||||
[[nodiscard]] Configs::ChainBean *ChainBean() const {
|
||||
return (Configs::ChainBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::SocksHttpBean *SocksHTTPBean() const {
|
||||
return (Configs::SocksHttpBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::ShadowSocksBean *ShadowSocksBean() const {
|
||||
return (Configs::ShadowSocksBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::VMessBean *VMessBean() const {
|
||||
return (Configs::VMessBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::TrojanVLESSBean *TrojanVLESSBean() const {
|
||||
return (Configs::TrojanVLESSBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::NaiveBean *NaiveBean() const {
|
||||
return (Configs::NaiveBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::QUICBean *QUICBean() const {
|
||||
return (Configs::QUICBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::AnyTLSBean *AnyTLSBean() const {
|
||||
return (Configs::AnyTLSBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::WireguardBean *WireguardBean() const {
|
||||
return (Configs::WireguardBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::TailscaleBean *TailscaleBean() const
|
||||
{
|
||||
return (Configs::TailscaleBean *) bean.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] Configs::SSHBean *SSHBean() const {
|
||||
return (Configs::SSHBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::CustomBean *CustomBean() const {
|
||||
return (Configs::CustomBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::ExtraCoreBean *ExtraCoreBean() const {
|
||||
return (Configs::ExtraCoreBean *) bean.get();
|
||||
};
|
||||
|
||||
// Outbound getter functions
|
||||
[[nodiscard]] Configs::socks *Socks() const {
|
||||
return dynamic_cast<Configs::socks *>(outbound.get());
|
||||
};
|
||||
@ -182,8 +100,8 @@ namespace Configs {
|
||||
return dynamic_cast<Configs::wireguard *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::custom *Custom() const {
|
||||
return dynamic_cast<Configs::custom *>(outbound.get());
|
||||
[[nodiscard]] Configs::Custom *Custom() const {
|
||||
return dynamic_cast<Configs::Custom *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::chain *Chain() const {
|
||||
|
||||
@ -48,6 +48,8 @@ namespace Configs_ConfigItem {
|
||||
|
||||
void _add(configItem *item);
|
||||
|
||||
void _remove(const QString &key);
|
||||
|
||||
QString _name(void *p);
|
||||
|
||||
std::shared_ptr<configItem> _get(const QString &name);
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QKeySequenceEdit" name="toggle_proxy"/>
|
||||
<widget class="QtExtKeySequenceEdit" name="toggle_proxy"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -94,8 +94,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>380</width>
|
||||
<height>311</height>
|
||||
<width>360</width>
|
||||
<height>264</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
@ -1,991 +0,0 @@
|
||||
#include "include/configs/ConfigBuilder.hpp"
|
||||
#include "include/dataStore/Database.hpp"
|
||||
#include "include/configs/proxy/includes.h"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
#include "include/api/RPC.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
namespace Configs {
|
||||
QString genTunName() {
|
||||
auto tun_name = "throne-tun";
|
||||
#ifdef Q_OS_MACOS
|
||||
tun_name = "";
|
||||
#endif
|
||||
return tun_name;
|
||||
}
|
||||
|
||||
void MergeJson(const QJsonObject &custom, QJsonObject &outbound) {
|
||||
// 合并
|
||||
if (custom.isEmpty()) return;
|
||||
for (const auto &key: custom.keys()) {
|
||||
if (outbound.contains(key)) {
|
||||
auto v = custom[key];
|
||||
auto v_orig = outbound[key];
|
||||
if (v.isObject() && v_orig.isObject()) { // isObject 则合并?
|
||||
auto vo = v.toObject();
|
||||
QJsonObject vo_orig = v_orig.toObject();
|
||||
MergeJson(vo, vo_orig);
|
||||
outbound[key] = vo_orig;
|
||||
} else {
|
||||
outbound[key] = v;
|
||||
}
|
||||
} else {
|
||||
outbound[key] = custom[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Common
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID) {
|
||||
auto result = std::make_shared<BuildConfigResult>();
|
||||
result->extraCoreData = std::make_shared<ExtraCoreData>();
|
||||
auto status = std::make_shared<BuildConfigStatus>();
|
||||
status->ent = ent;
|
||||
status->result = result;
|
||||
status->forTest = forTest;
|
||||
status->forExport = forExport;
|
||||
status->chainID = chainID;
|
||||
|
||||
auto customBean = dynamic_cast<Configs::CustomBean *>(ent->bean.get());
|
||||
if (customBean != nullptr && customBean->core == "internal-full") {
|
||||
if (dataStore->spmode_vpn)
|
||||
{
|
||||
status->result->error = QObject::tr("Tun mode cannot be used with Custom configs");
|
||||
return result;
|
||||
}
|
||||
result->coreConfig = QString2QJsonObject(customBean->config_simple);
|
||||
} else {
|
||||
BuildConfigSingBox(status);
|
||||
}
|
||||
|
||||
// apply custom config
|
||||
MergeJson(QString2QJsonObject(ent->bean->custom_config), result->coreConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsValid(const std::shared_ptr<ProxyEntity>& ent)
|
||||
{
|
||||
if (ent->type == "chain")
|
||||
{
|
||||
for (int eId : ent->ChainBean()->list)
|
||||
{
|
||||
auto e = profileManager->GetProfile(eId);
|
||||
if (e == nullptr)
|
||||
{
|
||||
MW_show_log("Null ent in validator");
|
||||
return false;
|
||||
}
|
||||
if (!IsValid(e))
|
||||
{
|
||||
MW_show_log("Invalid ent in chain: ID=" + QString::number(eId));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
QJsonObject conf;
|
||||
if (ent->type == "custom" && ent->CustomBean()->core == "internal-full")
|
||||
{
|
||||
conf = QString2QJsonObject(ent->CustomBean()->config_simple);
|
||||
} else
|
||||
{
|
||||
auto out = ent->bean->BuildCoreObjSingBox();
|
||||
auto outArr = QJsonArray{out.outbound};
|
||||
auto key = ent->bean->IsEndpoint() ? "endpoints" : "outbounds";
|
||||
conf = {
|
||||
{key, outArr},
|
||||
};
|
||||
}
|
||||
bool ok;
|
||||
auto resp = API::defaultClient->CheckConfig(&ok, QJsonObject2QString(conf, true));
|
||||
if (!ok)
|
||||
{
|
||||
MW_show_log("Failed to contact the Core: " + resp);
|
||||
return false;
|
||||
}
|
||||
if (resp.isEmpty()) return true;
|
||||
// else
|
||||
MW_show_log("Invalid ent " + ent->bean->name + ": " + resp);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles) {
|
||||
auto results = std::make_shared<BuildTestConfigResult>();
|
||||
|
||||
QJsonArray outboundArray = {
|
||||
QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", "direct"}
|
||||
}
|
||||
};
|
||||
QJsonArray endpointArray = {};
|
||||
int index = 0;
|
||||
|
||||
QJsonArray directDomainArray;
|
||||
for (const auto &item: profiles) {
|
||||
if (item->type == "extracore")
|
||||
{
|
||||
MW_show_log("Skipping ExtraCore conf");
|
||||
continue;
|
||||
}
|
||||
if (!IsValid(item)) {
|
||||
MW_show_log("Skipping invalid config: " + item->bean->name);
|
||||
item->latency = -1;
|
||||
continue;
|
||||
}
|
||||
auto res = BuildConfig(item, true, false, ++index);
|
||||
if (!res->error.isEmpty()) {
|
||||
results->error = res->error;
|
||||
return results;
|
||||
}
|
||||
if (item->type == "custom" && item->CustomBean()->core == "internal-full") {
|
||||
res->coreConfig["inbounds"] = QJsonArray();
|
||||
results->fullConfigs[item->id] = QJsonObject2QString(res->coreConfig, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// not full config, process it
|
||||
if (results->coreConfig.isEmpty()) {
|
||||
results->coreConfig = res->coreConfig;
|
||||
}
|
||||
// add the direct dns domains
|
||||
for (const auto &rule: res->coreConfig["dns"].toObject()["rules"].toArray()) {
|
||||
if (rule.toObject().contains("domain")) {
|
||||
for (const auto &domain: rule.toObject()["domain"].toArray()) {
|
||||
directDomainArray.append(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
// now we add the outbounds of the current config to the final one
|
||||
auto outbounds = res->coreConfig["outbounds"].toArray();
|
||||
if (outbounds.isEmpty()) {
|
||||
results->error = QString("outbounds is empty for %1").arg(item->bean->name);
|
||||
return results;
|
||||
}
|
||||
auto endpoints = res->coreConfig["endpoints"].toArray();
|
||||
for (auto endpoint : endpoints) outbounds.append(endpoint);
|
||||
for (const auto &outboundRef: outbounds) {
|
||||
auto outbound = outboundRef.toObject();
|
||||
if (outbound["tag"] == "direct" || outbound["tag"] == "block" || outbound["tag"] == "dns-out" || outbound["tag"].toString().startsWith("rout")) continue;
|
||||
if (outbound["tag"] == "proxy") {
|
||||
QString tag = "proxy";
|
||||
if (index > 1) tag += Int2String(index);
|
||||
outbound.insert("tag", tag);
|
||||
if (outbound["type"] == "wireguard" || outbound["type"] == "tailscale")
|
||||
{
|
||||
endpointArray.append(outbound);
|
||||
} else
|
||||
{
|
||||
outboundArray.append(outbound);
|
||||
}
|
||||
results->outboundTags << tag;
|
||||
results->tag2entID.insert(tag, item->id);
|
||||
continue;
|
||||
}
|
||||
outboundArray.append(outbound);
|
||||
}
|
||||
}
|
||||
|
||||
results->coreConfig["outbounds"] = outboundArray;
|
||||
results->coreConfig["endpoints"] = endpointArray;
|
||||
auto dnsObj = results->coreConfig["dns"].toObject();
|
||||
auto dnsRulesObj = QJsonArray();
|
||||
if (!directDomainArray.empty()) {
|
||||
dnsRulesObj += QJsonObject{
|
||||
{"domain", directDomainArray},
|
||||
{"action", "route"},
|
||||
{"server", "dns-direct"}
|
||||
};
|
||||
}
|
||||
dnsObj["rules"] = dnsRulesObj;
|
||||
results->coreConfig["dns"] = dnsObj;
|
||||
results->coreConfig["route"] = QJsonObject{
|
||||
{"auto_detect_interface", true},
|
||||
{"default_domain_resolver", QJsonObject{
|
||||
{"server", "dns-direct"},
|
||||
{"strategy", dataStore->routing->outbound_domain_strategy},
|
||||
}}
|
||||
};
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
auto group = profileManager->GetGroup(status->ent->gid);
|
||||
if (group == nullptr) {
|
||||
status->result->error = QString("This profile is not in any group, your data may be corrupted.");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto resolveChain = [=](const std::shared_ptr<ProxyEntity> &ent) {
|
||||
QList<std::shared_ptr<ProxyEntity>> resolved;
|
||||
if (ent->type == "chain") {
|
||||
auto list = ent->ChainBean()->list;
|
||||
std::reverse(std::begin(list), std::end(list));
|
||||
for (auto id: list) {
|
||||
resolved += profileManager->GetProfile(id);
|
||||
if (resolved.last() == nullptr) {
|
||||
status->result->error = QString("chain missing ent: %1").arg(id);
|
||||
break;
|
||||
}
|
||||
if (resolved.last()->type == "chain") {
|
||||
status->result->error = QString("chain in chain is not allowed: %1").arg(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolved += ent;
|
||||
};
|
||||
return resolved;
|
||||
};
|
||||
|
||||
// Make list
|
||||
auto ents = resolveChain(status->ent);
|
||||
if (!status->result->error.isEmpty()) return {};
|
||||
|
||||
if (group->front_proxy_id >= 0) {
|
||||
auto fEnt = profileManager->GetProfile(group->front_proxy_id);
|
||||
if (fEnt == nullptr) {
|
||||
status->result->error = QString("front proxy ent not found.");
|
||||
return {};
|
||||
}
|
||||
ents += resolveChain(fEnt);
|
||||
if (!status->result->error.isEmpty()) return {};
|
||||
}
|
||||
|
||||
if (group->landing_proxy_id >= 0) {
|
||||
auto lEnt = profileManager->GetProfile(group->landing_proxy_id);
|
||||
if (lEnt == nullptr) {
|
||||
status->result->error = QString("landing proxy ent not found.");
|
||||
return {};
|
||||
}
|
||||
ents = resolveChain(lEnt) + ents;
|
||||
if (!status->result->error.isEmpty()) return {};
|
||||
}
|
||||
|
||||
// BuildChain
|
||||
QString chainTagOut = BuildChainInternal(chainId, ents, status);
|
||||
|
||||
// Chain ent traffic stat
|
||||
if (ents.length() > 1) {
|
||||
status->ent->traffic_data->id = status->ent->id;
|
||||
status->ent->traffic_data->tag = chainTagOut.toStdString();
|
||||
status->ent->traffic_data->ignoreForRate = true;
|
||||
status->result->outboundStats += status->ent->traffic_data;
|
||||
}
|
||||
|
||||
return chainTagOut;
|
||||
}
|
||||
|
||||
QString BuildChainInternal(int chainId, const QList<std::shared_ptr<ProxyEntity>> &ents,
|
||||
const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
QString chainTag = "c-" + Int2String(chainId);
|
||||
QString chainTagOut;
|
||||
bool lastWasEndpoint = false;
|
||||
|
||||
for (int index = 0; index < ents.length(); index++) {
|
||||
const auto& ent = ents.at(index);
|
||||
auto tagOut = chainTag + "-" + Int2String(ent->id) + "-" + Int2String(index);
|
||||
|
||||
// last profile set as "proxy"
|
||||
if (index == 0) {
|
||||
tagOut = "proxy";
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// chain rules: past
|
||||
auto replaced = (lastWasEndpoint ? status->endpoints : status->outbounds).last().toObject();
|
||||
replaced["detour"] = tagOut;
|
||||
ent->traffic_data->isChainTail = true;
|
||||
(lastWasEndpoint ? status->endpoints : status->outbounds).removeLast();
|
||||
(lastWasEndpoint ? status->endpoints : status->outbounds) += replaced;
|
||||
} else {
|
||||
// index == 0 means last profile in chain / not chain
|
||||
chainTagOut = tagOut;
|
||||
}
|
||||
|
||||
// Outbound
|
||||
|
||||
QJsonObject outbound;
|
||||
|
||||
BuildOutbound(ent, status, outbound, tagOut);
|
||||
|
||||
// apply custom outbound settings
|
||||
MergeJson(QString2QJsonObject(ent->bean->custom_outbound), outbound);
|
||||
|
||||
// Bypass Lookup for the first profile
|
||||
auto serverAddress = ent->bean->serverAddress;
|
||||
|
||||
if (auto customBean = dynamic_cast<Configs::CustomBean *>(ent->bean.get()); customBean != nullptr && customBean->core == "internal") {
|
||||
auto server = QString2QJsonObject(customBean->config_simple)["server"].toString();
|
||||
if (!server.isEmpty()) serverAddress = server;
|
||||
}
|
||||
|
||||
if (!IsIpAddress(serverAddress)) {
|
||||
status->domainListDNSDirect += serverAddress;
|
||||
}
|
||||
|
||||
if (ent->bean->IsEndpoint())
|
||||
{
|
||||
status->endpoints += outbound;
|
||||
lastWasEndpoint = true;
|
||||
} else
|
||||
{
|
||||
status->outbounds += outbound;
|
||||
lastWasEndpoint = false;
|
||||
}
|
||||
}
|
||||
|
||||
return chainTagOut;
|
||||
}
|
||||
|
||||
void BuildOutbound(const std::shared_ptr<ProxyEntity> &ent, const std::shared_ptr<BuildConfigStatus> &status, QJsonObject& outbound, const QString& tag) {
|
||||
if (ent->type == "wireguard") {
|
||||
if (ent->WireguardBean()->useSystemInterface && !IsAdmin()) {
|
||||
MW_dialog_message("configBuilder" ,"NeedAdmin");
|
||||
status->result->error = "using wireguard system interface requires elevated permissions";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto coreR = ent->bean->BuildCoreObjSingBox();
|
||||
if (coreR.outbound.isEmpty()) {
|
||||
status->result->error = "unsupported outbound";
|
||||
return;
|
||||
}
|
||||
if (!coreR.error.isEmpty()) { // rejected
|
||||
status->result->error = coreR.error;
|
||||
return;
|
||||
}
|
||||
outbound = coreR.outbound;
|
||||
|
||||
// outbound misc
|
||||
outbound["tag"] = tag;
|
||||
ent->traffic_data->id = ent->id;
|
||||
ent->traffic_data->tag = tag.toStdString();
|
||||
status->result->outboundStats += ent->traffic_data;
|
||||
|
||||
// mux common
|
||||
auto needMux = ent->type == "vmess" || ent->type == "trojan" || ent->type == "vless" || ent->type == "shadowsocks";
|
||||
needMux &= dataStore->mux_concurrency > 0;
|
||||
|
||||
auto stream = GetStreamSettings(ent->bean.get());
|
||||
if (stream != nullptr) {
|
||||
if (stream->network == "grpc" || stream->network == "quic" || stream->network == "anytls" || (stream->network == "http" && stream->security == "tls")) {
|
||||
needMux = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto mux_state = ent->bean->mux_state;
|
||||
if (mux_state == 0) {
|
||||
if (!dataStore->mux_default_on && !ent->bean->enable_brutal) needMux = false;
|
||||
} else if (mux_state == 1) {
|
||||
needMux = true;
|
||||
} else if (mux_state == 2) {
|
||||
needMux = false;
|
||||
}
|
||||
|
||||
if (ent->type == "vless" && outbound["flow"] != "") {
|
||||
needMux = false;
|
||||
}
|
||||
|
||||
// common
|
||||
// apply mux
|
||||
if (needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"protocol", dataStore->mux_protocol},
|
||||
{"padding", dataStore->mux_padding},
|
||||
{"max_streams", dataStore->mux_concurrency},
|
||||
};
|
||||
if (ent->bean->enable_brutal) {
|
||||
auto brutalObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"up_mbps", ent->bean->brutal_speed},
|
||||
{"down_mbps", ent->bean->brutal_speed},
|
||||
};
|
||||
muxObj["max_connections"] = 1;
|
||||
muxObj["brutal"] = brutalObj;
|
||||
}
|
||||
outbound["multiplex"] = muxObj;
|
||||
}
|
||||
}
|
||||
|
||||
// SingBox
|
||||
|
||||
QJsonObject BuildDnsObject(QString address, bool tunEnabled)
|
||||
{
|
||||
bool usingSystemdResolved = false;
|
||||
#ifdef Q_OS_LINUX
|
||||
usingSystemdResolved = ReadFileText("/etc/resolv.conf").contains("systemd-resolved");
|
||||
#endif
|
||||
if (address.startsWith("local"))
|
||||
{
|
||||
if (tunEnabled && usingSystemdResolved)
|
||||
{
|
||||
return {
|
||||
{"type", "underlying"}
|
||||
};
|
||||
}
|
||||
if (tunEnabled && getOS() == Darwin)
|
||||
{
|
||||
return {
|
||||
{"type", "udp"},
|
||||
{"server", dataStore->core_box_underlying_dns}
|
||||
};
|
||||
}
|
||||
return {
|
||||
{"type", "local"}
|
||||
};
|
||||
}
|
||||
if (address.startsWith("dhcp://"))
|
||||
{
|
||||
auto ifcName = address.replace("dhcp://", "");
|
||||
if (ifcName == "auto") ifcName = "";
|
||||
return {
|
||||
{"type", "dhcp"},
|
||||
{"interface", ifcName},
|
||||
};
|
||||
}
|
||||
QString addr = address;
|
||||
int port = -1;
|
||||
QString type = "udp";
|
||||
QString path = "";
|
||||
if (address.startsWith("tcp://"))
|
||||
{
|
||||
type = "tcp";
|
||||
addr = addr.replace("tcp://", "");
|
||||
}
|
||||
if (address.startsWith("tls://"))
|
||||
{
|
||||
type = "tls";
|
||||
addr = addr.replace("tls://", "");
|
||||
}
|
||||
if (address.startsWith("quic://"))
|
||||
{
|
||||
type = "quic";
|
||||
addr = addr.replace("quic://", "");
|
||||
}
|
||||
if (address.startsWith("https://"))
|
||||
{
|
||||
type = "https";
|
||||
addr = addr.replace("https://", "");
|
||||
if (addr.contains("/"))
|
||||
{
|
||||
path = addr.split("/").last();
|
||||
addr = addr.left(addr.indexOf("/"));
|
||||
}
|
||||
}
|
||||
if (address.startsWith("h3://"))
|
||||
{
|
||||
type = "h3";
|
||||
addr = addr.replace("h3://", "");
|
||||
if (addr.contains("/"))
|
||||
{
|
||||
path = addr.split("/").last();
|
||||
addr = addr.left(addr.indexOf("/"));
|
||||
}
|
||||
}
|
||||
if (addr.contains(":"))
|
||||
{
|
||||
auto spl = addr.split(":");
|
||||
addr = spl[0];
|
||||
port = spl[1].toInt();
|
||||
}
|
||||
QJsonObject res = {
|
||||
{"type", type},
|
||||
{"server", addr},
|
||||
};
|
||||
if (port != -1) res["server_port"] = port;
|
||||
if (!path.isEmpty()) res["path"] = path;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
// Prefetch
|
||||
auto routeChain = profileManager->GetRouteChain(dataStore->routing->current_route_id);
|
||||
if (routeChain == nullptr) {
|
||||
status->result->error = "Routing profile does not exist, try resetting the route profile in Routing Settings";
|
||||
return;
|
||||
}
|
||||
|
||||
// will be removed on November 1st, 2025
|
||||
for (auto ruleItem = routeChain->Rules.begin(); ruleItem != routeChain->Rules.end(); ++ruleItem) {
|
||||
for (auto ruleSetItem = (*ruleItem)->rule_set.begin(); ruleSetItem != (*ruleItem)->rule_set.end(); ++ruleSetItem) {
|
||||
if ((*ruleSetItem).endsWith("_IP")) {
|
||||
*ruleSetItem = "geoip-" + (*ruleSetItem).left((*ruleSetItem).length() - 3);
|
||||
}
|
||||
if ((*ruleSetItem).endsWith("_SITE")) {
|
||||
*ruleSetItem = "geosite-" + (*ruleSetItem).left((*ruleSetItem).length() - 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
routeChain->Save();
|
||||
|
||||
if (getOS() == Darwin && dataStore->core_box_underlying_dns.isEmpty() && dataStore->spmode_vpn)
|
||||
{
|
||||
status->result->error = QObject::tr("Local DNS and Tun mode do not work together, please set an IP to be used as the Local DNS server in the Routing Settings -> Local override");
|
||||
return;
|
||||
}
|
||||
|
||||
// copy for modification
|
||||
routeChain = std::make_shared<RoutingChain>(*routeChain);
|
||||
|
||||
// Outbounds
|
||||
auto tagProxy = BuildChain(status->chainID, status);
|
||||
if (!status->result->error.isEmpty()) return;
|
||||
if (status->ent->type == "extracore")
|
||||
{
|
||||
auto bean = status->ent->ExtraCoreBean();
|
||||
status->result->extraCoreData->path = QFileInfo(bean->extraCorePath).canonicalFilePath();
|
||||
status->result->extraCoreData->args = bean->extraCoreArgs;
|
||||
status->result->extraCoreData->config = bean->extraCoreConf;
|
||||
status->result->extraCoreData->configDir = GetBasePath();
|
||||
status->result->extraCoreData->noLog = bean->noLogs;
|
||||
routeChain->Rules << RouteRule::get_processPath_direct_rule(status->result->extraCoreData->path);
|
||||
}
|
||||
|
||||
// Direct domains
|
||||
bool needDirectDnsRules = false;
|
||||
QJsonArray directDomains;
|
||||
QJsonArray directRuleSets;
|
||||
QJsonArray directSuffixes;
|
||||
QJsonArray directKeywords;
|
||||
QJsonArray directRegexes;
|
||||
|
||||
// server addresses
|
||||
for (const auto &item: status->domainListDNSDirect) {
|
||||
directDomains.append(item);
|
||||
needDirectDnsRules = true;
|
||||
}
|
||||
|
||||
auto sets = routeChain->get_direct_sites();
|
||||
for (const auto &item: sets) {
|
||||
if (item.startsWith("ruleset:")) {
|
||||
directRuleSets << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("domain:")) {
|
||||
directDomains << item.mid(7);
|
||||
}
|
||||
if (item.startsWith("suffix:")) {
|
||||
directSuffixes << item.mid(7);
|
||||
}
|
||||
if (item.startsWith("keyword:")) {
|
||||
directKeywords << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("regex:")) {
|
||||
directRegexes << item.mid(6);
|
||||
}
|
||||
needDirectDnsRules = true;
|
||||
}
|
||||
|
||||
// Direct IPs
|
||||
QJsonArray directIPSets;
|
||||
QJsonArray directIPCIDRs;
|
||||
|
||||
auto directIPraw = routeChain->get_direct_ips();
|
||||
for (const auto &item: directIPraw)
|
||||
{
|
||||
if (item.startsWith("ruleset:"))
|
||||
{
|
||||
directIPSets << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("ip:"))
|
||||
{
|
||||
directIPCIDRs << item.mid(3);
|
||||
}
|
||||
}
|
||||
|
||||
// Inbounds
|
||||
// mixed-in
|
||||
if (IsValidPort(dataStore->inbound_socks_port) && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "mixed-in";
|
||||
inboundObj["type"] = "mixed";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["listen_port"] = dataStore->inbound_socks_port;
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// tun-in
|
||||
if (dataStore->spmode_vpn && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "tun-in";
|
||||
inboundObj["type"] = "tun";
|
||||
inboundObj["interface_name"] = genTunName();
|
||||
inboundObj["auto_route"] = true;
|
||||
inboundObj["mtu"] = dataStore->vpn_mtu;
|
||||
inboundObj["stack"] = dataStore->vpn_implementation;
|
||||
inboundObj["strict_route"] = dataStore->vpn_strict_route;
|
||||
#ifdef Q_OS_LINUX
|
||||
inboundObj["auto_redirect"] = true;
|
||||
#endif
|
||||
auto tunAddress = QJsonArray{"172.19.0.1/24"};
|
||||
if (dataStore->vpn_ipv6) tunAddress += "fdfe:dcba:9876::1/96";
|
||||
inboundObj["address"] = tunAddress;
|
||||
|
||||
QJsonArray routeExcludeAddrs = {"127.0.0.0/8"};
|
||||
QJsonArray routeExcludeSets;
|
||||
if (dataStore->enable_tun_routing)
|
||||
{
|
||||
for (auto item:directIPCIDRs) routeExcludeAddrs << item;
|
||||
for (auto item: directIPSets) routeExcludeSets << item;
|
||||
}
|
||||
if (routeChain->defaultOutboundID == proxyID)
|
||||
{
|
||||
inboundObj["route_exclude_address"] = routeExcludeAddrs;
|
||||
if (!routeExcludeSets.isEmpty()) inboundObj["route_exclude_address_set"] = routeExcludeSets;
|
||||
}
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// ntp
|
||||
if (dataStore->enable_ntp) {
|
||||
QJsonObject ntpObj;
|
||||
ntpObj["enabled"] = true;
|
||||
ntpObj["server"] = dataStore->ntp_server_address;
|
||||
ntpObj["server_port"] = dataStore->ntp_server_port;
|
||||
ntpObj["interval"] = dataStore->ntp_interval;
|
||||
status->result->coreConfig["ntp"] = ntpObj;
|
||||
}
|
||||
|
||||
// direct
|
||||
status->outbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", "direct"},
|
||||
};
|
||||
status->result->outboundStats += std::make_shared<Stats::TrafficData>("direct");
|
||||
|
||||
// Hijack
|
||||
if (dataStore->enable_dns_server && !status->forTest)
|
||||
{
|
||||
auto sniffRule = std::make_shared<RouteRule>();
|
||||
sniffRule->action = "sniff";
|
||||
sniffRule->inbound = {"dns-in"};
|
||||
|
||||
auto redirRule = std::make_shared<RouteRule>();
|
||||
redirRule->action = "hijack-dns";
|
||||
redirRule->inbound = {"dns-in"};
|
||||
|
||||
routeChain->Rules.prepend(redirRule);
|
||||
routeChain->Rules.prepend(sniffRule);
|
||||
}
|
||||
if (dataStore->enable_redirect && !status->forTest) {
|
||||
status->inbounds.prepend(QJsonObject{
|
||||
{"tag", "hijack"},
|
||||
{"type", "direct"},
|
||||
{"listen", dataStore->redirect_listen_address},
|
||||
{"listen_port", dataStore->redirect_listen_port},
|
||||
});
|
||||
auto sniffRule = std::make_shared<RouteRule>();
|
||||
sniffRule->action = "sniff";
|
||||
sniffRule->sniffOverrideDest = true;
|
||||
sniffRule->inbound = {"hijack"};
|
||||
routeChain->Rules.prepend(sniffRule);
|
||||
}
|
||||
|
||||
// custom inbound
|
||||
if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray())
|
||||
|
||||
// DNS hijack deps
|
||||
QJsonArray hijackDomains;
|
||||
QJsonArray hijackDomainSuffix;
|
||||
QJsonArray hijackDomainRegex;
|
||||
QJsonArray hijackGeoAssets;
|
||||
|
||||
// manage routing section
|
||||
auto routeObj = QJsonObject();
|
||||
if (dataStore->spmode_vpn) {
|
||||
routeObj["auto_detect_interface"] = true;
|
||||
}
|
||||
if (!status->forTest) {
|
||||
if (dataStore->enable_stats)
|
||||
{
|
||||
routeObj["find_process"] = true;
|
||||
}
|
||||
routeObj["final"] = outboundIDToString(routeChain->defaultOutboundID);
|
||||
|
||||
if (!dataStore->routing->domain_strategy.isEmpty())
|
||||
{
|
||||
auto resolveRule = std::make_shared<RouteRule>();
|
||||
resolveRule->action = "resolve";
|
||||
resolveRule->strategy = dataStore->routing->domain_strategy;
|
||||
resolveRule->inbound = {"mixed-in", "tun-in"};
|
||||
routeChain->Rules.prepend(resolveRule);
|
||||
}
|
||||
if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE)
|
||||
{
|
||||
auto sniffRule = std::make_shared<RouteRule>();
|
||||
sniffRule->action = "sniff";
|
||||
sniffRule->inbound = {"mixed-in", "tun-in"};
|
||||
routeChain->Rules.prepend(sniffRule);
|
||||
}
|
||||
auto neededOutbounds = routeChain->get_used_outbounds();
|
||||
auto neededRuleSets = routeChain->get_used_rule_sets();
|
||||
std::map<int, QString> outboundMap;
|
||||
outboundMap[-1] = "proxy";
|
||||
outboundMap[-2] = "direct";
|
||||
int suffix = 0;
|
||||
for (const auto &item: *neededOutbounds) {
|
||||
if (item < 0) continue;
|
||||
auto neededEnt = profileManager->GetProfile(item);
|
||||
if (neededEnt == nullptr) {
|
||||
status->result->error = "The routing profile is referencing outbounds that no longer exists, consider revising your settings";
|
||||
return;
|
||||
}
|
||||
QJsonObject currOutbound;
|
||||
QString tag = "rout-" + Int2String(suffix++);
|
||||
BuildOutbound(neededEnt, status, currOutbound, tag);
|
||||
if (neededEnt->type == "wireguard")
|
||||
{
|
||||
status->endpoints += currOutbound;
|
||||
} else
|
||||
{
|
||||
status->outbounds += currOutbound;
|
||||
}
|
||||
outboundMap[item] = tag;
|
||||
|
||||
// add to dns direct resolve
|
||||
if (!IsIpAddress(neededEnt->bean->serverAddress)) {
|
||||
directDomains << neededEnt->bean->serverAddress;
|
||||
needDirectDnsRules = true;
|
||||
}
|
||||
}
|
||||
auto routeRules = routeChain->get_route_rules(false, outboundMap);
|
||||
routeObj["rules"] = routeRules;
|
||||
|
||||
if (dataStore->enable_dns_server) {
|
||||
for (const auto& rule : dataStore->dns_server_rules) {
|
||||
if (rule.startsWith("ruleset:")) {
|
||||
hijackGeoAssets << rule.mid(8);
|
||||
}
|
||||
if (rule.startsWith("domain:")) {
|
||||
hijackDomains << rule.mid(7);
|
||||
}
|
||||
if (rule.startsWith("suffix:")) {
|
||||
hijackDomainSuffix << rule.mid(7);
|
||||
}
|
||||
if (rule.startsWith("regex:")) {
|
||||
hijackDomainRegex << rule.mid(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto ruleSet : hijackGeoAssets) {
|
||||
if (!neededRuleSets->contains(ruleSet.toString())) neededRuleSets->append(ruleSet.toString());
|
||||
}
|
||||
|
||||
auto ruleSetArray = QJsonArray();
|
||||
for (const auto &item: *neededRuleSets) {
|
||||
if(auto url = QUrl(item); url.isValid() && url.fileName().contains(".srs")) {
|
||||
ruleSetArray += QJsonObject{
|
||||
{"type", "remote"},
|
||||
{"tag", get_rule_set_name(item)},
|
||||
{"format", "binary"},
|
||||
{"url", item},
|
||||
};
|
||||
}
|
||||
else
|
||||
if(ruleSetMap.count(item.toStdString()) > 0) {
|
||||
ruleSetArray += QJsonObject{
|
||||
{"type", "remote"},
|
||||
{"tag", item},
|
||||
{"format", "binary"},
|
||||
{"url", get_jsdelivr_link(QString::fromStdString(ruleSetMap.at(item.toStdString())))},
|
||||
};
|
||||
}
|
||||
}
|
||||
if (dataStore->adblock_enable) {
|
||||
ruleSetArray += QJsonObject{
|
||||
{"type", "remote"},
|
||||
{"tag", "throne-adblocksingbox"},
|
||||
{"format", "binary"},
|
||||
{"url", get_jsdelivr_link("https://raw.githubusercontent.com/217heidai/adblockfilters/main/rules/adblocksingbox.srs")},
|
||||
};
|
||||
}
|
||||
routeObj["rule_set"] = ruleSetArray;
|
||||
}
|
||||
|
||||
// DNS settings
|
||||
QJsonObject dns;
|
||||
QJsonArray dnsServers;
|
||||
QJsonArray dnsRules;
|
||||
|
||||
routeObj["default_domain_resolver"] = QJsonObject{
|
||||
{"server", "dns-direct"},
|
||||
{"strategy", dataStore->routing->outbound_domain_strategy},
|
||||
};
|
||||
|
||||
// Remote
|
||||
if (status->ent->type == "tailscale")
|
||||
{
|
||||
auto tailDns = QJsonObject{
|
||||
{"type", "tailscale"},
|
||||
{"tag", "dns-remote"},
|
||||
{"endpoint", "proxy"},
|
||||
{"accept_default_resolvers", status->ent->TailscaleBean()->globalDNS},
|
||||
};
|
||||
dnsServers += tailDns;
|
||||
} else
|
||||
{
|
||||
auto remoteDnsObj = BuildDnsObject(dataStore->routing->remote_dns, dataStore->spmode_vpn);
|
||||
remoteDnsObj["tag"] = "dns-remote";
|
||||
remoteDnsObj["domain_resolver"] = "dns-local";
|
||||
remoteDnsObj["detour"] = tagProxy;
|
||||
dnsServers += remoteDnsObj;
|
||||
}
|
||||
|
||||
// Direct
|
||||
auto directDNSAddress = dataStore->routing->direct_dns;
|
||||
auto directDnsObj = BuildDnsObject(directDNSAddress, dataStore->spmode_vpn);
|
||||
directDnsObj["tag"] = "dns-direct";
|
||||
directDnsObj["domain_resolver"] = "dns-local";
|
||||
|
||||
// default dns server
|
||||
if (dataStore->routing->dns_final_out == "direct") {
|
||||
dnsServers.prepend(directDnsObj);
|
||||
} else {
|
||||
dnsServers.append(directDnsObj);
|
||||
}
|
||||
|
||||
// Handle localhost
|
||||
dnsRules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "A"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN A 127.0.0.1"},
|
||||
};
|
||||
|
||||
dnsRules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "AAAA"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN AAAA ::1"},
|
||||
};
|
||||
|
||||
// Hijack
|
||||
if (dataStore->enable_dns_server && !status->forTest) {
|
||||
dnsRules += QJsonObject{
|
||||
{"rule_set", hijackGeoAssets},
|
||||
{"domain", hijackDomains},
|
||||
{"domain_suffix", hijackDomainSuffix},
|
||||
{"domain_regex", hijackDomainRegex},
|
||||
{"query_type", "A"},
|
||||
{"action", "predefined"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", QString("* IN A %1").arg(dataStore->dns_v4_resp)},
|
||||
};
|
||||
|
||||
if (!dataStore->dns_v6_resp.isEmpty())
|
||||
{
|
||||
dnsRules += QJsonObject{
|
||||
{"rule_set", hijackGeoAssets},
|
||||
{"domain", hijackDomains},
|
||||
{"domain_suffix", hijackDomainSuffix},
|
||||
{"domain_regex", hijackDomainRegex},
|
||||
{"query_type", "AAAA"},
|
||||
{"action", "predefined"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", QString("* IN AAAA %1").arg(dataStore->dns_v6_resp)},
|
||||
};
|
||||
}
|
||||
|
||||
status->inbounds.prepend(QJsonObject{
|
||||
{"tag", "dns-in"},
|
||||
{"type", "direct"},
|
||||
{"listen", dataStore->dns_server_listen_lan ? "0.0.0.0" : "127.1.1.1"},
|
||||
{"listen_port", dataStore->dns_server_listen_port},
|
||||
});
|
||||
}
|
||||
|
||||
// Fakedns
|
||||
if (dataStore->fake_dns) {
|
||||
dnsServers += QJsonObject{
|
||||
{"tag", "dns-fake"},
|
||||
{"type", "fakeip"},
|
||||
{"inet4_range", "198.18.0.0/15"},
|
||||
{"inet6_range", "fc00::/18"},
|
||||
};
|
||||
dnsRules += QJsonObject{
|
||||
{"query_type", QJsonArray{
|
||||
"A",
|
||||
"AAAA"
|
||||
}},
|
||||
{"action", "route"},
|
||||
{"server", "dns-fake"}
|
||||
};
|
||||
dns["independent_cache"] = true;
|
||||
}
|
||||
|
||||
if (needDirectDnsRules) {
|
||||
dnsRules += QJsonObject{
|
||||
{"rule_set", directRuleSets},
|
||||
{"domain", directDomains},
|
||||
{"domain_suffix", directSuffixes},
|
||||
{"domain_keyword", directKeywords},
|
||||
{"domain_regex", directRegexes},
|
||||
{"action", "route"},
|
||||
{"server", "dns-direct"},
|
||||
};
|
||||
}
|
||||
|
||||
// Underlying DNS
|
||||
auto dnsLocalAddress = dataStore->core_box_underlying_dns.isEmpty() ? "local" : dataStore->core_box_underlying_dns;
|
||||
auto dnsLocalObj = BuildDnsObject(dnsLocalAddress, dataStore->spmode_vpn);
|
||||
dnsLocalObj["tag"] = "dns-local";
|
||||
dnsServers += dnsLocalObj;
|
||||
|
||||
dns["servers"] = dnsServers;
|
||||
dns["rules"] = dnsRules;
|
||||
|
||||
if (dataStore->routing->use_dns_object) {
|
||||
dns = QString2QJsonObject(dataStore->routing->dns_object);
|
||||
}
|
||||
|
||||
// experimental
|
||||
QJsonObject experimentalObj;
|
||||
QJsonObject clash_api = {
|
||||
{"default_mode", ""} // dummy to make sure it is created
|
||||
};
|
||||
|
||||
if (!status->forTest)
|
||||
{
|
||||
if (dataStore->core_box_clash_api > 0){
|
||||
clash_api = {
|
||||
{"external_controller", dataStore->core_box_clash_listen_addr + ":" + Int2String(dataStore->core_box_clash_api)},
|
||||
{"secret", dataStore->core_box_clash_api_secret},
|
||||
{"external_ui", "dashboard"},
|
||||
};
|
||||
}
|
||||
if (dataStore->core_box_clash_api > 0 || dataStore->enable_stats)
|
||||
{
|
||||
experimentalObj["clash_api"] = clash_api;
|
||||
}
|
||||
|
||||
QJsonObject cache_file = {
|
||||
{"enabled", true},
|
||||
{"store_fakeip", true},
|
||||
{"store_rdrc", true}
|
||||
};
|
||||
experimentalObj["cache_file"] = cache_file;
|
||||
}
|
||||
|
||||
status->result->coreConfig.insert("log", QJsonObject{{"level", dataStore->log_level}});
|
||||
status->result->coreConfig.insert("certificate", QJsonObject{{"store", dataStore->use_mozilla_certs ? "mozilla" : "system"}});
|
||||
status->result->coreConfig.insert("dns", dns);
|
||||
status->result->coreConfig.insert("inbounds", status->inbounds);
|
||||
status->result->coreConfig.insert("outbounds", status->outbounds);
|
||||
status->result->coreConfig.insert("endpoints", status->endpoints);
|
||||
status->result->coreConfig.insert("route", routeObj);
|
||||
if (!experimentalObj.isEmpty()) status->result->coreConfig.insert("experimental", experimentalObj);
|
||||
}
|
||||
} // namespace Configs
|
||||
@ -18,7 +18,7 @@ namespace Configs {
|
||||
}
|
||||
bool DialFields::ParseFromJson(const QJsonObject& object)
|
||||
{
|
||||
if (object == nullptr) return false;
|
||||
if (object.isEmpty()) return false;
|
||||
|
||||
if (object.contains("reuse_addr")) reuse_addr = object["reuse_addr"].toBool();
|
||||
if (object.contains("connect_timeout")) connect_timeout = object["connect_timeout"].toString();
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QUrlQuery>
|
||||
#include <include/global/DataStore.hpp>
|
||||
#include <include/global/Utils.hpp>
|
||||
|
||||
#include "include/configs/common/utils.h"
|
||||
@ -15,7 +13,6 @@ namespace Configs {
|
||||
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
|
||||
|
||||
// handle the common format
|
||||
fingerPrint = dataStore->utlsFingerprint;
|
||||
if (query.hasQueryItem("fp")) fingerPrint = query.queryItemValue("fp");
|
||||
if (!fingerPrint.isEmpty()) enabled = true;
|
||||
return true;
|
||||
@ -44,6 +41,11 @@ namespace Configs {
|
||||
}
|
||||
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(), ""};
|
||||
}
|
||||
|
||||
@ -165,6 +167,7 @@ namespace Configs {
|
||||
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);
|
||||
@ -275,6 +278,10 @@ namespace Configs {
|
||||
}
|
||||
BuildResult TLS::Build()
|
||||
{
|
||||
auto obj = ExportToJson();
|
||||
if (!obj.isEmpty() && obj["enabled"].toBool()) {
|
||||
if (dataStore->skip_cert) obj["insecure"] = true;
|
||||
}
|
||||
return {ExportToJson(), ""};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
#include "include/configs/common/multiplex.h"
|
||||
|
||||
#include <QUrlQuery>
|
||||
#include <include/global/DataStore.hpp>
|
||||
|
||||
#include "include/configs/common/utils.h"
|
||||
|
||||
namespace Configs {
|
||||
@ -19,7 +15,7 @@ namespace Configs {
|
||||
}
|
||||
bool TcpBrutal::ParseFromJson(const QJsonObject& object)
|
||||
{
|
||||
if (object == nullptr) return false;
|
||||
if (object.isEmpty()) 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();
|
||||
@ -56,7 +52,7 @@ namespace Configs {
|
||||
|
||||
if (query.hasQueryItem("mux")) enabled = query.queryItemValue("mux") == "true";
|
||||
else unspecified = true;
|
||||
protocol = query.hasQueryItem("mux_protocol") ? query.queryItemValue("mux_protocol") : "smux";
|
||||
if (query.hasQueryItem("mux_protocol")) protocol = query.queryItemValue("mux_protocol");
|
||||
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();
|
||||
@ -66,7 +62,7 @@ namespace Configs {
|
||||
}
|
||||
bool Multiplex::ParseFromJson(const QJsonObject& object)
|
||||
{
|
||||
if (object == nullptr)
|
||||
if (object.isEmpty())
|
||||
{
|
||||
unspecified = true;
|
||||
return false;
|
||||
@ -111,6 +107,7 @@ namespace Configs {
|
||||
{
|
||||
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;
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
#include "include/configs/generate.h"
|
||||
#include "include/dataStore/Database.hpp"
|
||||
#include "include/configs/proxy/includes.h"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
#include "include/api/RPC.h"
|
||||
#include "include/global/Configs.hpp"
|
||||
#include "include/global/Utils.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <include/configs/ConfigBuilder.hpp>
|
||||
|
||||
namespace Configs {
|
||||
|
||||
@ -340,28 +337,30 @@ namespace Configs {
|
||||
QJsonArray servers;
|
||||
QJsonArray rules;
|
||||
// remote
|
||||
if (isTailscale)
|
||||
{
|
||||
auto tailscale = ctx->ent->Tailscale();
|
||||
if (tailscale == nullptr)
|
||||
if (!ctx->forTest) {
|
||||
if (isTailscale)
|
||||
{
|
||||
ctx->error = "Corrupted state, needed tailscale been but could not cast";
|
||||
return;
|
||||
auto tailscale = ctx->ent->Tailscale();
|
||||
if (tailscale == nullptr)
|
||||
{
|
||||
ctx->error = "Corrupted state, needed tailscale been but could not cast";
|
||||
return;
|
||||
}
|
||||
auto tailDns = QJsonObject{
|
||||
{"type", "tailscale"},
|
||||
{"tag", "dns-remote"},
|
||||
{"endpoint", "proxy"},
|
||||
{"accept_default_resolvers", tailscale->globalDNS},
|
||||
};
|
||||
servers += tailDns;
|
||||
} else
|
||||
{
|
||||
auto remoteDnsObj = buildDnsObj(dataStore->routing->remote_dns, ctx);
|
||||
remoteDnsObj["tag"] = "dns-remote";
|
||||
remoteDnsObj["domain_resolver"] = "dns-local";
|
||||
remoteDnsObj["detour"] = "proxy";
|
||||
servers += remoteDnsObj;
|
||||
}
|
||||
auto tailDns = QJsonObject{
|
||||
{"type", "tailscale"},
|
||||
{"tag", "dns-remote"},
|
||||
{"endpoint", "proxy"},
|
||||
{"accept_default_resolvers", tailscale->globalDNS},
|
||||
};
|
||||
servers += tailDns;
|
||||
} else
|
||||
{
|
||||
auto remoteDnsObj = buildDnsObj(dataStore->routing->remote_dns, ctx);
|
||||
remoteDnsObj["tag"] = "dns-remote";
|
||||
remoteDnsObj["domain_resolver"] = "dns-local";
|
||||
remoteDnsObj["detour"] = "proxy";
|
||||
servers += remoteDnsObj;
|
||||
}
|
||||
|
||||
// direct
|
||||
@ -375,21 +374,23 @@ namespace Configs {
|
||||
}
|
||||
|
||||
// Handle localhost
|
||||
rules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "A"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN A 127.0.0.1"},
|
||||
};
|
||||
if (!ctx->forTest) {
|
||||
rules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "A"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN A 127.0.0.1"},
|
||||
};
|
||||
|
||||
rules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "AAAA"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN AAAA ::1"},
|
||||
};
|
||||
rules += QJsonObject{
|
||||
{"domain", "localhost"},
|
||||
{"action", "predefined"},
|
||||
{"query_type", "AAAA"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", "localhost. IN AAAA ::1"},
|
||||
};
|
||||
}
|
||||
|
||||
// HijackRules
|
||||
if (dataStore->enable_dns_server && !ctx->forTest)
|
||||
@ -453,7 +454,7 @@ namespace Configs {
|
||||
|
||||
// Local
|
||||
auto dnsLocalAddress = dataStore->core_box_underlying_dns.isEmpty() ? "local" : dataStore->core_box_underlying_dns;
|
||||
auto dnsLocalObj = BuildDnsObject(dnsLocalAddress, dataStore->spmode_vpn);
|
||||
auto dnsLocalObj = buildDnsObj(dnsLocalAddress, ctx);
|
||||
dnsLocalObj["tag"] = "dns-local";
|
||||
servers += dnsLocalObj;
|
||||
|
||||
@ -714,6 +715,9 @@ namespace Configs {
|
||||
route["rules"] = routeRules;
|
||||
route["rule_set"] = ruleSetArray;
|
||||
route["final"] = outboundIDToString(routeChain->defaultOutboundID);
|
||||
route["default_domain_resolver"] = QJsonObject{
|
||||
{"server", "dns-direct"},
|
||||
{"strategy", dataStore->routing->outbound_domain_strategy}};
|
||||
if (dataStore->spmode_vpn) route["auto_detect_interface"] = true;
|
||||
|
||||
ctx->buildConfigResult->coreConfig["route"] = route;
|
||||
@ -771,6 +775,8 @@ namespace Configs {
|
||||
auto ctx = std::make_shared<BuildSingBoxConfigContext>();
|
||||
ctx->ent = ent;
|
||||
|
||||
CalculatePrerequisities(ctx);
|
||||
|
||||
// log
|
||||
buildLogSections(ctx);
|
||||
// ntp
|
||||
@ -779,6 +785,7 @@ namespace Configs {
|
||||
buildDNSSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
MW_show_log("Config build error:" + ctx->error);
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
@ -788,6 +795,7 @@ namespace Configs {
|
||||
buildInboundSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
MW_show_log("Config build error:" + ctx->error);
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
@ -795,6 +803,7 @@ namespace Configs {
|
||||
buildOutboundsSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
MW_show_log("Config build error:" + ctx->error);
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
@ -802,6 +811,7 @@ namespace Configs {
|
||||
buildRouteSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
MW_show_log("Config build error:" + ctx->error);
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
@ -809,6 +819,7 @@ namespace Configs {
|
||||
buildExperimentalSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
MW_show_log("Config build error:" + ctx->error);
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
@ -879,10 +890,11 @@ namespace Configs {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles)
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity> > &profiles)
|
||||
{
|
||||
auto res = std::make_shared<BuildConfigResult>();
|
||||
auto res = std::make_shared<BuildTestConfigResult>();
|
||||
auto ctx = std::make_shared<BuildSingBoxConfigContext>();
|
||||
ctx->forTest = true;
|
||||
QList<int> entIDs;
|
||||
for (const auto& proxy : profiles) entIDs << proxy->id;
|
||||
ctx->buildPrerequisities->dnsDeps->directDomains = QListStr2QJsonArray(getEntDomains(entIDs, ctx->error));
|
||||
@ -896,7 +908,7 @@ namespace Configs {
|
||||
buildCertificateSection(ctx);
|
||||
buildNTPSection(ctx);
|
||||
int suffix = 1;
|
||||
for (auto item : profiles)
|
||||
for (const auto& item : profiles)
|
||||
{
|
||||
if (item->type == "extracore")
|
||||
{
|
||||
@ -930,10 +942,11 @@ namespace Configs {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buildOutboundChain(ctx, {item->id}, "proxy-" + Int2String(suffix++), false, true);
|
||||
auto tag = "proxy-" + Int2String(suffix++) + "-0";
|
||||
buildOutboundChain(ctx, {item->id}, "proxy-" + Int2String(suffix), false, true);
|
||||
auto tag = "proxy-" + Int2String(suffix) + "-0";
|
||||
res->outboundTags << tag;
|
||||
res->tag2entID.insert(tag, item->id);
|
||||
suffix++;
|
||||
}
|
||||
ctx->outbounds << QJsonObject{{"type", "direct"}, {"tag", "direct"}};
|
||||
ctx->buildConfigResult->coreConfig["outbounds"] = ctx->outbounds;
|
||||
|
||||
@ -75,7 +75,15 @@ namespace Configs {
|
||||
|
||||
BuildResult anyTLS::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "anytls";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -67,7 +67,15 @@ namespace Configs {
|
||||
}
|
||||
BuildResult http::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "http";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -134,7 +134,21 @@ namespace Configs {
|
||||
|
||||
BuildResult hysteria::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "hysteria";
|
||||
mergeJsonObjects(object, commons->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 (!obfs.isEmpty()) object["obfs"] = obfs;
|
||||
if (!auth.isEmpty()) object["auth"] = auth;
|
||||
if (!auth_str.isEmpty()) object["auth_str"] = auth_str;
|
||||
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;
|
||||
if (tls->enabled) object["tls"] = tls->Build().object;
|
||||
return {object, ""};
|
||||
}
|
||||
|
||||
QString hysteria::DisplayType()
|
||||
|
||||
@ -63,7 +63,11 @@ namespace Configs {
|
||||
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 (object.contains("obfsType")) obfsType = object["obfsType"].toString();
|
||||
if (object.contains("obfs")) {
|
||||
auto obfsObj = object["obfs"].toObject();
|
||||
if (obfsObj.contains("type")) obfsType = obfsObj["type"].toString();
|
||||
if (obfsObj.contains("password")) obfsPassword = obfsObj["password"].toString();
|
||||
}
|
||||
if (object.contains("obfsPassword")) obfsPassword = object["obfsPassword"].toString();
|
||||
if (object.contains("password")) password = object["password"].toString();
|
||||
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
|
||||
@ -127,8 +131,10 @@ namespace Configs {
|
||||
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 (!obfsType.isEmpty()) object["obfsType"] = obfsType;
|
||||
if (!obfsPassword.isEmpty()) object["obfsPassword"] = obfsPassword;
|
||||
QJsonObject obfsObj;
|
||||
if (!obfsType.isEmpty()) obfsObj["type"] = obfsType;
|
||||
if (!obfsPassword.isEmpty()) obfsObj["password"] = obfsPassword;
|
||||
if (!obfsObj.isEmpty()) object["obfs"] = obfsObj;
|
||||
if (!password.isEmpty()) object["password"] = password;
|
||||
if (tls->enabled) object["tls"] = tls->ExportToJson();
|
||||
return object;
|
||||
@ -136,7 +142,20 @@ namespace Configs {
|
||||
|
||||
BuildResult hysteria2::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "hysteria2";
|
||||
mergeJsonObjects(object, commons->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;
|
||||
QJsonObject obfsObj;
|
||||
obfsObj["type"] = obfsType;
|
||||
obfsObj["password"] = obfsPassword;
|
||||
if (!obfsObj.isEmpty()) object["obfs"] = obfsObj;
|
||||
if (!password.isEmpty()) object["password"] = password;
|
||||
if (tls->enabled) object["tls"] = tls->Build().object;
|
||||
return {object, ""};
|
||||
}
|
||||
|
||||
QString hysteria2::DisplayType()
|
||||
|
||||
@ -112,7 +112,16 @@ namespace Configs {
|
||||
|
||||
BuildResult shadowsocks::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "shadowsocks";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -95,7 +95,14 @@ namespace Configs {
|
||||
|
||||
BuildResult socks::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "socks";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -124,7 +124,18 @@ namespace Configs {
|
||||
|
||||
BuildResult ssh::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "ssh";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -98,7 +98,21 @@ namespace Configs {
|
||||
|
||||
BuildResult tailscale::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "tailscale";
|
||||
mergeJsonObjects(object, commons->Build().object);
|
||||
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, ""};
|
||||
}
|
||||
|
||||
QString tailscale::DisplayAddress()
|
||||
|
||||
@ -56,7 +56,14 @@ namespace Configs {
|
||||
}
|
||||
BuildResult Trojan::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "trojan";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -86,7 +86,18 @@ namespace Configs {
|
||||
|
||||
BuildResult tuic::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "tuic";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -83,7 +83,16 @@ namespace Configs {
|
||||
|
||||
BuildResult vless::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "vless";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -128,7 +128,19 @@ namespace Configs {
|
||||
|
||||
BuildResult vmess::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "vmess";
|
||||
mergeJsonObjects(object, commons->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()
|
||||
|
||||
@ -27,7 +27,7 @@ namespace Configs {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.hasQueryItem("persistent_keepalive")) persistent_keepalive = query.queryItemValue("persistent_keepalive").toInt();
|
||||
if (query.hasQueryItem("persistent_keepalive_interval")) persistent_keepalive = query.queryItemValue("persistent_keepalive").toInt();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -42,7 +42,7 @@ namespace Configs {
|
||||
if (object.contains("reserved")) {
|
||||
reserved = QJsonArray2QListInt(object["reserved"].toArray());
|
||||
}
|
||||
if (object.contains("persistent_keepalive")) persistent_keepalive = object["persistent_keepalive"].toInt();
|
||||
if (object.contains("persistent_keepalive_interval")) persistent_keepalive = object["persistent_keepalive"].toInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ namespace Configs {
|
||||
}
|
||||
query.addQueryItem("reserved", reservedStr.join("-"));
|
||||
}
|
||||
if (persistent_keepalive > 0) query.addQueryItem("persistent_keepalive", QString::number(persistent_keepalive));
|
||||
if (persistent_keepalive > 0) query.addQueryItem("persistent_keepalive_interval", QString::number(persistent_keepalive));
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
@ -72,13 +72,21 @@ namespace Configs {
|
||||
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"] = persistent_keepalive;
|
||||
if (persistent_keepalive > 0) object["persistent_keepalive_interval"] = persistent_keepalive;
|
||||
return object;
|
||||
}
|
||||
|
||||
BuildResult Peer::Build()
|
||||
{
|
||||
return {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;
|
||||
object["allowed_ips"] = QListStr2QJsonArray({"0.0.0.0/0", "::/0"});
|
||||
return {object, ""};
|
||||
}
|
||||
|
||||
bool wireguard::ParseFromLink(const QString& link)
|
||||
@ -98,7 +106,7 @@ namespace Configs {
|
||||
QString value = trimmed.mid(eqIdx + 1).trimmed();
|
||||
|
||||
if (key == "PrivateKey") private_key = value;
|
||||
if (key == "Address") address = value.split(",");
|
||||
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;
|
||||
@ -216,7 +224,8 @@ namespace Configs {
|
||||
{
|
||||
QJsonObject object;
|
||||
object["type"] = "wireguard";
|
||||
mergeJsonObjects(object, commons->ExportToJson());
|
||||
if (!commons->name.isEmpty()) object["tag"] = commons->name;
|
||||
mergeJsonObjects(object, commons->dialFields->ExportToJson());
|
||||
if (!private_key.isEmpty()) object["private_key"] = private_key;
|
||||
if (!address.isEmpty()) object["address"] = QListStr2QJsonArray(address);
|
||||
if (mtu > 0) object["mtu"] = mtu;
|
||||
@ -226,7 +235,7 @@ namespace Configs {
|
||||
|
||||
auto peerObj = peer->ExportToJson();
|
||||
if (!peerObj.isEmpty()) {
|
||||
object["peer"] = QJsonArray({peerObj});
|
||||
object["peers"] = QJsonArray({peerObj});
|
||||
}
|
||||
|
||||
return object;
|
||||
@ -234,7 +243,22 @@ namespace Configs {
|
||||
|
||||
BuildResult wireguard::Build()
|
||||
{
|
||||
return {ExportToJson(), ""};
|
||||
QJsonObject object;
|
||||
object["type"] = "wireguard";
|
||||
if (!commons->name.isEmpty()) object["tag"] = commons->name;
|
||||
mergeJsonObjects(object, commons->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;
|
||||
|
||||
auto peerObj = peer->Build().object;
|
||||
if (!peerObj.isEmpty()) {
|
||||
object["peers"] = QJsonArray({peerObj});
|
||||
}
|
||||
return {object, ""};
|
||||
}
|
||||
|
||||
QString wireguard::DisplayAddress()
|
||||
|
||||
@ -123,14 +123,14 @@ namespace Subscription {
|
||||
// Json
|
||||
if (str.startsWith('{')) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("custom");
|
||||
auto bean = ent->CustomBean();
|
||||
auto custom = ent->Custom();
|
||||
auto obj = QString2QJsonObject(str);
|
||||
if (obj.contains("outbounds")) {
|
||||
bean->core = "internal-full";
|
||||
bean->config_simple = str;
|
||||
custom->type = "fullconfig";
|
||||
custom->config = str;
|
||||
} else if (obj.contains("server")) {
|
||||
bean->core = "internal";
|
||||
bean->config_simple = str;
|
||||
custom->type = "outbound";
|
||||
custom->config = str;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -140,49 +140,49 @@ namespace Subscription {
|
||||
if (str.startsWith("socks5://") || str.startsWith("socks4://") ||
|
||||
str.startsWith("socks4a://") || str.startsWith("socks://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("socks");
|
||||
auto ok = ent->SocksHTTPBean()->TryParseLink(str);
|
||||
auto ok = ent->Socks()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// HTTP
|
||||
if (str.startsWith("http://") || str.startsWith("https://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("http");
|
||||
auto ok = ent->SocksHTTPBean()->TryParseLink(str);
|
||||
auto ok = ent->Http()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// ShadowSocks
|
||||
if (str.startsWith("ss://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("shadowsocks");
|
||||
auto ok = ent->ShadowSocksBean()->TryParseLink(str);
|
||||
auto ok = ent->ShadowSocks()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// VMess
|
||||
if (str.startsWith("vmess://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("vmess");
|
||||
auto ok = ent->VMessBean()->TryParseLink(str);
|
||||
auto ok = ent->VMess()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// VLESS
|
||||
if (str.startsWith("vless://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("vless");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseLink(str);
|
||||
auto ok = ent->VLESS()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// Trojan
|
||||
if (str.startsWith("trojan://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("trojan");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseLink(str);
|
||||
auto ok = ent->Trojan()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// AnyTLS
|
||||
if (str.startsWith("anytls://")) {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("anytls");
|
||||
auto ok = ent->AnyTLSBean()->TryParseLink(str);
|
||||
auto ok = ent->AnyTLS()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ namespace Subscription {
|
||||
if (str.startsWith("hysteria://")) {
|
||||
needFix = false;
|
||||
ent = Configs::ProfileManager::NewProxyEntity("hysteria");
|
||||
auto ok = ent->QUICBean()->TryParseLink(str);
|
||||
auto ok = ent->Hysteria()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ namespace Subscription {
|
||||
if (str.startsWith("hysteria2://") || str.startsWith("hy2://")) {
|
||||
needFix = false;
|
||||
ent = Configs::ProfileManager::NewProxyEntity("hysteria2");
|
||||
auto ok = ent->QUICBean()->TryParseLink(str);
|
||||
auto ok = ent->Hysteria2()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -206,7 +206,7 @@ namespace Subscription {
|
||||
if (str.startsWith("tuic://")) {
|
||||
needFix = false;
|
||||
ent = Configs::ProfileManager::NewProxyEntity("tuic");
|
||||
auto ok = ent->QUICBean()->TryParseLink(str);
|
||||
auto ok = ent->TUIC()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ namespace Subscription {
|
||||
if (str.startsWith("wg://")) {
|
||||
needFix = false;
|
||||
ent = Configs::ProfileManager::NewProxyEntity("wireguard");
|
||||
auto ok = ent->WireguardBean()->TryParseLink(str);
|
||||
auto ok = ent->Wireguard()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ namespace Subscription {
|
||||
if (str.startsWith("ssh://")) {
|
||||
needFix = false;
|
||||
ent = Configs::ProfileManager::NewProxyEntity("ssh");
|
||||
auto ok = ent->SSHBean()->TryParseLink(str);
|
||||
auto ok = ent->SSH()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
@ -266,83 +266,83 @@ namespace Subscription {
|
||||
// SOCKS
|
||||
if (out["type"] == "socks") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("socks");
|
||||
auto ok = ent->SocksHTTPBean()->TryParseJson(out);
|
||||
auto ok = ent->Socks()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// HTTP
|
||||
if (out["type"] == "http") {
|
||||
auto ok = ent->SocksHTTPBean()->TryParseJson(out);
|
||||
auto ok = ent->Http()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// ShadowSocks
|
||||
if (out["type"] == "shadowsocks") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("shadowsocks");
|
||||
auto ok = ent->ShadowSocksBean()->TryParseJson(out);
|
||||
auto ok = ent->ShadowSocks()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// VMess
|
||||
if (out["type"] == "vmess") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("vmess");
|
||||
auto ok = ent->VMessBean()->TryParseJson(out);
|
||||
auto ok = ent->VMess()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// VLESS
|
||||
if (out["type"] == "vless") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("vless");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseJson(out);
|
||||
auto ok = ent->VLESS()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Trojan
|
||||
if (out["type"] == "trojan") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("trojan");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseJson(out);
|
||||
auto ok = ent->Trojan()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// AnyTLS
|
||||
if (out["type"] == "anytls") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("anytls");
|
||||
auto ok = ent->AnyTLSBean()->TryParseJson(out);
|
||||
auto ok = ent->AnyTLS()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Hysteria1
|
||||
if (out["type"] == "hysteria") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("hysteria");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
auto ok = ent->Hysteria()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Hysteria2
|
||||
if (out["type"] == "hysteria2") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("hysteria2");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
auto ok = ent->Hysteria2()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// TUIC
|
||||
if (out["type"] == "tuic") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("tuic");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
auto ok = ent->TUIC()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Wireguard
|
||||
if (out["type"] == "wireguard") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("wireguard");
|
||||
auto ok = ent->WireguardBean()->TryParseJson(out);
|
||||
auto ok = ent->Wireguard()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// SSH
|
||||
if (out["type"] == "ssh") {
|
||||
ent = Configs::ProfileManager::NewProxyEntity("ssh");
|
||||
auto ok = ent->SSHBean()->TryParseJson(out);
|
||||
auto ok = ent->SSH()->ParseFromJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
@ -355,7 +355,7 @@ namespace Subscription {
|
||||
void RawUpdater::updateWireguardFileConfig(const QString& str)
|
||||
{
|
||||
auto ent = Configs::ProfileManager::NewProxyEntity("wireguard");
|
||||
auto ok = ent->WireguardBean()->TryParseLink(str);
|
||||
auto ok = ent->Wireguard()->ParseFromLink(str);
|
||||
if (!ok) return;
|
||||
updated_order += ent;
|
||||
}
|
||||
@ -434,23 +434,23 @@ namespace Subscription {
|
||||
if (type == "socks5") type = "socks";
|
||||
|
||||
auto ent = Configs::ProfileManager::NewProxyEntity(type);
|
||||
if (ent->bean->version == -114514) continue;
|
||||
if (ent->outbound->DisplayType().isEmpty()) continue;
|
||||
bool needFix = false;
|
||||
|
||||
// common
|
||||
ent->bean->name = Node2QString(proxy["name"]);
|
||||
ent->bean->serverAddress = Node2QString(proxy["server"]);
|
||||
ent->bean->serverPort = Node2Int(proxy["port"]);
|
||||
ent->outbound->commons->name = Node2QString(proxy["name"]);
|
||||
ent->outbound->commons->server = Node2QString(proxy["server"]);
|
||||
ent->outbound->commons->server_port = Node2Int(proxy["port"]);
|
||||
|
||||
if (type_clash == "ss") {
|
||||
auto bean = ent->ShadowSocksBean();
|
||||
auto bean = ent->ShadowSocks();
|
||||
bean->method = Node2QString(proxy["cipher"]).replace("dummy", "none");
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
|
||||
// UDP over TCP
|
||||
if (Node2Bool(proxy["udp-over-tcp"])) {
|
||||
bean->uot = Node2Int(proxy["udp-over-tcp-version"]);
|
||||
if (bean->uot == 0) bean->uot = 2;
|
||||
if (bean->uot == 0) bean->uot = true;
|
||||
}
|
||||
|
||||
if (proxy.contains("plugin") && proxy.contains("plugin-opts")) {
|
||||
@ -480,56 +480,51 @@ namespace Subscription {
|
||||
|
||||
// sing-mux
|
||||
auto smux = NodeChild(proxy, {"smux"});
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->mux_state = 1;
|
||||
} else if (type == "socks" || type == "http") {
|
||||
auto bean = ent->SocksHTTPBean();
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
||||
} else if (type == "http") {
|
||||
auto bean = ent->Http();
|
||||
bean->username = Node2QString(proxy["username"]);
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
if (type == "http" && Node2Bool(proxy["tls"])) {
|
||||
bean->stream->security = "tls";
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true;
|
||||
bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(",");
|
||||
bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]);
|
||||
if (bean->stream->utlsFingerprint.isEmpty()) {
|
||||
bean->stream->utlsFingerprint = Configs::dataStore->utlsFingerprint;
|
||||
bean->tls->enabled = true;
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
||||
bean->tls->utls->enabled = true;
|
||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
||||
}
|
||||
|
||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
||||
if (reality.is_mapping()) {
|
||||
bean->stream->reality_pbk = Node2QString(reality["public-key"]);
|
||||
bean->stream->reality_sid = Node2QString(reality["short-id"]);
|
||||
bean->tls->reality->enabled = true;
|
||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
||||
}
|
||||
}
|
||||
} else if (type == "trojan" || type == "vless") {
|
||||
} else if (type == "socks") {
|
||||
auto bean = ent->Socks();
|
||||
bean->username = Node2QString(proxy["username"]);
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
} else if (type == "trojan") {
|
||||
needFix = true;
|
||||
auto bean = ent->TrojanVLESSBean();
|
||||
if (type == "vless") {
|
||||
bean->flow = Node2QString(proxy["flow"]);
|
||||
bean->password = Node2QString(proxy["uuid"]);
|
||||
// meta packet encoding
|
||||
if (Node2Bool(proxy["packet-addr"])) {
|
||||
bean->stream->packet_encoding = "packetaddr";
|
||||
} else {
|
||||
// For VLESS, default to use xudp
|
||||
bean->stream->packet_encoding = "xudp";
|
||||
}
|
||||
} else {
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
}
|
||||
bean->stream->security = "tls";
|
||||
bean->stream->network = Node2QString(proxy["network"], "tcp");
|
||||
bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(",");
|
||||
bean->stream->allow_insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]);
|
||||
if (bean->stream->utlsFingerprint.isEmpty()) {
|
||||
bean->stream->utlsFingerprint = Configs::dataStore->utlsFingerprint;
|
||||
auto bean = ent->Trojan();
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
bean->tls->enabled = true;
|
||||
bean->transport->type = Node2QString(proxy["network"], "tcp");
|
||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
||||
bean->tls->utls->enabled = true;
|
||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
||||
}
|
||||
|
||||
// sing-mux
|
||||
auto smux = NodeChild(proxy, {"smux"});
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->mux_state = 1;
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
||||
|
||||
// opts
|
||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
||||
@ -539,51 +534,57 @@ namespace Subscription {
|
||||
for (auto header: headers.as_map()) {
|
||||
if (Node2QString(header.first).toLower() == "host") {
|
||||
if (header.second.is_string())
|
||||
bean->stream->host = Node2QString(header.second);
|
||||
bean->transport->host = Node2QString(header.second);
|
||||
else if (header.second.is_sequence() && header.second[0].is_string())
|
||||
bean->stream->host = Node2QString(header.second[0]);
|
||||
bean->transport->host = Node2QString(header.second[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bean->stream->path = Node2QString(ws["path"]);
|
||||
bean->stream->ws_early_data_length = Node2Int(ws["max-early-data"]);
|
||||
bean->stream->ws_early_data_name = Node2QString(ws["early-data-header-name"]);
|
||||
bean->transport->path = Node2QString(ws["path"]);
|
||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
||||
}
|
||||
|
||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
||||
if (grpc.is_mapping()) {
|
||||
bean->stream->path = Node2QString(grpc["grpc-service-name"]);
|
||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
||||
}
|
||||
|
||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
||||
if (reality.is_mapping()) {
|
||||
bean->stream->reality_pbk = Node2QString(reality["public-key"]);
|
||||
bean->stream->reality_sid = Node2QString(reality["short-id"]);
|
||||
bean->tls->reality->enabled = true;
|
||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
||||
}
|
||||
} else if (type == "vmess") {
|
||||
} else if (type == "vless") {
|
||||
needFix = true;
|
||||
auto bean = ent->VMessBean();
|
||||
bean->uuid = Node2QString(proxy["uuid"]);
|
||||
bean->aid = Node2Int(proxy["alterId"]);
|
||||
bean->security = Node2QString(proxy["cipher"], bean->security);
|
||||
bean->stream->network = Node2QString(proxy["network"], "tcp").replace("h2", "http");
|
||||
bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(",");
|
||||
if (Node2Bool(proxy["tls"])) bean->stream->security = "tls";
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true;
|
||||
bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]);
|
||||
if (bean->stream->utlsFingerprint.isEmpty()) {
|
||||
bean->stream->utlsFingerprint = Configs::dataStore->utlsFingerprint;
|
||||
auto bean = ent->VLESS();
|
||||
if (type == "vless") {
|
||||
bean->flow = Node2QString(proxy["flow"]);
|
||||
bean->uuid = Node2QString(proxy["uuid"]);
|
||||
// meta packet encoding
|
||||
if (Node2Bool(proxy["packet-addr"])) {
|
||||
bean->packet_encoding = "packetaddr";
|
||||
} else {
|
||||
// For VLESS, default to use xudp
|
||||
bean->packet_encoding = "xudp";
|
||||
}
|
||||
}
|
||||
bean->tls->enabled = true;
|
||||
bean->transport->type = Node2QString(proxy["network"], "tcp");
|
||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->tls->utls->enabled = true;
|
||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
||||
}
|
||||
|
||||
// sing-mux
|
||||
auto smux = NodeChild(proxy, {"smux"});
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->mux_state = 1;
|
||||
|
||||
// meta packet encoding
|
||||
if (Node2Bool(proxy["xudp"])) bean->stream->packet_encoding = "xudp";
|
||||
if (Node2Bool(proxy["packet-addr"])) bean->stream->packet_encoding = "packetaddr";
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = 1;
|
||||
|
||||
// opts
|
||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
||||
@ -592,100 +593,156 @@ namespace Subscription {
|
||||
if (headers.is_mapping()) {
|
||||
for (auto header: headers.as_map()) {
|
||||
if (Node2QString(header.first).toLower() == "host") {
|
||||
bean->stream->host = Node2QString(header.second);
|
||||
if (header.second.is_string())
|
||||
bean->transport->host = Node2QString(header.second);
|
||||
else if (header.second.is_sequence() && header.second[0].is_string())
|
||||
bean->transport->host = Node2QString(header.second[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bean->stream->path = Node2QString(ws["path"]);
|
||||
bean->stream->ws_early_data_length = Node2Int(ws["max-early-data"]);
|
||||
bean->stream->ws_early_data_name = Node2QString(ws["early-data-header-name"]);
|
||||
bean->transport->path = Node2QString(ws["path"]);
|
||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
||||
}
|
||||
|
||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
||||
if (grpc.is_mapping()) {
|
||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
||||
}
|
||||
|
||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
||||
if (reality.is_mapping()) {
|
||||
bean->tls->reality->enabled = true;
|
||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
||||
}
|
||||
} else if (type == "vmess") {
|
||||
needFix = true;
|
||||
auto bean = ent->VMess();
|
||||
bean->uuid = Node2QString(proxy["uuid"]);
|
||||
bean->alter_id = Node2Int(proxy["alterId"]);
|
||||
bean->security = Node2QString(proxy["cipher"], bean->security);
|
||||
bean->transport->type = Node2QString(proxy["network"], "tcp").replace("h2", "http");
|
||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
if (Node2Bool(proxy["tls"])) bean->tls->enabled = true;
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
||||
bean->tls->utls->enabled = true;
|
||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
||||
}
|
||||
|
||||
// sing-mux
|
||||
auto smux = NodeChild(proxy, {"smux"});
|
||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
||||
|
||||
// meta packet encoding
|
||||
if (Node2Bool(proxy["xudp"])) bean->packet_encoding = "xudp";
|
||||
if (Node2Bool(proxy["packet-addr"])) bean->packet_encoding = "packetaddr";
|
||||
|
||||
// opts
|
||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
||||
if (ws.is_mapping()) {
|
||||
auto headers = ws["headers"];
|
||||
if (headers.is_mapping()) {
|
||||
for (auto header: headers.as_map()) {
|
||||
if (Node2QString(header.first).toLower() == "host") {
|
||||
bean->transport->host = Node2QString(header.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bean->transport->path = Node2QString(ws["path"]);
|
||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
||||
// for Xray
|
||||
if (Node2QString(ws["early-data-header-name"]) == "Sec-WebSocket-Protocol") {
|
||||
bean->stream->path += "?ed=" + Node2QString(ws["max-early-data"]);
|
||||
bean->transport->path += "?ed=" + Node2QString(ws["max-early-data"]);
|
||||
}
|
||||
}
|
||||
|
||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
||||
if (grpc.is_mapping()) {
|
||||
bean->stream->path = Node2QString(grpc["grpc-service-name"]);
|
||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
||||
}
|
||||
|
||||
auto h2 = NodeChild(proxy, {"h2-opts", "h2-opt"});
|
||||
if (h2.is_mapping()) {
|
||||
auto hosts = h2["host"];
|
||||
for (auto host: hosts) {
|
||||
bean->stream->host = Node2QString(host);
|
||||
bean->transport->host = Node2QString(host);
|
||||
break;
|
||||
}
|
||||
bean->stream->path = Node2QString(h2["path"]);
|
||||
bean->transport->path = Node2QString(h2["path"]);
|
||||
}
|
||||
auto tcp_http = NodeChild(proxy, {"http-opts", "http-opt"});
|
||||
if (tcp_http.is_mapping()) {
|
||||
bean->stream->network = "tcp";
|
||||
bean->stream->header_type = "http";
|
||||
bean->transport->type = "http";
|
||||
auto headers = tcp_http["headers"];
|
||||
if (headers.is_mapping()) {
|
||||
for (auto header: headers.as_map()) {
|
||||
if (Node2QString(header.first).toLower() == "host") {
|
||||
bean->stream->host = Node2QString(header.second);
|
||||
bean->transport->host = Node2QString(header.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto paths = tcp_http["path"];
|
||||
if (paths.is_string())
|
||||
bean->stream->path = Node2QString(paths);
|
||||
bean->transport->path = Node2QString(paths);
|
||||
else if (paths.is_sequence() && paths[0].is_string())
|
||||
bean->stream->path = Node2QString(paths[0]);
|
||||
bean->transport->path = Node2QString(paths[0]);
|
||||
}
|
||||
} else if (type == "anytls") {
|
||||
needFix = true;
|
||||
auto bean = ent->AnyTLSBean();
|
||||
auto bean = ent->AnyTLS();
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
bean->stream->security = "tls";
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true;
|
||||
bean->stream->sni = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->stream->alpn = Node2QStringList(proxy["alpn"]).join(",");
|
||||
bean->stream->utlsFingerprint = Node2QString(proxy["client-fingerprint"]);
|
||||
if (bean->stream->utlsFingerprint.isEmpty()) {
|
||||
bean->stream->utlsFingerprint = Configs::dataStore->utlsFingerprint;
|
||||
bean->tls->enabled = true;
|
||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
||||
bean->tls->utls->enabled = true;
|
||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
||||
}
|
||||
|
||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
||||
if (reality.is_mapping()) {
|
||||
bean->stream->reality_pbk = Node2QString(reality["public-key"]);
|
||||
bean->stream->reality_sid = Node2QString(reality["short-id"]);
|
||||
bean->tls->reality->enabled = true;
|
||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
||||
}
|
||||
} else if (type == "hysteria") {
|
||||
auto bean = ent->QUICBean();
|
||||
auto bean = ent->Hysteria();
|
||||
|
||||
bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->tls->enabled = true;
|
||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
auto alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->caText = Node2QString(proxy["ca-str"]);
|
||||
if (!alpn.isEmpty()) bean->alpn = alpn[0];
|
||||
bean->sni = Node2QString(proxy["sni"]);
|
||||
bean->tls->certificate = Node2QString(proxy["ca-str"]);
|
||||
if (!alpn.isEmpty()) bean->tls->alpn = {alpn[0]};
|
||||
bean->tls->server_name = Node2QString(proxy["sni"]);
|
||||
|
||||
auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"]));
|
||||
auto auth = Node2QString(proxy["auth"]);
|
||||
if (!auth_str.isEmpty()) {
|
||||
bean->authPayloadType = Configs::QUICBean::hysteria_auth_string;
|
||||
bean->authPayload = auth_str;
|
||||
bean->auth_str = auth_str;
|
||||
}
|
||||
if (!auth.isEmpty()) {
|
||||
bean->authPayloadType = Configs::QUICBean::hysteria_auth_base64;
|
||||
bean->authPayload = auth;
|
||||
bean->auth = auth;
|
||||
}
|
||||
bean->obfsPassword = Node2QString(proxy["obfs"]);
|
||||
bean->obfs = Node2QString(proxy["obfs"]);
|
||||
|
||||
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disableMtuDiscovery = true;
|
||||
bean->streamReceiveWindow = Node2Int(proxy["recv-window"]);
|
||||
bean->connectionReceiveWindow = Node2Int(proxy["recv-window-conn"]);
|
||||
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disable_mtu_discovery = true;
|
||||
bean->recv_window = Node2Int(proxy["recv-window"]);
|
||||
bean->recv_window_conn = Node2Int(proxy["recv-window-conn"]);
|
||||
|
||||
auto upMbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
|
||||
auto downMbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
|
||||
if (upMbps > 0) bean->uploadMbps = upMbps;
|
||||
if (downMbps > 0) bean->downloadMbps = downMbps;
|
||||
if (upMbps > 0) bean->up_mbps = upMbps;
|
||||
if (downMbps > 0) bean->down_mbps = downMbps;
|
||||
|
||||
auto ports = Node2QString(proxy["ports"]);
|
||||
if (!ports.isEmpty()) {
|
||||
@ -699,20 +756,21 @@ namespace Subscription {
|
||||
modifiedPort.replace("-", ":");
|
||||
serverPorts.append(modifiedPort);
|
||||
}
|
||||
bean->serverPorts = serverPorts;
|
||||
bean->server_ports = serverPorts;
|
||||
}
|
||||
} else if (type == "hysteria2") {
|
||||
auto bean = ent->QUICBean();
|
||||
auto bean = ent->Hysteria2();
|
||||
|
||||
bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->caText = Node2QString(proxy["ca-str"]);
|
||||
bean->sni = Node2QString(proxy["sni"]);
|
||||
bean->tls->enabled = true;
|
||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->tls->certificate = Node2QString(proxy["ca-str"]);
|
||||
bean->tls->server_name = Node2QString(proxy["sni"]);
|
||||
|
||||
bean->obfsPassword = Node2QString(proxy["obfs-password"]);
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
|
||||
bean->uploadMbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
|
||||
bean->downloadMbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
|
||||
bean->up_mbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
|
||||
bean->down_mbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
|
||||
|
||||
auto ports = Node2QString(proxy["ports"]);
|
||||
if (!ports.isEmpty()) {
|
||||
@ -726,10 +784,10 @@ namespace Subscription {
|
||||
modifiedPort.replace("-", ":");
|
||||
serverPorts.append(modifiedPort);
|
||||
}
|
||||
bean->serverPorts = serverPorts;
|
||||
bean->server_ports = serverPorts;
|
||||
}
|
||||
} else if (type == "tuic") {
|
||||
auto bean = ent->QUICBean();
|
||||
auto bean = ent->TUIC();
|
||||
|
||||
bean->uuid = Node2QString(proxy["uuid"]);
|
||||
bean->password = Node2QString(proxy["password"]);
|
||||
@ -738,21 +796,22 @@ namespace Subscription {
|
||||
bean->heartbeat = Int2String(Node2Int(proxy["heartbeat-interval"])) + "ms";
|
||||
}
|
||||
|
||||
bean->udpRelayMode = Node2QString(proxy["udp-relay-mode"], bean->udpRelayMode);
|
||||
bean->congestionControl = Node2QString(proxy["congestion-controller"], bean->congestionControl);
|
||||
bean->udp_relay_mode = Node2QString(proxy["udp-relay-mode"], "native");
|
||||
bean->congestion_control = Node2QString(proxy["congestion-controller"], "bbr");
|
||||
|
||||
bean->disableSni = Node2Bool(proxy["disable-sni"]);
|
||||
bean->zeroRttHandshake = Node2Bool(proxy["reduce-rtt"]);
|
||||
bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->alpn = Node2QStringList(proxy["alpn"]).join(",");
|
||||
bean->caText = Node2QString(proxy["ca-str"]);
|
||||
bean->sni = Node2QString(proxy["sni"]);
|
||||
bean->tls->enabled = true;
|
||||
bean->tls->disable_sni = Node2Bool(proxy["disable-sni"]);
|
||||
bean->zero_rtt_handshake = Node2Bool(proxy["reduce-rtt"]);
|
||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
||||
bean->tls->certificate = Node2QString(proxy["ca-str"]);
|
||||
bean->tls->server_name = Node2QString(proxy["sni"]);
|
||||
|
||||
if (Node2Bool(proxy["udp-over-stream"])) bean->uos = true;
|
||||
if (Node2Bool(proxy["udp-over-stream"])) bean->udp_over_stream = true;
|
||||
|
||||
if (!Node2QString(proxy["ip"]).isEmpty()) {
|
||||
if (bean->sni.isEmpty()) bean->sni = bean->serverAddress;
|
||||
bean->serverAddress = Node2QString(proxy["ip"]);
|
||||
if (bean->tls->server_name.isEmpty()) bean->tls->server_name = bean->commons->server;
|
||||
bean->commons->server = Node2QString(proxy["ip"]);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
@ -762,7 +821,7 @@ namespace Subscription {
|
||||
updated_order += ent;
|
||||
}
|
||||
} catch (const fkyaml::exception &ex) {
|
||||
runOnUiThread([=,this] {
|
||||
runOnUiThread([=] {
|
||||
MessageBoxWarning("YAML Exception", ex.what());
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ namespace Configs {
|
||||
continue;
|
||||
}
|
||||
profiles[id] = ent;
|
||||
if (ent->type == "extracore") extraCorePaths.insert(ent->ExtraCoreBean()->extraCorePath);
|
||||
if (ent->type == "extracore") extraCorePaths.insert(ent->ExtraCore()->extraCorePath);
|
||||
}
|
||||
// Clear Corrupted profile
|
||||
for (auto id: delProfile) {
|
||||
@ -117,9 +117,77 @@ namespace Configs {
|
||||
JsonStore::Save();
|
||||
}
|
||||
|
||||
void migrateBeanToOutbound(const std::shared_ptr<ProxyEntity> &ent)
|
||||
{
|
||||
if (ent->type == "chain")
|
||||
{
|
||||
auto bean = dynamic_cast<Configs::ChainBean*>(ent->bean.get());
|
||||
if (bean == nullptr)
|
||||
{
|
||||
qDebug() << "Broken state in migrate for chain";
|
||||
return;
|
||||
}
|
||||
auto chain = ent->Chain();
|
||||
if (chain == nullptr)
|
||||
{
|
||||
qDebug() << "Invalid state in migrate for chain";
|
||||
return;
|
||||
}
|
||||
chain->commons->name = bean->name;
|
||||
chain->list = bean->list;
|
||||
} else if (ent->type == "custom")
|
||||
{
|
||||
auto bean = dynamic_cast<Configs::CustomBean*>(ent->bean.get());
|
||||
if (bean == nullptr)
|
||||
{
|
||||
qDebug() << "Broken state in migrate for custom";
|
||||
return;
|
||||
}
|
||||
auto custom = ent->Custom();
|
||||
if (custom == nullptr)
|
||||
{
|
||||
qDebug() << "Invalid state in migrate for custom";
|
||||
return;
|
||||
}
|
||||
custom->commons->name = bean->name;
|
||||
custom->config = bean->config_simple;
|
||||
custom->type = bean->core == "internal" ? "outbound" : "fullconfig";
|
||||
} else if (ent->type == "extracore")
|
||||
{
|
||||
auto bean = dynamic_cast<Configs::ExtraCoreBean*>(ent->bean.get());
|
||||
if (bean == nullptr)
|
||||
{
|
||||
qDebug() << "Broken state in migrate for extracore";
|
||||
return;
|
||||
}
|
||||
auto extraCore = ent->ExtraCore();
|
||||
if (extraCore == nullptr)
|
||||
{
|
||||
qDebug() << "Invalid state in migrate for extracore";
|
||||
return;
|
||||
}
|
||||
extraCore->commons->name = bean->name;
|
||||
extraCore->socksAddress = bean->socksAddress;
|
||||
extraCore->socksPort = bean->socksPort;
|
||||
extraCore->extraCorePath = bean->extraCorePath;
|
||||
extraCore->extraCoreArgs = bean->extraCoreArgs;
|
||||
extraCore->extraCoreConf = bean->extraCoreConf;
|
||||
extraCore->noLogs = bean->noLogs;
|
||||
} else
|
||||
{
|
||||
auto beanJson = ent->bean->BuildCoreObjSingBox();
|
||||
auto obj = beanJson.outbound;
|
||||
if (ent->bean->mux_state == 0) obj["multiplex"] = QJsonObject{};
|
||||
else if (ent->bean->mux_state == 1) obj["multiplex"] = QJsonObject{{"enabled", true}};
|
||||
else if (ent->bean->mux_state == 2) obj["multiplex"] = QJsonObject{{"enabled", false}};
|
||||
ent->outbound->ParseFromJson(obj);
|
||||
ent->outbound->commons->name = ent->bean->name;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ProxyEntity> ProfileManager::LoadProxyEntity(const QString &jsonPath) {
|
||||
// Load type
|
||||
ProxyEntity ent0(nullptr, nullptr);
|
||||
ProxyEntity ent0(nullptr, nullptr, nullptr);
|
||||
ent0.fn = jsonPath;
|
||||
auto validJson = ent0.Load();
|
||||
auto type = ent0.type;
|
||||
@ -137,6 +205,15 @@ namespace Configs {
|
||||
ent->load_control_must = true;
|
||||
ent->fn = jsonPath;
|
||||
ent->Load();
|
||||
ent->_remove("bean");
|
||||
}
|
||||
|
||||
if (!QString2QJsonObject(QString(ent->last_save_content.toStdString().c_str())).contains("outbound"))
|
||||
{
|
||||
qDebug() << "migrating" << ent->type;
|
||||
// migrate
|
||||
migrateBeanToOutbound(ent);
|
||||
ent->Save();
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
@ -151,48 +228,64 @@ namespace Configs {
|
||||
return routingChain;
|
||||
}
|
||||
|
||||
// 新建的不给 fn 和 id
|
||||
|
||||
std::shared_ptr<ProxyEntity> ProfileManager::NewProxyEntity(const QString &type) {
|
||||
Configs::AbstractBean *bean;
|
||||
Configs::outbound *outbound;
|
||||
|
||||
if (type == "socks") {
|
||||
bean = new Configs::SocksHttpBean(Configs::SocksHttpBean::type_Socks5);
|
||||
outbound = new Configs::socks();
|
||||
} else if (type == "http") {
|
||||
bean = new Configs::SocksHttpBean(Configs::SocksHttpBean::type_HTTP);
|
||||
outbound = new Configs::http();
|
||||
} else if (type == "shadowsocks") {
|
||||
bean = new Configs::ShadowSocksBean();
|
||||
outbound = new Configs::shadowsocks();
|
||||
} else if (type == "chain") {
|
||||
bean = new Configs::ChainBean();
|
||||
outbound = new Configs::chain();
|
||||
} else if (type == "vmess") {
|
||||
bean = new Configs::VMessBean();
|
||||
outbound = new Configs::vmess();
|
||||
} else if (type == "trojan") {
|
||||
bean = new Configs::TrojanVLESSBean(Configs::TrojanVLESSBean::proxy_Trojan);
|
||||
outbound = new Configs::Trojan();
|
||||
} else if (type == "vless") {
|
||||
bean = new Configs::TrojanVLESSBean(Configs::TrojanVLESSBean::proxy_VLESS);
|
||||
outbound = new Configs::vless();
|
||||
} else if (type == "hysteria") {
|
||||
bean = new Configs::QUICBean(Configs::QUICBean::proxy_Hysteria);
|
||||
outbound = new Configs::hysteria();
|
||||
} else if (type == "hysteria2") {
|
||||
bean = new Configs::QUICBean(Configs::QUICBean::proxy_Hysteria2);
|
||||
outbound = new Configs::hysteria2();
|
||||
} else if (type == "tuic") {
|
||||
bean = new Configs::QUICBean(Configs::QUICBean::proxy_TUIC);
|
||||
outbound = new Configs::tuic();
|
||||
} else if (type == "anytls") {
|
||||
bean = new Configs::AnyTLSBean();
|
||||
outbound = new Configs::anyTLS();
|
||||
} else if (type == "wireguard") {
|
||||
bean = new Configs::WireguardBean(Configs::WireguardBean());
|
||||
outbound = new Configs::wireguard();
|
||||
} else if (type == "tailscale") {
|
||||
bean = new Configs::TailscaleBean(Configs::TailscaleBean());
|
||||
outbound = new Configs::tailscale();
|
||||
} else if (type == "ssh") {
|
||||
bean = new Configs::SSHBean(Configs::SSHBean());
|
||||
outbound = new Configs::ssh();
|
||||
} else if (type == "custom") {
|
||||
bean = new Configs::CustomBean();
|
||||
outbound = new Configs::Custom();
|
||||
} else if (type == "extracore") {
|
||||
bean = new Configs::ExtraCoreBean();
|
||||
outbound = new Configs::extracore();
|
||||
} else {
|
||||
bean = new Configs::AbstractBean(-114514);
|
||||
outbound = new Configs::outbound();
|
||||
}
|
||||
|
||||
auto ent = std::make_shared<ProxyEntity>(bean, type);
|
||||
auto ent = std::make_shared<ProxyEntity>(outbound, bean, type);
|
||||
return ent;
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
ProxyEntity::ProxyEntity(Configs::AbstractBean *bean, const QString &type_) {
|
||||
ProxyEntity::ProxyEntity(Configs::outbound *outbound, Configs::AbstractBean *bean, const QString &type_)
|
||||
{
|
||||
if (type_ != nullptr) this->type = type_;
|
||||
|
||||
_add(new configItem("type", &type, itemType::string));
|
||||
@ -17,22 +18,7 @@ namespace Configs
|
||||
if (bean != nullptr) {
|
||||
this->bean = std::shared_ptr<Configs::AbstractBean>(bean);
|
||||
_add(new configItem("bean", dynamic_cast<JsonStore *>(bean), itemType::jsonStore));
|
||||
_add(new configItem("traffic", dynamic_cast<JsonStore *>(traffic_data.get()), itemType::jsonStore));
|
||||
}
|
||||
};
|
||||
|
||||
ProxyEntity::ProxyEntity(Configs::outbound *outbound, const QString &type_)
|
||||
{
|
||||
if (type_ != nullptr) this->type = type_;
|
||||
|
||||
_add(new configItem("type", &type, itemType::string));
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("gid", &gid, itemType::integer));
|
||||
_add(new configItem("yc", &latency, itemType::integer));
|
||||
_add(new configItem("dl", &dl_speed, itemType::string));
|
||||
_add(new configItem("ul", &ul_speed, itemType::string));
|
||||
_add(new configItem("report", &full_test_report, itemType::string));
|
||||
_add(new configItem("country", &test_country, itemType::string));
|
||||
|
||||
if (outbound != nullptr) {
|
||||
this->outbound = std::shared_ptr<Configs::outbound>(outbound);
|
||||
|
||||
@ -27,11 +27,14 @@
|
||||
|
||||
namespace Configs_ConfigItem {
|
||||
|
||||
// 添加关联
|
||||
void JsonStore::_add(configItem *item) {
|
||||
_map.insert(item->name, std::shared_ptr<configItem>(item));
|
||||
}
|
||||
|
||||
void JsonStore::_remove(const QString &key) {
|
||||
_map.remove(key);
|
||||
}
|
||||
|
||||
QString JsonStore::_name(void *p) {
|
||||
for (const auto &_item: _map) {
|
||||
if (_item->ptr == p) return _item->name;
|
||||
@ -40,7 +43,6 @@ namespace Configs_ConfigItem {
|
||||
}
|
||||
|
||||
std::shared_ptr<configItem> JsonStore::_get(const QString &name) {
|
||||
// 直接 [] 会设置一个 nullptr ,所以先判断是否存在
|
||||
if (_map.contains(name)) {
|
||||
return _map[name];
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "include/ui/mainwindow.h"
|
||||
|
||||
#include "include/dataStore/ProfileFilter.hpp"
|
||||
#include "include/configs/ConfigBuilder.hpp"
|
||||
#include "include/configs/sub/GroupUpdater.hpp"
|
||||
#include "include/sys/Process.hpp"
|
||||
#include "include/sys/AutoRun.hpp"
|
||||
@ -18,6 +17,7 @@
|
||||
#include "3rdparty/qrcodegen.hpp"
|
||||
#include "3rdparty/qv2ray/v2/ui/LogHighlighter.hpp"
|
||||
#include "3rdparty/QrDecoder.h"
|
||||
#include "include/configs/generate.h"
|
||||
#include "include/ui/group/dialog_edit_group.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@ -1777,7 +1777,7 @@ void MainWindow::on_menu_export_config_triggered() {
|
||||
auto ent = ents.first();
|
||||
if (ent->bean->DisplayCoreType() != software_core_name) return;
|
||||
|
||||
auto result = BuildConfig(ent, false, true);
|
||||
auto result = Configs::BuildSingBoxConfig(ent);
|
||||
QString config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
|
||||
@ -1789,12 +1789,12 @@ void MainWindow::on_menu_export_config_triggered() {
|
||||
msg.setDefaultButton(QMessageBox::Ok);
|
||||
msg.exec();
|
||||
if (msg.clickedButton() == button_1) {
|
||||
result = BuildConfig(ent, false, false);
|
||||
result = BuildSingBoxConfig(ent);
|
||||
config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
} else if (msg.clickedButton() == button_2) {
|
||||
result = BuildConfig(ent, true, false);
|
||||
config_core = QJsonObject2QString(result->coreConfig, true);
|
||||
auto res = Configs::BuildTestConfig({ent});
|
||||
config_core = QJsonObject2QString(res->coreConfig, true);
|
||||
QApplication::clipboard()->setText(config_core);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "include/ui/mainwindow.h"
|
||||
|
||||
#include "include/dataStore/Database.hpp"
|
||||
#include "include/configs/ConfigBuilder.hpp"
|
||||
#include "include/stats/traffic/TrafficLooper.hpp"
|
||||
#include "include/api/RPC.h"
|
||||
#include "include/ui/utils//MessageBoxTimer.h"
|
||||
@ -12,6 +11,9 @@
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "include/configs/generate.h"
|
||||
#include "include/sys/Process.hpp"
|
||||
|
||||
// rpc
|
||||
|
||||
using namespace API;
|
||||
@ -466,7 +468,7 @@ void MainWindow::profile_start(int _id) {
|
||||
auto group = Configs::profileManager->GetGroup(ent->gid);
|
||||
if (group == nullptr || group->archive) return;
|
||||
|
||||
auto result = BuildConfig(ent, false, false);
|
||||
auto result = Configs::BuildSingBoxConfig(ent);
|
||||
if (!result->error.isEmpty()) {
|
||||
MessageBoxWarning(tr("BuildConfig return error"), result->error);
|
||||
return;
|
||||
@ -673,7 +675,7 @@ void MainWindow::profile_stop(bool crash, bool block, bool manual) {
|
||||
|
||||
if (block) blocker.unlock();
|
||||
|
||||
runOnUiThread([=, this, &blocker] {
|
||||
runOnUiThread([=, this] {
|
||||
refresh_status();
|
||||
refresh_proxy_list_impl_refresh_data(id, true);
|
||||
|
||||
|
||||
@ -245,7 +245,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
auto _innerWidget = new EditCustom(this);
|
||||
innerWidget = _innerWidget;
|
||||
innerEditor = _innerWidget;
|
||||
customType = newEnt ? type : ent->CustomBean()->core;
|
||||
customType = newEnt ? type : ent->Custom()->type;
|
||||
_innerWidget->preset_core = customType;
|
||||
type = "custom";
|
||||
ui->apply_to_group->hide();
|
||||
|
||||
@ -19,20 +19,20 @@ EditAnyTLS::~EditAnyTLS() {
|
||||
|
||||
void EditAnyTLS::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->AnyTLSBean();
|
||||
auto bean = this->ent->AnyTLS();
|
||||
|
||||
ui->password->setText(bean->password);
|
||||
ui->interval->setText(Int2String(bean->idle_session_check_interval));
|
||||
ui->timeout->setText(Int2String(bean->idle_session_timeout));
|
||||
ui->interval->setText(bean->idle_session_check_interval);
|
||||
ui->timeout->setText(bean->idle_session_timeout);
|
||||
ui->min->setText(Int2String(bean->min_idle_session));
|
||||
}
|
||||
|
||||
bool EditAnyTLS::onEnd() {
|
||||
auto bean = this->ent->AnyTLSBean();
|
||||
auto bean = this->ent->AnyTLS();
|
||||
|
||||
bean->password = ui->password->text();
|
||||
bean->idle_session_check_interval = ui->interval->text().toInt();
|
||||
bean->idle_session_timeout = ui->timeout->text().toInt();
|
||||
bean->idle_session_check_interval = ui->interval->text();
|
||||
bean->idle_session_timeout = ui->timeout->text();
|
||||
bean->min_idle_session = ui->min->text().toInt();
|
||||
|
||||
return true;
|
||||
|
||||
@ -16,7 +16,7 @@ EditChain::~EditChain() {
|
||||
|
||||
void EditChain::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->ChainBean();
|
||||
auto bean = this->ent->Chain();
|
||||
|
||||
for (auto id: bean->list) {
|
||||
AddProfileToListIfExist(id);
|
||||
@ -29,7 +29,7 @@ bool EditChain::onEnd() {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bean = this->ent->ChainBean();
|
||||
auto bean = this->ent->Chain();
|
||||
|
||||
QList<int> idList;
|
||||
for (int i = 0; i < ui->listWidget->count(); i++) {
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
#include "include/ui/profile/edit_custom.h"
|
||||
|
||||
#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||
#include "include/configs/proxy/CustomBean.hpp"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
#include "include/configs/ConfigBuilder.hpp"
|
||||
#include "include/dataStore/Database.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
@ -26,16 +23,16 @@ EditCustom::~EditCustom() {
|
||||
|
||||
void EditCustom::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->CustomBean();
|
||||
auto bean = this->ent->Custom();
|
||||
|
||||
if (preset_core == "internal") {
|
||||
if (preset_core == "outbound") {
|
||||
preset_command = preset_config = "";
|
||||
ui->config_simple->setPlaceholderText(
|
||||
"{\n"
|
||||
" \"type\": \"socks\",\n"
|
||||
" // ...\n"
|
||||
"}");
|
||||
} else if (preset_core == "internal-full") {
|
||||
} else if (preset_core == "fullconfig") {
|
||||
preset_command = preset_config = "";
|
||||
ui->config_simple->setPlaceholderText(
|
||||
"{\n"
|
||||
@ -45,18 +42,16 @@ void EditCustom::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
// load core ui
|
||||
ui->config_simple->setPlainText(bean->config_simple);
|
||||
ui->config_simple->setPlainText(bean->config);
|
||||
|
||||
// custom internal
|
||||
if (preset_core == "internal" || preset_core == "internal-full") {
|
||||
if (preset_core == "internal") {
|
||||
ui->core_l->setText(tr("Outbound JSON, please read the documentation."));
|
||||
} else {
|
||||
ui->core_l->setText(tr("Please fill the complete config."));
|
||||
}
|
||||
ui->w_ext1->hide();
|
||||
ui->w_ext2->hide();
|
||||
if (preset_core == "outbound") {
|
||||
ui->core_l->setText(tr("Outbound JSON, please read the documentation."));
|
||||
} else {
|
||||
ui->core_l->setText(tr("Please fill the complete config."));
|
||||
}
|
||||
ui->w_ext1->hide();
|
||||
ui->w_ext2->hide();
|
||||
}
|
||||
|
||||
bool EditCustom::onEnd() {
|
||||
@ -65,10 +60,10 @@ bool EditCustom::onEnd() {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bean = this->ent->CustomBean();
|
||||
auto bean = this->ent->Custom();
|
||||
|
||||
P_SAVE_STRING_PLAIN(config_simple)
|
||||
bean->core = preset_core;
|
||||
bean->config = ui->config_simple->toPlainText();
|
||||
bean->type = preset_core;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ EditExtraCore::~EditExtraCore() {
|
||||
void EditExtraCore::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
|
||||
auto bean = ent->ExtraCoreBean();
|
||||
auto bean = ent->ExtraCore();
|
||||
ui->socks_address->setText(bean->socksAddress);
|
||||
ui->socks_port->setValidator(new QIntValidator(1, 65534));
|
||||
ui->socks_port->setText(Int2String(bean->socksPort));
|
||||
@ -46,7 +46,7 @@ void EditExtraCore::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
bool EditExtraCore::onEnd() {
|
||||
auto bean = ent->ExtraCoreBean();
|
||||
auto bean = ent->ExtraCore();
|
||||
bean->socksAddress = ui->socks_address->text();
|
||||
bean->socksPort = ui->socks_port->text().toInt();
|
||||
bean->extraCoreConf = ui->config->toPlainText();
|
||||
|
||||
@ -15,126 +15,126 @@ EditQUIC::~EditQUIC() {
|
||||
}
|
||||
|
||||
void EditQUIC::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->QUICBean();
|
||||
|
||||
P_LOAD_INT(uploadMbps);
|
||||
P_LOAD_INT(downloadMbps);
|
||||
P_LOAD_BOOL(disableMtuDiscovery)
|
||||
P_LOAD_STRING(obfsPassword);
|
||||
P_LOAD_STRING(authPayload);
|
||||
P_LOAD_INT(streamReceiveWindow);
|
||||
P_LOAD_INT(connectionReceiveWindow);
|
||||
|
||||
P_LOAD_COMBO_INT(authPayloadType);
|
||||
P_LOAD_STRING(uuid);
|
||||
P_LOAD_STRING(password);
|
||||
|
||||
P_LOAD_COMBO_STRING(congestionControl);
|
||||
P_LOAD_COMBO_STRING(udpRelayMode);
|
||||
P_LOAD_BOOL(zeroRttHandshake);
|
||||
P_LOAD_STRING(heartbeat);
|
||||
P_LOAD_BOOL(uos);
|
||||
|
||||
// TLS
|
||||
P_LOAD_STRING(sni);
|
||||
P_LOAD_STRING(alpn);
|
||||
P_C_LOAD_STRING(caText);
|
||||
P_LOAD_BOOL(allowInsecure);
|
||||
P_LOAD_BOOL(disableSni);
|
||||
|
||||
if (bean->proxy_type == Configs::QUICBean::proxy_Hysteria || bean->proxy_type == Configs::QUICBean::proxy_Hysteria2) {
|
||||
ui->uuid->hide();
|
||||
ui->uuid_l->hide();
|
||||
ui->uuidgen->hide();
|
||||
ui->congestionControl->hide();
|
||||
ui->congestionControl_l->hide();
|
||||
ui->udpRelayMode->hide();
|
||||
ui->udpRelayMode_l->hide();
|
||||
ui->zeroRttHandshake->hide();
|
||||
ui->heartbeat->hide();
|
||||
ui->heartbeat_l->hide();
|
||||
ui->uos->hide();
|
||||
ui->port_range->setText(bean->serverPorts.join(","));
|
||||
ui->hop_interval->setText(bean->hop_interval);
|
||||
|
||||
if (bean->proxy_type == Configs::QUICBean::proxy_Hysteria) { // hy1
|
||||
ui->password->hide();
|
||||
ui->password_l->hide();
|
||||
} else { // hy2
|
||||
ui->authPayload->hide();
|
||||
ui->authPayload_l->hide();
|
||||
ui->authPayloadType->hide();
|
||||
ui->authPayloadType_l->hide();
|
||||
ui->alpn->hide();
|
||||
ui->alpn_l->hide();
|
||||
ui->TLS->removeItem(ui->alpn_sp);
|
||||
ui->disableMtuDiscovery->hide();
|
||||
ui->connectionReceiveWindow->hide();
|
||||
ui->connectionReceiveWindow_l->hide();
|
||||
ui->streamReceiveWindow->hide();
|
||||
ui->streamReceiveWindow_l->hide();
|
||||
}
|
||||
} else if (bean->proxy_type == Configs::QUICBean::proxy_TUIC) {
|
||||
ui->uploadMbps->hide();
|
||||
ui->uploadMbps_l->hide();
|
||||
ui->downloadMbps->hide();
|
||||
ui->downloadMbps_l->hide();
|
||||
ui->disableMtuDiscovery->hide();
|
||||
ui->obfsPassword->hide();
|
||||
ui->obfsPassword_l->hide();
|
||||
ui->authPayload->hide();
|
||||
ui->authPayload_l->hide();
|
||||
ui->authPayloadType->hide();
|
||||
ui->authPayloadType_l->hide();
|
||||
ui->streamReceiveWindow->hide();
|
||||
ui->streamReceiveWindow_l->hide();
|
||||
ui->connectionReceiveWindow->hide();
|
||||
ui->connectionReceiveWindow_l->hide();
|
||||
ui->port_range->hide();
|
||||
ui->port_range_l->hide();
|
||||
ui->hop_interval->hide();
|
||||
ui->hop_interval_l->hide();
|
||||
}
|
||||
// this->ent = _ent;
|
||||
// auto bean = this->ent->QUICBean();
|
||||
//
|
||||
// P_LOAD_INT(uploadMbps);
|
||||
// P_LOAD_INT(downloadMbps);
|
||||
// P_LOAD_BOOL(disableMtuDiscovery)
|
||||
// P_LOAD_STRING(obfsPassword);
|
||||
// P_LOAD_STRING(authPayload);
|
||||
// P_LOAD_INT(streamReceiveWindow);
|
||||
// P_LOAD_INT(connectionReceiveWindow);
|
||||
//
|
||||
// P_LOAD_COMBO_INT(authPayloadType);
|
||||
// P_LOAD_STRING(uuid);
|
||||
// P_LOAD_STRING(password);
|
||||
//
|
||||
// P_LOAD_COMBO_STRING(congestionControl);
|
||||
// P_LOAD_COMBO_STRING(udpRelayMode);
|
||||
// P_LOAD_BOOL(zeroRttHandshake);
|
||||
// P_LOAD_STRING(heartbeat);
|
||||
// P_LOAD_BOOL(uos);
|
||||
//
|
||||
// // TLS
|
||||
// P_LOAD_STRING(sni);
|
||||
// P_LOAD_STRING(alpn);
|
||||
// P_C_LOAD_STRING(caText);
|
||||
// P_LOAD_BOOL(allowInsecure);
|
||||
// P_LOAD_BOOL(disableSni);
|
||||
//
|
||||
// if (bean->proxy_type == Configs::QUICBean::proxy_Hysteria || bean->proxy_type == Configs::QUICBean::proxy_Hysteria2) {
|
||||
// ui->uuid->hide();
|
||||
// ui->uuid_l->hide();
|
||||
// ui->uuidgen->hide();
|
||||
// ui->congestionControl->hide();
|
||||
// ui->congestionControl_l->hide();
|
||||
// ui->udpRelayMode->hide();
|
||||
// ui->udpRelayMode_l->hide();
|
||||
// ui->zeroRttHandshake->hide();
|
||||
// ui->heartbeat->hide();
|
||||
// ui->heartbeat_l->hide();
|
||||
// ui->uos->hide();
|
||||
// ui->port_range->setText(bean->serverPorts.join(","));
|
||||
// ui->hop_interval->setText(bean->hop_interval);
|
||||
//
|
||||
// if (bean->proxy_type == Configs::QUICBean::proxy_Hysteria) { // hy1
|
||||
// ui->password->hide();
|
||||
// ui->password_l->hide();
|
||||
// } else { // hy2
|
||||
// ui->authPayload->hide();
|
||||
// ui->authPayload_l->hide();
|
||||
// ui->authPayloadType->hide();
|
||||
// ui->authPayloadType_l->hide();
|
||||
// ui->alpn->hide();
|
||||
// ui->alpn_l->hide();
|
||||
// ui->TLS->removeItem(ui->alpn_sp);
|
||||
// ui->disableMtuDiscovery->hide();
|
||||
// ui->connectionReceiveWindow->hide();
|
||||
// ui->connectionReceiveWindow_l->hide();
|
||||
// ui->streamReceiveWindow->hide();
|
||||
// ui->streamReceiveWindow_l->hide();
|
||||
// }
|
||||
// } else if (bean->proxy_type == Configs::QUICBean::proxy_TUIC) {
|
||||
// ui->uploadMbps->hide();
|
||||
// ui->uploadMbps_l->hide();
|
||||
// ui->downloadMbps->hide();
|
||||
// ui->downloadMbps_l->hide();
|
||||
// ui->disableMtuDiscovery->hide();
|
||||
// ui->obfsPassword->hide();
|
||||
// ui->obfsPassword_l->hide();
|
||||
// ui->authPayload->hide();
|
||||
// ui->authPayload_l->hide();
|
||||
// ui->authPayloadType->hide();
|
||||
// ui->authPayloadType_l->hide();
|
||||
// ui->streamReceiveWindow->hide();
|
||||
// ui->streamReceiveWindow_l->hide();
|
||||
// ui->connectionReceiveWindow->hide();
|
||||
// ui->connectionReceiveWindow_l->hide();
|
||||
// ui->port_range->hide();
|
||||
// ui->port_range_l->hide();
|
||||
// ui->hop_interval->hide();
|
||||
// ui->hop_interval_l->hide();
|
||||
// }
|
||||
}
|
||||
|
||||
bool EditQUIC::onEnd() {
|
||||
auto bean = this->ent->QUICBean();
|
||||
|
||||
// Hysteria
|
||||
P_SAVE_INT(uploadMbps);
|
||||
P_SAVE_INT(downloadMbps);
|
||||
P_SAVE_BOOL(disableMtuDiscovery)
|
||||
P_SAVE_STRING(obfsPassword);
|
||||
P_SAVE_COMBO_INT(authPayloadType);
|
||||
P_SAVE_STRING(authPayload);
|
||||
P_SAVE_INT(streamReceiveWindow);
|
||||
P_SAVE_INT(connectionReceiveWindow);
|
||||
auto rawPorts = ui->port_range->text();
|
||||
QStringList serverPorts;
|
||||
for (const auto& portRange : rawPorts.split(','))
|
||||
{
|
||||
if (portRange.trimmed().isEmpty()) continue;
|
||||
serverPorts.append(portRange.trimmed());
|
||||
}
|
||||
bean->serverPorts = serverPorts;
|
||||
bean->hop_interval = ui->hop_interval->text();
|
||||
|
||||
// TUIC
|
||||
P_SAVE_STRING(uuid);
|
||||
P_SAVE_STRING(password);
|
||||
P_SAVE_COMBO_STRING(congestionControl);
|
||||
P_SAVE_COMBO_STRING(udpRelayMode);
|
||||
P_SAVE_BOOL(zeroRttHandshake);
|
||||
P_SAVE_STRING(heartbeat);
|
||||
P_SAVE_BOOL(uos);
|
||||
|
||||
// TLS
|
||||
P_SAVE_STRING(sni);
|
||||
P_SAVE_STRING(alpn);
|
||||
P_SAVE_BOOL(allowInsecure);
|
||||
P_C_SAVE_STRING(caText);
|
||||
P_SAVE_BOOL(disableSni);
|
||||
// auto bean = this->ent->QUICBean();
|
||||
//
|
||||
// // Hysteria
|
||||
// P_SAVE_INT(uploadMbps);
|
||||
// P_SAVE_INT(downloadMbps);
|
||||
// P_SAVE_BOOL(disableMtuDiscovery)
|
||||
// P_SAVE_STRING(obfsPassword);
|
||||
// P_SAVE_COMBO_INT(authPayloadType);
|
||||
// P_SAVE_STRING(authPayload);
|
||||
// P_SAVE_INT(streamReceiveWindow);
|
||||
// P_SAVE_INT(connectionReceiveWindow);
|
||||
// auto rawPorts = ui->port_range->text();
|
||||
// QStringList serverPorts;
|
||||
// for (const auto& portRange : rawPorts.split(','))
|
||||
// {
|
||||
// if (portRange.trimmed().isEmpty()) continue;
|
||||
// serverPorts.append(portRange.trimmed());
|
||||
// }
|
||||
// bean->serverPorts = serverPorts;
|
||||
// bean->hop_interval = ui->hop_interval->text();
|
||||
//
|
||||
// // TUIC
|
||||
// P_SAVE_STRING(uuid);
|
||||
// P_SAVE_STRING(password);
|
||||
// P_SAVE_COMBO_STRING(congestionControl);
|
||||
// P_SAVE_COMBO_STRING(udpRelayMode);
|
||||
// P_SAVE_BOOL(zeroRttHandshake);
|
||||
// P_SAVE_STRING(heartbeat);
|
||||
// P_SAVE_BOOL(uos);
|
||||
//
|
||||
// // TLS
|
||||
// P_SAVE_STRING(sni);
|
||||
// P_SAVE_STRING(alpn);
|
||||
// P_SAVE_BOOL(allowInsecure);
|
||||
// P_C_SAVE_STRING(caText);
|
||||
// P_SAVE_BOOL(disableSni);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#include "include/ui/profile/edit_shadowsocks.h"
|
||||
|
||||
#include "include/configs/proxy/ShadowSocksBean.hpp"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
|
||||
EditShadowSocks::EditShadowSocks(QWidget *parent) : QWidget(parent),
|
||||
@ -15,7 +14,7 @@ EditShadowSocks::~EditShadowSocks() {
|
||||
|
||||
void EditShadowSocks::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->ShadowSocksBean();
|
||||
auto bean = this->ent->ShadowSocks();
|
||||
|
||||
ui->method->setCurrentText(bean->method);
|
||||
ui->uot->setCurrentIndex(bean->uot);
|
||||
@ -28,7 +27,7 @@ void EditShadowSocks::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
bool EditShadowSocks::onEnd() {
|
||||
auto bean = this->ent->ShadowSocksBean();
|
||||
auto bean = this->ent->ShadowSocks();
|
||||
|
||||
bean->method = ui->method->currentText();
|
||||
bean->password = ui->password->text();
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#include "include/ui/profile/edit_socks_http.h"
|
||||
|
||||
#include "include/configs/proxy/SocksHttpBean.hpp"
|
||||
|
||||
EditSocksHttp::EditSocksHttp(QWidget *parent) : QWidget(parent),
|
||||
ui(new Ui::EditSocksHttp) {
|
||||
ui->setupUi(this);
|
||||
@ -13,35 +11,35 @@ EditSocksHttp::~EditSocksHttp() {
|
||||
|
||||
void EditSocksHttp::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->SocksHTTPBean();
|
||||
|
||||
if (bean->socks_http_type == Configs::SocksHttpBean::type_Socks4) {
|
||||
ui->version->setCurrentIndex(1);
|
||||
} else {
|
||||
ui->version->setCurrentIndex(0);
|
||||
}
|
||||
if (bean->socks_http_type == Configs::SocksHttpBean::type_HTTP) {
|
||||
ui->version->setVisible(false);
|
||||
ui->version_l->setVisible(false);
|
||||
}
|
||||
|
||||
ui->username->setText(bean->username);
|
||||
ui->password->setText(bean->password);
|
||||
// auto bean = this->ent->SocksHTTPBean();
|
||||
//
|
||||
// if (bean->socks_http_type == Configs::SocksHttpBean::type_Socks4) {
|
||||
// ui->version->setCurrentIndex(1);
|
||||
// } else {
|
||||
// ui->version->setCurrentIndex(0);
|
||||
// }
|
||||
// if (bean->socks_http_type == Configs::SocksHttpBean::type_HTTP) {
|
||||
// ui->version->setVisible(false);
|
||||
// ui->version_l->setVisible(false);
|
||||
// }
|
||||
//
|
||||
// ui->username->setText(bean->username);
|
||||
// ui->password->setText(bean->password);
|
||||
}
|
||||
|
||||
bool EditSocksHttp::onEnd() {
|
||||
auto bean = this->ent->SocksHTTPBean();
|
||||
|
||||
if (ui->version->isVisible()) {
|
||||
if (ui->version->currentIndex() == 1) {
|
||||
bean->socks_http_type = Configs::SocksHttpBean::type_Socks4;
|
||||
} else {
|
||||
bean->socks_http_type = Configs::SocksHttpBean::type_Socks5;
|
||||
}
|
||||
}
|
||||
|
||||
bean->username = ui->username->text();
|
||||
bean->password = ui->password->text();
|
||||
// auto bean = this->ent->SocksHTTPBean();
|
||||
//
|
||||
// if (ui->version->isVisible()) {
|
||||
// if (ui->version->currentIndex() == 1) {
|
||||
// bean->socks_http_type = Configs::SocksHttpBean::type_Socks4;
|
||||
// } else {
|
||||
// bean->socks_http_type = Configs::SocksHttpBean::type_Socks5;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// bean->username = ui->username->text();
|
||||
// bean->password = ui->password->text();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13,16 +13,16 @@ EditSSH::~EditSSH() {
|
||||
|
||||
void EditSSH::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->SSHBean();
|
||||
auto bean = this->ent->SSH();
|
||||
|
||||
ui->user->setText(bean->user);
|
||||
ui->password->setText(bean->password);
|
||||
ui->private_key->setText(bean->privateKey);
|
||||
ui->private_key_path->setText(bean->privateKeyPath);
|
||||
ui->private_key_pass->setText(bean->privateKeyPass);
|
||||
ui->host_key->setText(bean->hostKey.join(","));
|
||||
ui->host_key_algs->setText(bean->hostKeyAlgs.join(","));
|
||||
ui->client_version->setText(bean->clientVersion);
|
||||
ui->private_key->setText(bean->private_key);
|
||||
ui->private_key_path->setText(bean->private_key_path);
|
||||
ui->private_key_pass->setText(bean->private_key_passphrase);
|
||||
ui->host_key->setText(bean->host_key.join(","));
|
||||
ui->host_key_algs->setText(bean->host_key_algorithms.join(","));
|
||||
ui->client_version->setText(bean->client_version);
|
||||
|
||||
connect(ui->choose_pk, &QPushButton::clicked, this, [=,this] {
|
||||
auto fn = QFileDialog::getOpenFileName(this, QObject::tr("Select"), QDir::currentPath(),
|
||||
@ -34,18 +34,18 @@ void EditSSH::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
bool EditSSH::onEnd() {
|
||||
auto bean = this->ent->SSHBean();
|
||||
auto bean = this->ent->SSH();
|
||||
|
||||
bean->user = ui->user->text();
|
||||
bean->password = ui->password->text();
|
||||
bean->privateKey = ui->private_key->toPlainText();
|
||||
bean->privateKeyPath = ui->private_key_path->text();
|
||||
bean->privateKeyPass = ui->private_key_pass->text();
|
||||
if (!ui->host_key->text().trimmed().isEmpty()) bean->hostKey = ui->host_key->text().split(",");
|
||||
else bean->hostKey = {};
|
||||
if (!ui->host_key_algs->text().trimmed().isEmpty()) bean->hostKeyAlgs = ui->host_key_algs->text().split(",");
|
||||
else bean->hostKeyAlgs = {};
|
||||
bean->clientVersion = ui->client_version->text();
|
||||
bean->private_key = ui->private_key->toPlainText();
|
||||
bean->private_key_path = ui->private_key_path->text();
|
||||
bean->private_key_passphrase = ui->private_key_pass->text();
|
||||
if (!ui->host_key->text().trimmed().isEmpty()) bean->host_key = ui->host_key->text().split(",");
|
||||
else bean->host_key = {};
|
||||
if (!ui->host_key_algs->text().trimmed().isEmpty()) bean->host_key_algorithms = ui->host_key_algs->text().split(",");
|
||||
else bean->host_key_algorithms = {};
|
||||
bean->client_version = ui->client_version->text();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -12,7 +12,7 @@ EditTailScale::~EditTailScale() {
|
||||
|
||||
void EditTailScale::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->TailscaleBean();
|
||||
auto bean = this->ent->Tailscale();
|
||||
|
||||
ui->state_dir->setText(bean->state_directory);
|
||||
ui->auth_key->setText(bean->auth_key);
|
||||
@ -28,7 +28,7 @@ void EditTailScale::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
bool EditTailScale::onEnd() {
|
||||
auto bean = this->ent->TailscaleBean();
|
||||
auto bean = this->ent->Tailscale();
|
||||
|
||||
bean->state_directory = ui->state_dir->text();
|
||||
bean->auth_key = ui->auth_key->text();
|
||||
|
||||
@ -14,22 +14,22 @@ EditTrojanVLESS::~EditTrojanVLESS() {
|
||||
|
||||
void EditTrojanVLESS::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->TrojanVLESSBean();
|
||||
if (bean->proxy_type == Configs::TrojanVLESSBean::proxy_VLESS) {
|
||||
ui->label->setText("UUID");
|
||||
}
|
||||
if (bean->proxy_type != Configs::TrojanVLESSBean::proxy_VLESS) {
|
||||
ui->flow->hide();
|
||||
ui->flow_l->hide();
|
||||
}
|
||||
ui->password->setText(bean->password);
|
||||
ui->flow->addItems(Preset::SingBox::Flows );
|
||||
ui->flow->setCurrentText(bean->flow);
|
||||
// auto bean = this->ent->TrojanVLESSBean();
|
||||
// if (bean->proxy_type == Configs::TrojanVLESSBean::proxy_VLESS) {
|
||||
// ui->label->setText("UUID");
|
||||
// }
|
||||
// if (bean->proxy_type != Configs::TrojanVLESSBean::proxy_VLESS) {
|
||||
// ui->flow->hide();
|
||||
// ui->flow_l->hide();
|
||||
// }
|
||||
// ui->password->setText(bean->password);
|
||||
// ui->flow->addItems(Preset::SingBox::Flows );
|
||||
// ui->flow->setCurrentText(bean->flow);
|
||||
}
|
||||
|
||||
bool EditTrojanVLESS::onEnd() {
|
||||
auto bean = this->ent->TrojanVLESSBean();
|
||||
bean->password = ui->password->text();
|
||||
bean->flow = ui->flow->currentText();
|
||||
// auto bean = this->ent->TrojanVLESSBean();
|
||||
// bean->password = ui->password->text();
|
||||
// bean->flow = ui->flow->currentText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -15,18 +15,18 @@ EditVMess::~EditVMess() {
|
||||
|
||||
void EditVMess::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->VMessBean();
|
||||
auto bean = this->ent->VMess();
|
||||
|
||||
ui->uuid->setText(bean->uuid);
|
||||
ui->aid->setText(Int2String(bean->aid));
|
||||
ui->aid->setText(Int2String(bean->alter_id));
|
||||
ui->security->setCurrentText(bean->security);
|
||||
}
|
||||
|
||||
bool EditVMess::onEnd() {
|
||||
auto bean = this->ent->VMessBean();
|
||||
auto bean = this->ent->VMess();
|
||||
|
||||
bean->uuid = ui->uuid->text();
|
||||
bean->aid = ui->aid->text().toInt();
|
||||
bean->alter_id = ui->aid->text().toInt();
|
||||
bean->security = ui->security->currentText();
|
||||
|
||||
return true;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#include "include/ui/profile/edit_wireguard.h"
|
||||
|
||||
#include "include/configs/proxy/WireguardBean.h"
|
||||
|
||||
EditWireguard::EditWireguard(QWidget *parent) : QWidget(parent), ui(new Ui::EditWireguard) {
|
||||
ui->setupUi(this);
|
||||
}
|
||||
@ -12,22 +10,22 @@ EditWireguard::~EditWireguard() {
|
||||
|
||||
void EditWireguard::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
auto bean = this->ent->WireguardBean();
|
||||
auto bean = this->ent->Wireguard();
|
||||
|
||||
#ifndef Q_OS_LINUX
|
||||
adjustSize();
|
||||
#endif
|
||||
|
||||
ui->private_key->setText(bean->privateKey);
|
||||
ui->public_key->setText(bean->publicKey);
|
||||
ui->preshared_key->setText(bean->preSharedKey);
|
||||
auto reservedStr = bean->FormatReserved().replace("-", ",");
|
||||
ui->reserved->setText(reservedStr);
|
||||
ui->persistent_keepalive->setText(Int2String(bean->persistentKeepalive));
|
||||
ui->mtu->setText(Int2String(bean->MTU));
|
||||
ui->sys_ifc->setChecked(bean->useSystemInterface);
|
||||
ui->local_addr->setText(bean->localAddress.join(","));
|
||||
ui->workers->setText(Int2String(bean->workerCount));
|
||||
ui->private_key->setText(bean->private_key);
|
||||
ui->public_key->setText(bean->peer->public_key);
|
||||
ui->preshared_key->setText(bean->peer->pre_shared_key);
|
||||
// auto reservedStr = bean->peer->reserved;
|
||||
// ui->reserved->setText(reservedStr);
|
||||
ui->persistent_keepalive->setText(Int2String(bean->peer->persistent_keepalive));
|
||||
ui->mtu->setText(Int2String(bean->mtu));
|
||||
ui->sys_ifc->setChecked(bean->system);
|
||||
ui->local_addr->setText(bean->address.join(","));
|
||||
ui->workers->setText(Int2String(bean->worker_count));
|
||||
|
||||
ui->enable_amnezia->setChecked(bean->enable_amnezia);
|
||||
ui->junk_packet_count->setText(Int2String(bean->junk_packet_count));
|
||||
@ -42,22 +40,22 @@ void EditWireguard::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
|
||||
}
|
||||
|
||||
bool EditWireguard::onEnd() {
|
||||
auto bean = this->ent->WireguardBean();
|
||||
auto bean = this->ent->Wireguard();
|
||||
|
||||
bean->privateKey = ui->private_key->text();
|
||||
bean->publicKey = ui->public_key->text();
|
||||
bean->preSharedKey = ui->preshared_key->text();
|
||||
bean->private_key = ui->private_key->text();
|
||||
bean->peer->public_key = ui->public_key->text();
|
||||
bean->peer->pre_shared_key = ui->preshared_key->text();
|
||||
auto rawReserved = ui->reserved->text();
|
||||
bean->reserved = {};
|
||||
for (const auto& item: rawReserved.split(",")) {
|
||||
if (item.trimmed().isEmpty()) continue;
|
||||
bean->reserved += item.trimmed().toInt();
|
||||
}
|
||||
bean->persistentKeepalive = ui->persistent_keepalive->text().toInt();
|
||||
bean->MTU = ui->mtu->text().toInt();
|
||||
bean->useSystemInterface = ui->sys_ifc->isChecked();
|
||||
bean->localAddress = ui->local_addr->text().replace(" ", "").split(",");
|
||||
bean->workerCount = ui->workers->text().toInt();
|
||||
// bean->reserved = {};
|
||||
// for (const auto& item: rawReserved.split(",")) {
|
||||
// if (item.trimmed().isEmpty()) continue;
|
||||
// bean->reserved += item.trimmed().toInt();
|
||||
// }
|
||||
bean->peer->persistent_keepalive = ui->persistent_keepalive->text().toInt();
|
||||
bean->mtu = ui->mtu->text().toInt();
|
||||
bean->system = ui->sys_ifc->isChecked();
|
||||
bean->address = ui->local_addr->text().replace(" ", "").split(",");
|
||||
bean->worker_count = ui->workers->text().toInt();
|
||||
|
||||
bean->enable_amnezia = ui->enable_amnezia->isChecked();
|
||||
bean->junk_packet_count = ui->junk_packet_count->text().toInt();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user