refactor: remove geo assets

This commit is contained in:
parhelia512 2025-08-14 11:43:03 +08:00
parent e288bfb476
commit 667040cf1d
29 changed files with 69 additions and 739 deletions

View File

@ -13,11 +13,6 @@ service LibcoreService {
rpc QueryStats(EmptyReq) returns (QueryStatsResp);
rpc ListConnections(EmptyReq) returns (ListConnectionsResp);
//
rpc GetGeoIPList(GeoListRequest) returns (GetGeoIPListResponse);
rpc GetGeoSiteList(GeoListRequest) returns (GetGeoSiteListResponse);
rpc CompileGeoIPToSrs(CompileGeoIPToSrsRequest) returns (EmptyResp);
rpc CompileGeoSiteToSrs(CompileGeoSiteToSrsRequest) returns (EmptyResp);
//
rpc SetSystemDNS(SetSystemDNSRequest) returns (EmptyResp);
//
rpc IsPrivileged(EmptyReq) returns (IsPrivilegedResponse);
@ -86,28 +81,6 @@ message ConnectionMetaData {
optional string process = 10 [default = ""];
}
message GetGeoIPListResponse {
repeated string items = 1;
}
message GetGeoSiteListResponse {
repeated string items = 2;
}
message GeoListRequest {
optional string path = 1 [default = ""];
}
message CompileGeoIPToSrsRequest {
optional string item = 1 [default = ""];
optional string path = 2 [default = ""];
}
message CompileGeoSiteToSrsRequest {
optional string item = 1 [default = ""];
optional string path = 2 [default = ""];
}
message SetSystemDNSRequest {
optional bool clear = 1 [default = false];
}
@ -150,3 +123,7 @@ message QuerySpeedTestResponse {
message QueryURLTestResponse {
repeated URLTestResp results = 1;
}
message RuleSet{
map<string, string> items = 1;
}

View File

@ -9,7 +9,6 @@ require (
github.com/gofrs/uuid/v5 v5.3.2
github.com/golang/protobuf v1.5.4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/oschwald/maxminddb-golang v1.13.1
github.com/sagernet/sing v0.7.5
github.com/sagernet/sing-box v1.12.1
github.com/sagernet/sing-tun v0.7.0-beta.1

View File

@ -132,8 +132,6 @@ github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

View File

@ -1,24 +0,0 @@
package boxmain
import (
E "github.com/sagernet/sing/common/exceptions"
"github.com/oschwald/maxminddb-golang"
)
var (
geoipReader *maxminddb.Reader
)
func geoipPreRun(path string) error {
reader, err := maxminddb.Open(path)
if err != nil {
return err
}
if reader.Metadata.DatabaseType != "sing-geoip" {
reader.Close()
return E.New("incorrect database type, expected sing-geoip, got ", reader.Metadata.DatabaseType)
}
geoipReader = reader
return nil
}

View File

@ -1,60 +0,0 @@
package boxmain
import (
"bytes"
"net"
"strings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/oschwald/maxminddb-golang"
)
func geoipExport(path string, countryCode string) ([]byte, error) {
if err := geoipPreRun(path); err != nil {
return nil, err
}
networks := geoipReader.Networks(maxminddb.SkipAliasedNetworks)
countryMap := make(map[string][]*net.IPNet)
var (
ipNet *net.IPNet
nextCountryCode string
err error
)
for networks.Next() {
ipNet, err = networks.Network(&nextCountryCode)
if err != nil {
return nil, err
}
countryMap[nextCountryCode] = append(countryMap[nextCountryCode], ipNet)
}
ipNets := countryMap[strings.ToLower(countryCode)]
if len(ipNets) == 0 {
return nil, E.New("country code not found: ", countryCode)
}
outputWriter := &bytes.Buffer{}
encoder := json.NewEncoder(outputWriter)
encoder.SetIndent("", " ")
var headlessRule option.DefaultHeadlessRule
headlessRule.IPCIDR = make([]string, 0, len(ipNets))
for _, cidr := range ipNets {
headlessRule.IPCIDR = append(headlessRule.IPCIDR, cidr.String())
}
var plainRuleSet option.PlainRuleSetCompat
plainRuleSet.Version = C.RuleSetVersion1
plainRuleSet.Options.Rules = []option.HeadlessRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: headlessRule,
},
}
err = encoder.Encode(plainRuleSet)
return outputWriter.Bytes(), err
}

View File

@ -1,9 +0,0 @@
package boxmain
func ListGeoip(path string) ([]string, error) {
if err := geoipPreRun(path); err != nil {
return nil, err
}
return geoipReader.Metadata.Languages, nil
}

View File

@ -1,21 +0,0 @@
package boxmain
import (
"github.com/sagernet/sing-box/common/geosite"
E "github.com/sagernet/sing/common/exceptions"
)
var (
geositeReader *geosite.Reader
geositeCodeList []string
)
func geositePreRun(path string) error {
reader, codeList, err := geosite.Open(path)
if err != nil {
return E.Cause(err, "open geosite file ")
}
geositeReader = reader
geositeCodeList = codeList
return nil
}

View File

@ -1,42 +0,0 @@
package boxmain
import (
"bytes"
"github.com/sagernet/sing-box/common/geosite"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/json"
)
func geositeExport(path string, category string) ([]byte, error) {
if err := geositePreRun(path); err != nil {
return nil, err
}
sourceSet, err := geositeReader.Read(category)
if err != nil {
return nil, err
}
outputWriter := &bytes.Buffer{}
encoder := json.NewEncoder(outputWriter)
encoder.SetIndent("", " ")
var headlessRule option.DefaultHeadlessRule
defaultRule := geosite.Compile(sourceSet)
headlessRule.Domain = defaultRule.Domain
headlessRule.DomainSuffix = defaultRule.DomainSuffix
headlessRule.DomainKeyword = defaultRule.DomainKeyword
headlessRule.DomainRegex = defaultRule.DomainRegex
var plainRuleSet option.PlainRuleSetCompat
plainRuleSet.Version = C.RuleSetVersion1
plainRuleSet.Options.Rules = []option.HeadlessRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: headlessRule,
},
}
err = encoder.Encode(plainRuleSet)
return outputWriter.Bytes(), err
}

