mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 05:30:06 +08:00
Implement sing-box json subsciption format
This commit is contained in:
parent
20998445ad
commit
387cc409ee
@ -208,6 +208,7 @@ set(PROJECT_SOURCES
|
||||
include/ui/mainwindow_interface.h
|
||||
include/stats/connections/connectionLister.hpp
|
||||
src/stats/connectionLister/connectionLister.cpp
|
||||
src/configs/proxy/Json2Bean.cpp
|
||||
)
|
||||
|
||||
# Qt exe
|
||||
|
||||
@ -117,6 +117,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
@ -31,6 +31,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
}
|
||||
|
||||
@ -32,6 +32,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -30,6 +30,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -29,6 +29,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
@ -26,6 +26,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -44,6 +44,8 @@ namespace NekoGui_fmt {
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -9,6 +9,8 @@ namespace NekoGui_sub {
|
||||
|
||||
void update(const QString &str, bool needParse);
|
||||
|
||||
void updateSingBox(const QString &str);
|
||||
|
||||
int gid_add_to = -1;
|
||||
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> updated_order;
|
||||
|
||||
226
src/configs/proxy/Json2Bean.cpp
Normal file
226
src/configs/proxy/Json2Bean.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
|
||||
|
||||
#include <include/configs/proxy/QUICBean.hpp>
|
||||
#include <include/configs/proxy/ShadowSocksBean.hpp>
|
||||
#include <include/configs/proxy/SocksHttpBean.hpp>
|
||||
#include <include/configs/proxy/TrojanVLESSBean.hpp>
|
||||
#include <include/configs/proxy/VMessBean.hpp>
|
||||
#include <include/configs/proxy/WireguardBean.h>
|
||||
|
||||
#include "include/configs/proxy/SSHBean.h"
|
||||
|
||||
namespace NekoGui_fmt
|
||||
{
|
||||
bool QUICBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
auto type = obj["type"].toString();
|
||||
if (type == "hysteria")
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
proxy_type = proxy_Hysteria;
|
||||
|
||||
serverAddress = obj["server"].isString() ? obj["server"].toString() : "127.0.0.1";
|
||||
serverPort = obj["server_port"].isDouble() ? obj["port"].toInt() : 1080;
|
||||
hop_interval = obj["hop_interval"].toString();
|
||||
uploadMbps = obj["up_mbps"].isDouble() ? obj["up_mbps"].toInt() : 0;
|
||||
downloadMbps = obj["down_mbps"].isDouble() ? obj["down_mbps"].toInt() : 0;
|
||||
obfsPassword = obj["obfs"].toString();
|
||||
authPayloadType = obj["auth"].isString() ? hysteria_auth_base64 : hysteria_auth_string;
|
||||
authPayload = obj["auth"].isString() ? obj["auth"].toString() : obj["auth_str"].toString();
|
||||
disableMtuDiscovery = obj["disable_mtu_discovery"].toBool();
|
||||
connectionReceiveWindow = obj["recv_window_conn"].toInt();
|
||||
streamReceiveWindow = obj["recv_window"].toInt();
|
||||
alpn = obj["tls"].toObject()["alpn"].isArray() ? QJsonArray2QListString(obj["tls"].toObject()["alpn"].toArray()).join(",") : obj["tls"].toObject()["alpn"].toString();
|
||||
sni = obj["tls"].toObject()["server_name"].toString();
|
||||
disableSni = obj["tls"].toObject()["disable_sni"].toBool();
|
||||
allowInsecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
return true;
|
||||
}
|
||||
if (type == "hysteria2")
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
proxy_type = proxy_Hysteria2;
|
||||
|
||||
serverAddress = obj["server"].isString() ? obj["server"].toString() : "127.0.0.1";
|
||||
serverPort = obj["server_port"].isDouble() ? obj["port"].toInt() : 1080;
|
||||
serverPorts = obj["server_ports"].isArray() ? QJsonArray2QListString(obj["server_ports"].toArray()) : QStringList();
|
||||
hop_interval = obj["hop_interval"].toString();
|
||||
uploadMbps = obj["up_mbps"].isDouble() ? obj["up_mbps"].toInt() : 0;
|
||||
downloadMbps = obj["down_mbps"].isDouble() ? obj["down_mbps"].toInt() : 0;
|
||||
password = obj["password"].toString();
|
||||
obfsPassword = obj["obfs"].toObject()["password"].toString();
|
||||
alpn = obj["tls"].toObject()["alpn"].isArray() ? QJsonArray2QListString(obj["tls"].toObject()["alpn"].toArray()).join(",") : obj["tls"].toObject()["alpn"].toString();
|
||||
sni = obj["tls"].toObject()["server_name"].toString();
|
||||
disableSni = obj["tls"].toObject()["disable_sni"].toBool();
|
||||
allowInsecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
return true;
|
||||
}
|
||||
if (type == "tuic")
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
proxy_type = proxy_TUIC;
|
||||
|
||||
serverAddress = obj["server"].isString() ? obj["server"].toString() : "127.0.0.1";
|
||||
serverPort = obj["server_port"].isDouble() ? obj["port"].toInt() : 1080;
|
||||
uuid = obj["uuid"].toString();
|
||||
password = obj["password"].toString();
|
||||
congestionControl = obj["congestion_control"].toString();
|
||||
udpRelayMode = obj["udp_relay_mode"].toString();
|
||||
uos = obj["udp_over_stream"].toBool();
|
||||
zeroRttHandshake = obj["zero_rtt_handshake"].toBool();
|
||||
heartbeat = obj["heartbeat"].toString();
|
||||
alpn = obj["tls"].toObject()["alpn"].isArray() ? QJsonArray2QListString(obj["tls"].toObject()["alpn"].toArray()).join(",") : obj["tls"].toObject()["alpn"].toString();
|
||||
sni = obj["tls"].toObject()["server_name"].toString();
|
||||
disableSni = obj["tls"].toObject()["disable_sni"].toBool();
|
||||
allowInsecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShadowSocksBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
serverAddress = obj["server"].toString();
|
||||
serverPort = obj["server_port"].toInt();
|
||||
method = obj["method"].toString();
|
||||
password = obj["password"].toString();
|
||||
plugin = obj["plugin"].toString();
|
||||
uot = obj["udp_over_tcp"].toBool();
|
||||
mux_state = obj["multiplex"].isObject() ? (obj["multiplex"].toObject()["enabled"].toBool() ? 1 : 2) : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocksHttpBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
socks_http_type = obj["type"] == "http" ? type_HTTP : type_Socks5;
|
||||
serverAddress = obj["server"].toString();
|
||||
serverPort = obj["server_port"].toInt();
|
||||
username = obj["username"].toString();
|
||||
password = obj["password"].toString();
|
||||
stream->security = obj["tls"].isObject() ? "tls" : "";
|
||||
stream->sni = obj["tls"].toObject()["server_name"].toString();
|
||||
stream->allow_insecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SSHBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
serverAddress = obj["server"].toString();
|
||||
serverPort = obj["server_port"].toInt();
|
||||
user = obj["user"].toString();
|
||||
password = obj["password"].toString();
|
||||
privateKey = obj["private_key"].toString();
|
||||
privateKeyPath = obj["private_key_path"].toString();
|
||||
privateKeyPass = obj["private_key_passphrase"].toString();
|
||||
hostKey = QJsonArray2QListString(obj["host_key"].toArray());
|
||||
hostKeyAlgs = QJsonArray2QListString(obj["host_key_algorithms"].toArray());
|
||||
clientVersion = obj["client_version"].toString();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrojanVLESSBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
proxy_type = obj["type"].toString() == "trojan" ? proxy_Trojan : proxy_VLESS;
|
||||
serverAddress = obj["server"].toString();
|
||||
serverPort = obj["server_port"].toInt();
|
||||
password = obj["password"].toString();
|
||||
if (proxy_type == proxy_VLESS) password = obj["uuid"].toString();
|
||||
flow = obj["flow"].toString();
|
||||
stream->packet_encoding = obj["packet_encoding"].toString();
|
||||
mux_state = obj["multiplex"].isObject() ? (obj["multiplex"].toObject()["enabled"].toBool() ? 1 : 2) : 0;
|
||||
stream->security = obj["tls"].isObject() ? "tls" : "";
|
||||
if (obj["tls"].toObject()["reality"].toObject()["enabled"].toBool())
|
||||
{
|
||||
stream->security = "reality";
|
||||
}
|
||||
stream->reality_pbk = obj["tls"].toObject()["reality"].toObject()["public_key"].toString();
|
||||
stream->reality_sid = obj["tls"].toObject()["reality"].toObject()["short_id"].toString();
|
||||
stream->utlsFingerprint = obj["tls"].toObject()["utls"].toObject()["fingerprint"].toString();
|
||||
stream->sni = obj["tls"].toObject()["server_name"].toString();
|
||||
stream->alpn = obj["tls"].toObject()["alpn"].isArray() ? QJsonArray2QListString(obj["tls"].toObject()["alpn"].toArray()).join(",") : obj["tls"].toObject()["alpn"].toString();
|
||||
stream->allow_insecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
stream->network = obj["transport"].toObject()["type"].toString();
|
||||
if (stream->network == "ws")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
} else if (stream->network == "http")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
stream->method = obj["transport"].toObject()["method"].toString();
|
||||
} else if (stream->network == "httpupgrade")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
} else if (stream->network == "grpc")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["service_name"].toString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VMessBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
serverAddress = obj["server"].toString();
|
||||
serverPort = obj["server_port"].toInt();
|
||||
uuid = obj["uuid"].toString();
|
||||
security = obj["security"].toString();
|
||||
aid = obj["alter_id"].toInt();
|
||||
stream->packet_encoding = obj["packet_encoding"].toString();
|
||||
mux_state = obj["multiplex"].isObject() ? (obj["multiplex"].toObject()["enabled"].toBool() ? 1 : 2) : 0;
|
||||
stream->security = obj["tls"].isObject() ? "tls" : "";
|
||||
if (obj["tls"].toObject()["reality"].toObject()["enabled"].toBool())
|
||||
{
|
||||
stream->security = "reality";
|
||||
}
|
||||
stream->reality_pbk = obj["tls"].toObject()["reality"].toObject()["public_key"].toString();
|
||||
stream->reality_sid = obj["tls"].toObject()["reality"].toObject()["short_id"].toString();
|
||||
stream->utlsFingerprint = obj["tls"].toObject()["utls"].toObject()["fingerprint"].toString();
|
||||
stream->sni = obj["tls"].toObject()["server_name"].toString();
|
||||
stream->alpn = obj["tls"].toObject()["alpn"].isArray() ? QJsonArray2QListString(obj["tls"].toObject()["alpn"].toArray()).join(",") : obj["tls"].toObject()["alpn"].toString();
|
||||
stream->allow_insecure = obj["tls"].toObject()["insecure"].toBool();
|
||||
stream->network = obj["transport"].toObject()["type"].toString();
|
||||
if (stream->network == "ws")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
} else if (stream->network == "http")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
stream->method = obj["transport"].toObject()["method"].toString();
|
||||
} else if (stream->network == "httpupgrade")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["path"].toString();
|
||||
stream->host = obj["transport"].toObject()["host"].toString();
|
||||
} else if (stream->network == "grpc")
|
||||
{
|
||||
stream->path = obj["transport"].toObject()["service_name"].toString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WireguardBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
name = obj["tag"].toString();
|
||||
auto peers = obj["peers"].toArray();
|
||||
if (peers.empty()) return false;
|
||||
serverAddress = peers[0].toObject()["address"].toString();
|
||||
serverPort = peers[0].toObject()["server_port"].toInt();
|
||||
publicKey = peers[0].toObject()["public_key"].toString();
|
||||
reserved = QJsonArray2QListInt(peers[0].toObject()["reserved"].toArray());
|
||||
workerCount = obj["workers"].toInt();
|
||||
privateKey = obj["private_key"].toString();
|
||||
localAddress = QJsonArray2QListString(obj["address"].toArray());
|
||||
MTU = obj["mtu"].toInt();
|
||||
useSystemInterface = obj["system"].toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QUrlQuery>
|
||||
#include <absl/strings/internal/str_format/extension.h>
|
||||
|
||||
#ifndef NKR_NO_YAML
|
||||
|
||||
@ -84,6 +85,13 @@ namespace NekoGui_sub {
|
||||
return;
|
||||
}
|
||||
|
||||
// SingBox
|
||||
if (str.contains("outbounds") || str.contains("endpoints"))
|
||||
{
|
||||
updateSingBox(str);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multi line
|
||||
if (str.count("\n") > 0 && needParse) {
|
||||
auto list = Disect(str);
|
||||
@ -222,6 +230,106 @@ namespace NekoGui_sub {
|
||||
updated_order += ent;
|
||||
}
|
||||
|
||||
void RawUpdater::updateSingBox(const QString& str)
|
||||
{
|
||||
auto json = QString2QJsonObject(str);
|
||||
auto outbounds = json["outbounds"].toArray() + json["endpoints"].toArray();
|
||||
|
||||
for (auto o : outbounds)
|
||||
{
|
||||
auto out = o.toObject();
|
||||
if (out.isEmpty())
|
||||
{
|
||||
MW_show_log("invalid outbound: " + o.toVariant().toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<NekoGui::ProxyEntity> ent;
|
||||
|
||||
// SOCKS
|
||||
if (out["type"] == "socks") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("socks");
|
||||
auto ok = ent->SocksHTTPBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// HTTP
|
||||
if (out["type"] == "http") {
|
||||
auto ok = ent->SocksHTTPBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// ShadowSocks
|
||||
if (out["type"] == "shadowsocks") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("shadowsocks");
|
||||
auto ok = ent->ShadowSocksBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// VMess
|
||||
if (out["type"] == "vmess") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("vmess");
|
||||
auto ok = ent->VMessBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// VLESS
|
||||
if (out["type"] == "vless") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("vless");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Trojan
|
||||
if (out["type"] == "trojan") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("trojan");
|
||||
auto ok = ent->TrojanVLESSBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Hysteria1
|
||||
if (out["type"] == "hysteria") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("hysteria");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Hysteria2
|
||||
if (out["type"] == "hysteria2") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("hysteria2");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// TUIC
|
||||
if (out["type"] == "tuic") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("tuic");
|
||||
auto ok = ent->QUICBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// Wireguard
|
||||
if (out["type"] == "wireguard") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("wireguard");
|
||||
auto ok = ent->WireguardBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
// SSH
|
||||
if (out["type"] == "ssh") {
|
||||
ent = NekoGui::ProfileManager::NewProxyEntity("ssh");
|
||||
auto ok = ent->SSHBean()->TryParseJson(out);
|
||||
if (!ok) continue;
|
||||
}
|
||||
|
||||
if (ent == nullptr) continue;
|
||||
|
||||
NekoGui::profileManager->AddProfile(ent, gid_add_to);
|
||||
updated_order += ent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef NKR_NO_YAML
|
||||
|
||||
QString Node2QString(const YAML::Node &n, const QString &def = "") {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user