prepare routting rules for SB 1.11

This commit is contained in:
Nova 2025-02-05 07:44:02 +03:30
parent 26efd64821
commit 4b39687871
3 changed files with 204 additions and 41 deletions

View File

@ -8,4 +8,6 @@ namespace Preset::SingBox {
inline QStringList V2RAYTransports = {"http", "grpc", "quic", "httpupgrade", "ws", "tcp"}; inline QStringList V2RAYTransports = {"http", "grpc", "quic", "httpupgrade", "ws", "tcp"};
inline QStringList Flows = {"xtls-rprx-vision"}; inline QStringList Flows = {"xtls-rprx-vision"};
inline QStringList SniffProtocols = {"http", "tls", "quic", "stun", "dns", "bittorrent", "dtls", "ssh", "rdp"}; inline QStringList SniffProtocols = {"http", "tls", "quic", "stun", "dns", "bittorrent", "dtls", "ssh", "rdp"};
inline QStringList ActionTypes = {"route", "reject", "hijack-dns", "route-options", "sniff", "resolve"};
inline QStringList rejectMethods = {"default", "drop"};
} // namespace Preset::SingBox } // namespace Preset::SingBox

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <include/configs/proxy/Preset.hpp>
#include "include/global/NekoGui.hpp" #include "include/global/NekoGui.hpp"
namespace NekoGui { namespace NekoGui {
@ -36,15 +38,37 @@ namespace NekoGui {
QList<QString> rule_set; QList<QString> rule_set;
bool invert = false; bool invert = false;
int outboundID = -2; // -1 is proxy -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
// since sing-box 1.11.0
QString actionType = "route";
[[nodiscard]] QJsonObject get_rule_json(bool forView = false, const QString& outboundTag = "") const; // reject options
QString rejectMethod;
bool no_drop = false;
// route options
QString override_address;
QString override_port;
// TODO maybe add some of dial fields?
// sniff options
QStringList sniffers;
bool sniffOverrideDest = false;
// resolve options
QString strategy;
[[nodiscard]] QJsonObject get_rule_json(bool forView = false, const QString& outboundTag = "");
static QStringList get_attributes(); static QStringList get_attributes();
static QStringList get_route_options();
static QStringList get_reject_options();
static QStringList get_sniff_options();
static QStringList get_resolve_options();
static inputType get_input_type(const QString& fieldName); static inputType get_input_type(const QString& fieldName);
static QStringList get_values_for_field(const QString& fieldName); static QStringList get_values_for_field(const QString& fieldName);
QStringList get_current_value_string(const QString& fieldName); QStringList get_current_value_string(const QString& fieldName);
[[nodiscard]] QString 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); void set_field_value(const QString& fieldName, const QStringList& value);
[[nodiscard]] bool isEmpty() const; [[nodiscard]] bool isEmpty();
}; };
class RoutingChain : public JsonStore { class RoutingChain : public JsonStore {

View File

@ -46,6 +46,14 @@ namespace NekoGui {
rule_set << other.rule_set; rule_set << other.rule_set;
invert = other.invert; invert = other.invert;
outboundID = other.outboundID; outboundID = other.outboundID;
actionType = other.actionType;
rejectMethod = other.rejectMethod;
no_drop = other.no_drop;
override_address = other.override_address;
override_port = other.override_port;
sniffers << other.sniffers;
sniffOverrideDest = other.sniffOverrideDest;
strategy = other.strategy;
_add(new configItem("name", &name, itemType::string)); _add(new configItem("name", &name, itemType::string));
_add(new configItem("ip_version", &ip_version, itemType::string)); _add(new configItem("ip_version", &ip_version, itemType::string));
@ -70,9 +78,17 @@ namespace NekoGui {
_add(new configItem("rule_set", &rule_set, itemType::stringList)); _add(new configItem("rule_set", &rule_set, itemType::stringList));
_add(new configItem("invert", &invert, itemType::boolean)); _add(new configItem("invert", &invert, itemType::boolean));
_add(new configItem("outboundID", &outboundID, itemType::integer)); _add(new configItem("outboundID", &outboundID, itemType::integer));
_add(new configItem("actionType", &actionType, itemType::string));
_add(new configItem("rejectMethod", &rejectMethod, itemType::string));
_add(new configItem("noDrop", &no_drop, itemType::boolean));
_add(new configItem("override_address", &override_address, itemType::string));
_add(new configItem("override_port", &override_port, itemType::integer));
_add(new configItem("sniffers", &sniffers, itemType::stringList));
_add(new configItem("sniffOverrideDest", &sniffOverrideDest, itemType::boolean));
_add(new configItem("strategy", &strategy, itemType::string));
} }
QJsonObject RouteRule::get_rule_json(bool forView, const QString& outboundTag) const { QJsonObject RouteRule::get_rule_json(bool forView, const QString& outboundTag) {
QJsonObject obj; QJsonObject obj;
if (!ip_version.isEmpty()) obj["ip_version"] = ip_version.toInt(); if (!ip_version.isEmpty()) obj["ip_version"] = ip_version.toInt();
@ -96,32 +112,56 @@ namespace NekoGui {
if (isValidStrArray(process_path_regex)) obj["process_path_regex"] = get_as_array(process_path_regex); if (isValidStrArray(process_path_regex)) obj["process_path_regex"] = get_as_array(process_path_regex);
if (isValidStrArray(rule_set)) obj["rule_set"] = get_as_array(rule_set); if (isValidStrArray(rule_set)) obj["rule_set"] = get_as_array(rule_set);
if (invert) obj["invert"] = invert; if (invert) obj["invert"] = invert;
// fix action type
if (actionType == "route")
{
if (outboundID == -3) actionType = "reject";
if (outboundID == -4) actionType = "resolve";
}
obj["action_type"] = actionType;
if (forView) { if (actionType == "reject")
switch (outboundID) { // TODO use constants {
case -1: if (!rejectMethod.isEmpty()) obj["reject_method"] = rejectMethod;
obj["outbound"] = "proxy"; if (no_drop) obj["no_drop"] = no_drop;
break; }
case -2: if (actionType == "route" || actionType == "route-options")
obj["outbound"] = "direct"; {
break; if (!override_address.isEmpty()) obj["override_address"] = override_address;
case -3: if (override_port.toInt() > 0) obj["override_port"] = override_port.toInt();
obj["outbound"] = "block";
break; if (actionType == "route")
case -4: {
obj["outbound"] = "dns-out"; if (forView) {
break; switch (outboundID) { // TODO use constants
default: case -1:
auto prof = NekoGui::profileManager->GetProfile(outboundID); obj["outbound"] = "proxy";
if (prof == nullptr) { break;
MW_show_log("The outbound described in the rule chain is missing, maybe your data is corrupted"); case -2:
return {}; obj["outbound"] = "direct";
break;
default:
auto prof = NekoGui::profileManager->GetProfile(outboundID);
if (prof == nullptr) {
MW_show_log("The outbound described in the rule chain is missing, maybe your data is corrupted");
return {};
}
obj["outbound"] = prof->bean->DisplayName();
} }
obj["outbound"] = prof->bean->DisplayName(); } else {
if (!outboundTag.isEmpty()) obj["outbound"] = outboundTag;
else obj["outbound"] = outboundID;
}
} }
} else { }
if (!outboundTag.isEmpty()) obj["outbound"] = outboundTag; if (actionType == "sniff")
else obj["outbound"] = outboundID; {
if (isValidStrArray(sniffers)) obj["sniffers"] = get_as_array(sniffers);
if (sniffOverrideDest) obj["override_destination"] = sniffOverrideDest;
}
if (actionType == "resolve")
{
if (!strategy.isEmpty()) obj["strategy"] = strategy;
} }
return obj; return obj;
@ -151,17 +191,51 @@ namespace NekoGui {
"process_path_regex", "process_path_regex",
"rule_set", "rule_set",
"invert", "invert",
"action_type",
}; };
} }
QStringList RouteRule::get_route_options()
{
QStringList options;
options << "override_address" << "override_port";
return options;
}
QStringList RouteRule::get_reject_options()
{
QStringList options;
options << "method" << "no_drop";
return options;
}
QStringList RouteRule::get_resolve_options()
{
QStringList options;
options << "strategy";
return options;
}
QStringList RouteRule::get_sniff_options()
{
QStringList options;
options << "override_destination";
return options;
}
inputType RouteRule::get_input_type(const QString& fieldName) { inputType RouteRule::get_input_type(const QString& fieldName) {
if (fieldName == "invert" || if (fieldName == "invert" ||
fieldName == "source_ip_is_private" || fieldName == "source_ip_is_private" ||
fieldName == "ip_is_private") return trufalse; fieldName == "ip_is_private" ||
fieldName == "no_drop" ||
fieldName == "override_destination") return trufalse;
if (fieldName == "ip_version" || if (fieldName == "ip_version" ||
fieldName == "network" || fieldName == "network" ||
fieldName == "protocol") return select; fieldName == "protocol" ||
fieldName == "action_type" ||
fieldName == "method" ||
fieldName == "strategy") return select;
return text; return text;
} }
@ -178,6 +252,22 @@ namespace NekoGui {
resp.prepend(""); resp.prepend("");
return resp; return resp;
} }
if (fieldName == "action_type")
{
return Preset::SingBox::ActionTypes;
}
if (fieldName == "method")
{
auto resp = Preset::SingBox::rejectMethods;
resp.prepend("");
return resp;
}
if (fieldName == "strategy")
{
auto resp = Preset::SingBox::DomainStrategy;
resp.prepend("");
return resp;
}
return {}; return {};
} }
@ -191,6 +281,26 @@ namespace NekoGui {
if (fieldName == "protocol") { if (fieldName == "protocol") {
return {protocol}; return {protocol};
} }
if (fieldName == "action_type")
{
return {actionType};
}
if (fieldName == "method")
{
return {rejectMethod};
}
if (fieldName == "strategy")
{
return {strategy};
}
if (fieldName == "override_address")
{
return {override_address};
}
if (fieldName == "override_port")
{
return {override_port};
}
if (fieldName == "inbound") return inbound; if (fieldName == "inbound") return inbound;
if (fieldName == "domain") return domain; if (fieldName == "domain") return domain;
if (fieldName == "domain_suffix") return domain_suffix; if (fieldName == "domain_suffix") return domain_suffix;
@ -219,6 +329,14 @@ namespace NekoGui {
if (fieldName == "invert") { if (fieldName == "invert") {
return invert? "true":"false"; return invert? "true":"false";
} }
if (fieldName == "no_drop")
{
return no_drop? "true":"false";
}
if (fieldName == "override_destination")
{
return sniffOverrideDest? "true":"false";
}
return nullptr; return nullptr;
} }
@ -295,9 +413,37 @@ namespace NekoGui {
if (fieldName == "invert") { if (fieldName == "invert") {
invert = value[0]=="true"; invert = value[0]=="true";
} }
if (fieldName == "action_type")
{
actionType = value[0];
}
if (fieldName == "method")
{
rejectMethod = value[0];
}
if (fieldName == "no_drop")
{
no_drop = value[0]=="true";
}
if (fieldName == "override_address")
{
override_address = value[0];
}
if (fieldName == "override_port")
{
override_port = value[0];
}
if (fieldName == "override_destination")
{
sniffOverrideDest = value[0]=="true";
}
if (fieldName == "strategy")
{
strategy = value[0];
}
} }
bool RouteRule::isEmpty() const { bool RouteRule::isEmpty() {
return get_rule_json().keys().length() == 1; return get_rule_json().keys().length() == 1;
} }
@ -307,10 +453,6 @@ namespace NekoGui {
return true; return true;
case -2: case -2:
return true; return true;
case -3:
return true;
case -4:
return true;
default: default:
return profileManager->profiles.count(id) > 0; return profileManager->profiles.count(id) > 0;
} }
@ -319,8 +461,6 @@ namespace NekoGui {
int getOutboundID(const QString& name) { int getOutboundID(const QString& name) {
if (name == "proxy") return -1; if (name == "proxy") return -1;
if (name == "direct") return -2; if (name == "direct") return -2;
if (name == "block") return -3;
if (name == "dns-out" || name == "dns_out") return -4;
for (const auto& item: profileManager->profiles) { for (const auto& item: profileManager->profiles) {
if (item.second->bean->name == name) return item.first; if (item.second->bean->name == name) return item.first;
} }
@ -411,8 +551,7 @@ namespace NekoGui {
defaultChain->name = "Default"; defaultChain->name = "Default";
auto defaultRule = std::make_shared<RouteRule>(); auto defaultRule = std::make_shared<RouteRule>();
defaultRule->name = "Route DNS"; defaultRule->name = "Route DNS";
defaultRule->protocol = "dns"; defaultRule->actionType = "hijack-dns";
defaultRule->outboundID = -4;
defaultChain->Rules << defaultRule; defaultChain->Rules << defaultRule;
return defaultChain; return defaultChain;
} }
@ -425,8 +564,7 @@ namespace NekoGui {
auto rule0 = std::make_shared<RouteRule>(); auto rule0 = std::make_shared<RouteRule>();
rule0->name = "Route DNS"; rule0->name = "Route DNS";
rule0->protocol = "dns"; rule0->actionType = "hijack-dns";
rule0->outboundID = -4;
chain->Rules << rule0; chain->Rules << rule0;
auto rule1 = std::make_shared<RouteRule>(); auto rule1 = std::make_shared<RouteRule>();
@ -452,8 +590,7 @@ namespace NekoGui {
auto rule0 = std::make_shared<RouteRule>(); auto rule0 = std::make_shared<RouteRule>();
rule0->name = "Route DNS"; rule0->name = "Route DNS";
rule0->protocol = "dns"; rule0->actionType = "hijack-dns";
rule0->outboundID = -4;
chain->Rules << rule0; chain->Rules << rule0;
auto rule1 = std::make_shared<RouteRule>(); auto rule1 = std::make_shared<RouteRule>();