mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 05:30:06 +08:00
feat: Complete the new routing system's implementation &&
update sing-box to 1.9.3
This commit is contained in:
parent
215f0a17b0
commit
4142de8535
@ -3,12 +3,12 @@
|
||||
#include "fmt/includes.h"
|
||||
#include "fmt/Preset.hpp"
|
||||
#include "main/QJS.hpp"
|
||||
#include "rpc/gRPC.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
#define BOX_UNDERLYING_DNS dataStore->core_box_underlying_dns.isEmpty() ? "underlying://0.0.0.0" : dataStore->core_box_underlying_dns
|
||||
#define BOX_UNDERLYING_DNS_EXPORT dataStore->core_box_underlying_dns.isEmpty() ? (status->forExport ? "local" : "underlying://0.0.0.0") : dataStore->core_box_underlying_dns
|
||||
|
||||
namespace NekoGui {
|
||||
@ -151,35 +151,10 @@ namespace NekoGui {
|
||||
return chainTagOut;
|
||||
}
|
||||
|
||||
#define DOMAIN_USER_RULE \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->proxy_domain)) { \
|
||||
if (dataStore->routing->dns_routing) status->domainListDNSRemote += line; \
|
||||
status->domainListRemote += line; \
|
||||
} \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->direct_domain)) { \
|
||||
if (dataStore->routing->dns_routing) status->domainListDNSDirect += line; \
|
||||
status->domainListDirect += line; \
|
||||
} \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->block_domain)) { \
|
||||
status->domainListBlock += line; \
|
||||
}
|
||||
|
||||
#define IP_USER_RULE \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->block_ip)) { \
|
||||
status->ipListBlock += line; \
|
||||
} \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->proxy_ip)) { \
|
||||
status->ipListRemote += line; \
|
||||
} \
|
||||
for (const auto &line: SplitLinesSkipSharp(dataStore->routing->direct_ip)) { \
|
||||
status->ipListDirect += line; \
|
||||
}
|
||||
|
||||
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 muxApplied = false;
|
||||
|
||||
QString pastTag;
|
||||
int pastExternalStat = 0;
|
||||
@ -290,7 +265,6 @@ namespace NekoGui {
|
||||
// Outbound
|
||||
|
||||
QJsonObject outbound;
|
||||
auto stream = GetStreamSettings(ent->bean.get());
|
||||
|
||||
if (thisExternalStat > 0) {
|
||||
auto extR = ent->bean->BuildExternal(ext_mapping_port, ext_socks_port, thisExternalStat);
|
||||
@ -309,71 +283,9 @@ namespace NekoGui {
|
||||
outbound["type"] = "socks";
|
||||
outbound["server"] = "127.0.0.1";
|
||||
outbound["server_port"] = ext_socks_port;
|
||||
} else {
|
||||
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"] = tagOut;
|
||||
ent->traffic_data->id = ent->id;
|
||||
ent->traffic_data->tag = tagOut.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;
|
||||
|
||||
if (stream != nullptr) {
|
||||
if (stream->network == "grpc" || stream->network == "quic" || (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 domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && 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;
|
||||
muxApplied = true;
|
||||
}
|
||||
BuildOutbound(ent, status, outbound, tagOut);
|
||||
|
||||
// apply custom outbound settings
|
||||
MergeJson(QString2QJsonObject(ent->bean->custom_outbound), outbound);
|
||||
@ -388,7 +300,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
if (!IsIpAddress(serverAddress)) {
|
||||
status->domainListDNSDirect += "full:" + serverAddress;
|
||||
status->domainListDNSDirect += serverAddress;
|
||||
}
|
||||
|
||||
status->outbounds += outbound;
|
||||
@ -400,6 +312,72 @@ namespace NekoGui {
|
||||
return chainTagOut;
|
||||
}
|
||||
|
||||
void BuildOutbound(const std::shared_ptr<ProxyEntity> &ent, const std::shared_ptr<BuildConfigStatus> &status, QJsonObject& outbound, const QString& tag) {
|
||||
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 == "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 domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// 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
|
||||
|
||||
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
@ -467,15 +445,11 @@ namespace NekoGui {
|
||||
auto tagProxy = BuildChain(0, status);
|
||||
if (!status->result->error.isEmpty()) return;
|
||||
|
||||
// direct & bypass & block
|
||||
// direct & block & dns-out
|
||||
status->outbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", "direct"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", "bypass"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"type", "block"},
|
||||
{"tag", "block"},
|
||||
@ -493,60 +467,89 @@ namespace NekoGui {
|
||||
status->result->coreConfig.insert("inbounds", status->inbounds);
|
||||
status->result->coreConfig.insert("outbounds", status->outbounds);
|
||||
|
||||
// Routing
|
||||
// geopath
|
||||
auto geoip = FindCoreAsset("geoip.db");
|
||||
auto geosite = FindCoreAsset("geosite.db");
|
||||
if (geoip.isEmpty()) status->result->error = +"geoip.db not found";
|
||||
if (geosite.isEmpty()) status->result->error = +"geosite.db not found";
|
||||
|
||||
// sing-box common rule object
|
||||
auto make_rule = [&](const QStringList &list, bool isIP = false) {
|
||||
QJsonObject rule;
|
||||
//
|
||||
QJsonArray ip_cidr;
|
||||
QJsonArray geoip;
|
||||
//
|
||||
QJsonArray domain_keyword;
|
||||
QJsonArray domain_subdomain;
|
||||
QJsonArray domain_regexp;
|
||||
QJsonArray domain_full;
|
||||
QJsonArray geosite;
|
||||
for (auto item: list) {
|
||||
if (isIP) {
|
||||
if (item.startsWith("geoip:")) {
|
||||
geoip += item.replace("geoip:", "");
|
||||
} else {
|
||||
ip_cidr += item;
|
||||
}
|
||||
} else {
|
||||
// https://www.v2fly.org/config/dns.html#dnsobject
|
||||
if (item.startsWith("geosite:")) {
|
||||
geosite += item.replace("geosite:", "");
|
||||
} else if (item.startsWith("full:")) {
|
||||
domain_full += item.replace("full:", "").toLower();
|
||||
} else if (item.startsWith("domain:")) {
|
||||
domain_subdomain += item.replace("domain:", "").toLower();
|
||||
} else if (item.startsWith("regexp:")) {
|
||||
domain_regexp += item.replace("regexp:", "").toLower();
|
||||
} else if (item.startsWith("keyword:")) {
|
||||
domain_keyword += item.replace("keyword:", "").toLower();
|
||||
} else {
|
||||
domain_full += item.toLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isIP) {
|
||||
if (ip_cidr.isEmpty() && geoip.isEmpty()) return rule;
|
||||
rule["ip_cidr"] = ip_cidr;
|
||||
rule["geoip"] = geoip;
|
||||
} else {
|
||||
if (domain_keyword.isEmpty() && domain_subdomain.isEmpty() && domain_regexp.isEmpty() && domain_full.isEmpty() && geosite.isEmpty()) {
|
||||
return rule;
|
||||
}
|
||||
rule["domain"] = domain_full;
|
||||
rule["domain_suffix"] = domain_subdomain; // v2ray Subdomain => sing-box suffix
|
||||
rule["domain_keyword"] = domain_keyword;
|
||||
rule["domain_regex"] = domain_regexp;
|
||||
rule["geosite"] = geosite;
|
||||
}
|
||||
return rule;
|
||||
};
|
||||
// manage routing section
|
||||
auto routeObj = QJsonObject{
|
||||
{"auto_detect_interface", true},
|
||||
{
|
||||
"geoip",
|
||||
QJsonObject{
|
||||
{"path", geoip},
|
||||
},
|
||||
},
|
||||
{
|
||||
"geosite",
|
||||
QJsonObject{
|
||||
{"path", geosite},
|
||||
},
|
||||
}};
|
||||
if (!status->forTest) routeObj["final"] = dataStore->routing->def_outbound;
|
||||
if (status->forExport) {
|
||||
routeObj.remove("geoip");
|
||||
routeObj.remove("geosite");
|
||||
}
|
||||
|
||||
auto routeChain = NekoGui::profileManager->GetRouteChain(NekoGui::dataStore->routing->current_route_id);
|
||||
if (routeChain == nullptr) {
|
||||
MessageBoxWarning("Corrupted Data", "Routing profile does not exist, try resetting the route profile in Routing Settings");
|
||||
return;
|
||||
}
|
||||
auto neededOutbounds = routeChain->get_used_outbounds();
|
||||
auto neededRuleSets = routeChain->get_used_rule_sets();
|
||||
std::map<int, QString> outboundMap;
|
||||
outboundMap[-1] = "proxy";
|
||||
outboundMap[-2] = "direct";
|
||||
outboundMap[-3] = "block";
|
||||
outboundMap[-4] = "dns-out";
|
||||
int suffix = 0;
|
||||
for (const auto &item: *neededOutbounds) {
|
||||
if (item < 0) continue;
|
||||
auto neededEnt = NekoGui::profileManager->GetProfile(item);
|
||||
if (neededEnt == nullptr) {
|
||||
MessageBoxWarning("Invalid Data", "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);
|
||||
status->outbounds += currOutbound;
|
||||
outboundMap[item] = tag;
|
||||
|
||||
// add to dns direct resolve
|
||||
if (!IsIpAddress(neededEnt->bean->serverAddress)) {
|
||||
status->domainListDNSDirect << neededEnt->bean->serverAddress;
|
||||
}
|
||||
}
|
||||
routeObj["rules"] = routeChain->get_route_rules(false, outboundMap);
|
||||
|
||||
auto ruleSetArray = QJsonArray();
|
||||
for (const auto &item: *neededRuleSets) {
|
||||
ruleSetArray += QJsonObject{
|
||||
{"type", "local"},
|
||||
{"tag", item},
|
||||
{"format", "binary"},
|
||||
{"path", RULE_SETS_DIR + QString("/%1.srs").arg(item)},
|
||||
};
|
||||
if (QFile(QString(RULE_SETS_DIR + "/%1.srs").arg(item)).exists()) continue;
|
||||
bool ok;
|
||||
auto err = NekoGui_rpc::defaultClient->CompileGeoSet(&ok, item.contains("_IP") ? NekoGui_rpc::GeoRuleSetType::ip : NekoGui_rpc::GeoRuleSetType::site, item.toStdString());
|
||||
if (!ok) {
|
||||
MW_show_log("Failed to generate rule set asset for " + item);
|
||||
status->result->error = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
routeObj["rule_set"] = ruleSetArray;
|
||||
|
||||
status->result->coreConfig["route"] = routeObj;
|
||||
|
||||
// DNS settings
|
||||
// final add DNS
|
||||
QJsonObject dns;
|
||||
QJsonArray dnsServers;
|
||||
@ -600,6 +603,16 @@ namespace NekoGui {
|
||||
};
|
||||
}
|
||||
|
||||
// Direct dns domains
|
||||
QJsonArray directDnsDomains;
|
||||
for (const auto &item: status->domainListDNSDirect) {
|
||||
directDnsDomains.append(item);
|
||||
}
|
||||
dnsRules += QJsonObject{
|
||||
{"domain", directDnsDomains},
|
||||
{"server", "dns-direct"},
|
||||
};
|
||||
|
||||
// Underlying 100% Working DNS
|
||||
dnsServers += QJsonObject{
|
||||
{"tag", "dns-local"},
|
||||
@ -607,28 +620,6 @@ namespace NekoGui {
|
||||
{"detour", "direct"},
|
||||
};
|
||||
|
||||
// sing-box dns rule object
|
||||
auto add_rule_dns = [&](const QStringList &list, const QString &server) {
|
||||
auto rule = make_rule(list, false);
|
||||
if (rule.isEmpty()) return;
|
||||
rule["server"] = server;
|
||||
dnsRules += rule;
|
||||
};
|
||||
add_rule_dns(status->domainListDNSRemote, "dns-remote");
|
||||
add_rule_dns(status->domainListDNSDirect, "dns-direct");
|
||||
|
||||
// built-in rules
|
||||
if (!status->forTest) {
|
||||
dnsRules += QJsonObject{
|
||||
{"query_type", QJsonArray{32, 33}},
|
||||
{"server", "dns-block"},
|
||||
};
|
||||
dnsRules += QJsonObject{
|
||||
{"domain_suffix", ".lan"},
|
||||
{"server", "dns-block"},
|
||||
};
|
||||
}
|
||||
|
||||
// fakedns rule
|
||||
if (dataStore->fake_dns && dataStore->spmode_vpn && !status->forTest) {
|
||||
dnsRules += QJsonObject{
|
||||
@ -646,99 +637,6 @@ namespace NekoGui {
|
||||
}
|
||||
status->result->coreConfig.insert("dns", dns);
|
||||
|
||||
// Routing
|
||||
|
||||
// dns hijack
|
||||
if (!status->forTest) {
|
||||
status->routingRules += QJsonObject{
|
||||
{"protocol", "dns"},
|
||||
{"outbound", "dns-out"},
|
||||
};
|
||||
}
|
||||
|
||||
// sing-box routing rule object
|
||||
auto add_rule_route = [&](const QStringList &list, bool isIP, const QString &out) {
|
||||
auto rule = make_rule(list, isIP);
|
||||
if (rule.isEmpty()) return;
|
||||
rule["outbound"] = out;
|
||||
status->routingRules += rule;
|
||||
};
|
||||
|
||||
// final add user rule
|
||||
add_rule_route(status->domainListBlock, false, "block");
|
||||
add_rule_route(status->domainListRemote, false, tagProxy);
|
||||
add_rule_route(status->domainListDirect, false, "bypass");
|
||||
add_rule_route(status->ipListBlock, true, "block");
|
||||
add_rule_route(status->ipListRemote, true, tagProxy);
|
||||
add_rule_route(status->ipListDirect, true, "bypass");
|
||||
|
||||
// built-in rules
|
||||
status->routingRules += QJsonObject{
|
||||
{"ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}},
|
||||
{"outbound", "block"},
|
||||
};
|
||||
status->routingRules += QJsonObject{
|
||||
{"source_ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}},
|
||||
{"outbound", "block"},
|
||||
};
|
||||
|
||||
// tun user rule
|
||||
if (dataStore->spmode_vpn && !status->forTest) {
|
||||
auto match_out = dataStore->vpn_rule_white ? "proxy" : "bypass";
|
||||
|
||||
QString process_name_rule = dataStore->vpn_rule_process.trimmed();
|
||||
if (!process_name_rule.isEmpty()) {
|
||||
auto arr = SplitLinesSkipSharp(process_name_rule);
|
||||
QJsonObject rule{{"outbound", match_out},
|
||||
{"process_name", QList2QJsonArray(arr)}};
|
||||
status->routingRules += rule;
|
||||
}
|
||||
|
||||
QString cidr_rule = dataStore->vpn_rule_cidr.trimmed();
|
||||
if (!cidr_rule.isEmpty()) {
|
||||
auto arr = SplitLinesSkipSharp(cidr_rule);
|
||||
QJsonObject rule{{"outbound", match_out},
|
||||
{"ip_cidr", QList2QJsonArray(arr)}};
|
||||
status->routingRules += rule;
|
||||
}
|
||||
|
||||
auto autoBypassExternalProcessPaths = getAutoBypassExternalProcessPaths(status->result);
|
||||
if (!autoBypassExternalProcessPaths.isEmpty()) {
|
||||
QJsonObject rule{{"outbound", "bypass"},
|
||||
{"process_name", QList2QJsonArray(autoBypassExternalProcessPaths)}};
|
||||
status->routingRules += rule;
|
||||
}
|
||||
}
|
||||
|
||||
// geopath
|
||||
auto geoip = FindCoreAsset("geoip.db");
|
||||
auto geosite = FindCoreAsset("geosite.db");
|
||||
if (geoip.isEmpty()) status->result->error = +"geoip.db not found";
|
||||
if (geosite.isEmpty()) status->result->error = +"geosite.db not found";
|
||||
|
||||
// final add routing rule
|
||||
auto routeObj = QJsonObject{
|
||||
{"auto_detect_interface", true},
|
||||
{
|
||||
"geoip",
|
||||
QJsonObject{
|
||||
{"path", geoip},
|
||||
},
|
||||
},
|
||||
{
|
||||
"geosite",
|
||||
QJsonObject{
|
||||
{"path", geosite},
|
||||
},
|
||||
}};
|
||||
if (!status->forTest) routeObj["final"] = dataStore->routing->def_outbound;
|
||||
if (status->forExport) {
|
||||
routeObj.remove("geoip");
|
||||
routeObj.remove("geosite");
|
||||
routeObj.remove("auto_detect_interface");
|
||||
}
|
||||
status->result->coreConfig.insert("route", routeObj);
|
||||
|
||||
// experimental
|
||||
QJsonObject experimentalObj;
|
||||
|
||||
|
||||
@ -28,14 +28,7 @@ namespace NekoGui {
|
||||
|
||||
// xxList is V2Ray format string list
|
||||
|
||||
QStringList domainListDNSRemote;
|
||||
QStringList domainListDNSDirect;
|
||||
QStringList domainListRemote;
|
||||
QStringList domainListDirect;
|
||||
QStringList ipListRemote;
|
||||
QStringList ipListDirect;
|
||||
QStringList domainListBlock;
|
||||
QStringList ipListBlock;
|
||||
|
||||
// config format
|
||||
|
||||
@ -52,4 +45,6 @@ namespace NekoGui {
|
||||
|
||||
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);
|
||||
} // namespace NekoGui
|
||||
|
||||
@ -40,7 +40,7 @@ namespace NekoGui {
|
||||
routes = {};
|
||||
profilesIdOrder = filterIntJsonFile("profiles");
|
||||
groupsIdOrder = filterIntJsonFile("groups");
|
||||
routesIdOrder = filterIntJsonFile("routes");
|
||||
routesIdOrder = filterIntJsonFile("route_profiles");
|
||||
// Load Proxys
|
||||
QList<int> delProfile;
|
||||
for (auto id: profilesIdOrder) {
|
||||
@ -79,9 +79,9 @@ namespace NekoGui {
|
||||
}
|
||||
// Load Routing profiles
|
||||
for (auto id : routesIdOrder) {
|
||||
auto route = LoadRouteChain(QString("routes/%1.json").arg(id));
|
||||
auto route = LoadRouteChain(QString("route_profiles/%1.json").arg(id));
|
||||
if (route == nullptr) {
|
||||
MW_show_log(QString("File routes/%1.json is corrupted, consider manually handling it").arg(id));
|
||||
MW_show_log(QString("File route_profiles/%1.json is corrupted, consider manually handling it").arg(id));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
std::shared_ptr<RoutingChain> ProfileManager::LoadRouteChain(const QString &jsonPath) {
|
||||
std::shared_ptr<RoutingChain> routingChain;
|
||||
auto routingChain = std::make_shared<RoutingChain>();
|
||||
routingChain->fn = jsonPath;
|
||||
if (!routingChain->Load()) {
|
||||
return nullptr;
|
||||
@ -402,6 +402,34 @@ namespace NekoGui {
|
||||
return GetGroup(dataStore->current_group);
|
||||
}
|
||||
|
||||
RouteRule::RouteRule() {
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("ip_version", &ip_version, itemType::string));
|
||||
_add(new configItem("protocol", &protocol, itemType::string));
|
||||
_add(new configItem("domain", &domain, itemType::stringList));
|
||||
_add(new configItem("domain_suffix", &domain_suffix, itemType::stringList));
|
||||
_add(new configItem("domain_keyword", &domain_keyword, itemType::stringList));
|
||||
_add(new configItem("domain_regex", &domain_regex, itemType::stringList));
|
||||
_add(new configItem("source_ip_cidr", &source_ip_cidr, itemType::stringList));
|
||||
_add(new configItem("source_ip_is_private", &source_ip_is_private, itemType::boolean));
|
||||
_add(new configItem("ip_cidr", &ip_cidr, itemType::stringList));
|
||||
_add(new configItem("ip_is_private", &ip_is_private, itemType::boolean));
|
||||
_add(new configItem("source_port", &source_port, itemType::stringList));
|
||||
_add(new configItem("source_port_range", &source_port_range, itemType::stringList));
|
||||
_add(new configItem("port", &port, itemType::stringList));
|
||||
_add(new configItem("port_range", &port_range, itemType::stringList));
|
||||
_add(new configItem("process_name", &process_name, itemType::stringList));
|
||||
_add(new configItem("process_path", &process_path, itemType::stringList));
|
||||
_add(new configItem("rule_set", &rule_set, itemType::stringList));
|
||||
_add(new configItem("invert", &invert, itemType::boolean));
|
||||
_add(new configItem("outboundID", &outboundID, itemType::integer));
|
||||
}
|
||||
|
||||
RoutingChain::RoutingChain() {
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("rules", &castedRules, itemType::jsonStoreList));
|
||||
}
|
||||
|
||||
std::shared_ptr<RoutingChain> ProfileManager::NewRouteChain() {
|
||||
auto route = std::make_shared<RoutingChain>();
|
||||
@ -415,15 +443,15 @@ namespace NekoGui {
|
||||
return routesIdOrder.last() + 1;
|
||||
}
|
||||
|
||||
bool ProfileManager::AddRouteChain(const std::shared_ptr<RoutingChain> chain) {
|
||||
bool ProfileManager::AddRouteChain(const std::shared_ptr<RoutingChain>& chain) {
|
||||
if (chain->id >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
chain->id = NewRouteChainID();
|
||||
chain->fn = QString("route_profiles/%1.json").arg(chain->id);
|
||||
routes[chain->id] = chain;
|
||||
routesIdOrder.push_back(chain->id);
|
||||
chain->fn = QString("routes/%1.json").arg(chain->id);
|
||||
chain->Save();
|
||||
|
||||
return true;
|
||||
@ -433,6 +461,25 @@ namespace NekoGui {
|
||||
return routes.count(id) > 0 ? routes[id] : nullptr;
|
||||
}
|
||||
|
||||
void ProfileManager::UpdateRouteChains(const QList<std::shared_ptr<RoutingChain>>& newChain) {
|
||||
routes.clear();
|
||||
routesIdOrder.clear();
|
||||
|
||||
for (const auto &item: newChain) {
|
||||
if (!AddRouteChain(item)) {
|
||||
routes[item->id] = item;
|
||||
item->Save();
|
||||
}
|
||||
routesIdOrder << item->id;
|
||||
}
|
||||
auto currFiles = filterIntJsonFile("route_profiles");
|
||||
for (const auto &item: currFiles) { // clean up removed route profiles
|
||||
if (!routes.count(item)) {
|
||||
QFile(QString(ROUTES_PREFIX+"%1.json").arg(item)).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<ProxyEntity>> Group::Profiles() const {
|
||||
QList<std::shared_ptr<ProxyEntity>> ret;
|
||||
for (const auto &[_, profile]: profileManager->profiles) {
|
||||
|
||||
@ -48,10 +48,12 @@ namespace NekoGui {
|
||||
|
||||
std::shared_ptr<Group> CurrentGroup();
|
||||
|
||||
bool AddRouteChain(std::shared_ptr<RoutingChain> chain);
|
||||
bool AddRouteChain(const std::shared_ptr<RoutingChain>& chain);
|
||||
|
||||
std::shared_ptr<RoutingChain> GetRouteChain(int id);
|
||||
|
||||
void UpdateRouteChains(const QList<std::shared_ptr<RoutingChain>>& newChain);
|
||||
|
||||
private:
|
||||
// sort by id
|
||||
QList<int> profilesIdOrder;
|
||||
|
||||
@ -7,43 +7,57 @@ namespace NekoGui {
|
||||
QJsonArray get_as_array(const QList<QString>& str, bool castToNum = false) {
|
||||
QJsonArray res;
|
||||
for (const auto &item: str) {
|
||||
if (item.trimmed().isEmpty()) continue;
|
||||
if (castToNum) res.append(item.toInt());
|
||||
else res.append(item);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QJsonObject RouteRule::get_rule_json(bool forView) const {
|
||||
bool isValidStrArray(const QStringList& arr) {
|
||||
for (const auto& item: arr) {
|
||||
if (!item.trimmed().isEmpty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject RouteRule::get_rule_json(bool forView, const QString& outboundTag) const {
|
||||
QJsonObject obj;
|
||||
|
||||
if (ip_version != "") obj["ip_version"] = ip_version.toInt();
|
||||
if (network != "") obj["network"] = network;
|
||||
if (protocol != "") obj["protocol"] = protocol;
|
||||
if (!domain.empty()) obj["domain"] = get_as_array(domain);
|
||||
if (!domain_suffix.empty()) obj["domain_suffix"] = get_as_array(domain_suffix);
|
||||
if (!domain_keyword.empty()) obj["domain_keyword"] = get_as_array(domain_keyword);
|
||||
if (!domain_regex.empty()) obj["domain_regex"] = get_as_array(domain_regex);
|
||||
if (!source_ip_cidr.empty()) obj["source_ip_cidr"] = get_as_array(source_ip_cidr);
|
||||
if (source_ip_is_private != nullptr) obj["source_ip_is_private"] = *source_ip_is_private;
|
||||
if (!ip_cidr.empty()) obj["ip_cidr"] = get_as_array(ip_cidr);
|
||||
if (ip_is_private != nullptr) obj["ip_is_private"] = *ip_is_private;
|
||||
if (!source_port.empty()) obj["source_port"] = get_as_array(source_port, true);
|
||||
if (!source_port_range.empty()) obj["source_port_range"] = get_as_array(source_port_range);
|
||||
if (!port.empty()) obj["port"] = get_as_array(port, true);
|
||||
if (!port_range.empty()) obj["port_range"] = get_as_array(port_range);
|
||||
if (!process_name.empty()) obj["process_name"] = get_as_array(process_name);
|
||||
if (!process_path.empty()) obj["process_path"] = get_as_array(process_path);
|
||||
if (!rule_set.empty()) obj["rule_set"] = get_as_array(rule_set);
|
||||
if (isValidStrArray(domain)) obj["domain"] = get_as_array(domain);
|
||||
if (isValidStrArray(domain_suffix)) obj["domain_suffix"] = get_as_array(domain_suffix);
|
||||
if (isValidStrArray(domain_keyword)) obj["domain_keyword"] = get_as_array(domain_keyword);
|
||||
if (isValidStrArray(domain_regex)) obj["domain_regex"] = get_as_array(domain_regex);
|
||||
if (isValidStrArray(source_ip_cidr)) obj["source_ip_cidr"] = get_as_array(source_ip_cidr);
|
||||
if (source_ip_is_private) obj["source_ip_is_private"] = source_ip_is_private;
|
||||
if (isValidStrArray(ip_cidr)) obj["ip_cidr"] = get_as_array(ip_cidr);
|
||||
if (ip_is_private) obj["ip_is_private"] = ip_is_private;
|
||||
if (isValidStrArray(source_port)) obj["source_port"] = get_as_array(source_port, true);
|
||||
if (isValidStrArray(source_port_range)) obj["source_port_range"] = get_as_array(source_port_range);
|
||||
if (isValidStrArray(port)) obj["port"] = get_as_array(port, true);
|
||||
if (isValidStrArray(port_range)) obj["port_range"] = get_as_array(port_range);
|
||||
if (isValidStrArray(process_name)) obj["process_name"] = get_as_array(process_name);
|
||||
if (isValidStrArray(process_path)) obj["process_path"] = get_as_array(process_path);
|
||||
if (isValidStrArray(rule_set)) obj["rule_set"] = get_as_array(rule_set);
|
||||
if (invert) obj["invert"] = invert;
|
||||
|
||||
if (forView) {
|
||||
switch (outboundID) { // TODO use constants
|
||||
case -1:
|
||||
obj["outbound"] = "proxy";
|
||||
break;
|
||||
case -2:
|
||||
obj["outbound"] = "direct";
|
||||
break;
|
||||
case -3:
|
||||
obj["outbound"] = "block";
|
||||
break;
|
||||
case -4:
|
||||
obj["outbound"] = "dns_out";
|
||||
break;
|
||||
default:
|
||||
auto prof = NekoGui::profileManager->GetProfile(outboundID);
|
||||
if (prof == nullptr) {
|
||||
@ -53,13 +67,13 @@ namespace NekoGui {
|
||||
obj["outbound"] = prof->bean->DisplayName();
|
||||
}
|
||||
} else {
|
||||
obj["outbound"] = outboundID;
|
||||
if (!outboundTag.isEmpty()) obj["outbound"] = outboundTag;
|
||||
else obj["outbound"] = outboundID;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
// TODO use constant for field names
|
||||
QStringList RouteRule::get_attributes() {
|
||||
return {
|
||||
@ -111,13 +125,13 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
QStringList RouteRule::get_current_value_string(const QString& fieldName) {
|
||||
if (fieldName == "ip_version" && ip_version != "") {
|
||||
if (fieldName == "ip_version") {
|
||||
return {ip_version};
|
||||
}
|
||||
if (fieldName == "network" && network != "") {
|
||||
if (fieldName == "network") {
|
||||
return {network};
|
||||
}
|
||||
if (fieldName == "protocol" && protocol != "") {
|
||||
if (fieldName == "protocol") {
|
||||
return {protocol};
|
||||
}
|
||||
if (fieldName == "domain") return domain;
|
||||
@ -136,41 +150,20 @@ namespace NekoGui {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool* RouteRule::get_current_value_bool(const QString& fieldName) const {
|
||||
QString RouteRule::get_current_value_bool(const QString& fieldName) const {
|
||||
if (fieldName == "source_ip_is_private") {
|
||||
return source_ip_is_private;
|
||||
return source_ip_is_private? "true":"false";
|
||||
}
|
||||
if (fieldName == "ip_is_private") {
|
||||
return ip_is_private;
|
||||
return ip_is_private? "true":"false";
|
||||
}
|
||||
if (fieldName == "invert") {
|
||||
return reinterpret_cast<bool*>(invert);
|
||||
return invert? "true":"false";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RouteRule::set_field_value(const QString& fieldName, const QStringList& value) {
|
||||
/*
|
||||
* "ip_version",
|
||||
"network",
|
||||
"protocol",
|
||||
"domain",
|
||||
"domain_suffix",
|
||||
"domain_keyword",
|
||||
"domain_regex",
|
||||
"source_ip_cidr",
|
||||
"source_ip_is_private",
|
||||
"ip_cidr",
|
||||
"ip_is_private",
|
||||
"source_port",
|
||||
"source_port_range",
|
||||
"port",
|
||||
"port_range",
|
||||
"process_name",
|
||||
"process_path",
|
||||
"rule_set",
|
||||
"invert",
|
||||
*/
|
||||
if (fieldName == "ip_version") {
|
||||
ip_version = value[0];
|
||||
}
|
||||
@ -196,13 +189,13 @@ namespace NekoGui {
|
||||
source_ip_cidr = value;
|
||||
}
|
||||
if (fieldName == "source_ip_is_private") {
|
||||
source_ip_is_private = reinterpret_cast<bool*>((value[0] == "true"));
|
||||
source_ip_is_private = value[0]=="true";
|
||||
}
|
||||
if (fieldName == "ip_cidr") {
|
||||
ip_cidr = value;
|
||||
}
|
||||
if (fieldName == "ip_is_private") {
|
||||
ip_is_private = reinterpret_cast<bool*>((value[0] == "true"));
|
||||
ip_is_private = value[0]=="true";
|
||||
}
|
||||
if (fieldName == "source_port") {
|
||||
source_port = value;
|
||||
@ -230,10 +223,16 @@ namespace NekoGui {
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray RoutingChain::get_route_rules(bool forView) {
|
||||
bool RouteRule::isEmpty() const {
|
||||
return get_rule_json().keys().length() == 1;
|
||||
}
|
||||
|
||||
QJsonArray RoutingChain::get_route_rules(bool forView, std::map<int, QString> outboundMap) {
|
||||
QJsonArray res;
|
||||
for (const auto &item: Rules) {
|
||||
auto rule_json = item->get_rule_json(forView);
|
||||
auto outboundTag = QString();
|
||||
if (outboundMap.count(item->outboundID)) outboundTag = outboundMap[item->outboundID];
|
||||
auto rule_json = item->get_rule_json(forView, outboundTag);
|
||||
if (rule_json.empty()) {
|
||||
MW_show_log("Aborted generating routing section, an error has occurred");
|
||||
return {};
|
||||
@ -245,12 +244,63 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
std::shared_ptr<RoutingChain> RoutingChain::GetDefaultChain() {
|
||||
auto defaultChain = RoutingChain();
|
||||
defaultChain.name = "Default";
|
||||
auto defaultRule = RouteRule();
|
||||
defaultRule.protocol = {"dns"};
|
||||
defaultRule.outboundID = -4;
|
||||
defaultChain.Rules << std::make_shared<RouteRule>(defaultRule);
|
||||
return std::make_shared<RoutingChain>(defaultChain);
|
||||
auto defaultChain = std::make_shared<RoutingChain>();
|
||||
defaultChain->name = "Default";
|
||||
auto defaultRule = std::make_shared<RouteRule>();
|
||||
defaultRule->protocol = "dns";
|
||||
defaultRule->outboundID = -4;
|
||||
defaultChain->Rules << defaultRule;
|
||||
return defaultChain;
|
||||
}
|
||||
|
||||
std::shared_ptr<QList<int>> RoutingChain::get_used_outbounds() {
|
||||
auto res = std::make_shared<QList<int>>();
|
||||
for (const auto& item: Rules) {
|
||||
res->push_back(item->outboundID);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<QStringList> RoutingChain::get_used_rule_sets() {
|
||||
auto res = std::make_shared<QStringList>();
|
||||
for (const auto& item: Rules) {
|
||||
for (const auto& ruleItem: item->rule_set) {
|
||||
res->push_back(ruleItem);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool RoutingChain::Save() {
|
||||
castedRules.clear();
|
||||
for (const auto &item: Rules) {
|
||||
castedRules.push_back(dynamic_cast<JsonStore*>(item.get()));
|
||||
}
|
||||
return JsonStore::Save();
|
||||
}
|
||||
|
||||
void RoutingChain::FromJson(QJsonObject object) {
|
||||
for (const auto &key: object.keys()) {
|
||||
if (_map.count(key) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value = object[key];
|
||||
auto item = _map[key].get();
|
||||
|
||||
if (item == nullptr) continue;
|
||||
if (item->type == itemType::jsonStoreList) {
|
||||
// it is of rule type
|
||||
if (!value.isArray()) continue;
|
||||
Rules.clear();
|
||||
auto arr = value.toArray();
|
||||
for (auto obj : arr) {
|
||||
auto rule = std::make_shared<RouteRule>();
|
||||
rule->FromJson(obj.toObject());
|
||||
Rules << rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonStore::FromJson(object);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,8 @@ namespace NekoGui {
|
||||
|
||||
class RouteRule : public JsonStore {
|
||||
public:
|
||||
RouteRule();
|
||||
|
||||
QString name = "";
|
||||
QString ip_version;
|
||||
QString network;
|
||||
@ -16,9 +18,9 @@ namespace NekoGui {
|
||||
QList<QString> domain_keyword;
|
||||
QList<QString> domain_regex;
|
||||
QList<QString> source_ip_cidr;
|
||||
bool* source_ip_is_private = nullptr;
|
||||
bool source_ip_is_private = false;
|
||||
QList<QString> ip_cidr;
|
||||
bool* ip_is_private = nullptr;
|
||||
bool ip_is_private = false;
|
||||
QList<QString> source_port;
|
||||
QList<QString> source_port_range;
|
||||
QList<QString> port;
|
||||
@ -27,15 +29,16 @@ namespace NekoGui {
|
||||
QList<QString> process_path;
|
||||
QList<QString> rule_set;
|
||||
bool invert = false;
|
||||
int outboundID = -1; // -1 is invalid -2 is direct -3 is block -4 is dns_out
|
||||
int outboundID = -2; // -1 is proxy -2 is direct -3 is block -4 is dns_out
|
||||
|
||||
[[nodiscard]] QJsonObject get_rule_json(bool forView = false) const;
|
||||
[[nodiscard]] QJsonObject get_rule_json(bool forView = false, const QString& outboundTag = "") const;
|
||||
static QStringList get_attributes();
|
||||
static inputType get_input_type(const QString& fieldName);
|
||||
static QStringList get_values_for_field(const QString& fieldName);
|
||||
QStringList get_current_value_string(const QString& fieldName);
|
||||
[[nodiscard]] bool* get_current_value_bool(const QString& fieldName) const;
|
||||
[[nodiscard]] QString get_current_value_bool(const QString& fieldName) const;
|
||||
void set_field_value(const QString& fieldName, const QStringList& value);
|
||||
[[nodiscard]] bool isEmpty() const;
|
||||
};
|
||||
|
||||
class RoutingChain : public JsonStore {
|
||||
@ -43,9 +46,20 @@ namespace NekoGui {
|
||||
int id = -1;
|
||||
QString name = "";
|
||||
QList<std::shared_ptr<RouteRule>> Rules;
|
||||
QList<JsonStore*> castedRules;
|
||||
|
||||
QJsonArray get_route_rules(bool forView = false);
|
||||
RoutingChain();
|
||||
|
||||
bool Save() override;
|
||||
|
||||
void FromJson(QJsonObject object);
|
||||
|
||||
QJsonArray get_route_rules(bool forView = false, std::map<int, QString> outboundMap = {});
|
||||
|
||||
static std::shared_ptr<RoutingChain> GetDefaultChain();
|
||||
|
||||
std::shared_ptr<QList<int>> get_used_outbounds();
|
||||
|
||||
std::shared_ptr<QStringList> get_used_rule_sets();
|
||||
};
|
||||
} // namespace NekoGui
|
||||
@ -25,7 +25,7 @@ namespace NekoGui_fmt {
|
||||
}
|
||||
} else if (network == "http") {
|
||||
if (!path.isEmpty()) transport["path"] = path;
|
||||
if (!host.isEmpty()) transport["host"] = QList2QJsonArray(host.split(","));
|
||||
if (!host.isEmpty()) transport["host"] = QListStr2QJsonArray(host.split(","));
|
||||
} else if (network == "grpc") {
|
||||
if (!path.isEmpty()) transport["service_name"] = path;
|
||||
} else if (network == "httpupgrade") {
|
||||
@ -39,7 +39,7 @@ namespace NekoGui_fmt {
|
||||
{"type", "http"},
|
||||
{"method", "GET"},
|
||||
{"path", path},
|
||||
{"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}},
|
||||
{"headers", QJsonObject{{"Host", QListStr2QJsonArray(host.split(","))}}},
|
||||
};
|
||||
outbound->insert("transport", transport);
|
||||
}
|
||||
@ -53,7 +53,7 @@ namespace NekoGui_fmt {
|
||||
tls["certificate"] = certificate.trimmed();
|
||||
}
|
||||
if (!alpn.trimmed().isEmpty()) {
|
||||
tls["alpn"] = QList2QJsonArray(alpn.split(","));
|
||||
tls["alpn"] = QListStr2QJsonArray(alpn.split(","));
|
||||
}
|
||||
QString fp = utlsFingerprint;
|
||||
if (!reality_pbk.trimmed().isEmpty()) {
|
||||
@ -182,7 +182,7 @@ namespace NekoGui_fmt {
|
||||
{"certificate", caText.trimmed()},
|
||||
{"server_name", sni},
|
||||
};
|
||||
if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QList2QJsonArray(alpn.split(","));
|
||||
if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QListStr2QJsonArray(alpn.split(","));
|
||||
if (proxy_type == proxy_Hysteria2) coreTlsObj["alpn"] = "h3";
|
||||
|
||||
QJsonObject outbound{
|
||||
|
||||
@ -141,7 +141,7 @@ namespace NekoGui_fmt {
|
||||
relay["zero_rtt_handshake"] = zeroRttHandshake;
|
||||
relay["disable_sni"] = disableSni;
|
||||
if (!heartbeat.trimmed().isEmpty()) relay["heartbeat"] = heartbeat;
|
||||
if (!alpn.trimmed().isEmpty()) relay["alpn"] = QList2QJsonArray(alpn.split(","));
|
||||
if (!alpn.trimmed().isEmpty()) relay["alpn"] = QListStr2QJsonArray(alpn.split(","));
|
||||
|
||||
if (!caText.trimmed().isEmpty()) {
|
||||
WriteTempFile("tuic_" + GetRandomString(10) + ".crt", caText.toUtf8());
|
||||
|
||||
@ -3,13 +3,13 @@ module nekobox_core
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240524210208-2f5446580565
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240614112142-907408da0959
|
||||
github.com/matsuridayo/libneko v0.0.0-20230913024055-5277a5bfc889
|
||||
github.com/sagernet/sing-box v1.9.0-rc.21
|
||||
github.com/sagernet/sing-box v1.9.3
|
||||
grpc_server v1.0.0
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing-box => github.com/Mahdi-zarei/sing-box v1.3.5-0.20240524203629-e284713b657d
|
||||
replace github.com/sagernet/sing-box => github.com/Mahdi-zarei/sing-box v1.3.5-0.20240613091205-668453f192a9
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
@ -55,16 +55,16 @@ require (
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
|
||||
github.com/sagernet/quic-go v0.43.1-beta.1 // indirect
|
||||
github.com/sagernet/quic-go v0.43.1-beta.2 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing v0.4.0-beta.20 // indirect
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.18 // indirect
|
||||
github.com/sagernet/sing v0.4.1 // indirect
|
||||
github.com/sagernet/sing-dns v0.2.0 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.0 // indirect
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.5 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.6 // indirect
|
||||
github.com/sagernet/sing-tun v0.3.2 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
|
||||
@ -84,7 +84,7 @@ require (
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
|
||||
@ -4,10 +4,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Mahdi-zarei/sing-box v1.3.5-0.20240524203629-e284713b657d h1:l+/67xmhHesa9urn+sdBfiwuvpt+r4Wj7hwrFnWSq+A=
|
||||
github.com/Mahdi-zarei/sing-box v1.3.5-0.20240524203629-e284713b657d/go.mod h1:7OFR+knnBYl9AeUpGT+EGFkhzM8KPXSZbbMi/vFoCDY=
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240524210208-2f5446580565 h1:GrE6+ZvQWjmbDtcOQh3avtXKTynt72iLUytMWNWZUt0=
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240524210208-2f5446580565/go.mod h1:H3YA/kY0yKLmSP359MpY7oms4dQ9YW4s3dcobiarQS8=
|
||||
github.com/Mahdi-zarei/sing-box v1.3.5-0.20240613091205-668453f192a9 h1:sB4V56lOFjDwefldsXLF0LldVFIr3K6eXXVTqb/PjQc=
|
||||
github.com/Mahdi-zarei/sing-box v1.3.5-0.20240613091205-668453f192a9/go.mod h1:6Rx5nzbqIfN7HlUaHgO/IdkP7fDPPQ/U/TAC5asEjSM=
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240614112142-907408da0959 h1:1UClVl6qz9Su3gFUcY9ot21yXcvn4yPjbDfBMB1Bd+Q=
|
||||
github.com/Mahdi-zarei/sing-box-extra v0.0.0-20240614112142-907408da0959/go.mod h1:umyw9HvgSA5opzUDaTL2w9fzY+6zIhmMY0pTiZfY7Lg=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
@ -134,15 +134,15 @@ github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
|
||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.1 h1:alizUjpvWYcz08dBCQsULOd+1xu0o7UtlyYf6SLbRNg=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.1/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhYiNPyYEBL/BVJ1ifc=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.2 h1:6YRCE9t1Q3UbNX1/dJGqpwFQbh6DXC6XBrQr2xp6hXY=
|
||||
github.com/sagernet/quic-go v0.43.1-beta.2/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhYiNPyYEBL/BVJ1ifc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.4.0-beta.20 h1:8rEepj4LMcR0Wd389fJIziv/jr3MBtX5qXBHsfxJ+dY=
|
||||
github.com/sagernet/sing v0.4.0-beta.20/go.mod h1:PFQKbElc2Pke7faBLv8oEba5ehtKO21Ho+TkYemTI3Y=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.18 h1:6vzXZThRdA7YUzBOpSbUT48XRumtl/KIpIHFSOP0za8=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.18/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24=
|
||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
||||
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/sagernet/sing-dns v0.2.0 h1:dka3weRX6+CrYO3v+hrTy2z68rCOCZXNBiNXpLZ6JNs=
|
||||
github.com/sagernet/sing-dns v0.2.0/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.5 h1:ceKFLd1iS5AtM+pScKmcDp5k7R6WgYIe8vl6nB0aVsE=
|
||||
@ -153,8 +153,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.6 h1:L11kMrM7UfUW0pzQiU66Fffh4o86KZc1SFGbkYi8Ma8=
|
||||
github.com/sagernet/sing-tun v0.3.0-beta.6/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
|
||||
github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY=
|
||||
github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
@ -257,8 +257,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"grpc_server"
|
||||
@ -150,3 +151,53 @@ func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.Li
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *server) GetGeoIPList(ctx context.Context, in *gen.EmptyReq) (*gen.GetGeoIPListResponse, error) {
|
||||
resp, err := boxmain.ListGeoip()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]string, 0)
|
||||
for _, r := range resp {
|
||||
r += "_IP"
|
||||
res = append(res, r)
|
||||
}
|
||||
|
||||
return &gen.GetGeoIPListResponse{Items: res}, nil
|
||||
}
|
||||
|
||||
func (s *server) GetGeoSiteList(ctx context.Context, in *gen.EmptyReq) (*gen.GetGeoSiteListResponse, error) {
|
||||
resp, err := boxmain.GeositeList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]string, 0)
|
||||
for _, r := range resp {
|
||||
r += "_SITE"
|
||||
res = append(res, r)
|
||||
}
|
||||
|
||||
return &gen.GetGeoSiteListResponse{Items: res}, nil
|
||||
}
|
||||
|
||||
func (s *server) CompileGeoIPToSrs(ctx context.Context, in *gen.CompileGeoIPToSrsRequest) (*gen.EmptyResp, error) {
|
||||
category := strings.TrimSuffix(in.Item, "_IP")
|
||||
err := boxmain.CompileRuleSet(category, boxmain.IpRuleSet, "./rule_sets/"+in.Item+".srs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gen.EmptyResp{}, nil
|
||||
}
|
||||
|
||||
func (s *server) CompileGeoSiteToSrs(ctx context.Context, in *gen.CompileGeoSiteToSrsRequest) (*gen.EmptyResp, error) {
|
||||
category := strings.TrimSuffix(in.Item, "_SITE")
|
||||
err := boxmain.CompileRuleSet(category, boxmain.SiteRuleSet, "./rule_sets/"+in.Item+".srs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gen.EmptyResp{}, nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc v5.26.1
|
||||
// source: libcore.proto
|
||||
|
||||
@ -819,6 +819,194 @@ func (x *ListConnectionsResp) GetNekorayConnectionsJson() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetGeoIPListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Items []string `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetGeoIPListResponse) Reset() {
|
||||
*x = GetGeoIPListResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_libcore_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetGeoIPListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetGeoIPListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetGeoIPListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_libcore_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetGeoIPListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetGeoIPListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_libcore_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *GetGeoIPListResponse) GetItems() []string {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetGeoSiteListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Items []string `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetGeoSiteListResponse) Reset() {
|
||||
*x = GetGeoSiteListResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_libcore_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetGeoSiteListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetGeoSiteListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetGeoSiteListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_libcore_proto_msgTypes[12]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetGeoSiteListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetGeoSiteListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_libcore_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *GetGeoSiteListResponse) GetItems() []string {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CompileGeoIPToSrsRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Item string `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CompileGeoIPToSrsRequest) Reset() {
|
||||
*x = CompileGeoIPToSrsRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_libcore_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CompileGeoIPToSrsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CompileGeoIPToSrsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CompileGeoIPToSrsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_libcore_proto_msgTypes[13]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CompileGeoIPToSrsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CompileGeoIPToSrsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_libcore_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *CompileGeoIPToSrsRequest) GetItem() string {
|
||||
if x != nil {
|
||||
return x.Item
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CompileGeoSiteToSrsRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Item string `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CompileGeoSiteToSrsRequest) Reset() {
|
||||
*x = CompileGeoSiteToSrsRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_libcore_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CompileGeoSiteToSrsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CompileGeoSiteToSrsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CompileGeoSiteToSrsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_libcore_proto_msgTypes[14]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CompileGeoSiteToSrsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CompileGeoSiteToSrsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_libcore_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *CompileGeoSiteToSrsRequest) GetItem() string {
|
||||
if x != nil {
|
||||
return x.Item
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_libcore_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_libcore_proto_rawDesc = []byte{
|
||||
@ -905,13 +1093,25 @@ var file_libcore_proto_rawDesc = []byte{
|
||||
0x12, 0x38, 0x0a, 0x18, 0x6e, 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x16, 0x6e, 0x65, 0x6b, 0x6f, 0x72, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x2a, 0x32, 0x0a, 0x08, 0x54, 0x65,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x14, 0x47, 0x65,
|
||||
0x74, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x2e, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x47,
|
||||
0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x2e, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70,
|
||||
0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x30, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x70,
|
||||
0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x2a, 0x32, 0x0a, 0x08, 0x54, 0x65,
|
||||
0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x63, 0x70, 0x50, 0x69, 0x6e,
|
||||
0x67, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x72, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x10, 0x01,
|
||||
0x12, 0x0c, 0x0a, 0x08, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x10, 0x02, 0x2a, 0x27,
|
||||
0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09,
|
||||
0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x6f, 0x77,
|
||||
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x0e, 0x4c, 0x69, 0x62, 0x63,
|
||||
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0x01, 0x32, 0xb8, 0x05, 0x0a, 0x0e, 0x4c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x45, 0x78,
|
||||
0x69, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
@ -936,9 +1136,27 @@ var file_libcore_proto_rawDesc = []byte{
|
||||
0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x11, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1c,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, 0x11,
|
||||
0x5a, 0x0f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x67, 0x65,
|
||||
0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x40,
|
||||
0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x11,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
|
||||
0x71, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47,
|
||||
0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x44, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
|
||||
0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f,
|
||||
0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
|
||||
0x73, 0x70, 0x12, 0x4e, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f,
|
||||
0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x12, 0x23, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53, 0x69,
|
||||
0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
|
||||
0x73, 0x70, 0x42, 0x11, 0x5a, 0x0f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x2f, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -954,21 +1172,25 @@ func file_libcore_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_libcore_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_libcore_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_libcore_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_libcore_proto_goTypes = []interface{}{
|
||||
(TestMode)(0), // 0: libcore.TestMode
|
||||
(UpdateAction)(0), // 1: libcore.UpdateAction
|
||||
(*EmptyReq)(nil), // 2: libcore.EmptyReq
|
||||
(*EmptyResp)(nil), // 3: libcore.EmptyResp
|
||||
(*ErrorResp)(nil), // 4: libcore.ErrorResp
|
||||
(*LoadConfigReq)(nil), // 5: libcore.LoadConfigReq
|
||||
(*TestReq)(nil), // 6: libcore.TestReq
|
||||
(*TestResp)(nil), // 7: libcore.TestResp
|
||||
(*QueryStatsReq)(nil), // 8: libcore.QueryStatsReq
|
||||
(*QueryStatsResp)(nil), // 9: libcore.QueryStatsResp
|
||||
(*UpdateReq)(nil), // 10: libcore.UpdateReq
|
||||
(*UpdateResp)(nil), // 11: libcore.UpdateResp
|
||||
(*ListConnectionsResp)(nil), // 12: libcore.ListConnectionsResp
|
||||
(TestMode)(0), // 0: libcore.TestMode
|
||||
(UpdateAction)(0), // 1: libcore.UpdateAction
|
||||
(*EmptyReq)(nil), // 2: libcore.EmptyReq
|
||||
(*EmptyResp)(nil), // 3: libcore.EmptyResp
|
||||
(*ErrorResp)(nil), // 4: libcore.ErrorResp
|
||||
(*LoadConfigReq)(nil), // 5: libcore.LoadConfigReq
|
||||
(*TestReq)(nil), // 6: libcore.TestReq
|
||||
(*TestResp)(nil), // 7: libcore.TestResp
|
||||
(*QueryStatsReq)(nil), // 8: libcore.QueryStatsReq
|
||||
(*QueryStatsResp)(nil), // 9: libcore.QueryStatsResp
|
||||
(*UpdateReq)(nil), // 10: libcore.UpdateReq
|
||||
(*UpdateResp)(nil), // 11: libcore.UpdateResp
|
||||
(*ListConnectionsResp)(nil), // 12: libcore.ListConnectionsResp
|
||||
(*GetGeoIPListResponse)(nil), // 13: libcore.GetGeoIPListResponse
|
||||
(*GetGeoSiteListResponse)(nil), // 14: libcore.GetGeoSiteListResponse
|
||||
(*CompileGeoIPToSrsRequest)(nil), // 15: libcore.CompileGeoIPToSrsRequest
|
||||
(*CompileGeoSiteToSrsRequest)(nil), // 16: libcore.CompileGeoSiteToSrsRequest
|
||||
}
|
||||
var file_libcore_proto_depIdxs = []int32{
|
||||
0, // 0: libcore.TestReq.mode:type_name -> libcore.TestMode
|
||||
@ -981,15 +1203,23 @@ var file_libcore_proto_depIdxs = []int32{
|
||||
6, // 7: libcore.LibcoreService.Test:input_type -> libcore.TestReq
|
||||
8, // 8: libcore.LibcoreService.QueryStats:input_type -> libcore.QueryStatsReq
|
||||
2, // 9: libcore.LibcoreService.ListConnections:input_type -> libcore.EmptyReq
|
||||
3, // 10: libcore.LibcoreService.Exit:output_type -> libcore.EmptyResp
|
||||
11, // 11: libcore.LibcoreService.Update:output_type -> libcore.UpdateResp
|
||||
4, // 12: libcore.LibcoreService.Start:output_type -> libcore.ErrorResp
|
||||
4, // 13: libcore.LibcoreService.Stop:output_type -> libcore.ErrorResp
|
||||
7, // 14: libcore.LibcoreService.Test:output_type -> libcore.TestResp
|
||||
9, // 15: libcore.LibcoreService.QueryStats:output_type -> libcore.QueryStatsResp
|
||||
12, // 16: libcore.LibcoreService.ListConnections:output_type -> libcore.ListConnectionsResp
|
||||
10, // [10:17] is the sub-list for method output_type
|
||||
3, // [3:10] is the sub-list for method input_type
|
||||
2, // 10: libcore.LibcoreService.GetGeoIPList:input_type -> libcore.EmptyReq
|
||||
2, // 11: libcore.LibcoreService.GetGeoSiteList:input_type -> libcore.EmptyReq
|
||||
15, // 12: libcore.LibcoreService.CompileGeoIPToSrs:input_type -> libcore.CompileGeoIPToSrsRequest
|
||||
16, // 13: libcore.LibcoreService.CompileGeoSiteToSrs:input_type -> libcore.CompileGeoSiteToSrsRequest
|
||||
3, // 14: libcore.LibcoreService.Exit:output_type -> libcore.EmptyResp
|
||||
11, // 15: libcore.LibcoreService.Update:output_type -> libcore.UpdateResp
|
||||
4, // 16: libcore.LibcoreService.Start:output_type -> libcore.ErrorResp
|
||||
4, // 17: libcore.LibcoreService.Stop:output_type -> libcore.ErrorResp
|
||||
7, // 18: libcore.LibcoreService.Test:output_type -> libcore.TestResp
|
||||
9, // 19: libcore.LibcoreService.QueryStats:output_type -> libcore.QueryStatsResp
|
||||
12, // 20: libcore.LibcoreService.ListConnections:output_type -> libcore.ListConnectionsResp
|
||||
13, // 21: libcore.LibcoreService.GetGeoIPList:output_type -> libcore.GetGeoIPListResponse
|
||||
14, // 22: libcore.LibcoreService.GetGeoSiteList:output_type -> libcore.GetGeoSiteListResponse
|
||||
3, // 23: libcore.LibcoreService.CompileGeoIPToSrs:output_type -> libcore.EmptyResp
|
||||
3, // 24: libcore.LibcoreService.CompileGeoSiteToSrs:output_type -> libcore.EmptyResp
|
||||
14, // [14:25] is the sub-list for method output_type
|
||||
3, // [3:14] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
@ -1133,6 +1363,54 @@ func file_libcore_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_libcore_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetGeoIPListResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_libcore_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetGeoSiteListResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_libcore_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CompileGeoIPToSrsRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_libcore_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CompileGeoSiteToSrsRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@ -1140,7 +1418,7 @@ func file_libcore_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_libcore_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 11,
|
||||
NumMessages: 15,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@ -12,6 +12,11 @@ service LibcoreService {
|
||||
rpc Test(TestReq) returns (TestResp) {}
|
||||
rpc QueryStats(QueryStatsReq) returns (QueryStatsResp) {}
|
||||
rpc ListConnections(EmptyReq) returns (ListConnectionsResp) {}
|
||||
//
|
||||
rpc GetGeoIPList(EmptyReq) returns (GetGeoIPListResponse);
|
||||
rpc GetGeoSiteList(EmptyReq) returns (GetGeoSiteListResponse);
|
||||
rpc CompileGeoIPToSrs(CompileGeoIPToSrsRequest) returns (EmptyResp);
|
||||
rpc CompileGeoSiteToSrs(CompileGeoSiteToSrsRequest) returns (EmptyResp);
|
||||
}
|
||||
|
||||
message EmptyReq {}
|
||||
@ -93,3 +98,19 @@ message UpdateResp {
|
||||
message ListConnectionsResp {
|
||||
string nekoray_connections_json = 1;
|
||||
}
|
||||
|
||||
message GetGeoIPListResponse {
|
||||
repeated string items = 1;
|
||||
}
|
||||
|
||||
message GetGeoSiteListResponse {
|
||||
repeated string items = 2;
|
||||
}
|
||||
|
||||
message CompileGeoIPToSrsRequest {
|
||||
string item = 1;
|
||||
}
|
||||
|
||||
message CompileGeoSiteToSrsRequest {
|
||||
string item = 1;
|
||||
}
|
||||
@ -19,13 +19,17 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
LibcoreService_Exit_FullMethodName = "/libcore.LibcoreService/Exit"
|
||||
LibcoreService_Update_FullMethodName = "/libcore.LibcoreService/Update"
|
||||
LibcoreService_Start_FullMethodName = "/libcore.LibcoreService/Start"
|
||||
LibcoreService_Stop_FullMethodName = "/libcore.LibcoreService/Stop"
|
||||
LibcoreService_Test_FullMethodName = "/libcore.LibcoreService/Test"
|
||||
LibcoreService_QueryStats_FullMethodName = "/libcore.LibcoreService/QueryStats"
|
||||
LibcoreService_ListConnections_FullMethodName = "/libcore.LibcoreService/ListConnections"
|
||||
LibcoreService_Exit_FullMethodName = "/libcore.LibcoreService/Exit"
|
||||
LibcoreService_Update_FullMethodName = "/libcore.LibcoreService/Update"
|
||||
LibcoreService_Start_FullMethodName = "/libcore.LibcoreService/Start"
|
||||
LibcoreService_Stop_FullMethodName = "/libcore.LibcoreService/Stop"
|
||||
LibcoreService_Test_FullMethodName = "/libcore.LibcoreService/Test"
|
||||
LibcoreService_QueryStats_FullMethodName = "/libcore.LibcoreService/QueryStats"
|
||||
LibcoreService_ListConnections_FullMethodName = "/libcore.LibcoreService/ListConnections"
|
||||
LibcoreService_GetGeoIPList_FullMethodName = "/libcore.LibcoreService/GetGeoIPList"
|
||||
LibcoreService_GetGeoSiteList_FullMethodName = "/libcore.LibcoreService/GetGeoSiteList"
|
||||
LibcoreService_CompileGeoIPToSrs_FullMethodName = "/libcore.LibcoreService/CompileGeoIPToSrs"
|
||||
LibcoreService_CompileGeoSiteToSrs_FullMethodName = "/libcore.LibcoreService/CompileGeoSiteToSrs"
|
||||
)
|
||||
|
||||
// LibcoreServiceClient is the client API for LibcoreService service.
|
||||
@ -39,6 +43,10 @@ type LibcoreServiceClient interface {
|
||||
Test(ctx context.Context, in *TestReq, opts ...grpc.CallOption) (*TestResp, error)
|
||||
QueryStats(ctx context.Context, in *QueryStatsReq, opts ...grpc.CallOption) (*QueryStatsResp, error)
|
||||
ListConnections(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*ListConnectionsResp, error)
|
||||
GetGeoIPList(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*GetGeoIPListResponse, error)
|
||||
GetGeoSiteList(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*GetGeoSiteListResponse, error)
|
||||
CompileGeoIPToSrs(ctx context.Context, in *CompileGeoIPToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error)
|
||||
CompileGeoSiteToSrs(ctx context.Context, in *CompileGeoSiteToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error)
|
||||
}
|
||||
|
||||
type libcoreServiceClient struct {
|
||||
@ -112,6 +120,42 @@ func (c *libcoreServiceClient) ListConnections(ctx context.Context, in *EmptyReq
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *libcoreServiceClient) GetGeoIPList(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*GetGeoIPListResponse, error) {
|
||||
out := new(GetGeoIPListResponse)
|
||||
err := c.cc.Invoke(ctx, LibcoreService_GetGeoIPList_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *libcoreServiceClient) GetGeoSiteList(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*GetGeoSiteListResponse, error) {
|
||||
out := new(GetGeoSiteListResponse)
|
||||
err := c.cc.Invoke(ctx, LibcoreService_GetGeoSiteList_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *libcoreServiceClient) CompileGeoIPToSrs(ctx context.Context, in *CompileGeoIPToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error) {
|
||||
out := new(EmptyResp)
|
||||
err := c.cc.Invoke(ctx, LibcoreService_CompileGeoIPToSrs_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *libcoreServiceClient) CompileGeoSiteToSrs(ctx context.Context, in *CompileGeoSiteToSrsRequest, opts ...grpc.CallOption) (*EmptyResp, error) {
|
||||
out := new(EmptyResp)
|
||||
err := c.cc.Invoke(ctx, LibcoreService_CompileGeoSiteToSrs_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// LibcoreServiceServer is the server API for LibcoreService service.
|
||||
// All implementations must embed UnimplementedLibcoreServiceServer
|
||||
// for forward compatibility
|
||||
@ -123,6 +167,10 @@ type LibcoreServiceServer interface {
|
||||
Test(context.Context, *TestReq) (*TestResp, error)
|
||||
QueryStats(context.Context, *QueryStatsReq) (*QueryStatsResp, error)
|
||||
ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error)
|
||||
GetGeoIPList(context.Context, *EmptyReq) (*GetGeoIPListResponse, error)
|
||||
GetGeoSiteList(context.Context, *EmptyReq) (*GetGeoSiteListResponse, error)
|
||||
CompileGeoIPToSrs(context.Context, *CompileGeoIPToSrsRequest) (*EmptyResp, error)
|
||||
CompileGeoSiteToSrs(context.Context, *CompileGeoSiteToSrsRequest) (*EmptyResp, error)
|
||||
mustEmbedUnimplementedLibcoreServiceServer()
|
||||
}
|
||||
|
||||
@ -151,6 +199,18 @@ func (UnimplementedLibcoreServiceServer) QueryStats(context.Context, *QueryStats
|
||||
func (UnimplementedLibcoreServiceServer) ListConnections(context.Context, *EmptyReq) (*ListConnectionsResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListConnections not implemented")
|
||||
}
|
||||
func (UnimplementedLibcoreServiceServer) GetGeoIPList(context.Context, *EmptyReq) (*GetGeoIPListResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetGeoIPList not implemented")
|
||||
}
|
||||
func (UnimplementedLibcoreServiceServer) GetGeoSiteList(context.Context, *EmptyReq) (*GetGeoSiteListResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetGeoSiteList not implemented")
|
||||
}
|
||||
func (UnimplementedLibcoreServiceServer) CompileGeoIPToSrs(context.Context, *CompileGeoIPToSrsRequest) (*EmptyResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CompileGeoIPToSrs not implemented")
|
||||
}
|
||||
func (UnimplementedLibcoreServiceServer) CompileGeoSiteToSrs(context.Context, *CompileGeoSiteToSrsRequest) (*EmptyResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CompileGeoSiteToSrs not implemented")
|
||||
}
|
||||
func (UnimplementedLibcoreServiceServer) mustEmbedUnimplementedLibcoreServiceServer() {}
|
||||
|
||||
// UnsafeLibcoreServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
@ -290,6 +350,78 @@ func _LibcoreService_ListConnections_Handler(srv interface{}, ctx context.Contex
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LibcoreService_GetGeoIPList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EmptyReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LibcoreServiceServer).GetGeoIPList(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LibcoreService_GetGeoIPList_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LibcoreServiceServer).GetGeoIPList(ctx, req.(*EmptyReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LibcoreService_GetGeoSiteList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EmptyReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LibcoreServiceServer).GetGeoSiteList(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LibcoreService_GetGeoSiteList_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LibcoreServiceServer).GetGeoSiteList(ctx, req.(*EmptyReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LibcoreService_CompileGeoIPToSrs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CompileGeoIPToSrsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LibcoreServiceServer).CompileGeoIPToSrs(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LibcoreService_CompileGeoIPToSrs_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LibcoreServiceServer).CompileGeoIPToSrs(ctx, req.(*CompileGeoIPToSrsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LibcoreService_CompileGeoSiteToSrs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CompileGeoSiteToSrsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LibcoreServiceServer).CompileGeoSiteToSrs(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LibcoreService_CompileGeoSiteToSrs_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LibcoreServiceServer).CompileGeoSiteToSrs(ctx, req.(*CompileGeoSiteToSrsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// LibcoreService_ServiceDesc is the grpc.ServiceDesc for LibcoreService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -325,6 +457,22 @@ var LibcoreService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListConnections",
|
||||
Handler: _LibcoreService_ListConnections_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetGeoIPList",
|
||||
Handler: _LibcoreService_GetGeoIPList_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetGeoSiteList",
|
||||
Handler: _LibcoreService_GetGeoSiteList_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CompileGeoIPToSrs",
|
||||
Handler: _LibcoreService_CompileGeoIPToSrs_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CompileGeoSiteToSrs",
|
||||
Handler: _LibcoreService_CompileGeoSiteToSrs_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "libcore.proto",
|
||||
|
||||
@ -1,3 +1 @@
|
||||
protoc -I . --go_out=. --go_opt paths=source_relative --go-grpc_out=. --go-grpc_opt paths=source_relative libcore.proto
|
||||
|
||||
# protoc -I . --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` libcore.proto
|
||||
protoc -I . --go_out=. --go_opt paths=source_relative --go-grpc_out=. --go-grpc_opt paths=source_relative libcore.proto
|
||||
@ -25,5 +25,5 @@ popd
|
||||
|
||||
#### Go: nekobox_core ####
|
||||
pushd go/cmd/nekobox_core
|
||||
go build -v -o $DEST -trimpath -ldflags "-w -s -X $neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech"
|
||||
go build -v -o $DEST -trimpath -ldflags "-w -s -X $neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech,with_dhcp"
|
||||
popd
|
||||
|
||||
@ -86,16 +86,28 @@ namespace NekoGui_ConfigItem {
|
||||
case itemType::boolean:
|
||||
object.insert(item->name, *(bool *) item->ptr);
|
||||
break;
|
||||
case itemType::stringList:
|
||||
object.insert(item->name, QList2QJsonArray<QString>(*(QList<QString> *) item->ptr));
|
||||
case itemType::stringList: {
|
||||
if (QListStr2QJsonArray(*(QList<QString> *) item->ptr).isEmpty()) continue;
|
||||
object.insert(item->name, QListStr2QJsonArray(*(QList<QString> *) item->ptr));
|
||||
break;
|
||||
case itemType::integerList:
|
||||
object.insert(item->name, QList2QJsonArray<int>(*(QList<int> *) item->ptr));
|
||||
}
|
||||
case itemType::integerList: {
|
||||
if (QListInt2QJsonArray(*(QList<int> *) item->ptr).isEmpty()) continue;
|
||||
object.insert(item->name, QListInt2QJsonArray(*(QList<int> *) item->ptr));
|
||||
break;
|
||||
}
|
||||
case itemType::jsonStore:
|
||||
// _add 时应关联对应 JsonStore 的指针
|
||||
object.insert(item->name, ((JsonStore *) item->ptr)->ToJson());
|
||||
break;
|
||||
case itemType::jsonStoreList:
|
||||
QJsonArray jsonArray;
|
||||
auto arr = *(QList<JsonStore*> *) item->ptr;
|
||||
for ( JsonStore* obj : arr) {
|
||||
jsonArray.push_back(obj->ToJson());
|
||||
}
|
||||
object.insert(item->name, jsonArray);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return object;
|
||||
@ -330,6 +342,7 @@ namespace NekoGui {
|
||||
if (!Preset::SingBox::DomainStrategy.contains(domain_strategy)) domain_strategy = "";
|
||||
if (!Preset::SingBox::DomainStrategy.contains(outbound_domain_strategy)) outbound_domain_strategy = "";
|
||||
_add(new configItem("current_route_id", &this->current_route_id, itemType::integer));
|
||||
_add(new configItem("default_outbound", &this->def_outbound, itemType::string));
|
||||
//
|
||||
_add(new configItem("remote_dns", &this->remote_dns, itemType::string));
|
||||
_add(new configItem("remote_dns_strategy", &this->remote_dns_strategy, itemType::string));
|
||||
@ -345,20 +358,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
QStringList Routing::List() {
|
||||
QDir dr(ROUTES_PREFIX);
|
||||
return dr.entryList(QDir::Files);
|
||||
}
|
||||
|
||||
bool Routing::SetToActive(const QString &name) {
|
||||
NekoGui::dataStore->routing = std::make_unique<Routing>();
|
||||
NekoGui::dataStore->routing->load_control_must = true;
|
||||
NekoGui::dataStore->routing->fn = ROUTES_PREFIX + name;
|
||||
auto ok = NekoGui::dataStore->routing->Load();
|
||||
if (ok) {
|
||||
NekoGui::dataStore->active_routing = name;
|
||||
NekoGui::dataStore->Save();
|
||||
}
|
||||
return ok;
|
||||
return {"Default"};
|
||||
}
|
||||
|
||||
// NO default extra core
|
||||
|
||||
@ -18,5 +18,6 @@ namespace NekoGui {
|
||||
} // namespace NekoGui
|
||||
|
||||
#define IS_NEKO_BOX (NekoGui::coreType == NekoGui::CoreType::SING_BOX)
|
||||
#define ROUTES_PREFIX_NAME QString(IS_NEKO_BOX ? "routes_box" : "routes")
|
||||
#define ROUTES_PREFIX_NAME QString("route_profiles")
|
||||
#define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/")
|
||||
#define RULE_SETS_DIR QString("rule_sets")
|
||||
|
||||
@ -10,6 +10,7 @@ namespace NekoGui_ConfigItem {
|
||||
stringList,
|
||||
integerList,
|
||||
jsonStore,
|
||||
jsonStoreList,
|
||||
};
|
||||
|
||||
class configItem {
|
||||
@ -57,11 +58,11 @@ namespace NekoGui_ConfigItem {
|
||||
|
||||
QByteArray ToJsonBytes();
|
||||
|
||||
void FromJson(QJsonObject object);
|
||||
virtual void FromJson(QJsonObject object);
|
||||
|
||||
void FromJsonBytes(const QByteArray &data);
|
||||
|
||||
bool Save();
|
||||
virtual bool Save();
|
||||
|
||||
bool Load();
|
||||
};
|
||||
|
||||
@ -25,8 +25,6 @@ namespace NekoGui {
|
||||
explicit Routing(int preset = 0);
|
||||
|
||||
static QStringList List();
|
||||
|
||||
static bool SetToActive(const QString &name);
|
||||
};
|
||||
|
||||
class ExtraCore : public JsonStore {
|
||||
@ -179,7 +177,7 @@ namespace NekoGui {
|
||||
|
||||
void UpdateStartedId(int id);
|
||||
|
||||
QString GetUserAgent(bool isDefault = false) const;
|
||||
[[nodiscard]] QString GetUserAgent(bool isDefault = false) const;
|
||||
};
|
||||
|
||||
extern DataStore *dataStore;
|
||||
|
||||
@ -105,17 +105,26 @@ QString QJsonObject2QString(const QJsonObject &jsonObject, bool compact) {
|
||||
return QJsonDocument(jsonObject).toJson(compact ? QJsonDocument::Compact : QJsonDocument::Indented);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QJsonArray QList2QJsonArray(const QList<T> &list) {
|
||||
QJsonArray QListStr2QJsonArray(const QList<QString> &list) {
|
||||
QVariantList list2;
|
||||
bool isEmpty = true;
|
||||
for (auto &item: list) {
|
||||
if (item.trimmed().isEmpty()) continue;
|
||||
list2.append(item);
|
||||
isEmpty = false;
|
||||
}
|
||||
|
||||
if (isEmpty) return {};
|
||||
else return QJsonArray::fromVariantList(list2);
|
||||
}
|
||||
|
||||
QJsonArray QListInt2QJsonArray(const QList<int> &list) {
|
||||
QVariantList list2;
|
||||
for (auto &item: list)
|
||||
list2.append(item);
|
||||
return QJsonArray::fromVariantList(list2);
|
||||
}
|
||||
|
||||
template QJsonArray QList2QJsonArray<int>(const QList<int> &list);
|
||||
template QJsonArray QList2QJsonArray<QString>(const QList<QString> &list);
|
||||
|
||||
QList<int> QJsonArray2QListInt(const QJsonArray &arr) {
|
||||
QList<int> list2;
|
||||
for (auto item: arr)
|
||||
|
||||
@ -83,8 +83,9 @@ QJsonObject QString2QJsonObject(const QString &jsonString);
|
||||
|
||||
QString QJsonObject2QString(const QJsonObject &jsonObject, bool compact);
|
||||
|
||||
template<typename T>
|
||||
QJsonArray QList2QJsonArray(const QList<T> &list);
|
||||
QJsonArray QListInt2QJsonArray(const QList<int> &list);
|
||||
|
||||
QJsonArray QListStr2QJsonArray(const QList<QString> &list);
|
||||
|
||||
QList<int> QJsonArray2QListInt(const QJsonArray &arr);
|
||||
|
||||
|
||||
@ -187,20 +187,16 @@ int main(int argc, char* argv[]) {
|
||||
if (!dir.exists(ROUTES_PREFIX_NAME)) {
|
||||
dir_success &= dir.mkdir(ROUTES_PREFIX_NAME);
|
||||
}
|
||||
if (!dir.exists(RULE_SETS_DIR)) {
|
||||
dir_success &= dir.mkdir(RULE_SETS_DIR);
|
||||
}
|
||||
if (!dir_success) {
|
||||
QMessageBox::warning(nullptr, "Error", "No permission to write " + dir.absolutePath());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load dataStore
|
||||
switch (NekoGui::coreType) {
|
||||
case NekoGui::CoreType::SING_BOX:
|
||||
NekoGui::dataStore->fn = "groups/nekobox.json";
|
||||
break;
|
||||
default:
|
||||
MessageBoxWarning("Error", "Unknown coreType.");
|
||||
return 0;
|
||||
}
|
||||
NekoGui::dataStore->fn = "groups/nekobox.json";
|
||||
auto isLoaded = NekoGui::dataStore->Load();
|
||||
if (!isLoaded) {
|
||||
NekoGui::dataStore->Save();
|
||||
@ -211,7 +207,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// load routing
|
||||
NekoGui::dataStore->routing = std::make_unique<NekoGui::Routing>();
|
||||
NekoGui::dataStore->routing->fn = ROUTES_PREFIX + NekoGui::dataStore->active_routing;
|
||||
NekoGui::dataStore->routing->fn = ROUTES_PREFIX + "Default";
|
||||
isLoaded = NekoGui::dataStore->routing->Load();
|
||||
if (!isLoaded) {
|
||||
NekoGui::dataStore->routing->Save();
|
||||
|
||||
80
rpc/gRPC.cpp
80
rpc/gRPC.cpp
@ -3,8 +3,6 @@
|
||||
#include <utility>
|
||||
#include <QStringList>
|
||||
|
||||
#ifndef NKR_NO_GRPC
|
||||
|
||||
#include "main/NekoGui.hpp"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@ -16,6 +14,8 @@
|
||||
#include <QMutex>
|
||||
#include <QAbstractNetworkCache>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace QtGrpc {
|
||||
const char *GrpcAcceptEncodingHeader = "grpc-accept-encoding";
|
||||
const char *AcceptEncodingHeader = "accept-encoding";
|
||||
@ -280,6 +280,78 @@ namespace NekoGui_rpc {
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
} // namespace NekoGui_rpc
|
||||
|
||||
#endif
|
||||
QStringList Client::GetGeoList(bool *rpcOK, GeoRuleSetType mode) {
|
||||
switch (mode) {
|
||||
case GeoRuleSetType::ip: {
|
||||
libcore::EmptyReq req;
|
||||
libcore::GetGeoIPListResponse resp;
|
||||
|
||||
auto status = default_grpc_channel->Call("GetGeoIPList", req, &resp);
|
||||
if (status == QNetworkReply::NoError) {
|
||||
QStringList res;
|
||||
for (const auto & i : resp.items()) {
|
||||
res.append(QString::fromStdString(i));
|
||||
}
|
||||
*rpcOK = true;
|
||||
return res;
|
||||
} else {
|
||||
NOT_OK
|
||||
return {};
|
||||
}
|
||||
}
|
||||
case GeoRuleSetType::site: {
|
||||
libcore::EmptyReq req;
|
||||
libcore::GetGeoSiteListResponse resp;
|
||||
|
||||
auto status = default_grpc_channel->Call("GetGeoSiteList", req, &resp);
|
||||
if (status == QNetworkReply::NoError) {
|
||||
QStringList res;
|
||||
for (const auto & i : resp.items()) {
|
||||
res.append(QString::fromStdString(i));
|
||||
}
|
||||
*rpcOK = true;
|
||||
return res;
|
||||
} else {
|
||||
NOT_OK
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString Client::CompileGeoSet(bool *rpcOK, GeoRuleSetType mode, std::string category) {
|
||||
switch (mode) {
|
||||
case ip: {
|
||||
libcore::CompileGeoIPToSrsRequest req;
|
||||
libcore::EmptyResp resp;
|
||||
req.set_item(category);
|
||||
|
||||
auto status = default_grpc_channel->Call("CompileGeoIPToSrs", req, &resp);
|
||||
if (status == QNetworkReply::NoError) {
|
||||
*rpcOK = true;
|
||||
return "";
|
||||
} else {
|
||||
NOT_OK
|
||||
return qt_error_string(status);
|
||||
}
|
||||
}
|
||||
case site: {
|
||||
libcore::CompileGeoSiteToSrsRequest req;
|
||||
libcore::EmptyResp resp;
|
||||
req.set_item(category);
|
||||
|
||||
auto status = default_grpc_channel->Call("CompileGeoSiteToSrs", req, &resp);
|
||||
if (status == QNetworkReply::NoError) {
|
||||
*rpcOK = true;
|
||||
return "";
|
||||
} else {
|
||||
NOT_OK
|
||||
return qt_error_string(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace NekoGui_rpc
|
||||
|
||||
@ -10,6 +10,8 @@ namespace QtGrpc {
|
||||
}
|
||||
|
||||
namespace NekoGui_rpc {
|
||||
enum GeoRuleSetType {ip, site};
|
||||
|
||||
class Client {
|
||||
public:
|
||||
explicit Client(std::function<void(const QString &)> onError, const QString &target, const QString &token);
|
||||
@ -28,6 +30,10 @@ namespace NekoGui_rpc {
|
||||
|
||||
libcore::UpdateResp Update(bool *rpcOK, const libcore::UpdateReq &request);
|
||||
|
||||
QStringList GetGeoList(bool *rpcOK, GeoRuleSetType mode);
|
||||
|
||||
QString CompileGeoSet(bool *rpcOK, GeoRuleSetType mode, std::string category);
|
||||
|
||||
private:
|
||||
std::function<std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate>()> make_grpc_channel;
|
||||
std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate> default_grpc_channel;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "dialog_manage_routes.h"
|
||||
#include "ui_dialog_manage_routes.h"
|
||||
#include "db/Database.hpp"
|
||||
//#include "ui_RouteItem.h"
|
||||
|
||||
#include "3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
|
||||
#include "3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp"
|
||||
@ -11,53 +10,47 @@
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
|
||||
QList<QString> getRouteProfiles() {
|
||||
auto routeProfiles = NekoGui::profileManager->routes;
|
||||
QList<QString> res;
|
||||
|
||||
for (const auto &item: routeProfiles) {
|
||||
res << item.second->name;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int getRouteID(const QString& name) {
|
||||
auto routeProfiles = NekoGui::profileManager->routes;
|
||||
|
||||
for (const auto &item: routeProfiles) {
|
||||
if (item.second->name == name) return item.first;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString getRouteName(int id) {
|
||||
return NekoGui::profileManager->routes.count(id) ? NekoGui::profileManager->routes[id]->name : "";
|
||||
}
|
||||
|
||||
QList<QString> deleteItemFromList(const QList<QString>& base, const QString& target) {
|
||||
QList<QString> res;
|
||||
for (const auto &item: base) {
|
||||
if (item == target) continue;
|
||||
res << item;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void DialogManageRoutes::reloadProfileItems() {
|
||||
QSignalBlocker blocker = QSignalBlocker(ui->route_prof); // apparently the currentIndexChanged will make us crash if we clear the QComboBox
|
||||
ui->route_prof->clear();
|
||||
blocker.unblock();
|
||||
|
||||
ui->route_profiles->clear();
|
||||
ui->route_prof->addItems(currentRouteProfiles);
|
||||
ui->route_profiles->addItems(currentRouteProfiles);
|
||||
bool selectedChainGone = true;
|
||||
int i=0;
|
||||
for (const auto &item: chainList) {
|
||||
ui->route_prof->addItem(item->name);
|
||||
ui->route_profiles->addItem(item->name);
|
||||
if (item->id == currentRouteProfileID) {
|
||||
ui->route_prof->setCurrentIndex(i);
|
||||
selectedChainGone=false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (selectedChainGone) {
|
||||
currentRouteProfileID=0;
|
||||
ui->route_prof->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(new Ui::DialogManageRoutes) {
|
||||
ui->setupUi(this);
|
||||
currentRouteProfiles = getRouteProfiles();
|
||||
auto profiles = NekoGui::profileManager->routes;
|
||||
for (const auto &item: profiles) {
|
||||
chainList << item.second;
|
||||
}
|
||||
if (chainList.empty()) {
|
||||
auto defaultChain = NekoGui::RoutingChain::GetDefaultChain();
|
||||
NekoGui::profileManager->AddRouteChain(defaultChain);
|
||||
chainList.append(defaultChain);
|
||||
}
|
||||
currentRouteProfileID = NekoGui::dataStore->routing->current_route_id;
|
||||
if (currentRouteProfileID < 0) currentRouteProfileID = chainList[0]->id;
|
||||
|
||||
QStringList qsValue = {""};
|
||||
QString dnsHelpDocumentUrl;
|
||||
|
||||
ui->default_out->setCurrentText(NekoGui::dataStore->routing->def_outbound);
|
||||
ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy);
|
||||
ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy);
|
||||
qsValue += QString("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" ");
|
||||
@ -96,17 +89,11 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(ne
|
||||
ui->direct_dns_strategy->setCurrentText(NekoGui::dataStore->routing->direct_dns_strategy);
|
||||
ui->dns_final_out->setCurrentText(NekoGui::dataStore->routing->dns_final_out);
|
||||
reloadProfileItems();
|
||||
ui->route_prof->setCurrentText(getRouteName(NekoGui::dataStore->routing->current_route_id));
|
||||
|
||||
|
||||
connect(ui->delete_route, &QPushButton::clicked, this, [=]{
|
||||
auto current = ui->route_profiles->currentItem()->text();
|
||||
currentRouteProfiles = deleteItemFromList(currentRouteProfiles, current);
|
||||
reloadProfileItems();
|
||||
connect(ui->route_prof, &QComboBox::currentIndexChanged, this, [=](const int& idx) {
|
||||
currentRouteProfileID = chainList[idx]->id;
|
||||
});
|
||||
|
||||
|
||||
|
||||
ADD_ASTERISK(this)
|
||||
}
|
||||
|
||||
@ -115,7 +102,7 @@ DialogManageRoutes::~DialogManageRoutes() {
|
||||
}
|
||||
|
||||
void DialogManageRoutes::accept() {
|
||||
if (currentRouteProfiles.empty()) {
|
||||
if (chainList.empty()) {
|
||||
MessageBoxInfo("Invalid settings", "Routing profile cannot be empty");
|
||||
return;
|
||||
}
|
||||
@ -132,7 +119,55 @@ void DialogManageRoutes::accept() {
|
||||
NekoGui::dataStore->routing->direct_dns_strategy = ui->direct_dns_strategy->currentText();
|
||||
NekoGui::dataStore->routing->dns_final_out = ui->dns_final_out->currentText();
|
||||
|
||||
// TODO add mine
|
||||
NekoGui::profileManager->UpdateRouteChains(chainList);
|
||||
NekoGui::dataStore->routing->current_route_id = currentRouteProfileID;
|
||||
NekoGui::dataStore->routing->def_outbound = ui->default_out->currentText();
|
||||
|
||||
|
||||
//
|
||||
QStringList msg{"UpdateDataStore"};
|
||||
msg << "RouteChanged";
|
||||
MW_dialog_message("", msg.join(","));
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void DialogManageRoutes::on_new_route_clicked() {
|
||||
routeChainWidget = new RouteItem(this, NekoGui::ProfileManager::NewRouteChain());
|
||||
routeChainWidget->setWindowModality(Qt::ApplicationModal);
|
||||
routeChainWidget->show();
|
||||
connect(routeChainWidget, &RouteItem::settingsChanged, this, [=](const std::shared_ptr<NekoGui::RoutingChain>& chain) {
|
||||
chainList << chain;
|
||||
reloadProfileItems();
|
||||
});
|
||||
}
|
||||
|
||||
void DialogManageRoutes::on_edit_route_clicked() {
|
||||
auto idx = ui->route_profiles->currentRow();
|
||||
if (idx < 0) return;
|
||||
|
||||
routeChainWidget = new RouteItem(this, chainList[idx]);
|
||||
routeChainWidget->setWindowModality(Qt::ApplicationModal);
|
||||
routeChainWidget->show();
|
||||
connect(routeChainWidget, &RouteItem::settingsChanged, this, [=](const std::shared_ptr<NekoGui::RoutingChain>& chain) {
|
||||
chainList[idx] = chain;
|
||||
reloadProfileItems();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void DialogManageRoutes::on_delete_route_clicked() {
|
||||
auto idx = ui->route_profiles->currentRow();
|
||||
if (idx < 0) return;
|
||||
if (chainList.size() == 1) {
|
||||
MessageBoxWarning("Invalid operation", "Routing Profiles cannot be empty, try adding another profile or editing this one");
|
||||
return;
|
||||
}
|
||||
|
||||
auto profileToDel = chainList[idx];
|
||||
chainList.removeAt(idx);
|
||||
if (profileToDel->id == currentRouteProfileID) {
|
||||
currentRouteProfileID = chainList[0]->id;
|
||||
}
|
||||
reloadProfileItems();
|
||||
}
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp"
|
||||
#include "main/NekoGui.hpp"
|
||||
#include "widget/RouteItem.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
@ -23,9 +24,13 @@ public:
|
||||
private:
|
||||
Ui::DialogManageRoutes *ui;
|
||||
|
||||
RouteItem* routeChainWidget;
|
||||
|
||||
void reloadProfileItems();
|
||||
|
||||
QList<QString> currentRouteProfiles;
|
||||
QList<std::shared_ptr<NekoGui::RoutingChain>> chainList;
|
||||
|
||||
int currentRouteProfileID = -1;
|
||||
public slots:
|
||||
void accept() override;
|
||||
|
||||
|
||||
@ -27,45 +27,6 @@
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="outbound_domain_strategy">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Server Address Strategy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="toolTip">
|
||||
<string notr="true">For V2Ray, it sets routing.domainStrategy
|
||||
For sing-box, it sets inbound.domain_strategy</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Domain Strategy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Sniffing Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="domainStrategyCombo">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="sniffing_mode">
|
||||
<item>
|
||||
@ -85,8 +46,26 @@ For sing-box, it sets inbound.domain_strategy</string>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="route_prof"/>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Sniffing Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Server Address Strategy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="domainStrategyCombo">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="route_prof_l">
|
||||
@ -95,6 +74,48 @@ For sing-box, it sets inbound.domain_strategy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="route_prof"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="outbound_domain_strategy">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="toolTip">
|
||||
<string notr="true">For V2Ray, it sets routing.domainStrategy
|
||||
For sing-box, it sets inbound.domain_strategy</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Domain Strategy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="default_out">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>proxy</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>direct</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="default_out_l">
|
||||
<property name="text">
|
||||
<string>Default Outbound</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -92,17 +92,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
}
|
||||
|
||||
// software_name
|
||||
if (IS_NEKO_BOX) {
|
||||
software_name = "NekoBox";
|
||||
software_core_name = "sing-box";
|
||||
// replace default values
|
||||
if (NekoGui::dataStore->log_level == "warning") NekoGui::dataStore->log_level = "info";
|
||||
if (NekoGui::dataStore->mux_protocol.isEmpty()) NekoGui::dataStore->mux_protocol = "h2mux";
|
||||
//
|
||||
if (QDir("dashboard").count() == 0) {
|
||||
QDir().mkdir("dashboard");
|
||||
QFile::copy(":/neko/dashboard-notice.html", "dashboard/index.html");
|
||||
}
|
||||
software_name = "NekoBox";
|
||||
software_core_name = "sing-box";
|
||||
// replace default values
|
||||
if (NekoGui::dataStore->log_level == "warning") NekoGui::dataStore->log_level = "info";
|
||||
if (NekoGui::dataStore->mux_protocol.isEmpty()) NekoGui::dataStore->mux_protocol = "h2mux";
|
||||
//
|
||||
if (QDir("dashboard").count() == 0) {
|
||||
QDir().mkdir("dashboard");
|
||||
QFile::copy(":/neko/dashboard-notice.html", "dashboard/index.html");
|
||||
}
|
||||
|
||||
// top bar
|
||||
@ -275,17 +273,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
ui->menuActive_Server->addAction(a);
|
||||
if (++active_server_item_count == 100) break;
|
||||
}
|
||||
// active routing
|
||||
for (const auto &old: ui->menuActive_Routing->actions()) {
|
||||
ui->menuActive_Routing->removeAction(old);
|
||||
old->deleteLater();
|
||||
}
|
||||
for (const auto &name: NekoGui::Routing::List()) {
|
||||
auto a = new QAction(name, this);
|
||||
a->setCheckable(true);
|
||||
a->setChecked(name == NekoGui::dataStore->active_routing);
|
||||
ui->menuActive_Routing->addAction(a);
|
||||
}
|
||||
});
|
||||
connect(ui->menuActive_Server, &QMenu::triggered, this, [=](QAction *a) {
|
||||
bool ok;
|
||||
@ -297,24 +284,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
neko_start(id);
|
||||
}
|
||||
});
|
||||
connect(ui->menuActive_Routing, &QMenu::triggered, this, [=](QAction *a) {
|
||||
auto fn = a->text();
|
||||
if (!fn.isEmpty()) {
|
||||
NekoGui::Routing r;
|
||||
r.load_control_must = true;
|
||||
r.fn = ROUTES_PREFIX + fn;
|
||||
if (r.Load()) {
|
||||
if (QMessageBox::question(GetMessageBoxParent(), software_name, tr("Load routing and apply: %1").arg(fn) + "\n" ) == QMessageBox::Yes) {
|
||||
NekoGui::Routing::SetToActive(fn);
|
||||
if (NekoGui::dataStore->started_id >= 0) {
|
||||
neko_start(NekoGui::dataStore->started_id);
|
||||
} else {
|
||||
refresh_status();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(ui->actionRemember_last_proxy, &QAction::triggered, this, [=](bool checked) {
|
||||
NekoGui::dataStore->remember_enable = checked;
|
||||
NekoGui::dataStore->Save();
|
||||
@ -531,6 +500,7 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
|
||||
if (info.contains("UpdateDataStore")) {
|
||||
auto suggestRestartProxy = NekoGui::dataStore->Save();
|
||||
if (info.contains("RouteChanged")) {
|
||||
NekoGui::dataStore->routing->Save();
|
||||
suggestRestartProxy = true;
|
||||
}
|
||||
if (info.contains("NeedRestart")) {
|
||||
|
||||
@ -179,7 +179,7 @@ private:
|
||||
|
||||
void HotkeyEvent(const QString &key);
|
||||
|
||||
// grpc and ...
|
||||
// grpc
|
||||
|
||||
static void setup_grpc();
|
||||
|
||||
|
||||
@ -408,12 +408,6 @@
|
||||
</property>
|
||||
<addaction name="actionfake_2"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuActive_Routing">
|
||||
<property name="title">
|
||||
<string>Active Routing</string>
|
||||
</property>
|
||||
<addaction name="actionfake_3"/>
|
||||
</widget>
|
||||
<addaction name="actionShow_window"/>
|
||||
<addaction name="menu_add_from_clipboard2"/>
|
||||
<addaction name="menu_scan_qr"/>
|
||||
@ -422,7 +416,6 @@
|
||||
<addaction name="actionRemember_last_proxy"/>
|
||||
<addaction name="actionAllow_LAN"/>
|
||||
<addaction name="menuActive_Server"/>
|
||||
<addaction name="menuActive_Routing"/>
|
||||
<addaction name="menu_spmode"/>
|
||||
<addaction name="menu_program_preference"/>
|
||||
<addaction name="separator"/>
|
||||
|
||||
@ -409,7 +409,6 @@ void MainWindow::neko_stop(bool crash, bool sem) {
|
||||
},
|
||||
DS_cores);
|
||||
|
||||
#ifndef NKR_NO_GRPC
|
||||
NekoGui_traffic::trafficLooper->loop_enabled = false;
|
||||
NekoGui_traffic::trafficLooper->loop_mutex.lock();
|
||||
if (NekoGui::dataStore->traffic_loop_interval != 0) {
|
||||
@ -431,7 +430,6 @@ void MainWindow::neko_stop(bool crash, bool sem) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
NekoGui::dataStore->UpdateStartedId(-1919);
|
||||
NekoGui::dataStore->need_keep_vpn_off = false;
|
||||
@ -475,7 +473,6 @@ void MainWindow::neko_stop(bool crash, bool sem) {
|
||||
|
||||
void MainWindow::CheckUpdate() {
|
||||
// on new thread...
|
||||
#ifndef NKR_NO_GRPC
|
||||
bool ok;
|
||||
libcore::UpdateReq request;
|
||||
request.set_action(libcore::UpdateAction::Check);
|
||||
@ -536,5 +533,4 @@ void MainWindow::CheckUpdate() {
|
||||
QDesktopServices::openUrl(QUrl(response.release_url().c_str()));
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,8 @@
|
||||
#include "ui_RouteItem.h"
|
||||
#include "db/RouteEntity.h"
|
||||
#include "db/Database.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include "rpc/gRPC.h"
|
||||
|
||||
int RouteItem::getIndexOf(const QString& name) const {
|
||||
for (int i=0;i<chain->Rules.size();i++) {
|
||||
@ -13,7 +14,8 @@ int RouteItem::getIndexOf(const QString& name) const {
|
||||
}
|
||||
|
||||
QString get_outbound_name(int id) {
|
||||
// -2 is direct -3 is block -4 is dns_out
|
||||
// -1 is proxy -2 is direct -3 is block -4 is dns_out
|
||||
if (id == -1) return "proxy";
|
||||
if (id == -2) return "direct";
|
||||
if (id == -3) return "block";
|
||||
if (id == -4) return "dns_out";
|
||||
@ -23,6 +25,7 @@ QString get_outbound_name(int id) {
|
||||
}
|
||||
|
||||
int get_outbound_id(const QString& name) {
|
||||
if (name == "proxy") return -1;
|
||||
if (name == "direct") return -2;
|
||||
if (name == "block") return -3;
|
||||
if (name == "dns_out") return -4;
|
||||
@ -45,18 +48,42 @@ QStringList get_all_outbounds() {
|
||||
}
|
||||
|
||||
RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChain>& routeChain)
|
||||
: QGroupBox(parent), ui(new Ui::RouteItem) {
|
||||
: QDialog(parent), ui(new Ui::RouteItem) {
|
||||
ui->setupUi(this);
|
||||
|
||||
// make a copy
|
||||
chain = routeChain;
|
||||
|
||||
// add the default rule if empty
|
||||
if (chain->Rules.empty()) {
|
||||
auto routeItem = std::make_shared<NekoGui::RouteRule>();
|
||||
routeItem->name = "dns-hijack";
|
||||
routeItem->protocol = "dns";
|
||||
routeItem->outboundID = -4;
|
||||
chain->Rules << routeItem;
|
||||
}
|
||||
|
||||
// setup rule set helper
|
||||
bool ok; // for now we discard this
|
||||
auto geoIpList = NekoGui_rpc::defaultClient->GetGeoList(&ok, NekoGui_rpc::GeoRuleSetType::ip);
|
||||
auto geoSiteList = NekoGui_rpc::defaultClient->GetGeoList(&ok, NekoGui_rpc::GeoRuleSetType::site);
|
||||
geo_items << geoIpList << geoSiteList;
|
||||
helperModel = new QStringListModel(geo_items, this);
|
||||
ui->rule_set_helper->hide();
|
||||
ui->rule_set_helper->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ui->rule_set_helper->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->rule_set_helper->setSelectionRectVisible(false);
|
||||
ui->rule_set_helper->setModel(helperModel);
|
||||
connect(ui->rule_set_helper, &QListView::clicked, this, [=](const QModelIndex& index){
|
||||
applyRuleHelperSelect(index);
|
||||
});
|
||||
|
||||
std::map<QString, int> valueMap;
|
||||
for (auto &item: chain->Rules) {
|
||||
auto baseName = item->name;
|
||||
int randPart;
|
||||
if (baseName == "") {
|
||||
randPart = GetRandomUint64()%1000;
|
||||
randPart = int(GetRandomUint64()%1000);
|
||||
baseName = "rule_" + Int2String(randPart);
|
||||
lastNum = std::max(lastNum, randPart);
|
||||
}
|
||||
@ -64,7 +91,7 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChai
|
||||
valueMap[baseName]++;
|
||||
if (valueMap[baseName] > 1) {
|
||||
valueMap[baseName]--;
|
||||
randPart = GetRandomUint64()%1000;
|
||||
randPart = int(GetRandomUint64()%1000);
|
||||
baseName = "rule_" + Int2String(randPart);
|
||||
lastNum = std::max(lastNum, randPart);
|
||||
continue;
|
||||
@ -75,13 +102,17 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChai
|
||||
ui->route_items->addItem(item->name);
|
||||
}
|
||||
|
||||
QStringList outboundOptions = {"direct", "block", "dns_out"};
|
||||
QStringList outboundOptions = {"proxy", "direct", "block", "dns_out"};
|
||||
outboundOptions << get_all_outbounds();
|
||||
|
||||
ui->route_name->setText(chain->name);
|
||||
ui->rule_attr->addItems(NekoGui::RouteRule::get_attributes());
|
||||
ui->rule_out->addItems(outboundOptions);
|
||||
ui->rule_attr_text->hide();
|
||||
ui->rule_attr_data->setTitle("");
|
||||
ui->rule_attr_box->setEnabled(false);
|
||||
ui->rule_preview->setEnabled(false);
|
||||
updateRuleSection();
|
||||
|
||||
connect(ui->route_name, &QLineEdit::textChanged, this, [=](const QString& text) {
|
||||
chain->name = text;
|
||||
@ -93,7 +124,7 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChai
|
||||
for (int i=0;i<rules.size();i++) {
|
||||
auto item = rules[i];
|
||||
res += QJsonObject2QString(item.toObject(), false);
|
||||
if (i != rules.size()-1) res+=",\n";
|
||||
if (i != rules.size()-1) res+=",";
|
||||
}
|
||||
MessageBoxInfo("JSON object", res);
|
||||
});
|
||||
@ -101,17 +132,21 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChai
|
||||
connect(ui->rule_name, &QLineEdit::textChanged, this, [=](const QString& text) {
|
||||
if (currentIndex == -1) return;
|
||||
chain->Rules[currentIndex]->name = text;
|
||||
updateRouteItemsView();
|
||||
});
|
||||
|
||||
connect(ui->rule_attr_selector, &QComboBox::currentTextChanged, this, [=](const QString& text){
|
||||
if (currentIndex == -1) return;
|
||||
chain->Rules[currentIndex]->set_field_value(ui->rule_attr->currentText(), {text});
|
||||
updateRulePreview();
|
||||
});
|
||||
|
||||
connect(ui->rule_attr_text, &QTextEdit::textChanged, this, [=] {
|
||||
if (currentIndex == -1) return;
|
||||
auto currentVal = ui->rule_attr_text->toPlainText().split('\n');
|
||||
chain->Rules[currentIndex]->set_field_value(ui->rule_attr->currentText(), currentVal);
|
||||
if (ui->rule_attr->currentText() == "rule_set") updateHelperItems(currentVal.last());
|
||||
updateRulePreview();
|
||||
});
|
||||
|
||||
connect(ui->rule_out, &QComboBox::currentTextChanged, this, [=](const QString& text) {
|
||||
@ -122,53 +157,56 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<NekoGui::RoutingChai
|
||||
return;
|
||||
}
|
||||
chain->Rules[currentIndex]->outboundID = id;
|
||||
updateRulePreview();
|
||||
});
|
||||
|
||||
connect(ui->route_items, &QListWidget::itemClicked, this, [=](const QListWidgetItem *item) {
|
||||
auto idx = getIndexOf(item->text());
|
||||
if (idx == -1) return;
|
||||
currentIndex = idx;
|
||||
auto ruleItem = chain->Rules[idx];
|
||||
ui->rule_out->setCurrentText(get_outbound_name(ruleItem->outboundID));
|
||||
setDefaultRuleData(ruleItem->ip_version);
|
||||
updateRuleSection();
|
||||
});
|
||||
|
||||
connect(ui->rule_attr, &QComboBox::currentTextChanged, this, [=](const QString& text){
|
||||
if (currentIndex == -1) return;
|
||||
ui->rule_attr_data->setTitle(text);
|
||||
auto inputType = NekoGui::RouteRule::get_input_type(text);
|
||||
switch (inputType) {
|
||||
case NekoGui::trufalse: {
|
||||
QStringList items = {"", "true", "false"};
|
||||
auto currentValPtr = chain->Rules[currentIndex]->get_current_value_bool(text);
|
||||
QString currentVal = currentValPtr == nullptr ? "" : *currentValPtr ? "true" : "false";
|
||||
showSelectItem(items, currentVal);
|
||||
break;
|
||||
}
|
||||
case NekoGui::select: {
|
||||
auto items = NekoGui::RouteRule::get_values_for_field(text);
|
||||
auto currentVal = chain->Rules[currentIndex]->get_current_value_string(text)[0];
|
||||
showSelectItem(items, currentVal);
|
||||
break;
|
||||
}
|
||||
case NekoGui::text: {
|
||||
auto currentItems = chain->Rules[currentIndex]->get_current_value_string(text);
|
||||
showTextEnterItem(currentItems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateRuleSection();
|
||||
});
|
||||
|
||||
connect(ui->new_route_item, &QPushButton::clicked, this, &RouteItem::on_new_route_item_clicked);
|
||||
connect(ui->moveup_route_item, &QPushButton::clicked, this, &RouteItem::on_moveup_route_item_clicked);
|
||||
connect(ui->movedown_route_item, &QPushButton::clicked, this, &RouteItem::on_movedown_route_item_clicked);
|
||||
connect(ui->delete_route_item, &QPushButton::clicked, this, &RouteItem::on_delete_route_item_clicked);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, [=]{
|
||||
accept();
|
||||
});
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [=]{
|
||||
QDialog::reject();
|
||||
});
|
||||
}
|
||||
|
||||
RouteItem::~RouteItem() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RouteItem::accept() {
|
||||
if (chain->name == "") {
|
||||
MessageBoxWarning("Invalid operation", "Cannot create Route Profile with empty name");
|
||||
return;
|
||||
}
|
||||
|
||||
int i=0;
|
||||
for (const auto& item: chain->Rules) {
|
||||
if (item->isEmpty()) {
|
||||
chain->Rules.remove(i);
|
||||
i--;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (chain->Rules.empty()) {
|
||||
MessageBoxInfo("Empty Route Profile", "No valid rules are in the profile");
|
||||
return;
|
||||
}
|
||||
|
||||
emit settingsChanged(chain);
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void RouteItem::updateRouteItemsView() {
|
||||
ui->route_items->clear();
|
||||
if (chain->Rules.empty()) return;
|
||||
@ -176,7 +214,45 @@ void RouteItem::updateRouteItemsView() {
|
||||
for (const auto& item: chain->Rules) {
|
||||
ui->route_items->addItem(item->name);
|
||||
}
|
||||
ui->route_items->setCurrentRow(currentIndex);
|
||||
if (currentIndex != -1) ui->route_items->setCurrentRow(currentIndex);
|
||||
}
|
||||
|
||||
void RouteItem::updateRuleSection() {
|
||||
if (currentIndex == -1) return;
|
||||
|
||||
auto ruleItem = chain->Rules[currentIndex];
|
||||
auto currentAttr = ui->rule_attr->currentText();
|
||||
switch (ruleItem->get_input_type(currentAttr)) {
|
||||
case NekoGui::trufalse: {
|
||||
QStringList items = {"false", "true"};
|
||||
QString currentVal = chain->Rules[currentIndex]->get_current_value_bool(currentAttr);
|
||||
showSelectItem(items, currentVal);
|
||||
break;
|
||||
}
|
||||
case NekoGui::select: {
|
||||
auto items = NekoGui::RouteRule::get_values_for_field(currentAttr);
|
||||
auto currentVal = chain->Rules[currentIndex]->get_current_value_string(currentAttr)[0];
|
||||
showSelectItem(items, currentVal);
|
||||
break;
|
||||
}
|
||||
case NekoGui::text: {
|
||||
auto currentItems = chain->Rules[currentIndex]->get_current_value_string(currentAttr);
|
||||
showTextEnterItem(currentItems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ui->rule_name->setText(ruleItem->name);
|
||||
ui->rule_attr_box->setEnabled(true);
|
||||
if (currentAttr == "rule_set") ui->rule_set_helper->show();
|
||||
else ui->rule_set_helper->hide();
|
||||
|
||||
updateRulePreview();
|
||||
}
|
||||
|
||||
void RouteItem::updateRulePreview() {
|
||||
if (currentIndex == -1) return;
|
||||
|
||||
ui->rule_preview->setText(QJsonObject2QString(chain->Rules[currentIndex]->get_rule_json(true), false));
|
||||
}
|
||||
|
||||
void RouteItem::setDefaultRuleData(const QString& currentData) {
|
||||
@ -189,9 +265,7 @@ void RouteItem::showSelectItem(const QStringList& items, const QString& currentI
|
||||
ui->rule_attr_text->hide();
|
||||
ui->rule_attr_selector->clear();
|
||||
ui->rule_attr_selector->show();
|
||||
QStringList fullItems = {""};
|
||||
fullItems << items;
|
||||
ui->rule_attr_selector->addItems(fullItems);
|
||||
ui->rule_attr_selector->addItems(items);
|
||||
ui->rule_attr_selector->setCurrentText(currentItem);
|
||||
adjustSize();
|
||||
}
|
||||
@ -204,13 +278,34 @@ void RouteItem::showTextEnterItem(const QStringList& items) {
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void RouteItem::updateHelperItems(const QString& base) {
|
||||
ui->rule_set_helper->clearSelection();
|
||||
current_helper_items.clear();
|
||||
for (const auto& item: geo_items) {
|
||||
if (item.contains(base)) current_helper_items << item;
|
||||
}
|
||||
helperModel->setStringList(current_helper_items);
|
||||
ui->rule_set_helper->setModel(helperModel);
|
||||
}
|
||||
|
||||
void RouteItem::applyRuleHelperSelect(const QModelIndex& index) {
|
||||
auto option = ui->rule_set_helper->model()->data(index, Qt::DisplayRole).toString();
|
||||
auto currentText = ui->rule_attr_text->toPlainText();
|
||||
auto parts = currentText.split('\n');
|
||||
parts[parts.size() - 1] = option;
|
||||
ui->rule_attr_text->setText(parts.join('\n'));
|
||||
}
|
||||
|
||||
void RouteItem::on_new_route_item_clicked() {
|
||||
auto routeItem = std::make_shared<NekoGui::RouteRule>(NekoGui::RouteRule());
|
||||
auto routeItem = std::make_shared<NekoGui::RouteRule>();
|
||||
routeItem->name = "rule_" + Int2String(++lastNum);
|
||||
chain->Rules << routeItem;
|
||||
currentIndex = chain->Rules.size() - 1;
|
||||
ui->rule_name->setText(routeItem->name);
|
||||
currentIndex = chain->Rules.size()-1;
|
||||
|
||||
updateRouteItemsView();
|
||||
setDefaultRuleData("");
|
||||
updateRuleSection();
|
||||
}
|
||||
|
||||
void RouteItem::on_moveup_route_item_clicked() {
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QGroupBox>
|
||||
#include <QDialog>
|
||||
#include <QStringListModel>
|
||||
|
||||
#include "db/RouteEntity.h"
|
||||
|
||||
@ -12,7 +13,7 @@ namespace Ui {
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class RouteItem : public QGroupBox {
|
||||
class RouteItem : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -21,13 +22,19 @@ public:
|
||||
|
||||
std::shared_ptr<NekoGui::RoutingChain> chain;
|
||||
signals:
|
||||
void settingsChanged(const std::shared_ptr<NekoGui::RoutingChain> routeChain);
|
||||
void settingsChanged(std::shared_ptr<NekoGui::RoutingChain> routingChain);
|
||||
|
||||
private:
|
||||
Ui::RouteItem *ui;
|
||||
int currentIndex = -1;
|
||||
|
||||
int lastNum;
|
||||
int lastNum = 0;
|
||||
|
||||
QStringList geo_items;
|
||||
|
||||
QStringList current_helper_items;
|
||||
|
||||
QStringListModel* helperModel;
|
||||
|
||||
[[nodiscard]] int getIndexOf(const QString& name) const;
|
||||
|
||||
@ -37,11 +44,19 @@ private:
|
||||
|
||||
void setDefaultRuleData(const QString& currentData);
|
||||
|
||||
void updateRuleSection();
|
||||
|
||||
void updateRulePreview();
|
||||
|
||||
void updateRouteItemsView();
|
||||
|
||||
void updateHelperItems(const QString& base);
|
||||
|
||||
void applyRuleHelperSelect(const QModelIndex& index);
|
||||
|
||||
private slots:
|
||||
void on_ok_button_clicked();
|
||||
void on_cancel_button_clicked();
|
||||
void accept() override;
|
||||
|
||||
void on_new_route_item_clicked();
|
||||
void on_moveup_route_item_clicked();
|
||||
void on_movedown_route_item_clicked();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RouteItem</class>
|
||||
<widget class="QGroupBox" name="RouteItem">
|
||||
<widget class="QDialog" name="RouteItem">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>GroupBox</string>
|
||||
<string>Route Profile</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
@ -81,7 +81,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<widget class="QGroupBox" name="rule_attr_box">
|
||||
<property name="title">
|
||||
<string>Rule Attributes</string>
|
||||
</property>
|
||||
@ -128,9 +128,22 @@
|
||||
<string>Name_Placeholder</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="rule_preview">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>190</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="rule_attr_text"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="rule_set_helper"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="rule_attr_selector"/>
|
||||
</item>
|
||||
@ -140,29 +153,16 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_4" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ok_button">
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancel_button">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user