View File

@ -1,9 +0,0 @@
package boxmain
func GeositeList(path string) ([]string, error) {
if err := geositePreRun(path); err != nil {
return nil, err
}
return geositeCodeList, nil
}

View File

@ -1,56 +0,0 @@
package boxmain
import (
"github.com/sagernet/sing-box/common/srs"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/json"
"os"
)
type RuleSetType int
const (
IpRuleSet RuleSetType = iota
SiteRuleSet
)
func CompileRuleSet(path string, category string, ruleSetType RuleSetType, destPath string) error {
var (
content []byte
err error
)
if ruleSetType == IpRuleSet {
content, err = geoipExport(path, category)
} else {
content, err = geositeExport(path, category)
}
if err != nil {
return err
}
plainRuleSet, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
if err != nil {
return err
}
if err != nil {
return err
}
ruleSet, err := plainRuleSet.Upgrade()
if err != nil {
return err
}
outputFile, err := os.Create(destPath)
if err != nil {
return err
}
err = srs.Write(outputFile, ruleSet, plainRuleSet.Version)
if err != nil {
outputFile.Close()
os.Remove(destPath)
return err
}
outputFile.Close()
return nil
}

View File

@ -296,58 +296,6 @@ func (s *server) ListConnections(in *gen.EmptyReq, out *gen.ListConnectionsResp)
return nil
}
func (s *server) GetGeoIPList(in *gen.GeoListRequest, out *gen.GetGeoIPListResponse) error {
resp, err := boxmain.ListGeoip(*in.Path + string(os.PathSeparator) + "geoip.db")
if err != nil {
return err
}
res := make([]string, 0)
for _, r := range resp {
r += "_IP"
res = append(res, r)
}
out.Items = res
return nil
}
func (s *server) GetGeoSiteList(in *gen.GeoListRequest, out *gen.GetGeoSiteListResponse) error {
resp, err := boxmain.GeositeList(*in.Path + string(os.PathSeparator) + "geosite.db")
if err != nil {
return err
}
res := make([]string, 0)
for _, r := range resp {
r += "_SITE"
res = append(res, r)
}
out.Items = res
return nil
}
func (s *server) CompileGeoIPToSrs(in *gen.CompileGeoIPToSrsRequest, out *gen.EmptyResp) error {
category := strings.TrimSuffix(*in.Item, "_IP")
err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geoip.db", category, boxmain.IpRuleSet, "./rule_sets/"+*in.Item+".srs")
if err != nil {
return err
}
return nil
}
func (s *server) CompileGeoSiteToSrs(in *gen.CompileGeoSiteToSrsRequest, out *gen.EmptyResp) error {
category := strings.TrimSuffix(*in.Item, "_SITE")
err := boxmain.CompileRuleSet(*in.Path+string(os.PathSeparator)+"geosite.db", category, boxmain.SiteRuleSet, "./rule_sets/"+*in.Item+".srs")
if err != nil {
return err
}
return nil
}
func (s *server) IsPrivileged(in *gen.EmptyReq, out *gen.IsPrivilegedResponse) error {
if runtime.GOOS == "windows" {
out.HasPrivilege = To(false)

View File

@ -7,8 +7,6 @@
#include "3rdparty/protorpc/rpc_client.h"
namespace API {
enum GeoRuleSetType {ip, site};
class Client {
public:
explicit Client(std::function<void(const QString &)> onError, const QString &host, int port);
@ -27,10 +25,6 @@ namespace API {
libcore::QueryURLTestResponse QueryURLTest(bool *rpcOK);
QStringList GetGeoList(bool *rpcOK, GeoRuleSetType mode, const QString& basePath);
QString CompileGeoSet(bool *rpcOK, GeoRuleSetType mode, std::string category, const QString& basePath);
QString SetSystemDNS(bool *rpcOK, bool clear) const;
libcore::ListConnectionsResp ListConnections(bool *rpcOK) const;

View File

@ -54,11 +54,11 @@ namespace Configs {
bool IsValid(const std::shared_ptr<ProxyEntity> &ent);
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles);
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles, const std::map<std::string, std::string>& ruleSetMap);
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID = 0);
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, const std::map<std::string, std::string>& ruleSetMap, bool forTest, bool forExport, int chainID = 0);
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status);
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status, const std::map<std::string, std::string>& ruleSetMap);
QJsonObject BuildDnsObject(QString address, bool tunEnabled);

