diff --git a/CMakeLists.txt b/CMakeLists.txt index 3769f6e..00cbb20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/include/configs/proxy/QUICBean.hpp b/include/configs/proxy/QUICBean.hpp index 18e7669..1f58cc9 100644 --- a/include/configs/proxy/QUICBean.hpp +++ b/include/configs/proxy/QUICBean.hpp @@ -117,6 +117,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt \ No newline at end of file diff --git a/include/configs/proxy/SSHBean.h b/include/configs/proxy/SSHBean.h index c663004..1c452e4 100644 --- a/include/configs/proxy/SSHBean.h +++ b/include/configs/proxy/SSHBean.h @@ -31,6 +31,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } diff --git a/include/configs/proxy/ShadowSocksBean.hpp b/include/configs/proxy/ShadowSocksBean.hpp index 607c6d8..be53149 100644 --- a/include/configs/proxy/ShadowSocksBean.hpp +++ b/include/configs/proxy/ShadowSocksBean.hpp @@ -32,6 +32,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt diff --git a/include/configs/proxy/SocksHttpBean.hpp b/include/configs/proxy/SocksHttpBean.hpp index 275b53b..7d21b2a 100644 --- a/include/configs/proxy/SocksHttpBean.hpp +++ b/include/configs/proxy/SocksHttpBean.hpp @@ -30,6 +30,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt diff --git a/include/configs/proxy/TrojanVLESSBean.hpp b/include/configs/proxy/TrojanVLESSBean.hpp index 2e49d0a..788ea01 100644 --- a/include/configs/proxy/TrojanVLESSBean.hpp +++ b/include/configs/proxy/TrojanVLESSBean.hpp @@ -29,6 +29,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt \ No newline at end of file diff --git a/include/configs/proxy/VMessBean.hpp b/include/configs/proxy/VMessBean.hpp index cfaa232..8657b06 100644 --- a/include/configs/proxy/VMessBean.hpp +++ b/include/configs/proxy/VMessBean.hpp @@ -26,6 +26,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt diff --git a/include/configs/proxy/WireguardBean.h b/include/configs/proxy/WireguardBean.h index a7aed3d..5baf585 100644 --- a/include/configs/proxy/WireguardBean.h +++ b/include/configs/proxy/WireguardBean.h @@ -44,6 +44,8 @@ namespace NekoGui_fmt { bool TryParseLink(const QString &link); + bool TryParseJson(const QJsonObject &obj); + QString ToShareLink() override; }; } // namespace NekoGui_fmt diff --git a/include/configs/sub/GroupUpdater.hpp b/include/configs/sub/GroupUpdater.hpp index 7ab9389..a3aa859 100644 --- a/include/configs/sub/GroupUpdater.hpp +++ b/include/configs/sub/GroupUpdater.hpp @@ -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> updated_order; diff --git a/src/configs/proxy/Json2Bean.cpp b/src/configs/proxy/Json2Bean.cpp new file mode 100644 index 0000000..fb717bd --- /dev/null +++ b/src/configs/proxy/Json2Bean.cpp @@ -0,0 +1,226 @@ + + +#include +#include +#include +#include +#include +#include + +#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; + } + +} diff --git a/src/configs/sub/GroupUpdater.cpp b/src/configs/sub/GroupUpdater.cpp index 77e8b90..f1e7636 100644 --- a/src/configs/sub/GroupUpdater.cpp +++ b/src/configs/sub/GroupUpdater.cpp @@ -6,6 +6,7 @@ #include #include +#include #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 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 = "") {