mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-18 20:50:09 +08:00
refactor: migrate from bulit-in C++ clash parser to clash2singbox
This commit is contained in:
parent
2b5bde651b
commit
046acde181
14730
3rdparty/fkYAML/node.hpp
vendored
14730
3rdparty/fkYAML/node.hpp
vendored
File diff suppressed because it is too large
Load Diff
@ -50,9 +50,9 @@ Various formats are supported, including share links, JSON array of outbounds an
|
|||||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
||||||
- [Qt](https://www.qt.io/)
|
- [Qt](https://www.qt.io/)
|
||||||
- [simple-protobuf](https://github.com/tonda-kriz/simple-protobuf)
|
- [simple-protobuf](https://github.com/tonda-kriz/simple-protobuf)
|
||||||
- [fkYAML](https://github.com/fktn-k/fkYAML)
|
|
||||||
- [quirc](https://github.com/dlbeer/quirc)
|
- [quirc](https://github.com/dlbeer/quirc)
|
||||||
- [QHotkey](https://github.com/Skycoder42/QHotkey)
|
- [QHotkey](https://github.com/Skycoder42/QHotkey)
|
||||||
|
- [clash2singbox](https://github.com/xmdhs/clash2singbox)
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
**How does this project differ from the original Nekoray?** <br/>
|
**How does this project differ from the original Nekoray?** <br/>
|
||||||
|
|||||||
@ -44,9 +44,9 @@
|
|||||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
||||||
- [Qt](https://www.qt.io/)
|
- [Qt](https://www.qt.io/)
|
||||||
- [simple-protobuf](https://github.com/tonda-kriz/simple-protobuf)
|
- [simple-protobuf](https://github.com/tonda-kriz/simple-protobuf)
|
||||||
- [fkYAML](https://github.com/fktn-k/fkYAML)
|
|
||||||
- [quirc](https://github.com/dlbeer/quirc)
|
- [quirc](https://github.com/dlbeer/quirc)
|
||||||
- [QHotkey](https://github.com/Skycoder42/QHotkey)
|
- [QHotkey](https://github.com/Skycoder42/QHotkey)
|
||||||
|
- [clash2singbox](https://github.com/xmdhs/clash2singbox)
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
**这个项目与原始的 Nekoray 有什么不同?** <br/>
|
**这个项目与原始的 Nekoray 有什么不同?** <br/>
|
||||||
|
|||||||
@ -20,6 +20,8 @@ service LibcoreService {
|
|||||||
rpc SpeedTest(SpeedTestRequest) returns(SpeedTestResponse);
|
rpc SpeedTest(SpeedTestRequest) returns(SpeedTestResponse);
|
||||||
rpc QuerySpeedTest(EmptyReq) returns(QuerySpeedTestResponse);
|
rpc QuerySpeedTest(EmptyReq) returns(QuerySpeedTestResponse);
|
||||||
rpc QueryCountryTest(EmptyReq) returns(QueryCountryTestResponse);
|
rpc QueryCountryTest(EmptyReq) returns(QueryCountryTestResponse);
|
||||||
|
//
|
||||||
|
rpc Clash2Singbox(Clash2SingboxRequest) returns(Clash2SingboxResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message EmptyReq {}
|
message EmptyReq {}
|
||||||
@ -132,3 +134,12 @@ message QueryCountryTestResponse {
|
|||||||
message QueryURLTestResponse {
|
message QueryURLTestResponse {
|
||||||
repeated URLTestResp results = 1;
|
repeated URLTestResp results = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Clash2SingboxRequest {
|
||||||
|
optional string clash_config = 1 [default = ""];
|
||||||
|
}
|
||||||
|
|
||||||
|
message Clash2SingboxResponse {
|
||||||
|
optional string singbox_config = 1 [default = ""];
|
||||||
|
optional string error = 2 [default = ""];
|
||||||
|
}
|
||||||
|
|||||||
@ -15,14 +15,18 @@ require (
|
|||||||
github.com/sagernet/sing-box v1.12.12
|
github.com/sagernet/sing-box v1.12.12
|
||||||
github.com/sagernet/sing-tun v0.7.3
|
github.com/sagernet/sing-tun v0.7.3
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.1
|
||||||
|
github.com/xmdhs/clash2singbox v0.1.5-0.20251129070952-d3d41337d2c1
|
||||||
golang.org/x/sys v0.37.0
|
golang.org/x/sys v0.37.0
|
||||||
google.golang.org/protobuf v1.36.10
|
google.golang.org/protobuf v1.36.10
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/sagernet/sing-box => github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4
|
replace github.com/sagernet/sing-box => github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4
|
||||||
|
|
||||||
replace github.com/sagernet/wireguard-go => github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26
|
replace github.com/sagernet/wireguard-go => github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26
|
||||||
|
|
||||||
|
replace github.com/xmdhs/clash2singbox => github.com/throneproj/clash2singbox v0.1.5-0.20251130124907-02ead6993794
|
||||||
|
|
||||||
replace github.com/chai2010/protorpc => ../protorpc
|
replace github.com/chai2010/protorpc => ../protorpc
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@ -216,6 +216,8 @@ github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
|||||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||||
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
|
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
|
||||||
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
|
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
|
||||||
|
github.com/throneproj/clash2singbox v0.1.5-0.20251130124907-02ead6993794 h1:U0zcOKETHWz2N/V721V498cpRgHwm1EBrutxmRSAjjc=
|
||||||
|
github.com/throneproj/clash2singbox v0.1.5-0.20251130124907-02ead6993794/go.mod h1:v5Kl3ZsY7KkoK7uY9oC56uEdXRp0upRxNEWX9Us8siA=
|
||||||
github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4 h1:tQumipJlxqgMXvbuOJR8qMU4HoM0qbcU6RPA0DeAV9M=
|
github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4 h1:tQumipJlxqgMXvbuOJR8qMU4HoM0qbcU6RPA0DeAV9M=
|
||||||
github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4/go.mod h1:4hUwNgXeaqRWAuYxixxVBOEGRFIamyw12lrpx8hbZBc=
|
github.com/throneproj/sing-box v1.11.16-0.20251027170654-efe4b5f5b1e4/go.mod h1:4hUwNgXeaqRWAuYxixxVBOEGRFIamyw12lrpx8hbZBc=
|
||||||
github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26 h1:bBzqh7xTshvPjTFz4URNj/xbPA/d0BOwUM2R83FEMGU=
|
github.com/throneproj/wireguard-go v0.0.1-beta.7.0.20250728063157-408bba78ad26 h1:bBzqh7xTshvPjTFz4URNj/xbPA/d0BOwUM2R83FEMGU=
|
||||||
@ -305,6 +307,7 @@ google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
|||||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@ -21,6 +21,11 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/xmdhs/clash2singbox/convert"
|
||||||
|
"github.com/xmdhs/clash2singbox/model"
|
||||||
|
"github.com/xmdhs/clash2singbox/model/clash"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var boxInstance *boxbox.Box
|
var boxInstance *boxbox.Box
|
||||||
@ -403,3 +408,29 @@ func (s *server) QueryCountryTest(in *gen.EmptyReq, out *gen.QueryCountryTestRes
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *server) Clash2Singbox(in *gen.Clash2SingboxRequest, out *gen.Clash2SingboxResponse) (_ error) {
|
||||||
|
var convErr error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if convErr != nil {
|
||||||
|
out.Error = To(convErr.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c := clash.Clash{}
|
||||||
|
err := yaml.Unmarshal([]byte(*in.ClashConfig), &c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sing, convErr := convert.Clash2sing(c, model.SINGLATEST)
|
||||||
|
|
||||||
|
outb, err := json.Marshal(map[string]any{"outbounds": sing})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out.SingboxConfig = To(string(outb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -39,6 +39,8 @@ namespace API {
|
|||||||
|
|
||||||
libcore::QueryCountryTestResponse QueryCountryTestResults(bool *rpcOK);
|
libcore::QueryCountryTestResponse QueryCountryTestResults(bool *rpcOK);
|
||||||
|
|
||||||
|
QString Clash2Singbox(bool *rpcOK, const QString& config) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<std::unique_ptr<protorpc::Client>()> make_rpc_client;
|
std::function<std::unique_ptr<protorpc::Client>()> make_rpc_client;
|
||||||
std::function<void(const QString &)> onError;
|
std::function<void(const QString &)> onError;
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
namespace Subscription {
|
namespace Subscription {
|
||||||
class RawUpdater {
|
class RawUpdater {
|
||||||
public:
|
public:
|
||||||
void updateClash(const QString &str);
|
|
||||||
|
|
||||||
void update(const QString &str, bool needParse);
|
void update(const QString &str, bool needParse);
|
||||||
|
|
||||||
void updateSingBox(const QString &str);
|
void updateSingBox(const QString &str);
|
||||||
|
|||||||
@ -240,4 +240,27 @@ if (!Configs::dataStore->core_running) MW_show_log("Cannot invoke method " + QSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Client::Clash2Singbox(bool* rpcOK, const QString& config) const
|
||||||
|
{
|
||||||
|
CHECK("Clash2Singbox")
|
||||||
|
libcore::Clash2SingboxRequest request;
|
||||||
|
libcore::Clash2SingboxResponse reply;
|
||||||
|
request.clash_config = config.toStdString();
|
||||||
|
std::string resp, req = spb::pb::serialize<std::string>(request);
|
||||||
|
auto err = make_rpc_client()->CallMethod("LibcoreService.Clash2Singbox", &req, &resp);
|
||||||
|
|
||||||
|
if(err.IsNil()) {
|
||||||
|
reply = spb::pb::deserialize< libcore::Clash2SingboxResponse >( resp );
|
||||||
|
*rpcOK = true;
|
||||||
|
QString error = QString::fromStdString(reply.error.value());
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
MW_show_log(QString("Failed to convert Clash config:\n") + error);
|
||||||
|
}
|
||||||
|
return QString::fromStdString(reply.singbox_config.value());
|
||||||
|
} else {
|
||||||
|
NOT_OK
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace API
|
} // namespace API
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "include/dataStore/ProfileFilter.hpp"
|
#include "include/dataStore/ProfileFilter.hpp"
|
||||||
#include "include/configs/proxy/includes.h"
|
#include "include/configs/proxy/includes.h"
|
||||||
#include "include/global/HTTPRequestHelper.hpp"
|
#include "include/global/HTTPRequestHelper.hpp"
|
||||||
|
#include "include/api/RPC.h"
|
||||||
|
|
||||||
#include "include/configs/sub/GroupUpdater.hpp"
|
#include "include/configs/sub/GroupUpdater.hpp"
|
||||||
|
|
||||||
@ -8,8 +9,6 @@
|
|||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include "3rdparty/fkYAML/node.hpp"
|
|
||||||
|
|
||||||
namespace Subscription {
|
namespace Subscription {
|
||||||
|
|
||||||
GroupUpdater *groupUpdater = new GroupUpdater;
|
GroupUpdater *groupUpdater = new GroupUpdater;
|
||||||
@ -58,7 +57,12 @@ namespace Subscription {
|
|||||||
|
|
||||||
// Clash
|
// Clash
|
||||||
if (str.contains("proxies:")) {
|
if (str.contains("proxies:")) {
|
||||||
updateClash(str);
|
bool ok;
|
||||||
|
QString resp = API::defaultClient->Clash2Singbox(&ok, str);
|
||||||
|
if (ok && !resp.isEmpty())
|
||||||
|
{
|
||||||
|
updateSingBox(resp);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,461 +327,6 @@ namespace Subscription {
|
|||||||
updated_order += ent;
|
updated_order += ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString Node2QString(const fkyaml::node &n, const QString &def = "") {
|
|
||||||
try {
|
|
||||||
return n.as_str().c_str();
|
|
||||||
} catch (const fkyaml::exception &ex) {
|
|
||||||
qDebug() << ex.what();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Node2QStringList(const fkyaml::node &n) {
|
|
||||||
try {
|
|
||||||
if (n.is_sequence()) {
|
|
||||||
QStringList list;
|
|
||||||
for (auto item: n) {
|
|
||||||
list << item.as_str().c_str();
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
} catch (const fkyaml::exception &ex) {
|
|
||||||
qDebug() << ex.what();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Node2Int(const fkyaml::node &n, const int &def = 0) {
|
|
||||||
try {
|
|
||||||
if (n.is_integer())
|
|
||||||
return n.as_int();
|
|
||||||
else if (n.is_string())
|
|
||||||
return atoi(n.as_str().c_str());
|
|
||||||
return def;
|
|
||||||
} catch (const fkyaml::exception &ex) {
|
|
||||||
qDebug() << ex.what();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Node2Bool(const fkyaml::node &n, const bool &def = false) {
|
|
||||||
try {
|
|
||||||
return n.as_bool();
|
|
||||||
} catch (const fkyaml::exception &ex) {
|
|
||||||
try {
|
|
||||||
return n.as_int();
|
|
||||||
} catch (const fkyaml::exception &ex2) {
|
|
||||||
ex2.what();
|
|
||||||
}
|
|
||||||
qDebug() << ex.what();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeChild returns the first defined children or Null Node
|
|
||||||
fkyaml::node NodeChild(const fkyaml::node &n, const std::list<std::string> &keys) {
|
|
||||||
for (const auto &key: keys) {
|
|
||||||
if (n.contains(key)) return n[key];
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/Dreamacro/clash/wiki/configuration
|
|
||||||
void RawUpdater::updateClash(const QString &str) {
|
|
||||||
try {
|
|
||||||
auto proxies = fkyaml::node::deserialize(str.toStdString())["proxies"];
|
|
||||||
for (auto proxy: proxies) {
|
|
||||||
auto type = Node2QString(proxy["type"]).toLower();
|
|
||||||
auto type_clash = type;
|
|
||||||
|
|
||||||
if (type == "ss" || type == "ssr") type = "shadowsocks";
|
|
||||||
if (type == "socks5") type = "socks";
|
|
||||||
|
|
||||||
auto ent = Configs::ProfileManager::NewProxyEntity(type);
|
|
||||||
if (ent->outbound->DisplayType().isEmpty()) continue;
|
|
||||||
bool needFix = false;
|
|
||||||
|
|
||||||
// common
|
|
||||||
ent->outbound->name = Node2QString(proxy["name"]);
|
|
||||||
ent->outbound->server = Node2QString(proxy["server"]);
|
|
||||||
ent->outbound->server_port = Node2Int(proxy["port"]);
|
|
||||||
|
|
||||||
if (type_clash == "ss") {
|
|
||||||
auto bean = ent->ShadowSocks();
|
|
||||||
bean->method = Node2QString(proxy["cipher"]).replace("dummy", "none");
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
|
|
||||||
// UDP over TCP
|
|
||||||
if (Node2Bool(proxy["udp-over-tcp"])) {
|
|
||||||
bean->uot = Node2Int(proxy["udp-over-tcp-version"]);
|
|
||||||
if (bean->uot == 0) bean->uot = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxy.contains("plugin") && proxy.contains("plugin-opts")) {
|
|
||||||
auto plugin_n = proxy["plugin"];
|
|
||||||
auto pluginOpts_n = proxy["plugin-opts"];
|
|
||||||
QStringList ssPlugin;
|
|
||||||
auto plugin = Node2QString(plugin_n);
|
|
||||||
if (plugin == "obfs") {
|
|
||||||
ssPlugin << "obfs-local";
|
|
||||||
ssPlugin << "obfs=" + Node2QString(pluginOpts_n["mode"]);
|
|
||||||
ssPlugin << "obfs-host=" + Node2QString(pluginOpts_n["host"]);
|
|
||||||
} else if (plugin == "v2ray-plugin") {
|
|
||||||
auto mode = Node2QString(pluginOpts_n["mode"]);
|
|
||||||
auto host = Node2QString(pluginOpts_n["host"]);
|
|
||||||
auto path = Node2QString(pluginOpts_n["path"]);
|
|
||||||
ssPlugin << "v2ray-plugin";
|
|
||||||
if (!mode.isEmpty() && mode != "websocket") ssPlugin << "mode=" + mode;
|
|
||||||
if (Node2Bool(pluginOpts_n["tls"])) ssPlugin << "tls";
|
|
||||||
if (!host.isEmpty()) ssPlugin << "host=" + host;
|
|
||||||
if (!path.isEmpty()) ssPlugin << "path=" + path;
|
|
||||||
// clash only: skip-cert-verify
|
|
||||||
// clash only: headers
|
|
||||||
// clash: mux=?
|
|
||||||
}
|
|
||||||
bean->plugin = ssPlugin.join(";");
|
|
||||||
}
|
|
||||||
|
|
||||||
// sing-mux
|
|
||||||
auto smux = NodeChild(proxy, {"smux"});
|
|
||||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
|
||||||
} else if (type == "http") {
|
|
||||||
auto bean = ent->Http();
|
|
||||||
bean->username = Node2QString(proxy["username"]);
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
if (type == "http" && Node2Bool(proxy["tls"])) {
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
|
||||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
|
||||||
bean->tls->utls->enabled = true;
|
|
||||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
|
||||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
|
||||||
if (reality.is_mapping()) {
|
|
||||||
bean->tls->reality->enabled = true;
|
|
||||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
|
||||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == "socks") {
|
|
||||||
auto bean = ent->Socks();
|
|
||||||
bean->username = Node2QString(proxy["username"]);
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
} else if (type == "trojan") {
|
|
||||||
needFix = true;
|
|
||||||
auto bean = ent->Trojan();
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
bean->transport->type = Node2QString(proxy["network"], "tcp");
|
|
||||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
|
||||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
|
||||||
bean->tls->utls->enabled = true;
|
|
||||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
|
||||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sing-mux
|
|
||||||
auto smux = NodeChild(proxy, {"smux"});
|
|
||||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
|
||||||
|
|
||||||
// opts
|
|
||||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
|
||||||
if (ws.is_mapping()) {
|
|
||||||
auto headers = ws["headers"];
|
|
||||||
if (headers.is_mapping()) {
|
|
||||||
for (auto header: headers.as_map()) {
|
|
||||||
if (Node2QString(header.first).toLower() == "host") {
|
|
||||||
if (header.second.is_string())
|
|
||||||
bean->transport->host = Node2QString(header.second);
|
|
||||||
else if (header.second.is_sequence() && header.second[0].is_string())
|
|
||||||
bean->transport->host = Node2QString(header.second[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bean->transport->path = Node2QString(ws["path"]);
|
|
||||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
|
||||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
|
||||||
if (Node2Bool(ws["v2ray-http-upgrade"])) {
|
|
||||||
bean->transport->type = "httpupgrade";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
|
||||||
if (grpc.is_mapping()) {
|
|
||||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
|
||||||
if (reality.is_mapping()) {
|
|
||||||
bean->tls->reality->enabled = true;
|
|
||||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
|
||||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
|
||||||
}
|
|
||||||
} else if (type == "vless") {
|
|
||||||
needFix = true;
|
|
||||||
auto bean = ent->VLESS();
|
|
||||||
if (type == "vless") {
|
|
||||||
bean->flow = Node2QString(proxy["flow"]);
|
|
||||||
bean->uuid = Node2QString(proxy["uuid"]);
|
|
||||||
// meta packet encoding
|
|
||||||
if (Node2Bool(proxy["packet-addr"])) {
|
|
||||||
bean->packet_encoding = "packetaddr";
|
|
||||||
} else {
|
|
||||||
// For VLESS, default to use xudp
|
|
||||||
bean->packet_encoding = "xudp";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
bean->transport->type = Node2QString(proxy["network"], "tcp");
|
|
||||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
|
||||||
bean->tls->utls->enabled = true;
|
|
||||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
|
||||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
|
||||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sing-mux
|
|
||||||
auto smux = NodeChild(proxy, {"smux"});
|
|
||||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = 1;
|
|
||||||
|
|
||||||
// opts
|
|
||||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
|
||||||
if (ws.is_mapping()) {
|
|
||||||
auto headers = ws["headers"];
|
|
||||||
if (headers.is_mapping()) {
|
|
||||||
for (auto header: headers.as_map()) {
|
|
||||||
if (Node2QString(header.first).toLower() == "host") {
|
|
||||||
if (header.second.is_string())
|
|
||||||
bean->transport->host = Node2QString(header.second);
|
|
||||||
else if (header.second.is_sequence() && header.second[0].is_string())
|
|
||||||
bean->transport->host = Node2QString(header.second[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bean->transport->path = Node2QString(ws["path"]);
|
|
||||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
|
||||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
|
||||||
if (Node2Bool(ws["v2ray-http-upgrade"])) {
|
|
||||||
bean->transport->type = "httpupgrade";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
|
||||||
if (grpc.is_mapping()) {
|
|
||||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
|
||||||
if (reality.is_mapping()) {
|
|
||||||
bean->tls->reality->enabled = true;
|
|
||||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
|
||||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
|
||||||
}
|
|
||||||
} else if (type == "vmess") {
|
|
||||||
needFix = true;
|
|
||||||
auto bean = ent->VMess();
|
|
||||||
bean->uuid = Node2QString(proxy["uuid"]);
|
|
||||||
bean->alter_id = Node2Int(proxy["alterId"]);
|
|
||||||
bean->security = Node2QString(proxy["cipher"], bean->security);
|
|
||||||
bean->transport->type = Node2QString(proxy["network"], "tcp").replace("h2", "http");
|
|
||||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
if (Node2Bool(proxy["tls"])) bean->tls->enabled = true;
|
|
||||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
|
||||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
|
||||||
bean->tls->utls->enabled = true;
|
|
||||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
|
||||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sing-mux
|
|
||||||
auto smux = NodeChild(proxy, {"smux"});
|
|
||||||
if (!smux.is_null() && Node2Bool(smux["enabled"])) bean->multiplex->enabled = true;
|
|
||||||
|
|
||||||
// meta packet encoding
|
|
||||||
if (Node2Bool(proxy["xudp"])) bean->packet_encoding = "xudp";
|
|
||||||
if (Node2Bool(proxy["packet-addr"])) bean->packet_encoding = "packetaddr";
|
|
||||||
|
|
||||||
// opts
|
|
||||||
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
|
||||||
if (ws.is_mapping()) {
|
|
||||||
auto headers = ws["headers"];
|
|
||||||
if (headers.is_mapping()) {
|
|
||||||
for (auto header: headers.as_map()) {
|
|
||||||
if (Node2QString(header.first).toLower() == "host") {
|
|
||||||
bean->transport->host = Node2QString(header.second);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bean->transport->path = Node2QString(ws["path"]);
|
|
||||||
bean->transport->max_early_data = Node2Int(ws["max-early-data"]);
|
|
||||||
bean->transport->early_data_header_name = Node2QString(ws["early-data-header-name"]);
|
|
||||||
if (Node2Bool(ws["v2ray-http-upgrade"])) {
|
|
||||||
bean->transport->type = "httpupgrade";
|
|
||||||
}
|
|
||||||
// for Xray
|
|
||||||
if (Node2QString(ws["early-data-header-name"]) == "Sec-WebSocket-Protocol") {
|
|
||||||
bean->transport->path += "?ed=" + Node2QString(ws["max-early-data"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
|
||||||
if (grpc.is_mapping()) {
|
|
||||||
bean->transport->path = Node2QString(grpc["grpc-service-name"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto h2 = NodeChild(proxy, {"h2-opts", "h2-opt"});
|
|
||||||
if (h2.is_mapping()) {
|
|
||||||
auto hosts = h2["host"];
|
|
||||||
for (auto host: hosts) {
|
|
||||||
bean->transport->host = Node2QString(host);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bean->transport->path = Node2QString(h2["path"]);
|
|
||||||
}
|
|
||||||
auto tcp_http = NodeChild(proxy, {"http-opts", "http-opt"});
|
|
||||||
if (tcp_http.is_mapping()) {
|
|
||||||
bean->transport->type = "http";
|
|
||||||
auto headers = tcp_http["headers"];
|
|
||||||
if (headers.is_mapping()) {
|
|
||||||
for (auto header: headers.as_map()) {
|
|
||||||
if (Node2QString(header.first).toLower() == "host") {
|
|
||||||
bean->transport->host = Node2QString(header.second);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto paths = tcp_http["path"];
|
|
||||||
if (paths.is_string())
|
|
||||||
bean->transport->path = Node2QString(paths);
|
|
||||||
else if (paths.is_sequence() && paths[0].is_string())
|
|
||||||
bean->transport->path = Node2QString(paths[0]);
|
|
||||||
}
|
|
||||||
} else if (type == "anytls") {
|
|
||||||
needFix = true;
|
|
||||||
auto bean = ent->AnyTLS();
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->tls->insecure = true;
|
|
||||||
bean->tls->server_name = FIRST_OR_SECOND(Node2QString(proxy["sni"]), Node2QString(proxy["servername"]));
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->utls->fingerPrint = Node2QString(proxy["client-fingerprint"]);
|
|
||||||
bean->tls->utls->enabled = true;
|
|
||||||
if (bean->tls->utls->fingerPrint.isEmpty()) {
|
|
||||||
bean->tls->utls->fingerPrint = Configs::dataStore->utlsFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reality = NodeChild(proxy, {"reality-opts"});
|
|
||||||
if (reality.is_mapping()) {
|
|
||||||
bean->tls->reality->enabled = true;
|
|
||||||
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
|
|
||||||
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
|
|
||||||
}
|
|
||||||
} else if (type == "hysteria" || type == "hysteria2") {
|
|
||||||
auto bean = ent->Hysteria();
|
|
||||||
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
|
||||||
auto alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->certificate = Node2QString(proxy["ca-str"]).split("\n", Qt::SkipEmptyParts);
|
|
||||||
if (!alpn.isEmpty()) bean->tls->alpn = {alpn[0]};
|
|
||||||
bean->tls->server_name = Node2QString(proxy["sni"]);
|
|
||||||
|
|
||||||
if (type == "hysteria") {
|
|
||||||
auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"]));
|
|
||||||
auto auth = Node2QString(proxy["auth"]);
|
|
||||||
if (!auth_str.isEmpty()) {
|
|
||||||
bean->auth_type = "STRING";
|
|
||||||
bean->auth = auth_str;
|
|
||||||
}
|
|
||||||
if (!auth.isEmpty()) {
|
|
||||||
bean->auth_type = "BASE64";
|
|
||||||
bean->auth = auth;
|
|
||||||
}
|
|
||||||
bean->obfs = Node2QString(proxy["obfs"]);
|
|
||||||
|
|
||||||
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disable_mtu_discovery = true;
|
|
||||||
bean->recv_window = Node2Int(proxy["recv-window"]);
|
|
||||||
bean->recv_window_conn = Node2Int(proxy["recv-window-conn"]);
|
|
||||||
} else {
|
|
||||||
bean->obfs = Node2QString(proxy["obfs-password"]);
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto upMbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
|
|
||||||
auto downMbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
|
|
||||||
if (upMbps > 0) bean->up_mbps = upMbps;
|
|
||||||
if (downMbps > 0) bean->down_mbps = downMbps;
|
|
||||||
|
|
||||||
auto ports = Node2QString(proxy["ports"]);
|
|
||||||
if (!ports.isEmpty()) {
|
|
||||||
QStringList serverPorts;
|
|
||||||
ports.replace("/", ",");
|
|
||||||
for (const QString& port : ports.split(",", Qt::SkipEmptyParts)) {
|
|
||||||
if (port.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QString modifiedPort = port;
|
|
||||||
modifiedPort.replace("-", ":");
|
|
||||||
serverPorts.append(modifiedPort);
|
|
||||||
}
|
|
||||||
bean->server_ports = serverPorts;
|
|
||||||
}
|
|
||||||
} else if (type == "tuic") {
|
|
||||||
auto bean = ent->TUIC();
|
|
||||||
|
|
||||||
bean->uuid = Node2QString(proxy["uuid"]);
|
|
||||||
bean->password = Node2QString(proxy["password"]);
|
|
||||||
|
|
||||||
if (Node2Int(proxy["heartbeat-interval"]) != 0) {
|
|
||||||
bean->heartbeat = Int2String(Node2Int(proxy["heartbeat-interval"])) + "ms";
|
|
||||||
}
|
|
||||||
|
|
||||||
bean->udp_relay_mode = Node2QString(proxy["udp-relay-mode"], "native");
|
|
||||||
bean->congestion_control = Node2QString(proxy["congestion-controller"], "bbr");
|
|
||||||
|
|
||||||
bean->tls->enabled = true;
|
|
||||||
bean->tls->disable_sni = Node2Bool(proxy["disable-sni"]);
|
|
||||||
bean->zero_rtt_handshake = Node2Bool(proxy["reduce-rtt"]);
|
|
||||||
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
|
|
||||||
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
|
|
||||||
bean->tls->certificate = Node2QString(proxy["ca-str"]).split("\n", Qt::SkipEmptyParts);
|
|
||||||
bean->tls->server_name = Node2QString(proxy["sni"]);
|
|
||||||
|
|
||||||
if (Node2Bool(proxy["udp-over-stream"])) bean->udp_over_stream = true;
|
|
||||||
|
|
||||||
if (!Node2QString(proxy["ip"]).isEmpty()) {
|
|
||||||
if (bean->tls->server_name.isEmpty()) bean->tls->server_name = bean->server;
|
|
||||||
bean->server = Node2QString(proxy["ip"]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (needFix) RawUpdater_FixEnt(ent); TODO
|
|
||||||
updated_order += ent;
|
|
||||||
}
|
|
||||||
} catch (const fkyaml::exception &ex) {
|
|
||||||
runOnUiThread([=] {
|
|
||||||
MessageBoxWarning("YAML Exception", ex.what());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在新的 thread 运行
|
// 在新的 thread 运行
|
||||||
void GroupUpdater::AsyncUpdate(const QString &str, int _sub_gid, const std::function<void()> &finish) {
|
void GroupUpdater::AsyncUpdate(const QString &str, int _sub_gid, const std::function<void()> &finish) {
|
||||||
auto content = str.trimmed();
|
auto content = str.trimmed();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user