View File

@ -13,12 +13,7 @@ namespace Configs {
bool IsAdmin(bool forceRenew=false);
QString GetBasePath();
QString GetCoreAssetDir(const QString &name);
bool NeedGeoAssets();
} // namespace Configs
#define ROUTES_PREFIX_NAME QString("route_profiles")
#define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/")
#define RULE_SETS_DIR QString("rule_sets")

View File

@ -29,12 +29,6 @@ namespace Configs {
inline QString SimpleRuleInfo = "You can add rules with the following format:\ndomain:<your-domain>\nsuffix:<your-domain-suffix>\nkeyword:<your-domain-keyword>\nregex:<your-domain-keyword>\nruleset:<ruleset-name> or ruleset:<remote-ruleset-URL>\nip:<ip-cidr>\nprocessName:<process name>\nprocessPath:<process path>\nRules are validated on tab change or when pressing ok.\nImportant: On saving the rules, the previous rules are discarded, meaning\nany changes made to the generated rules in the Advanced tab will be lost.";
}
namespace GeoAssets {
inline QStringList GeoIPURLs = {"https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db", "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/release/geoip.db"};
inline QStringList GeoSiteURLs = {"https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db", "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/release/geosite.db"};
inline QList ResetAssetsOptions = {0, 86400, 259200, 604800};
}
namespace TestConfig
{
enum SpeedTestMode

View File

@ -94,12 +94,6 @@ namespace Configs {
bool sub_insecure = false;
int sub_auto_update = -30;
// Assets
QString geoip_download_url = "";
QString geosite_download_url = "";
int auto_reset_assets_idx = 0;
long long last_asset_reset_epoch_secs = 0;
// Security
bool skip_cert = false;
QString utlsFingerprint = "";

View File

@ -84,10 +84,6 @@ public:
bool StopVPNProcess();
void DownloadAssets(const QString &geoipUrl, const QString &geositeUrl);
void ResetAssets(const QString& geoipUrl, const QString& geositeUrl);
void UpdateConnectionList(const QMap<QString, Stats::ConnectionMetadata>& toUpdate, const QMap<QString, Stats::ConnectionMetadata>& toAdd);
void UpdateConnectionListWithRecreate(const QList<Stats::ConnectionMetadata>& connections);
@ -196,9 +192,7 @@ private:
QSemaphore sem_stopped;
int exit_reason = 0;
//
QMutex mu_download_assets;
QMutex mu_download_update;
QMutex mu_reset_assets;
//
int toolTipID;
//
@ -212,6 +206,8 @@ private:
libcore::SpeedTestResult currentTestResult;
DownloadProgressReport currentDownloadReport; // could use a list, but don't think can show more than one anyways
std::map<std::string, std::string> ruleSetMap;
QStringList remoteRouteProfiles;
QMutex mu_remoteRouteProfiles;

View File

@ -20,7 +20,7 @@ class RouteItem : public QDialog {
Q_OBJECT
public:
explicit RouteItem(QWidget *parent = nullptr, const std::shared_ptr<Configs::RoutingChain>& routeChain = nullptr);
explicit RouteItem(QWidget *parent = nullptr, const std::shared_ptr<Configs::RoutingChain>& routeChain = nullptr, const std::map<std::string, std::string>& ruleSetMap = {});
~RouteItem() override;
std::shared_ptr<Configs::RoutingChain> chain;
@ -66,7 +66,7 @@ private:
void updateRulePreview();
void updateRouteItemsView();
private slots:
private slots:
void accept() override;
void on_new_route_item_clicked();

View File

@ -761,108 +761,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Assets</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Geo Assets and Rule-sets</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="2">
<widget class="QComboBox" name="auto_reset">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Every Day</string>
</property>
</item>
<item>
<property name="text">
<string>Every 3 Days</string>
</property>
</item>
<item>
<property name="text">
<string>Every Week</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QPushButton" name="download_geo_btn">
<property name="text">
<string>Download and Replace Geo files</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="geosite_url"/>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Auto Reset Assets</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="geoip_url"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="geosite_url_l">
<property name="text">
<string>GeoSite URL</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QPushButton" name="reset_assets">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Downloads and replaces the geo asstes, and removes all generated rule-sets, then restarts the proxy&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reset All Assets</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="geoip_url_l">
<property name="text">
<string>GeoIP URL</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QPushButton" name="remove_srs_btn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Remove the
currently generated rule-sets so that they can be regenerated&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</string>
</property>
<property name="text">
<string>Remove Generated Rule-sets</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_5">
<attribute name="title">
<string>Security</string>

View File

@ -18,7 +18,7 @@ class DialogManageRoutes : public QDialog {
Q_OBJECT
public:
explicit DialogManageRoutes(QWidget *parent = nullptr);
explicit DialogManageRoutes(QWidget *parent = nullptr, const std::map<std::string, std::string>& dataMap = {});
~DialogManageRoutes() override;
@ -35,6 +35,8 @@ private:
int tooltipID = 0;
std::map<std::string, std::string> ruleSetMap;
void set_dns_hijack_enability(bool enable) const;
static bool validate_dns_rules(const QString &rawString);

View File

@ -102,90 +102,6 @@ namespace API {
}
}
QStringList Client::GetGeoList(bool *rpcOK, GeoRuleSetType mode, const QString& basePath) {
switch (mode) {
case GeoRuleSetType::ip: {
libcore::GeoListRequest request;
libcore::GetGeoIPListResponse reply;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.GetGeoIPList", &req, &resp);
if(err.IsNil()) {
QStringList res;
reply = spb::pb::deserialize< libcore::GetGeoIPListResponse >( resp );
for (const auto & i : reply.items) {
res.append(QString::fromStdString(i));
}
*rpcOK = true;
return res;
} else {
NOT_OK
return {};
}
}
case GeoRuleSetType::site: {
libcore::GeoListRequest request;
libcore::GetGeoSiteListResponse reply;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.GetGeoSiteList", &req, &resp);
if(err.IsNil()) {
QStringList res;
reply = spb::pb::deserialize< libcore::GetGeoSiteListResponse >( resp );
for (const auto & i : reply.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, const QString& basePath) {
switch (mode) {
case ip: {
libcore::CompileGeoIPToSrsRequest request;
libcore::EmptyResp reply;
request.item = category;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.CompileGeoIPToSrs", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
return "";
} else {
NOT_OK
return QString::fromStdString(err.String());
}
}
case site: {
libcore::CompileGeoSiteToSrsRequest request;
libcore::EmptyResp reply;
request.item = category;
request.path = basePath.toStdString();
std::string resp, req = spb::pb::serialize<std::string>(request);
auto err = make_rpc_client()->CallMethod("LibcoreService.CompileGeoSiteToSrs", &req, &resp);
if(err.IsNil()) {
*rpcOK = true;
return "";
} else {
NOT_OK
return QString::fromStdString(err.String());
}
}
}
return "";
}
QString Client::SetSystemDNS(bool *rpcOK, const bool clear) const {
libcore::SetSystemDNSRequest request;
request.clear = clear;

View File

@ -41,7 +41,7 @@ namespace Configs {
// Common
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID) {
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, const std::map<std::string, std::string>& ruleSetMap, bool forTest, bool forExport, int chainID) {
auto result = std::make_shared<BuildConfigResult>();
result->extraCoreData = std::make_shared<ExtraCoreData>();
auto status = std::make_shared<BuildConfigStatus>();
@ -60,7 +60,7 @@ namespace Configs {
}
result->coreConfig = QString2QJsonObject(customBean->config_simple);
} else {
BuildConfigSingBox(status);
BuildConfigSingBox(status, ruleSetMap);
}
// apply custom config
@ -116,7 +116,7 @@ namespace Configs {
}
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles) {
std::shared_ptr<BuildTestConfigResult> BuildTestConfig(const QList<std::shared_ptr<ProxyEntity>>& profiles, const std::map<std::string, std::string>& ruleSetMap) {
auto results = std::make_shared<BuildTestConfigResult>();
QJsonArray outboundArray = {
@ -140,7 +140,7 @@ namespace Configs {
item->latency = -1;
continue;
}
auto res = BuildConfig(item, true, false, ++index);
auto res = BuildConfig(item, ruleSetMap, true, false, ++index);
if (!res->error.isEmpty()) {
results->error = res->error;
return results;
@ -498,7 +498,7 @@ namespace Configs {
}
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status) {
void BuildConfigSingBox(const std::shared_ptr<BuildConfigStatus> &status, const std::map<std::string, std::string>& ruleSetMap) {
// Prefetch
auto routeChain = profileManager->GetRouteChain(dataStore->routing->current_route_id);
if (routeChain == nullptr) {
@ -656,12 +656,6 @@ namespace Configs {
// custom inbound
if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray())
// Routing
if (NeedGeoAssets()) {
status->result->error = "Geo Assets are missing, please download them through Basic Settings -> Assets";
return;
}
// manage routing section
auto routeObj = QJsonObject();
if (dataStore->spmode_vpn) {
@ -749,8 +743,6 @@ namespace Configs {
}
auto ruleSetArray = QJsonArray();
auto geoSitePath = GetCoreAssetDir("geosite.db");
auto geoIpPath = GetCoreAssetDir("geoip.db");
for (const auto &item: *neededRuleSets) {
if(auto url = QUrl(item); url.isValid() && url.fileName().contains(".srs")) {
ruleSetArray += QJsonObject{
@ -761,26 +753,13 @@ namespace Configs {
};
}
else {
QString srsUrl = QString::fromStdString(ruleSetMap.at(item.toStdString()));
ruleSetArray += QJsonObject{
{"type", "local"},
{"tag", item},
{"type", "remote"},
{"tag", get_rule_set_name(srsUrl)},
{"format", "binary"},
{"path", RULE_SETS_DIR + QString("/%1.srs").arg(item)},
{"url", srsUrl},
};
if (QFile(QString(RULE_SETS_DIR + "/%1.srs").arg(item)).exists()) continue;
bool ok;
auto mode = API::GeoRuleSetType::site;
auto geoAssertPath = geoSitePath;
if (item.contains("_IP")) {
mode = API::GeoRuleSetType::ip;
geoAssertPath = geoIpPath;
}
auto err = API::defaultClient->CompileGeoSet(&ok, mode, item.toStdString(), geoAssertPath);
if (!ok) {
MW_show_log("Failed to generate rule set asset for " + item);
status->result->error = err;
return;
}
}
}
routeObj["rule_set"] = ruleSetArray;

View File

@ -291,10 +291,6 @@ namespace Configs {
_add(new configItem("ntp_server_address", &ntp_server_address, itemType::string));
_add(new configItem("ntp_server_port", &ntp_server_port, itemType::integer));
_add(new configItem("ntp_interval", &ntp_interval, itemType::string));
_add(new configItem("geoip_download_url", &geoip_download_url, itemType::string));
_add(new configItem("geosite_download_url", &geosite_download_url, itemType::string));
_add(new configItem("auto_reset_assets_idx", &auto_reset_assets_idx, itemType::integer));
_add(new configItem("last_asset_reset_unix_secs", &last_asset_reset_epoch_secs, itemType::integer64));
_add(new configItem("enable_dns_server", &enable_dns_server, itemType::boolean));
_add(new configItem("dns_server_listen_lan", &dns_server_listen_lan, itemType::boolean));
_add(new configItem("dns_server_listen_port", &dns_server_listen_port, itemType::integer));
@ -389,27 +385,4 @@ namespace Configs {
QStandardPaths::AppConfigLocation);
return qApp->applicationDirPath();
}
QString GetCoreAssetDir(const QString &name) {
QStringList search = {
GetBasePath(),
QString("/usr/share/sing-geoip"),
QString("/usr/share/sing-geosite"),
QString("/usr/share/sing-box"),
};
for (const auto &dir: search) {
if (dir.isEmpty())
continue;
if (QFile(QString("%1/%2").arg(dir, name)).exists())
return dir;
}
return "";
}
bool NeedGeoAssets(){
return GetCoreAssetDir("geoip.db").isEmpty() || GetCoreAssetDir("geosite.db").isEmpty();
}
} // namespace Configs

View File

@ -139,9 +139,6 @@ 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::critical(nullptr, "Error", "No permission to write " + dir.absolutePath());
return 1;

View File

@ -417,6 +417,25 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
}
});
auto getRuleSet = [=,this]
{
QString err;
for(int retry = 0; retry < 3; retry++) {
auto resp = NetworkRequestHelper::HttpGet("https://raw.githubusercontent.com/throneproj/routeprofiles/rule-set/list");
if (resp.error.isEmpty()) {
std::vector<uint8_t> respvec;
respvec.assign(resp.data.begin(), resp.data.end());
auto reply = spb::pb::deserialize<libcore::RuleSet>(respvec);
ruleSetMap = reply.items;
return;
}
else
err = resp.error;
}
MW_show_log(QObject::tr("Requesting rule-set error: %1").arg(err));
};
runOnNewThread(getRuleSet);
auto getRemoteRouteProfiles = [=,this]
{
auto resp = NetworkRequestHelper::HttpGet("https://api.github.com/repos/throneproj/routeprofiles/releases/latest");
@ -439,7 +458,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
connect(ui->menuRouting_Menu, &QMenu::aboutToShow, this, [=,this]()
{
// refresh it on every menu show
if(remoteRouteProfiles.isEmpty())
runOnNewThread(getRemoteRouteProfiles);
ui->menuRouting_Menu->clear();
ui->menuRouting_Menu->addAction(ui->menu_routing_settings);
@ -562,33 +581,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
connect(TM_auto_update_subsctiption, &QTimer::timeout, this, [&] { UI_update_all_groups(true); });
TM_auto_update_subsctiption_Reset_Minute(Configs::dataStore->sub_auto_update);
// asset updater timer
auto TM_auto_reset_assets = new QTimer(this);
connect(TM_auto_reset_assets, &QTimer::timeout, this, [&]()
{
auto reset_interval = Configs::GeoAssets::ResetAssetsOptions[Configs::dataStore->auto_reset_assets_idx];
if (reset_interval > 0 && QDateTime::currentSecsSinceEpoch() - Configs::dataStore->last_asset_reset_epoch_secs > reset_interval)
{
runOnNewThread([=,this]
{
ResetAssets(Configs::dataStore->geoip_download_url, Configs::dataStore->geosite_download_url);
});
}
});
TM_auto_reset_assets->start(1000 * 60 * 5); // check every 5 minutes
if (!Configs::dataStore->flag_tray) show();
if (Configs::NeedGeoAssets()) {
auto n = QMessageBox::warning(GetMessageBoxParent(), software_name, tr("Geo Assets are missing, want to download them now?"), QMessageBox::Yes | QMessageBox::No);
if (n == QMessageBox::Yes) {
runOnNewThread([=,this]
{
DownloadAssets(!Configs::dataStore->geoip_download_url.isEmpty() ? Configs::dataStore->geoip_download_url : Configs::GeoAssets::GeoIPURLs[0],
!Configs::dataStore->geosite_download_url.isEmpty() ? Configs::dataStore->geosite_download_url : Configs::GeoAssets::GeoSiteURLs[0]);
});
}
}
ui->data_view->setStyleSheet("background: transparent; border: none;");
}
@ -721,19 +715,6 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
on_menu_exit_triggered();
}
}
if (info.contains("DownloadAssets")) {
auto splitted = info.split(";");
runOnNewThread([=,this](){
DownloadAssets(splitted[1], splitted[2]);
});
}
if (info.contains("ResetAssets"))
{
auto splitted = info.split(";");
runOnNewThread([=,this](){
ResetAssets(splitted[1], splitted[2]);
});
}
//
if (info == "RestartProgram") {
this->exit_reason = 2;
@ -822,7 +803,14 @@ void MainWindow::on_menu_manage_groups_triggered() {
}
void MainWindow::on_menu_routing_settings_triggered() {
USE_DIALOG(DialogManageRoutes)
if (dialog_is_using) return;
dialog_is_using = true;
auto dialog = new DialogManageRoutes(this, ruleSetMap);
connect(dialog, &QDialog::finished, this, [=,this] {
dialog->deleteLater();
dialog_is_using = false;
});
dialog->show();
}
void MainWindow::on_menu_vpn_settings_triggered() {
@ -1642,7 +1630,7 @@ void MainWindow::on_menu_export_config_triggered() {
auto ent = ents.first();
if (ent->bean->DisplayCoreType() != software_core_name) return;
auto result = BuildConfig(ent, false, true);
auto result = BuildConfig(ent, ruleSetMap, false, true);
QString config_core = QJsonObject2QString(result->coreConfig, true);
QApplication::clipboard()->setText(config_core);
@ -1654,11 +1642,11 @@ void MainWindow::on_menu_export_config_triggered() {
msg.setDefaultButton(QMessageBox::Ok);
msg.exec();
if (msg.clickedButton() == button_1) {
result = BuildConfig(ent, false, false);
result = BuildConfig(ent, ruleSetMap, false, false);
config_core = QJsonObject2QString(result->coreConfig, true);
QApplication::clipboard()->setText(config_core);
} else if (msg.clickedButton() == button_2) {
result = BuildConfig(ent, true, false);
result = BuildConfig(ent, ruleSetMap, true, false);
config_core = QJsonObject2QString(result->coreConfig, true);
QApplication::clipboard()->setText(config_core);
}
@ -2298,62 +2286,6 @@ bool MainWindow::StopVPNProcess() {
return true;
}
void MainWindow::DownloadAssets(const QString &geoipUrl, const QString &geositeUrl) {
if (!mu_download_assets.tryLock()) {
runOnUiThread([=,this](){
MessageBoxWarning(tr("Cannot start"), tr("Last download request has not finished yet"));
});
return;
}
MW_show_log("Start downloading...");
QString errors;
if (!geoipUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadAsset(geoipUrl, "geoip.db");
if (!resp.isEmpty()) {
MW_show_log(QString(tr("Failed to download geoip: %1")).arg(resp));
errors += "geoip: " + resp;
}
}
if (!geositeUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadAsset(geositeUrl, "geosite.db");
if (!resp.isEmpty()) {
MW_show_log(QString(tr("Failed to download geosite: %1")).arg(resp));
errors += "\ngeosite: " + resp;
}
}
mu_download_assets.unlock();
if (!errors.isEmpty()) {
runOnUiThread([=,this](){
MessageBoxWarning(tr("Failed to download geo assets"), errors);
});
}
MW_show_log(tr("Geo Asset update completed!"));
}
void MainWindow::ResetAssets(const QString& geoipUrl, const QString& geositeUrl)
{
if (!mu_reset_assets.try_lock())
{
MW_show_log(tr("A reset of assets is already in progress"));
return;
}
DownloadAssets(geoipUrl, geositeUrl);
auto entries = QDir(RULE_SETS_DIR).entryList(QDir::Files);
for (const auto &item: entries) {
if (!QFile(RULE_SETS_DIR + "/" + item).remove()) {
MW_show_log("Failed to remove " + item + ", stop the core then try again");
}
}
MW_show_log(tr("Removed all rule-set files"));
runOnUiThread([=,this]
{
if (Configs::dataStore->started_id >= 0) profile_start(Configs::dataStore->started_id);
});
Configs::dataStore->last_asset_reset_epoch_secs = QDateTime::currentSecsSinceEpoch();
mu_reset_assets.unlock();
}
bool isNewer(QString assetName) {
if (QString(NKR_VERSION).isEmpty()) return false;
assetName = assetName.mid(7); // take out Throne-

View File

@ -141,7 +141,7 @@ void MainWindow::urltest_current_group(const QList<std::shared_ptr<Configs::Prox
}
runOnNewThread([this, profiles]() {
auto buildObject = Configs::BuildTestConfig(profiles);
auto buildObject = Configs::BuildTestConfig(profiles, ruleSetMap);
if (!buildObject->error.isEmpty()) {
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
speedtestRunning.unlock();
@ -236,7 +236,7 @@ void MainWindow::speedtest_current_group(const QList<std::shared_ptr<Configs::Pr
runOnNewThread([this, profiles, testCurrent]() {
if (!testCurrent)
{
auto buildObject = Configs::BuildTestConfig(profiles);
auto buildObject = Configs::BuildTestConfig(profiles, ruleSetMap);
if (!buildObject->error.isEmpty()) {
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
speedtestRunning.unlock();
@ -420,7 +420,7 @@ void MainWindow::profile_start(int _id) {
auto group = Configs::profileManager->GetGroup(ent->gid);
if (group == nullptr || group->archive) return;
auto result = BuildConfig(ent, false, false);
auto result = BuildConfig(ent, ruleSetMap, false, false);
if (!result->error.isEmpty()) {
MessageBoxWarning(tr("BuildConfig return error"), result->error);
return;

View File

@ -47,7 +47,7 @@ QStringList get_all_outbounds() {
return res;
}
RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<Configs::RoutingChain>& routeChain)
RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<Configs::RoutingChain>& routeChain, const std::map<std::string, std::string>& ruleSetMap)
: QDialog(parent), ui(new Ui::RouteItem) {
ui->setupUi(this);
@ -64,10 +64,9 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<Configs::RoutingChai
}
// setup rule set helper
bool ok; // for now we discard this
auto geoIpList = API::defaultClient->GetGeoList(&ok, API::GeoRuleSetType::ip, Configs::GetCoreAssetDir("geoip.db"));
auto geoSiteList = API::defaultClient->GetGeoList(&ok, API::GeoRuleSetType::site, Configs::GetCoreAssetDir("geosite.db"));
geo_items << geoIpList << geoSiteList;
for (const auto& item : ruleSetMap) {
geo_items.append("ruleset:" + QString::fromStdString(item.first));
}
rule_set_editor = new AutoCompleteTextEdit("", geo_items, this);
ui->rule_attr_data->layout()->addWidget(rule_set_editor);
ui->rule_attr_data->adjustSize();
@ -124,11 +123,8 @@ RouteItem::RouteItem(QWidget *parent, const std::shared_ptr<Configs::RoutingChai
// simple rules setup
QStringList ruleItems = {"domain:", "suffix:", "regex:", "keyword:", "ip:", "processName:", "processPath:", "ruleset:"};
for (const auto& geoIP : geoIpList) {
ruleItems.append("ruleset:"+geoIP);
}
for (const auto& geoSite: geoSiteList) {
ruleItems.append("ruleset:"+geoSite);
for (const auto& item : ruleSetMap) {
ruleItems.append("ruleset:" + QString::fromStdString(item.first));
}
simpleDirect = new AutoCompleteTextEdit("", ruleItems, this);
simpleBlock = new AutoCompleteTextEdit("", ruleItems, this);

View File

@ -120,33 +120,6 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
// Core
ui->groupBox_core->setTitle(software_core_name);
// Assets
ui->geoip_url->setEditable(true);
ui->geosite_url->setEditable(true);
ui->geoip_url->addItems(Configs::GeoAssets::GeoIPURLs);
ui->geosite_url->addItems(Configs::GeoAssets::GeoSiteURLs);
ui->geoip_url->setCurrentText(Configs::dataStore->geoip_download_url);
ui->geosite_url->setCurrentText(Configs::dataStore->geosite_download_url);
ui->auto_reset->setCurrentIndex(Configs::dataStore->auto_reset_assets_idx);
connect(ui->download_geo_btn, &QPushButton::clicked, this, [=,this]() {
MW_dialog_message(Dialog_DialogBasicSettings, "DownloadAssets;"+ui->geoip_url->currentText()+";"+ui->geosite_url->currentText());
});
connect(ui->remove_srs_btn, &QPushButton::clicked, this, [=,this](){
auto rsDir = QDir(RULE_SETS_DIR);
auto entries = rsDir.entryList(QDir::Files);
for (const auto &item: entries) {
if (!QFile(RULE_SETS_DIR + "/" + item).remove()) {
MW_show_log("Failed to remove " + item + ", stop the core then try again");
}
}
MW_show_log(tr("Removed all rule-set files"));
});
connect(ui->reset_assets, &QPushButton::clicked, this, [=,this]()
{
MW_dialog_message(Dialog_DialogBasicSettings, "ResetAssets;"+ui->geoip_url->currentText()+";"+ui->geosite_url->currentText());
});
// Mux
D_LOAD_INT(mux_concurrency)
D_LOAD_COMBO_STRING(mux_protocol)
@ -230,11 +203,6 @@ void DialogBasicSettings::accept() {
// Core
Configs::dataStore->disable_traffic_stats = ui->disable_stats->isChecked();
// Assets
Configs::dataStore->geoip_download_url = ui->geoip_url->currentText();
Configs::dataStore->geosite_download_url = ui->geosite_url->currentText();
Configs::dataStore->auto_reset_assets_idx = ui->auto_reset->currentIndex();
// Mux
D_SAVE_INT(mux_concurrency)
D_SAVE_COMBO_STRING(mux_protocol)

View File

@ -59,7 +59,7 @@ bool DialogManageRoutes::validate_dns_rules(const QString &rawString) {
return true;
}
DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(new Ui::DialogManageRoutes) {
DialogManageRoutes::DialogManageRoutes(QWidget *parent, const std::map<std::string, std::string>& dataMap) : QDialog(parent), ui(new Ui::DialogManageRoutes) {
ui->setupUi(this);
auto profiles = Configs::profileManager->routes;
for (const auto &item: profiles) {
@ -139,15 +139,10 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(ne
MessageBoxInfo("What is this?", Configs::Information::HijackInfo);
});
bool ok;
auto geoIpList = API::defaultClient->GetGeoList(&ok, API::GeoRuleSetType::ip, Configs::GetCoreAssetDir("geoip.db"));
auto geoSiteList = API::defaultClient->GetGeoList(&ok, API::GeoRuleSetType::site, Configs::GetCoreAssetDir("geosite.db"));
ruleSetMap = dataMap;
QStringList ruleItems = {"domain:", "suffix:", "regex:"};
for (const auto& geoIP : geoIpList) {
ruleItems.append("ruleset:"+geoIP);
}
for (const auto& geoSite: geoSiteList) {
ruleItems.append("ruleset:"+geoSite);
for (const auto& item : ruleSetMap) {
ruleItems.append("ruleset:" + QString::fromStdString(item.first));
}
rule_editor = new AutoCompleteTextEdit("", ruleItems, this);
ui->hijack_box->layout()->replaceWidget(ui->dnshijack_rules, rule_editor);
@ -235,7 +230,7 @@ void DialogManageRoutes::accept() {
}
void DialogManageRoutes::on_new_route_clicked() {
routeChainWidget = new RouteItem(this, Configs::ProfileManager::NewRouteChain());
routeChainWidget = new RouteItem(this, Configs::ProfileManager::NewRouteChain(), ruleSetMap);
routeChainWidget->setWindowModality(Qt::ApplicationModal);
routeChainWidget->show();
connect(routeChainWidget, &RouteItem::settingsChanged, this, [=,this](const std::shared_ptr<Configs::RoutingChain>& chain) {