mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 05:30:06 +08:00
Implement new config gen
This commit is contained in:
parent
3e9a5aa5c3
commit
56d6394e9c
@ -261,8 +261,12 @@ set(PROJECT_SOURCES
|
||||
src/configs/outbounds/vmess.cpp
|
||||
src/configs/outbounds/wireguard.cpp
|
||||
include/configs/generate.h
|
||||
src/configs/generate.cpp
|
||||
include/configs/common/utils.h
|
||||
src/configs/common/utils.cpp
|
||||
include/configs/outbounds/Custom.h
|
||||
include/configs/outbounds/Chain.h
|
||||
include/configs/outbounds/extracore.h
|
||||
)
|
||||
|
||||
if (NOT APPLE AND Qt6_VERSION VERSION_GREATER_EQUAL 6.9.0)
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <include/global/Utils.hpp>
|
||||
|
||||
#include "generate.h"
|
||||
#include "common/Outbound.h"
|
||||
#include "include/global/ConfigItem.hpp"
|
||||
#include <QJsonObject>
|
||||
#include "include/global/Configs.hpp"
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
struct BuildResult {
|
||||
QJsonObject object;
|
||||
QString error;
|
||||
};
|
||||
|
||||
class baseConfig : public JsonStore
|
||||
{
|
||||
public:
|
||||
@ -22,34 +23,4 @@ namespace Configs
|
||||
|
||||
virtual BuildResult Build();
|
||||
};
|
||||
|
||||
class outbound : public baseConfig
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<OutboundCommons> commons = std::make_shared<OutboundCommons>();
|
||||
|
||||
void ResolveDomainToIP(const std::function<void()> &onFinished);
|
||||
|
||||
virtual QString DisplayAddress()
|
||||
{
|
||||
return ::DisplayAddress(commons->server, commons->server_port);
|
||||
}
|
||||
|
||||
QString DisplayName()
|
||||
{
|
||||
if (commons->name.isEmpty()) {
|
||||
return DisplayAddress();
|
||||
}
|
||||
return commons->name;
|
||||
}
|
||||
|
||||
virtual QString DisplayType() { return {}; };
|
||||
|
||||
QString DisplayTypeAndName()
|
||||
{
|
||||
return QString("[%1] %2").arg(DisplayType(), DisplayName());
|
||||
}
|
||||
|
||||
static bool IsEndpoint() { return false; };
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ namespace Configs
|
||||
DialFields()
|
||||
{
|
||||
_add(new configItem("reuse_addr", &reuse_addr, itemType::boolean));
|
||||
_add(new configItem("connect_timeout", &connect_timeout, itemType::string));
|
||||
_add(new configItem("connect_timeout", &connect_timeout, string));
|
||||
_add(new configItem("tcp_fast_open", &tcp_fast_open, itemType::boolean));
|
||||
_add(new configItem("tcp_multi_path", &tcp_multi_path, itemType::boolean));
|
||||
_add(new configItem("udp_fragment", &udp_fragment, itemType::boolean));
|
||||
|
||||
@ -7,7 +7,6 @@ namespace Configs
|
||||
class OutboundCommons : public baseConfig
|
||||
{
|
||||
public:
|
||||
virtual ~OutboundCommons() = default;
|
||||
QString name;
|
||||
QString server;
|
||||
int server_port = 0;
|
||||
@ -28,4 +27,39 @@ namespace Configs
|
||||
QJsonObject ExportToJson() override;
|
||||
BuildResult Build() override;
|
||||
};
|
||||
|
||||
class outbound : public baseConfig
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<OutboundCommons> commons = std::make_shared<OutboundCommons>();
|
||||
|
||||
void ResolveDomainToIP(const std::function<void()> &onFinished);
|
||||
|
||||
virtual QString GetAddress()
|
||||
{
|
||||
return commons->server;
|
||||
}
|
||||
|
||||
virtual QString DisplayAddress()
|
||||
{
|
||||
return ::DisplayAddress(commons->server, commons->server_port);
|
||||
}
|
||||
|
||||
virtual QString DisplayName()
|
||||
{
|
||||
if (commons->name.isEmpty()) {
|
||||
return DisplayAddress();
|
||||
}
|
||||
return commons->name;
|
||||
}
|
||||
|
||||
virtual QString DisplayType() { return {}; };
|
||||
|
||||
QString DisplayTypeAndName()
|
||||
{
|
||||
return QString("[%1] %2").arg(DisplayType(), DisplayName());
|
||||
}
|
||||
|
||||
virtual bool IsEndpoint() { return false; };
|
||||
};
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ namespace Configs
|
||||
|
||||
uTLS()
|
||||
{
|
||||
_add(new configItem("enabled", &enabled, boolean));
|
||||
_add(new configItem("enabled", &enabled, itemType::boolean));
|
||||
_add(new configItem("fingerprint", &fingerPrint, string));
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ namespace Configs
|
||||
|
||||
ECH()
|
||||
{
|
||||
_add(new configItem("enabled", &enabled, boolean));
|
||||
_add(new configItem("enabled", &enabled, itemType::boolean));
|
||||
_add(new configItem("config", &config, stringList));
|
||||
_add(new configItem("config_path", &config_path, string));
|
||||
}
|
||||
@ -57,7 +57,7 @@ namespace Configs
|
||||
|
||||
Reality()
|
||||
{
|
||||
_add(new configItem("enabled", &enabled, boolean));
|
||||
_add(new configItem("enabled", &enabled, itemType::boolean));
|
||||
_add(new configItem("public_key", &public_key, string));
|
||||
_add(new configItem("short_id", &short_id, string));
|
||||
}
|
||||
@ -98,10 +98,10 @@ namespace Configs
|
||||
|
||||
TLS()
|
||||
{
|
||||
_add(new configItem("enabled", &enabled, boolean));
|
||||
_add(new configItem("disable_sni", &disable_sni, boolean));
|
||||
_add(new configItem("enabled", &enabled, itemType::boolean));
|
||||
_add(new configItem("disable_sni", &disable_sni, itemType::boolean));
|
||||
_add(new configItem("server_name", &server_name, string));
|
||||
_add(new configItem("insecure", &insecure, boolean));
|
||||
_add(new configItem("insecure", &insecure, itemType::boolean));
|
||||
_add(new configItem("alpn", &alpn, stringList));
|
||||
_add(new configItem("min_version", &min_version, string));
|
||||
_add(new configItem("max_version", &max_version, string));
|
||||
@ -114,9 +114,9 @@ namespace Configs
|
||||
_add(new configItem("client_certificate_path", &client_certificate_path, string));
|
||||
_add(new configItem("client_key", &client_key, stringList));
|
||||
_add(new configItem("client_key_path", &client_key_path, string));
|
||||
_add(new configItem("fragment", &fragment, boolean));
|
||||
_add(new configItem("fragment", &fragment, itemType::boolean));
|
||||
_add(new configItem("fragment_fallback_delay", &fragment_fallback_delay, string));
|
||||
_add(new configItem("record_fragment", &record_fragment, boolean));
|
||||
_add(new configItem("record_fragment", &record_fragment, itemType::boolean));
|
||||
_add(new configItem("ech", dynamic_cast<JsonStore *>(ech.get()), jsonStore));
|
||||
_add(new configItem("utls", dynamic_cast<JsonStore *>(utls.get()), jsonStore));
|
||||
_add(new configItem("reality", dynamic_cast<JsonStore *>(reality.get()), jsonStore));
|
||||
|
||||
@ -1,10 +1,165 @@
|
||||
#pragma once
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <include/dataStore/ProxyEntity.hpp>
|
||||
#include <include/stats/traffic/TrafficData.hpp>
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
struct BuildResult {
|
||||
QJsonObject object;
|
||||
QString error;
|
||||
enum OSType
|
||||
{
|
||||
Unknown = 0,
|
||||
Linux = 1,
|
||||
Windows = 2,
|
||||
Darwin = 3,
|
||||
};
|
||||
|
||||
class ExtraCoreData
|
||||
{
|
||||
public:
|
||||
QString path;
|
||||
QString args;
|
||||
QString config;
|
||||
QString configDir;
|
||||
bool noLog;
|
||||
};
|
||||
|
||||
class DNSDeps
|
||||
{
|
||||
public:
|
||||
bool needDirectDnsRules = false;
|
||||
QJsonArray directDomains;
|
||||
QJsonArray directRuleSets;
|
||||
QJsonArray directSuffixes;
|
||||
QJsonArray directKeywords;
|
||||
QJsonArray directRegexes;
|
||||
};
|
||||
|
||||
class HijackDeps
|
||||
{
|
||||
public:
|
||||
QJsonArray hijackDomains;
|
||||
QJsonArray hijackDomainSuffix;
|
||||
QJsonArray hijackDomainRegex;
|
||||
QJsonArray hijackGeoAssets;
|
||||
};
|
||||
|
||||
class TunDeps
|
||||
{
|
||||
public:
|
||||
QJsonArray directIPSets;
|
||||
QJsonArray directIPCIDRs;
|
||||
};
|
||||
|
||||
class RoutingDeps
|
||||
{
|
||||
public:
|
||||
int defaultOutboundID;
|
||||
QList<int> neededOutbounds;
|
||||
QStringList neededRuleSets;
|
||||
std::map<int, QString> outboundMap;
|
||||
};
|
||||
|
||||
class BuildPrerequisities
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<ExtraCoreData> extraCoreData = std::make_shared<ExtraCoreData>();
|
||||
std::shared_ptr<DNSDeps> dnsDeps = std::make_shared<DNSDeps>();
|
||||
std::shared_ptr<HijackDeps> hijackDeps = std::make_shared<HijackDeps>();
|
||||
std::shared_ptr<TunDeps> tunDeps = std::make_shared<TunDeps>();
|
||||
std::shared_ptr<RoutingDeps> routingDeps = std::make_shared<RoutingDeps>();
|
||||
};
|
||||
|
||||
class BuildConfigResult {
|
||||
public:
|
||||
QString error;
|
||||
QJsonObject coreConfig;
|
||||
std::shared_ptr<ExtraCoreData> extraCoreData;
|
||||
|
||||
QList<std::shared_ptr<Stats::TrafficData>> outboundStats;
|
||||
};
|
||||
|
||||
class BuildSingBoxConfigContext
|
||||
{
|
||||
public:
|
||||
bool forTest = false;
|
||||
bool forExport = false;
|
||||
bool tunEnabled = false;
|
||||
bool isResolvedUsed = false;
|
||||
std::shared_ptr<ProxyEntity> ent = std::make_shared<ProxyEntity>(nullptr, nullptr);
|
||||
std::shared_ptr<BuildPrerequisities> buildPrerequisities = std::make_shared<BuildPrerequisities>();
|
||||
OSType os;
|
||||
|
||||
QString error;
|
||||
QStringList warnings;
|
||||
QJsonArray outbounds;
|
||||
QJsonArray endpoints;
|
||||
std::shared_ptr<BuildConfigResult> buildConfigResult = std::make_shared<BuildConfigResult>();
|
||||
};
|
||||
|
||||
inline QString get_jsdelivr_link(QString link)
|
||||
{
|
||||
if(dataStore->routing->ruleset_mirror == Mirrors::GITHUB)
|
||||
return link;
|
||||
if(auto url = QUrl(link); url.isValid() && url.host() == "raw.githubusercontent.com")
|
||||
{
|
||||
QStringList list = url.path().split('/');
|
||||
QString result;
|
||||
switch(dataStore->routing->ruleset_mirror) {
|
||||
case Mirrors::GCORE: result = "https://gcore.jsdelivr.net/gh"; break;
|
||||
case Mirrors::QUANTIL: result = "https://quantil.jsdelivr.net/gh"; break;
|
||||
case Mirrors::FASTLY: result = "https://fastly.jsdelivr.net/gh"; break;
|
||||
case Mirrors::CDN: result = "https://cdn.jsdelivr.net/gh"; break;
|
||||
default: result = "https://testingcf.jsdelivr.net/gh";
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
foreach(QString item, list)
|
||||
{
|
||||
if(!item.isEmpty())
|
||||
{
|
||||
if(index == 2)
|
||||
result += "@" + item;
|
||||
else
|
||||
result += "/" + item;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
void CalculatePrerequisities(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildLogSections(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildDNSSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx, bool useDnsObj = true);
|
||||
|
||||
void buildNTPSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildCertificateSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildInboundSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildOutboundsSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildRouteSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
void buildExperimentalSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx);
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildSingBoxConfig(const std::shared_ptr<ProxyEntity> &ent);
|
||||
|
||||
class BuildTestConfigResult {
|
||||
public:
|
||||
QString error;
|
||||
QMap<int, QString> fullConfigs;
|
||||
QMap<QString, int> tag2entID;
|
||||
QJsonObject coreConfig;
|
||||
QStringList outboundTags;
|
||||
};
|
||||
|
||||
bool IsValid(const std::shared_ptr<ProxyEntity> &ent);
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles);
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
|
||||
26
include/configs/outbounds/chain.h
Normal file
26
include/configs/outbounds/chain.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
class chain : public outbound
|
||||
{
|
||||
public:
|
||||
QList<int> list; // from in to out
|
||||
|
||||
chain()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("list", &list, integerList));
|
||||
}
|
||||
|
||||
QString DisplayType() override { return QObject::tr("Chain Proxy"); };
|
||||
|
||||
QString DisplayAddress() override { return ""; };
|
||||
|
||||
BuildResult Build() override
|
||||
{
|
||||
return {{}, "Cannot call Build on chain config"};
|
||||
}
|
||||
};
|
||||
}
|
||||
54
include/configs/outbounds/custom.h
Normal file
54
include/configs/outbounds/custom.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
class custom : public outbound
|
||||
{
|
||||
public:
|
||||
QString config;
|
||||
QString type; // one of outbound or fullconfig
|
||||
|
||||
custom()
|
||||
{
|
||||
_add(new configItem("commons", dynamic_cast<JsonStore *>(commons.get()), jsonStore));
|
||||
_add(new configItem("config", &config, string));
|
||||
_add(new configItem("type", &type, string));
|
||||
}
|
||||
|
||||
QString GetAddress() override
|
||||
{
|
||||
if (type == "outbound") {
|
||||
auto obj = QString2QJsonObject(config);
|
||||
return obj["server"].toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString DisplayAddress() override
|
||||
{
|
||||
if (type == "outbound") {
|
||||
auto obj = QString2QJsonObject(config);
|
||||
return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString DisplayType() override
|
||||
{
|
||||
if (type == "outbound") {
|
||||
auto outboundType = QString2QJsonObject(config)["type"].toString();
|
||||
if (!outboundType.isEmpty()) outboundType[0] = outboundType[0].toUpper();
|
||||
return outboundType.isEmpty() ? "Custom Outbound" : "Custom " + outboundType + " Outbound";
|
||||
} else if (type == "fullconfig") {
|
||||
return "Custom Config";
|
||||
}
|
||||
return type;
|
||||
};
|
||||
|
||||
BuildResult Build() override
|
||||
{
|
||||
return {QString2QJsonObject(config), ""};
|
||||
}
|
||||
};
|
||||
}
|
||||
36
include/configs/outbounds/extracore.h
Normal file
36
include/configs/outbounds/extracore.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
class extracore : public outbound {
|
||||
public:
|
||||
QString socksAddress = "127.0.0.1";
|
||||
int socksPort;
|
||||
QString extraCorePath;
|
||||
QString extraCoreArgs;
|
||||
QString extraCoreConf;
|
||||
bool noLogs = false;
|
||||
|
||||
extracore() {
|
||||
_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));
|
||||
_add(new configItem("extra_core_path", &extraCorePath, itemType::string));
|
||||
_add(new configItem("extra_core_args", &extraCoreArgs, itemType::string));
|
||||
_add(new configItem("extra_core_conf", &extraCoreConf, itemType::string));
|
||||
_add(new configItem("no_logs", &noLogs, itemType::boolean));
|
||||
};
|
||||
|
||||
QString DisplayType() override { return "ExtraCore"; };
|
||||
|
||||
BuildResult Build() override
|
||||
{
|
||||
QJsonObject socksOutbound;
|
||||
socksOutbound["type"] = "socks";
|
||||
socksOutbound["server"] = socksAddress;
|
||||
socksOutbound["server_port"] = socksPort;
|
||||
return {socksOutbound, ""};
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/DialFields.h"
|
||||
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
#pragma once
|
||||
#include <Wbemidl.h>
|
||||
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/DialFields.h"
|
||||
#include "include/configs/common/multiplex.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
{
|
||||
|
||||
inline QStringList shadowsocksMethods = {"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20"};
|
||||
|
||||
class shadowsocks : public outbound
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/DialFields.h"
|
||||
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
@ -44,7 +43,7 @@ namespace Configs
|
||||
|
||||
QString DisplayAddress() override;
|
||||
QString DisplayType() override;
|
||||
static bool IsEndpoint();
|
||||
bool IsEndpoint() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/DialFields.h"
|
||||
#include "include/configs/common/multiplex.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/multiplex.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/DialFields.h"
|
||||
#include "include/configs/common/multiplex.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/common/TLS.h"
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "include/configs/baseConfig.h"
|
||||
#include "include/configs/common/Outbound.h"
|
||||
|
||||
namespace Configs
|
||||
@ -88,7 +87,7 @@ namespace Configs
|
||||
|
||||
QString DisplayAddress() override;
|
||||
QString DisplayType() override;
|
||||
static bool IsEndpoint();
|
||||
bool IsEndpoint() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <include/configs/outbounds/tailscale.h>
|
||||
#include <include/configs/outbounds/wireguard.h>
|
||||
|
||||
#include "include/configs/common/Outbound.h"
|
||||
#include "include/configs/outbounds/anyTLS.h"
|
||||
#include "include/configs/outbounds/chain.h"
|
||||
#include "include/configs/outbounds/custom.h"
|
||||
#include "include/configs/outbounds/extracore.h"
|
||||
#include "include/configs/outbounds/socks.h"
|
||||
#include "include/configs/outbounds/http.h"
|
||||
#include "include/configs/outbounds/hysteria.h"
|
||||
#include "include/configs/outbounds/hysteria2.h"
|
||||
#include "include/configs/outbounds/shadowsocks.h"
|
||||
#include "include/configs/outbounds/ssh.h"
|
||||
#include "include/configs/outbounds/trojan.h"
|
||||
#include "include/configs/outbounds/tuic.h"
|
||||
#include "include/configs/outbounds/vless.h"
|
||||
#include "include/configs/outbounds/vmess.h"
|
||||
|
||||
#include "include/global/Configs.hpp"
|
||||
#include "include/global/CountryHelper.hpp"
|
||||
#include "include/stats/traffic/TrafficData.hpp"
|
||||
@ -44,12 +63,15 @@ namespace Configs {
|
||||
QString ul_speed;
|
||||
QString test_country;
|
||||
std::shared_ptr<Configs::AbstractBean> bean;
|
||||
std::shared_ptr<Configs::outbound> outbound;
|
||||
std::shared_ptr<Stats::TrafficData> traffic_data = std::make_shared<Stats::TrafficData>("");
|
||||
|
||||
QString full_test_report;
|
||||
|
||||
ProxyEntity(Configs::AbstractBean *bean, const QString &type_);
|
||||
|
||||
ProxyEntity(Configs::outbound *outbound, const QString &type_);
|
||||
|
||||
[[nodiscard]] QString DisplayTestResult() const;
|
||||
|
||||
[[nodiscard]] QColor DisplayLatencyColor() const;
|
||||
@ -106,5 +128,70 @@ namespace Configs {
|
||||
[[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());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::http *Http() const {
|
||||
return dynamic_cast<Configs::http *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::shadowsocks *ShadowSocks() const {
|
||||
return dynamic_cast<Configs::shadowsocks *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::vmess *VMess() const {
|
||||
return dynamic_cast<Configs::vmess *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::Trojan *Trojan() const {
|
||||
return dynamic_cast<Configs::Trojan *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::vless *VLESS() const {
|
||||
return dynamic_cast<Configs::vless *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::anyTLS *AnyTLS() const {
|
||||
return dynamic_cast<Configs::anyTLS *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::hysteria *Hysteria() const {
|
||||
return dynamic_cast<Configs::hysteria *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::hysteria2 *Hysteria2() const {
|
||||
return dynamic_cast<Configs::hysteria2 *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::ssh *SSH() const {
|
||||
return dynamic_cast<Configs::ssh *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::tailscale *Tailscale() const {
|
||||
return dynamic_cast<Configs::tailscale *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::tuic *TUIC() const {
|
||||
return dynamic_cast<Configs::tuic *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::wireguard *Wireguard() const {
|
||||
return dynamic_cast<Configs::wireguard *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::custom *Custom() const {
|
||||
return dynamic_cast<Configs::custom *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::chain *Chain() const {
|
||||
return dynamic_cast<Configs::chain *>(outbound.get());
|
||||
};
|
||||
|
||||
[[nodiscard]] Configs::extracore *ExtraCore() const {
|
||||
return dynamic_cast<Configs::extracore *>(outbound.get());
|
||||
};
|
||||
};
|
||||
} // namespace Configs
|
||||
|
||||
953
src/configs/generate.cpp
Normal file
953
src/configs/generate.cpp
Normal file
@ -0,0 +1,953 @@
|
||||
#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 {
|
||||
|
||||
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()) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QStringList getChainDomains (const std::shared_ptr<ProxyEntity>& ent, QString &error)
|
||||
{
|
||||
QStringList domains;
|
||||
auto chain = ent->Chain();
|
||||
if (!chain)
|
||||
{
|
||||
error = "Ent is Nullptr after cast to chain in getChainDomains, data is corrupted";
|
||||
return domains;
|
||||
}
|
||||
auto entIDs = ent->Chain()->list;
|
||||
for (int id : entIDs)
|
||||
{
|
||||
if (auto subEnt = profileManager->GetProfile(id); subEnt != nullptr)
|
||||
{
|
||||
if (auto addr = subEnt->outbound->GetAddress(); !addr.isEmpty() && !IsIpAddress(addr)) domains.append(addr);
|
||||
}
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
QStringList getEntDomains(const QList<int>& entIDs, QString &error)
|
||||
{
|
||||
QStringList domains;
|
||||
for (const auto &id: entIDs)
|
||||
{
|
||||
if (auto ent = profileManager->GetProfile(id); ent != nullptr)
|
||||
{
|
||||
if (ent->type == "extracore") continue;
|
||||
if (ent->type == "chain") domains << getChainDomains(ent, error);
|
||||
else
|
||||
{
|
||||
if (auto addr = ent->outbound->GetAddress(); !addr.isEmpty() && !IsIpAddress(addr)) domains.append(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return domains;
|
||||
}
|
||||
|
||||
void CalculatePrerequisities(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
ctx->tunEnabled = dataStore->spmode_vpn;
|
||||
ctx->os = getOS();
|
||||
if (ctx->os == Linux)
|
||||
{
|
||||
ctx->isResolvedUsed = ReadFileText("/etc/resolv.conf").contains("systemd-resolved");
|
||||
}
|
||||
auto preReqs = ctx->buildPrerequisities;
|
||||
|
||||
// Get route chain
|
||||
auto routeChain = profileManager->GetRouteChain(dataStore->routing->current_route_id);
|
||||
if (routeChain == nullptr) {
|
||||
ctx->error = "Routing profile does not exist, try resetting the route profile in Routing Settings";
|
||||
return;
|
||||
}
|
||||
|
||||
// Routing dependencies
|
||||
auto neededOutbounds = routeChain->get_used_outbounds();
|
||||
auto neededRuleSets = routeChain->get_used_rule_sets();
|
||||
preReqs->routingDeps->defaultOutboundID = routeChain->defaultOutboundID;
|
||||
preReqs->routingDeps->outboundMap[-1] = "proxy";
|
||||
preReqs->routingDeps->outboundMap[-2] = "direct";
|
||||
int suffix = 0;
|
||||
for (const auto &item: *neededOutbounds) {
|
||||
if (item < 0) continue;
|
||||
auto neededEnt = profileManager->GetProfile(item);
|
||||
if (neededEnt == nullptr) {
|
||||
ctx->error = "The routing profile is referencing outbounds that no longer exists, consider revising your settings";
|
||||
return;
|
||||
}
|
||||
if (neededEnt->type == "extracore" || neededEnt->type == "custom" || neededEnt->type == "chain")
|
||||
{
|
||||
ctx->error = "Outbounds used in routing profile cannot be of types extracore, custom or chain";
|
||||
return;
|
||||
}
|
||||
if (auto entAddrs = getEntDomains({neededEnt->id}, ctx->error); !entAddrs.empty())
|
||||
{
|
||||
if (!ctx->error.isEmpty()) return;
|
||||
for (const auto &addr: entAddrs)
|
||||
{
|
||||
preReqs->dnsDeps->directDomains << addr;
|
||||
}
|
||||
preReqs->dnsDeps->needDirectDnsRules = true;
|
||||
}
|
||||
preReqs->routingDeps->outboundMap[item] = "route-" + Int2String(suffix++);
|
||||
preReqs->routingDeps->neededOutbounds << item;
|
||||
}
|
||||
|
||||
for (const auto &item: *neededRuleSets) {
|
||||
preReqs->routingDeps->neededRuleSets << item;
|
||||
}
|
||||
|
||||
// Direct domains
|
||||
auto sets = routeChain->get_direct_sites();
|
||||
for (const auto &item: sets) {
|
||||
if (item.startsWith("ruleset:")) {
|
||||
preReqs->dnsDeps->directRuleSets << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("domain:")) {
|
||||
preReqs->dnsDeps->directDomains << item.mid(7);
|
||||
}
|
||||
if (item.startsWith("suffix:")) {
|
||||
preReqs->dnsDeps->directSuffixes << item.mid(7);
|
||||
}
|
||||
if (item.startsWith("keyword:")) {
|
||||
preReqs->dnsDeps->directKeywords << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("regex:")) {
|
||||
preReqs->dnsDeps->directRegexes << item.mid(6);
|
||||
}
|
||||
preReqs->dnsDeps->needDirectDnsRules = true;
|
||||
}
|
||||
if (auto entAddrs = getEntDomains({ctx->ent->id}, ctx->error); !entAddrs.isEmpty())
|
||||
{
|
||||
if (!ctx->error.isEmpty()) return;
|
||||
for (const auto &addr: entAddrs) preReqs->dnsDeps->directDomains << addr;
|
||||
preReqs->dnsDeps->needDirectDnsRules = true;
|
||||
}
|
||||
if (auto group = profileManager->GetGroup(ctx->ent->gid); group != nullptr)
|
||||
{
|
||||
QList<int> groupEnts;
|
||||
if (auto frontEntID = group->front_proxy_id; frontEntID >= 0) groupEnts << frontEntID;
|
||||
if (auto landingEntID = group->landing_proxy_id; landingEntID >= 0) groupEnts << landingEntID;
|
||||
auto addrs = getEntDomains(groupEnts, ctx->error);
|
||||
if (!ctx->error.isEmpty()) return;
|
||||
for (const auto &addr: addrs)
|
||||
{
|
||||
preReqs->dnsDeps->directDomains << addr;
|
||||
}
|
||||
}
|
||||
|
||||
// Hijack
|
||||
if (dataStore->enable_dns_server) {
|
||||
for (const auto& rule : dataStore->dns_server_rules) {
|
||||
if (rule.startsWith("ruleset:")) {
|
||||
preReqs->hijackDeps->hijackGeoAssets << rule.mid(8);
|
||||
}
|
||||
if (rule.startsWith("domain:")) {
|
||||
preReqs->hijackDeps->hijackDomains << rule.mid(7);
|
||||
}
|
||||
if (rule.startsWith("suffix:")) {
|
||||
preReqs->hijackDeps->hijackDomainSuffix << rule.mid(7);
|
||||
}
|
||||
if (rule.startsWith("regex:")) {
|
||||
preReqs->hijackDeps->hijackDomainRegex << rule.mid(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto ruleSet : preReqs->hijackDeps->hijackGeoAssets) {
|
||||
if (!preReqs->routingDeps->neededRuleSets.contains(ruleSet.toString())) preReqs->routingDeps->neededRuleSets.append(ruleSet.toString());
|
||||
}
|
||||
|
||||
// Direct IPs
|
||||
auto directIPraw = routeChain->get_direct_ips();
|
||||
for (const auto &item: directIPraw) {
|
||||
if (item.startsWith("ruleset:")) {
|
||||
preReqs->tunDeps->directIPSets << item.mid(8);
|
||||
}
|
||||
if (item.startsWith("ip:")) {
|
||||
preReqs->tunDeps->directIPCIDRs << item.mid(3);
|
||||
}
|
||||
}
|
||||
|
||||
// Extra core
|
||||
if (ctx->ent->type == "extracore")
|
||||
{
|
||||
auto outbound = ctx->ent->ExtraCore();
|
||||
if (outbound == nullptr)
|
||||
{
|
||||
MW_show_log("INVALID ENT TYPE, NEEDED EXTRACORE GOT NULLPTR");
|
||||
ctx->error = "failed to cast to extracore, type is: " + ctx->ent->type;
|
||||
return;
|
||||
}
|
||||
preReqs->extraCoreData->path = QFileInfo(outbound->extraCorePath).canonicalFilePath();
|
||||
preReqs->extraCoreData->args = outbound->extraCoreArgs;
|
||||
preReqs->extraCoreData->config = outbound->extraCoreConf;
|
||||
preReqs->extraCoreData->configDir = GetBasePath();
|
||||
preReqs->extraCoreData->noLog = outbound->noLogs;
|
||||
}
|
||||
}
|
||||
|
||||
void buildLogSections(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
ctx->buildConfigResult->coreConfig.insert("log", QJsonObject{{"level", dataStore->log_level}});
|
||||
}
|
||||
|
||||
void buildNTPSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
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;
|
||||
ctx->buildConfigResult->coreConfig["ntp"] = ntpObj;
|
||||
}
|
||||
}
|
||||
|
||||
void buildCertificateSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
ctx->buildConfigResult->coreConfig.insert("certificate", QJsonObject{{"store", dataStore->use_mozilla_certs ? "mozilla" : "system"}});
|
||||
}
|
||||
|
||||
QJsonObject buildDnsObj(QString address, std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
if (address.startsWith("local")) {
|
||||
if (ctx->tunEnabled && ctx->isResolvedUsed) {
|
||||
return {{"type", "underlying"}};
|
||||
}
|
||||
if (ctx->tunEnabled && ctx->os == 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 buildDNSSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx, bool useDnsObj) {
|
||||
if (getOS() == Darwin && dataStore->core_box_underlying_dns.isEmpty() && dataStore->spmode_vpn)
|
||||
{
|
||||
ctx->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;
|
||||
}
|
||||
|
||||
if (dataStore->routing->use_dns_object && useDnsObj) {
|
||||
ctx->buildConfigResult->coreConfig["dns"] = QString2QJsonObject(dataStore->routing->dns_object);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dnsDeps = ctx->buildPrerequisities->dnsDeps;
|
||||
auto hijackDeps = ctx->buildPrerequisities->hijackDeps;
|
||||
bool isTailscale = ctx->ent->type == "tailscale";
|
||||
bool independentCache = false;
|
||||
QJsonArray servers;
|
||||
QJsonArray rules;
|
||||
// remote
|
||||
if (isTailscale)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// direct
|
||||
auto directDnsObj = buildDnsObj(dataStore->routing->direct_dns, ctx);
|
||||
directDnsObj["tag"] = "dns-direct";
|
||||
directDnsObj["domain_resolver"] = "dns-local";
|
||||
if (dataStore->routing->dns_final_out == "direct") {
|
||||
servers.prepend(directDnsObj);
|
||||
} else {
|
||||
servers.append(directDnsObj);
|
||||
}
|
||||
|
||||
// Handle localhost
|
||||
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"},
|
||||
};
|
||||
|
||||
// HijackRules
|
||||
if (dataStore->enable_dns_server && !ctx->forTest)
|
||||
{
|
||||
rules += QJsonObject{
|
||||
{"rule_set", hijackDeps->hijackGeoAssets},
|
||||
{"domain", hijackDeps->hijackDomains},
|
||||
{"domain_suffix", hijackDeps->hijackDomainSuffix},
|
||||
{"domain_regex", hijackDeps->hijackDomainRegex},
|
||||
{"query_type", "A"},
|
||||
{"action", "predefined"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", QString("* IN A %1").arg(dataStore->dns_v4_resp)},
|
||||
};
|
||||
|
||||
if (!dataStore->dns_v6_resp.isEmpty())
|
||||
{
|
||||
rules += QJsonObject{
|
||||
{"rule_set", hijackDeps->hijackGeoAssets},
|
||||
{"domain", hijackDeps->hijackDomains},
|
||||
{"domain_suffix", hijackDeps->hijackDomainSuffix},
|
||||
{"domain_regex", hijackDeps->hijackDomainRegex},
|
||||
{"query_type", "AAAA"},
|
||||
{"action", "predefined"},
|
||||
{"rcode", "NOERROR"},
|
||||
{"answer", QString("* IN AAAA %1").arg(dataStore->dns_v6_resp)},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// FakeIP
|
||||
if (dataStore->fake_dns) {
|
||||
servers += QJsonObject{
|
||||
{"tag", "dns-fake"},
|
||||
{"type", "fakeip"},
|
||||
{"inet4_range", "198.18.0.0/15"},
|
||||
{"inet6_range", "fc00::/18"},
|
||||
};
|
||||
rules += QJsonObject{
|
||||
{"query_type", QJsonArray{
|
||||
"A",
|
||||
"AAAA"
|
||||
}},
|
||||
{"action", "route"},
|
||||
{"server", "dns-fake"}
|
||||
};
|
||||
independentCache = true;
|
||||
}
|
||||
|
||||
if (dnsDeps->needDirectDnsRules) {
|
||||
rules += QJsonObject{
|
||||
{"rule_set", dnsDeps->directRuleSets},
|
||||
{"domain", dnsDeps->directDomains},
|
||||
{"domain_suffix", dnsDeps->directSuffixes},
|
||||
{"domain_keyword", dnsDeps->directKeywords},
|
||||
{"domain_regex", dnsDeps->directRegexes},
|
||||
{"action", "route"},
|
||||
{"server", "dns-direct"},
|
||||
};
|
||||
}
|
||||
|
||||
// Local
|
||||
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";
|
||||
servers += dnsLocalObj;
|
||||
|
||||
auto dnsObj = QJsonObject{
|
||||
{"servers", servers},
|
||||
{"rules", rules}
|
||||
};
|
||||
if (independentCache) dnsObj["independent_cache"] = true;
|
||||
ctx->buildConfigResult->coreConfig["dns"] = dnsObj;
|
||||
}
|
||||
|
||||
void buildInboundSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
auto tunDeps = ctx->buildPrerequisities->tunDeps;
|
||||
QJsonArray inbounds;
|
||||
// mixed
|
||||
if (!ctx->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "mixed-in";
|
||||
inboundObj["type"] = "mixed";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["listen_port"] = dataStore->inbound_socks_port;
|
||||
inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// Tun
|
||||
if (dataStore->spmode_vpn && !ctx->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;
|
||||
if (ctx->os == Linux) inboundObj["auto_redirect"] = true;
|
||||
auto tunAddress = QJsonArray{"172.19.0.1/24"};
|
||||
if (dataStore->vpn_ipv6) tunAddress += "fdfe:dcba:9876::1/96";
|
||||
inboundObj["address"] = tunAddress;
|
||||
|
||||
if (ctx->buildPrerequisities->routingDeps->defaultOutboundID == proxyID && dataStore->enable_tun_routing)
|
||||
{
|
||||
QJsonArray routeExcludeAddrs = {"127.0.0.0/8"};
|
||||
QJsonArray routeExcludeSets;
|
||||
for (auto item: tunDeps->directIPCIDRs) routeExcludeAddrs << item;
|
||||
for (auto item: tunDeps->directIPSets) routeExcludeSets << item;
|
||||
inboundObj["route_exclude_address"] = routeExcludeAddrs;
|
||||
if (!routeExcludeSets.isEmpty()) inboundObj["route_exclude_address_set"] = routeExcludeSets;
|
||||
}
|
||||
inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// Hijack
|
||||
if (dataStore->enable_redirect && !ctx->forTest) {
|
||||
inbounds.prepend(QJsonObject{
|
||||
{"tag", "hijack"},
|
||||
{"type", "direct"},
|
||||
{"listen", dataStore->redirect_listen_address},
|
||||
{"listen_port", dataStore->redirect_listen_port},
|
||||
});
|
||||
}
|
||||
if (dataStore->enable_dns_server && !ctx->forTest) {
|
||||
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},
|
||||
});
|
||||
}
|
||||
|
||||
// custom
|
||||
if (!ctx->forTest) QJSONARRAY_ADD(inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray())
|
||||
ctx->buildConfigResult->coreConfig["inbounds"] = inbounds;
|
||||
}
|
||||
|
||||
void unwrapChain(const QList<int>& entIDs, QList<std::shared_ptr<ProxyEntity>> &ents, QString& error)
|
||||
{
|
||||
bool hasExtracore = false;
|
||||
bool hasCustom = false;
|
||||
for (auto id : entIDs)
|
||||
{
|
||||
auto ent = profileManager->GetProfile(id);
|
||||
if (ent == nullptr)
|
||||
{
|
||||
error = "Null proxy in chain, you may want to check your configs";
|
||||
return;
|
||||
}
|
||||
if (ent->type == "chain")
|
||||
{
|
||||
error = "Chain in Chain is not allowed";
|
||||
return;
|
||||
}
|
||||
if (ent->type == "extracore") hasExtracore = true;
|
||||
if (ent->type == "custom") hasCustom = true;
|
||||
ents.append(ent);
|
||||
}
|
||||
if (ents.size() > 1 && (hasExtracore || hasCustom))
|
||||
{
|
||||
error = "Cannot use Extracore or Custom configs in a chain";
|
||||
}
|
||||
}
|
||||
|
||||
void buildOutboundChain(std::shared_ptr<BuildSingBoxConfigContext> &ctx, const QList<int>& entIDs, const QString& prefix, bool includeProxy, bool link)
|
||||
{
|
||||
QList<std::shared_ptr<ProxyEntity>> ents;
|
||||
unwrapChain(entIDs, ents, ctx->error);
|
||||
for (int idx = 0; idx < ents.size(); idx++)
|
||||
{
|
||||
auto tag = prefix + "-" + Int2String(idx);
|
||||
QString nextTag;
|
||||
if (idx < ents.size() - 1) nextTag = prefix + "-" + Int2String(idx+1);
|
||||
if (includeProxy && idx == 0) tag = "proxy";
|
||||
const auto& ent = ents[idx];
|
||||
auto [object, error] = ent->outbound->Build();
|
||||
if (!error.isEmpty())
|
||||
{
|
||||
ctx->error += error;
|
||||
return;
|
||||
}
|
||||
object["tag"] = tag;
|
||||
if (!nextTag.isEmpty() && link) object["detour"] = nextTag;
|
||||
if (ent->outbound->IsEndpoint())
|
||||
{
|
||||
ctx->endpoints.append(object);
|
||||
} else
|
||||
{
|
||||
ctx->outbounds.append(object);
|
||||
}
|
||||
ent->traffic_data->id = ent->id;
|
||||
ent->traffic_data->tag = tag.toStdString();
|
||||
ctx->buildConfigResult->outboundStats += ent->traffic_data;
|
||||
}
|
||||
}
|
||||
|
||||
void buildOutboundsSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
// First, our own ent
|
||||
QList<int> entIDs;
|
||||
auto group = profileManager->GetGroup(ctx->ent->gid);
|
||||
if (group == nullptr)
|
||||
{
|
||||
ctx->error = "No group found for ent, data is corrupted";
|
||||
return;
|
||||
}
|
||||
if (group->landing_proxy_id >= 0) entIDs.append(group->landing_proxy_id);
|
||||
if (ctx->ent->type == "chain")
|
||||
{
|
||||
auto chain = ctx->ent->Chain();
|
||||
if (chain == nullptr)
|
||||
{
|
||||
ctx->error = "Ent is nullptr after cast to chain, data is corrupted";
|
||||
return;
|
||||
}
|
||||
entIDs << chain->list;
|
||||
} else
|
||||
{
|
||||
entIDs.append(ctx->ent->id);
|
||||
}
|
||||
if (group->front_proxy_id >= 0) entIDs.append(group->front_proxy_id);
|
||||
buildOutboundChain(ctx, entIDs, "config", true, true);
|
||||
|
||||
// Now, build the outbounds needed by the route profile
|
||||
buildOutboundChain(ctx, ctx->buildPrerequisities->routingDeps->neededOutbounds, "route", false, false);
|
||||
|
||||
// Add the direct outbound
|
||||
ctx->outbounds.append(QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", "direct"}
|
||||
});
|
||||
|
||||
ctx->buildConfigResult->coreConfig["endpoints"] = ctx->endpoints;
|
||||
ctx->buildConfigResult->coreConfig["outbounds"] = ctx->outbounds;
|
||||
}
|
||||
|
||||
void buildRouteSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
auto routeChain = profileManager->GetRouteChain(dataStore->routing->current_route_id);
|
||||
if (routeChain == nullptr) {
|
||||
ctx->error = "Routing profile does not exist, try resetting the route profile in Routing Settings";
|
||||
return;
|
||||
}
|
||||
routeChain = std::make_shared<RoutingChain>(*routeChain);
|
||||
auto routeDeps = ctx->buildPrerequisities->routingDeps;
|
||||
|
||||
// hijack
|
||||
if (dataStore->enable_dns_server && !ctx->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 && !ctx->forTest) {
|
||||
auto sniffRule = std::make_shared<RouteRule>();
|
||||
sniffRule->action = "sniff";
|
||||
sniffRule->sniffOverrideDest = true;
|
||||
sniffRule->inbound = {"hijack"};
|
||||
routeChain->Rules.prepend(sniffRule);
|
||||
}
|
||||
|
||||
// sniff and resolve
|
||||
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);
|
||||
}
|
||||
|
||||
// rules
|
||||
auto routeRules = routeChain->get_route_rules(false, routeDeps->outboundMap);
|
||||
|
||||
// rulesets
|
||||
auto ruleSetArray = QJsonArray();
|
||||
for (const auto &item: routeDeps->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.contains(item.toStdString())) {
|
||||
ruleSetArray += QJsonObject{
|
||||
{"type", "remote"},
|
||||
{"tag", item},
|
||||
{"format", "binary"},
|
||||
{"url", get_jsdelivr_link(QString::fromStdString(ruleSetMap.at(item.toStdString())))},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// add block
|
||||
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")},
|
||||
};
|
||||
}
|
||||
|
||||
// apply
|
||||
QJsonObject route;
|
||||
route["rules"] = routeRules;
|
||||
route["rule_set"] = ruleSetArray;
|
||||
route["final"] = outboundIDToString(routeChain->defaultOutboundID);
|
||||
if (dataStore->spmode_vpn) route["auto_detect_interface"] = true;
|
||||
|
||||
ctx->buildConfigResult->coreConfig["route"] = route;
|
||||
}
|
||||
|
||||
void buildExperimentalSection(std::shared_ptr<BuildSingBoxConfigContext> &ctx) {
|
||||
if (ctx->forTest) return;
|
||||
|
||||
QJsonObject experimentalObj;
|
||||
QJsonObject clash_api = {
|
||||
{"default_mode", ""} // dummy to make sure it is created
|
||||
};
|
||||
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;
|
||||
|
||||
if (experimentalObj.isEmpty()) return;
|
||||
|
||||
// apply
|
||||
ctx->buildConfigResult->coreConfig["experimental"] = experimentalObj;
|
||||
}
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildSingBoxConfig(const std::shared_ptr<ProxyEntity>& ent) {
|
||||
if (ent->type == "custom")
|
||||
{
|
||||
auto res = std::make_shared<BuildConfigResult>();
|
||||
auto custom = ent->Custom();
|
||||
if (custom == nullptr)
|
||||
{
|
||||
res->error = "Corrupted data, needed custom ent, got nullptr";
|
||||
return res;
|
||||
}
|
||||
if (custom->type == "fullconfig")
|
||||
{
|
||||
res->coreConfig = custom->Build().object;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
auto ctx = std::make_shared<BuildSingBoxConfigContext>();
|
||||
ctx->ent = ent;
|
||||
|
||||
// log
|
||||
buildLogSections(ctx);
|
||||
// ntp
|
||||
buildNTPSection(ctx);
|
||||
// DNS
|
||||
buildDNSSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
// certificate
|
||||
buildCertificateSection(ctx);
|
||||
// Inbound
|
||||
buildInboundSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
// outbound
|
||||
buildOutboundsSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
// Route
|
||||
buildRouteSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
// experimental
|
||||
buildExperimentalSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
ctx->buildConfigResult->error = ctx->error;
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
return ctx->buildConfigResult;
|
||||
}
|
||||
|
||||
bool IsValid(const std::shared_ptr<ProxyEntity>& ent)
|
||||
{
|
||||
if (ent->type == "chain")
|
||||
{
|
||||
auto chain = ent->Chain();
|
||||
if (chain == nullptr)
|
||||
{
|
||||
MW_show_log("Corrupted data, needed chain ent, got nullptr");
|
||||
return false;
|
||||
}
|
||||
for (int eId : chain->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;
|
||||
bool fullConf = false;
|
||||
if (ent->type == "custom")
|
||||
{
|
||||
auto custom = ent->Custom();
|
||||
if (custom == nullptr)
|
||||
{
|
||||
MW_show_log("Corrupted data in isValid, needed custom ent, got nullptr");
|
||||
return false;
|
||||
}
|
||||
if (custom->type == "fullconfig")
|
||||
{
|
||||
conf = QString2QJsonObject(custom->config);
|
||||
fullConf = true;
|
||||
}
|
||||
}
|
||||
if (!fullConf)
|
||||
{
|
||||
auto out = ent->outbound->Build();
|
||||
auto outArr = QJsonArray{out.object};
|
||||
auto key = ent->outbound->IsEndpoint() ? "endpoints" : "outbounds";
|
||||
conf = {
|
||||
{key, outArr},
|
||||
};
|
||||
}
|
||||
bool ok;
|
||||
auto resp = API::defaultClient->CheckConfig(&ok, QJsonObject2QString(conf, true));
|
||||
if (!ok)
|
||||
{
|
||||
MW_show_log("Failed to Call the Core: " + resp);
|
||||
return false;
|
||||
}
|
||||
if (resp.isEmpty()) return true;
|
||||
// else
|
||||
MW_show_log("Invalid ent " + ent->outbound->commons->name + ": " + resp);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles)
|
||||
{
|
||||
auto res = std::make_shared<BuildConfigResult>();
|
||||
auto ctx = std::make_shared<BuildSingBoxConfigContext>();
|
||||
QList<int> entIDs;
|
||||
for (const auto& proxy : profiles) entIDs << proxy->id;
|
||||
ctx->buildPrerequisities->dnsDeps->directDomains = QListStr2QJsonArray(getEntDomains(entIDs, ctx->error));
|
||||
buildDNSSection(ctx);
|
||||
if (!ctx->error.isEmpty())
|
||||
{
|
||||
res->error = ctx->error;
|
||||
return res;
|
||||
}
|
||||
buildLogSections(ctx);
|
||||
buildCertificateSection(ctx);
|
||||
buildNTPSection(ctx);
|
||||
int suffix = 1;
|
||||
for (auto item : profiles)
|
||||
{
|
||||
if (item->type == "extracore")
|
||||
{
|
||||
MW_show_log("Skipping ExtraCore conf");
|
||||
continue;
|
||||
}
|
||||
if (item->type == "tailscale")
|
||||
{
|
||||
MW_show_log("Skipping Tailscale conf");
|
||||
continue;
|
||||
}
|
||||
if (!IsValid(item)) {
|
||||
MW_show_log("Skipping invalid config: " + item->outbound->commons->name);
|
||||
item->latency = -1;
|
||||
continue;
|
||||
}
|
||||
if (item->type == "custom")
|
||||
{
|
||||
auto custom = item->Custom();
|
||||
if (custom == nullptr)
|
||||
{
|
||||
MW_show_log("Corrupted data in build test config");
|
||||
res->error = "Corrupted data in build test config";
|
||||
return res;
|
||||
}
|
||||
if (custom->type == "fullconfig")
|
||||
{
|
||||
auto obj = QString2QJsonObject(custom->config);
|
||||
obj["inbounds"] = QJsonArray();
|
||||
res->fullConfigs[item->id] = QJsonObject2QString(obj, true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buildOutboundChain(ctx, {item->id}, "proxy-" + Int2String(suffix++), false, true);
|
||||
auto tag = "proxy-" + Int2String(suffix++) + "-0";
|
||||
res->outboundTags << tag;
|
||||
res->tag2entID.insert(tag, item->id);
|
||||
}
|
||||
ctx->outbounds << QJsonObject{{"type", "direct"}, {"tag", "direct"}};
|
||||
ctx->buildConfigResult->coreConfig["outbounds"] = ctx->outbounds;
|
||||
ctx->buildConfigResult->coreConfig["endpoints"] = ctx->endpoints;
|
||||
ctx->buildConfigResult->coreConfig["route"] = QJsonObject{
|
||||
{"auto_detect_interface", true},
|
||||
{"default_domain_resolver", QJsonObject{
|
||||
{"server", "dns-direct"},
|
||||
{"strategy", dataStore->routing->outbound_domain_strategy},
|
||||
}}
|
||||
};
|
||||
res->coreConfig = ctx->buildConfigResult->coreConfig;
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,26 @@ namespace Configs
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
_add(new configItem("outbound", dynamic_cast<JsonStore *>(outbound), itemType::jsonStore));
|
||||
_add(new configItem("traffic", dynamic_cast<JsonStore *>(traffic_data.get()), itemType::jsonStore));
|
||||
}
|
||||
}
|
||||
|
||||
QString ProxyEntity::DisplayTestResult() const {
|
||||
QString result;
|
||||
if (latency < 0) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user