From d8bf56a1ada9255c256b3ee240167a2d77a9bc85 Mon Sep 17 00:00:00 2001 From: HystericalDragon <138737572+HystericalDragon@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:16:44 +0800 Subject: [PATCH] feat: add TUIC protocol (#781) --------- Co-authored-by: 2022-blake3-chacha8-poly1305 <139959885+xchacha20-ietf-poly1305@users.noreply.github.com> Co-authored-by: arm64v8a <48624112+arm64v8a@users.noreply.github.com> --- CMakeLists.txt | 6 +- README.md | 1 + db/Database.cpp | 4 +- db/ProxyEntity.hpp | 6 +- fmt/Bean2CoreObj_box.cpp | 39 +- fmt/Bean2External.cpp | 180 ++++++---- fmt/Bean2Link.cpp | 47 +-- fmt/HysteriaBean.hpp | 80 ----- fmt/Link2Bean.cpp | 62 ++-- fmt/QUICBean.hpp | 103 ++++++ fmt/includes.h | 2 +- go/cmd/nekobox_core/go.mod | 9 +- go/cmd/nekobox_core/go.sum | 24 +- libs/get_source_env.sh | 2 +- sub/GroupUpdater.cpp | 44 ++- translations/fa_IR.ts | 62 +++- translations/ru_RU.ts | 62 +++- translations/zh_CN.ts | 60 +++- ui/dialog_basic_settings.cpp | 1 + ui/edit/dialog_edit_profile.cpp | 9 +- ui/edit/edit_hysteria.cpp | 72 ---- ui/edit/edit_hysteria.ui | 242 ------------- ui/edit/edit_quic.cpp | 132 +++++++ ui/edit/{edit_hysteria.h => edit_quic.h} | 12 +- ui/edit/edit_quic.ui | 436 +++++++++++++++++++++++ 25 files changed, 1087 insertions(+), 610 deletions(-) delete mode 100644 fmt/HysteriaBean.hpp create mode 100644 fmt/QUICBean.hpp delete mode 100644 ui/edit/edit_hysteria.cpp delete mode 100644 ui/edit/edit_hysteria.ui create mode 100644 ui/edit/edit_quic.cpp rename ui/edit/{edit_hysteria.h => edit_quic.h} (68%) create mode 100644 ui/edit/edit_quic.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index f47f09c..8b10d67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,9 +213,9 @@ set(PROJECT_SOURCES ui/edit/edit_naive.cpp ui/edit/edit_naive.ui - ui/edit/edit_hysteria.h - ui/edit/edit_hysteria.cpp - ui/edit/edit_hysteria.ui + ui/edit/edit_quic.h + ui/edit/edit_quic.cpp + ui/edit/edit_quic.ui ui/edit/edit_custom.h ui/edit/edit_custom.cpp diff --git a/README.md b/README.md index 9fd948a..9232683 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ https://matsuridayo.github.io * VMess * VLESS * Trojan +* TUIC ( sing-box ) * NaïveProxy ( Custom Core ) * Hysteria ( Custom Core or sing-box ) * Custom Outbound diff --git a/db/Database.cpp b/db/Database.cpp index d0e7f7b..dea8698 100644 --- a/db/Database.cpp +++ b/db/Database.cpp @@ -185,7 +185,9 @@ namespace NekoGui { } else if (type == "naive") { bean = new NekoGui_fmt::NaiveBean(); } else if (type == "hysteria") { - bean = new NekoGui_fmt::HysteriaBean(); + bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria); + } else if (type == "tuic") { + bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_TUIC); } else if (type == "custom") { bean = new NekoGui_fmt::CustomBean(); } else { diff --git a/db/ProxyEntity.hpp b/db/ProxyEntity.hpp index 81dec91..f0cded9 100644 --- a/db/ProxyEntity.hpp +++ b/db/ProxyEntity.hpp @@ -15,7 +15,7 @@ namespace NekoGui_fmt { class NaiveBean; - class HysteriaBean; + class QUICBean; class CustomBean; @@ -65,8 +65,8 @@ namespace NekoGui { return (NekoGui_fmt::NaiveBean *) bean.get(); }; - [[nodiscard]] NekoGui_fmt::HysteriaBean *HysteriaBean() const { - return (NekoGui_fmt::HysteriaBean *) bean.get(); + [[nodiscard]] NekoGui_fmt::QUICBean *QUICBean() const { + return (NekoGui_fmt::QUICBean *) bean.get(); }; [[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const { diff --git a/fmt/Bean2CoreObj_box.cpp b/fmt/Bean2CoreObj_box.cpp index 3771271..27e4403 100644 --- a/fmt/Bean2CoreObj_box.cpp +++ b/fmt/Bean2CoreObj_box.cpp @@ -169,36 +169,47 @@ namespace NekoGui_fmt { return result; } - CoreObjOutboundBuildResult HysteriaBean::BuildCoreObjSingBox() { + CoreObjOutboundBuildResult QUICBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject coreTlsObj{ {"enabled", true}, + {"disable_sni", disableSni}, {"insecure", allowInsecure}, {"certificate", caText.trimmed()}, {"server_name", sni}, }; - if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QJsonArray{alpn}; + if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QList2QJsonArray(alpn.split(",")); - QJsonObject coreHysteriaObj{ - {"type", "hysteria"}, + QJsonObject outbound{ {"server", serverAddress}, {"server_port", serverPort}, - {"obfs", obfsPassword}, - {"disable_mtu_discovery", disableMtuDiscovery}, - {"recv_window", streamReceiveWindow}, - {"recv_window_conn", connectionReceiveWindow}, - {"up_mbps", uploadMbps}, - {"down_mbps", downloadMbps}, {"tls", coreTlsObj}, }; - if (!hopPort.trimmed().isEmpty()) coreHysteriaObj["hop_ports"] = hopPort; + if (proxy_type == proxy_Hysteria) { + outbound["type"] = "hysteria"; + outbound["obfs"] = obfsPassword; + outbound["disable_mtu_discovery"] = disableMtuDiscovery; + outbound["recv_window"] = streamReceiveWindow; + outbound["recv_window_conn"] = connectionReceiveWindow; + outbound["up_mbps"] = uploadMbps; + outbound["down_mbps"] = downloadMbps; - if (authPayloadType == hysteria_auth_base64) coreHysteriaObj["auth"] = authPayload; - if (authPayloadType == hysteria_auth_string) coreHysteriaObj["auth_str"] = authPayload; + if (!hopPort.trimmed().isEmpty()) outbound["hop_ports"] = hopPort; + if (authPayloadType == hysteria_auth_base64) outbound["auth"] = authPayload; + if (authPayloadType == hysteria_auth_string) outbound["auth_str"] = authPayload; + } else if (proxy_type == proxy_TUIC) { + outbound["type"] = "tuic"; + outbound["uuid"] = uuid; + outbound["password"] = password; + outbound["congestion_control"] = congestionControl; + outbound["udp_relay_mode"] = udpRelayMode; + outbound["zero_rtt_handshake"] = zeroRttHandshake; + if (!heartbeat.trimmed().isEmpty()) outbound["heartbeat"] = heartbeat; + } - result.outbound = coreHysteriaObj; + result.outbound = outbound; return result; } diff --git a/fmt/Bean2External.cpp b/fmt/Bean2External.cpp index d424039..9eb89e5 100644 --- a/fmt/Bean2External.cpp +++ b/fmt/Bean2External.cpp @@ -35,29 +35,41 @@ namespace NekoGui_fmt { return 1; } - int HysteriaBean::NeedExternal(bool isFirstProfile) { + int QUICBean::NeedExternal(bool isFirstProfile) { auto hysteriaCore = [=] { if (isFirstProfile) { - if (NekoGui::dataStore->spmode_vpn && protocol != hysteria_protocol_facktcp && hopPort.trimmed().isEmpty()) { + if (NekoGui::dataStore->spmode_vpn && hyProtocol != hysteria_protocol_facktcp && hopPort.trimmed().isEmpty()) { return 1; } return 2; } else { - if (protocol == hysteria_protocol_facktcp || !hopPort.trimmed().isEmpty()) { + if (hyProtocol == hysteria_protocol_facktcp || !hopPort.trimmed().isEmpty()) { return -1; } } return 1; }; + auto tuicCore = [=] { + if (isFirstProfile) { + if (NekoGui::dataStore->spmode_vpn) { + return 1; + } + return 2; + } + return 1; + }; + if (IS_NEKO_BOX) { - if (protocol == hysteria_protocol_udp) { + if (proxy_type == proxy_TUIC || hyProtocol == hysteria_protocol_udp) { // sing-box support return 0; } else { // hysteria core support return hysteriaCore(); } + } else if (proxy_type == proxy_TUIC) { + return tuicCore(); } else { return hysteriaCore(); } @@ -104,65 +116,112 @@ namespace NekoGui_fmt { return result; } - ExternalBuildResult HysteriaBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { - ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria")}; + ExternalBuildResult QUICBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { + if (proxy_type == proxy_TUIC) { + ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("tuic")}; - QJsonObject config; + QJsonObject relay; - // determine server format - auto is_direct = external_stat == 2; - auto sniGen = sni; - if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress; + relay["uuid"] = uuid; + relay["password"] = password; + relay["udp_relay_mode"] = udpRelayMode; + relay["congestion_control"] = congestionControl; + relay["zero_rtt_handshake"] = zeroRttHandshake; + relay["disable_sni"] = disableSni; + if (!heartbeat.trimmed().isEmpty()) relay["heartbeat"] = heartbeat; + if (!alpn.trimmed().isEmpty()) relay["alpn"] = QList2QJsonArray(alpn.split(",")); - auto server = serverAddress; - if (!hopPort.trimmed().isEmpty()) { - server = WrapIPV6Host(server) + ":" + hopPort; - } else { - server = WrapIPV6Host(server) + ":" + Int2String(serverPort); + if (!caText.trimmed().isEmpty()) { + WriteTempFile("tuic_" + GetRandomString(10) + ".crt", caText.toUtf8()); + QJsonArray certificate; + certificate.append(TempFile); + relay["certificates"] = certificate; + } + + // The most confused part of TUIC...... + if (serverAddress == sni) { + relay["server"] = serverAddress + ":" + Int2String(serverPort); + } else { + relay["server"] = sni + ":" + Int2String(serverPort); + relay["ip"] = serverAddress; + } + + QJsonObject local{ + {"server", "127.0.0.1:" + Int2String(socks_port)}, + }; + + QJsonObject config{ + {"relay", relay}, + {"local", local}, + }; + + // + + result.config_export = QJsonObject2QString(config, false); + WriteTempFile("tuic_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); + result.arguments = QStringList{"-c", TempFile}; + + return result; + } else { // Hysteria + ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria")}; + + QJsonObject config; + + // determine server format + auto is_direct = external_stat == 2; + auto sniGen = sni; + if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress; + + auto server = serverAddress; + if (!hopPort.trimmed().isEmpty()) { + server = WrapIPV6Host(server) + ":" + hopPort; + } else { + server = WrapIPV6Host(server) + ":" + Int2String(serverPort); + } + config["server"] = is_direct ? server : "127.0.0.1:" + Int2String(mapping_port); + + // listen + config["socks5"] = QJsonObject{ + {"listen", "127.0.0.1:" + Int2String(socks_port)}, + }; + + // misc + + config["retry"] = 5; + config["fast_open"] = true; + config["lazy_start"] = true; + config["obfs"] = obfsPassword; + config["up_mbps"] = uploadMbps; + config["down_mbps"] = downloadMbps; + + if (authPayloadType == hysteria_auth_base64) config["auth"] = authPayload; + if (authPayloadType == hysteria_auth_string) config["auth_str"] = authPayload; + + if (hyProtocol == hysteria_protocol_facktcp) config["protocol"] = "faketcp"; + if (hyProtocol == hysteria_protocol_wechat_video) config["protocol"] = "wechat-video"; + + if (!sniGen.isEmpty()) config["server_name"] = sniGen; + if (!alpn.isEmpty()) config["alpn"] = alpn; + + if (!caText.trimmed().isEmpty()) { + WriteTempFile("hysteria_" + GetRandomString(10) + ".crt", caText.toUtf8()); + config["ca"] = TempFile; + } + + if (allowInsecure) config["insecure"] = true; + if (streamReceiveWindow > 0) config["recv_window_conn"] = streamReceiveWindow; + if (connectionReceiveWindow > 0) config["recv_window"] = connectionReceiveWindow; + if (disableMtuDiscovery) config["disable_mtu_discovery"] = true; + config["hop_interval"] = hopInterval; + + // + + result.config_export = QJsonObject2QString(config, false); + WriteTempFile("hysteria_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); + result.arguments = QStringList{"--no-check", "-c", TempFile}; + + return result; } - config["server"] = is_direct ? server : "127.0.0.1:" + Int2String(mapping_port); - - // listen - config["socks5"] = QJsonObject{ - {"listen", "127.0.0.1:" + Int2String(socks_port)}, - }; - - // misc - - config["retry"] = 5; - config["fast_open"] = true; - config["lazy_start"] = true; - config["obfs"] = obfsPassword; - config["up_mbps"] = uploadMbps; - config["down_mbps"] = downloadMbps; - - if (authPayloadType == hysteria_auth_base64) config["auth"] = authPayload; - if (authPayloadType == hysteria_auth_string) config["auth_str"] = authPayload; - - if (protocol == hysteria_protocol_facktcp) config["protocol"] = "faketcp"; - if (protocol == hysteria_protocol_wechat_video) config["protocol"] = "wechat-video"; - - if (!sniGen.isEmpty()) config["server_name"] = sniGen; - if (!alpn.isEmpty()) config["alpn"] = alpn; - - if (!caText.trimmed().isEmpty()) { - WriteTempFile("hysteria_" + GetRandomString(10) + ".crt", caText.toUtf8()); - config["ca"] = TempFile; - } - - if (allowInsecure) config["insecure"] = true; - if (streamReceiveWindow > 0) config["recv_window_conn"] = streamReceiveWindow; - if (connectionReceiveWindow > 0) config["recv_window"] = connectionReceiveWindow; - if (disableMtuDiscovery) config["disable_mtu_discovery"] = true; - config["hop_interval"] = hopInterval; - - // - - result.config_export = QJsonObject2QString(config, false); - WriteTempFile("hysteria_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); - result.arguments = QStringList{"--no-check", "-c", TempFile}; - - return result; } ExternalBuildResult CustomBean::BuildExternal(int mapping_port, int socks_port, int external_stat) { @@ -206,4 +265,5 @@ namespace NekoGui_fmt { return result; } -} // namespace NekoGui_fmt \ No newline at end of file + +} // namespace NekoGui_fmt diff --git a/fmt/Bean2Link.cpp b/fmt/Bean2Link.cpp index 38518ac..19257aa 100644 --- a/fmt/Bean2Link.cpp +++ b/fmt/Bean2Link.cpp @@ -1,3 +1,4 @@ +#include "QUICBean.hpp" #include "db/ProxyEntity.hpp" #include "fmt/includes.h" @@ -127,29 +128,33 @@ namespace NekoGui_fmt { return url.toString(QUrl::FullyEncoded); } - QString HysteriaBean::ToShareLink() { + QString QUICBean::ToShareLink() { QUrl url; - url.setScheme("hysteria"); - url.setHost(serverAddress); - url.setPort(serverPort); - QUrlQuery q; - q.addQueryItem("upmbps", Int2String(uploadMbps)); - q.addQueryItem("downmbps", Int2String(downloadMbps)); - if (!obfsPassword.isEmpty()) { - q.addQueryItem("obfs", "xplus"); - q.addQueryItem("obfsParam", obfsPassword); + if (proxy_type == proxy_Hysteria) { + url.setScheme("hysteria"); + url.setHost(serverAddress); + url.setPort(serverPort); + QUrlQuery q; + q.addQueryItem("upmbps", Int2String(uploadMbps)); + q.addQueryItem("downmbps", Int2String(downloadMbps)); + if (!obfsPassword.isEmpty()) { + q.addQueryItem("obfs", "xplus"); + q.addQueryItem("obfsParam", obfsPassword); + } + if (authPayloadType == hysteria_auth_string) q.addQueryItem("auth", authPayload); + if (hyProtocol == hysteria_protocol_facktcp) q.addQueryItem("protocol", "faketcp"); + if (hyProtocol == hysteria_protocol_wechat_video) q.addQueryItem("protocol", "wechat-video"); + if (!hopPort.trimmed().isEmpty()) q.addQueryItem("mport", hopPort); + if (allowInsecure) q.addQueryItem("insecure", "1"); + if (!sni.isEmpty()) q.addQueryItem("peer", sni); + if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn); + if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow)); + if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow)); + if (!q.isEmpty()) url.setQuery(q); + if (!name.isEmpty()) url.setFragment(name); + } else if (proxy_type == proxy_TUIC) { + // TODO std link } - if (authPayloadType == hysteria_auth_string) q.addQueryItem("auth", authPayload); - if (protocol == hysteria_protocol_facktcp) q.addQueryItem("protocol", "faketcp"); - if (protocol == hysteria_protocol_wechat_video) q.addQueryItem("protocol", "wechat-video"); - if (!hopPort.trimmed().isEmpty()) q.addQueryItem("mport", hopPort); - if (allowInsecure) q.addQueryItem("insecure", "1"); - if (!sni.isEmpty()) q.addQueryItem("peer", sni); - if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn); - if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow)); - if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow)); - if (!q.isEmpty()) url.setQuery(q); - if (!name.isEmpty()) url.setFragment(name); return url.toString(QUrl::FullyEncoded); } diff --git a/fmt/HysteriaBean.hpp b/fmt/HysteriaBean.hpp deleted file mode 100644 index 9695894..0000000 --- a/fmt/HysteriaBean.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "fmt/AbstractBean.hpp" - -namespace NekoGui_fmt { - class HysteriaBean : public AbstractBean { - public: - static constexpr int hysteria_protocol_udp = 0; - static constexpr int hysteria_protocol_facktcp = 1; - static constexpr int hysteria_protocol_wechat_video = 2; - - int protocol = 0; - - // - - static constexpr int hysteria_auth_none = 0; - static constexpr int hysteria_auth_string = 1; - static constexpr int hysteria_auth_base64 = 2; - - int authPayloadType = 0; - QString authPayload = ""; - QString obfsPassword = ""; - - // - - int uploadMbps = 100; - int downloadMbps = 100; - - qint64 streamReceiveWindow = 0; - qint64 connectionReceiveWindow = 0; - bool disableMtuDiscovery = false; - - bool allowInsecure = false; - QString sni = ""; - QString alpn = ""; // only 1 - QString caText = ""; - - // - - int hopInterval = 10; - QString hopPort = ""; - - HysteriaBean() : AbstractBean(0) { - _add(new configItem("protocol", &protocol, itemType::integer)); - _add(new configItem("authPayloadType", &authPayloadType, itemType::integer)); - _add(new configItem("authPayload", &authPayload, itemType::string)); - _add(new configItem("obfsPassword", &obfsPassword, itemType::string)); - _add(new configItem("uploadMbps", &uploadMbps, itemType::integer)); - _add(new configItem("downloadMbps", &downloadMbps, itemType::integer)); - _add(new configItem("streamReceiveWindow", &streamReceiveWindow, itemType::integer64)); - _add(new configItem("connectionReceiveWindow", &connectionReceiveWindow, itemType::integer64)); - _add(new configItem("disableMtuDiscovery", &disableMtuDiscovery, itemType::boolean)); - _add(new configItem("allowInsecure", &allowInsecure, itemType::boolean)); - _add(new configItem("sni", &sni, itemType::string)); - _add(new configItem("alpn", &alpn, itemType::string)); - _add(new configItem("caText", &caText, itemType::string)); - _add(new configItem("hopInterval", &hopInterval, itemType::integer)); - _add(new configItem("hopPort", &hopPort, itemType::string)); - }; - - QString DisplayAddress() override { - if (!hopPort.trimmed().isEmpty()) return WrapIPV6Host(serverAddress) + ":" + hopPort; - return ::DisplayAddress(serverAddress, serverPort); - } - - QString DisplayCoreType() override { return NeedExternal(true) == 0 ? software_core_name : "Hysteria"; }; - - QString DisplayType() override { return "Hysteria"; }; - - int NeedExternal(bool isFirstProfile) override; - - ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; - - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - bool TryParseLink(const QString &link); - - QString ToShareLink() override; - }; -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/Link2Bean.cpp b/fmt/Link2Bean.cpp index b980fb9..dcc3d9a 100644 --- a/fmt/Link2Bean.cpp +++ b/fmt/Link2Bean.cpp @@ -1,3 +1,4 @@ +#include "QUICBean.hpp" #include "db/ProxyEntity.hpp" #include "fmt/includes.h" @@ -174,40 +175,47 @@ namespace NekoGui_fmt { return !(username.isEmpty() || password.isEmpty() || serverAddress.isEmpty()); } - bool HysteriaBean::TryParseLink(const QString &link) { + bool QUICBean::TryParseLink(const QString &link) { // https://hysteria.network/docs/uri-scheme/ auto url = QUrl(link); auto query = QUrlQuery(url.query()); - if (url.host().isEmpty() || url.port() == -1 || !query.hasQueryItem("upmbps") || !query.hasQueryItem("downmbps")) return false; + if (url.host().isEmpty() || url.port() == -1) return false; - name = url.fragment(); - serverAddress = url.host(); - serverPort = url.port(); - serverAddress = url.host(); // default sni - hopPort = query.queryItemValue("mport"); - obfsPassword = query.queryItemValue("obfsParam"); - allowInsecure = query.queryItemValue("insecure") == "1"; - uploadMbps = query.queryItemValue("upmbps").toInt(); - downloadMbps = query.queryItemValue("downmbps").toInt(); + if (url.scheme() == "hysteria") { + if (!query.hasQueryItem("upmbps") || !query.hasQueryItem("downmbps")) return false; - auto protocolStr = (query.hasQueryItem("protocol") ? query.queryItemValue("protocol") : "udp").toLower(); - if (protocolStr == "faketcp") { - protocol = NekoGui_fmt::HysteriaBean::hysteria_protocol_facktcp; - } else if (protocolStr.startsWith("wechat")) { - protocol = NekoGui_fmt::HysteriaBean::hysteria_protocol_wechat_video; + name = url.fragment(); + serverAddress = url.host(); + serverPort = url.port(); + serverAddress = url.host(); // default sni + hopPort = query.queryItemValue("mport"); + obfsPassword = query.queryItemValue("obfsParam"); + allowInsecure = query.queryItemValue("insecure") == "1"; + uploadMbps = query.queryItemValue("upmbps").toInt(); + downloadMbps = query.queryItemValue("downmbps").toInt(); + + auto protocolStr = (query.hasQueryItem("protocol") ? query.queryItemValue("protocol") : "udp").toLower(); + if (protocolStr == "faketcp") { + hyProtocol = NekoGui_fmt::QUICBean::hysteria_protocol_facktcp; + } else if (protocolStr.startsWith("wechat")) { + hyProtocol = NekoGui_fmt::QUICBean::hysteria_protocol_wechat_video; + } + + if (query.hasQueryItem("auth")) { + authPayload = query.queryItemValue("auth"); + authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_string; + } + + alpn = query.queryItemValue("alpn"); + sni = FIRST_OR_SECOND(query.queryItemValue("peer"), query.queryItemValue("sni")); + + connectionReceiveWindow = query.queryItemValue("recv_window").toInt(); + streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt(); + } else { + // TODO TUIC std link + return false; } - if (query.hasQueryItem("auth")) { - authPayload = query.queryItemValue("auth"); - authPayloadType = NekoGui_fmt::HysteriaBean::hysteria_auth_string; - } - - alpn = query.queryItemValue("alpn"); - sni = FIRST_OR_SECOND(query.queryItemValue("peer"), query.queryItemValue("sni")); - - connectionReceiveWindow = query.queryItemValue("recv_window").toInt(); - streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt(); - return true; } diff --git a/fmt/QUICBean.hpp b/fmt/QUICBean.hpp new file mode 100644 index 0000000..56f7e94 --- /dev/null +++ b/fmt/QUICBean.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "fmt/AbstractBean.hpp" + +namespace NekoGui_fmt { + class QUICBean : public AbstractBean { + public: + static constexpr int proxy_Hysteria = 0; + static constexpr int proxy_TUIC = 1; + int proxy_type = proxy_Hysteria; + + // Hysteria + + static constexpr int hysteria_protocol_udp = 0; + static constexpr int hysteria_protocol_facktcp = 1; + static constexpr int hysteria_protocol_wechat_video = 2; + int hyProtocol = 0; + + static constexpr int hysteria_auth_none = 0; + static constexpr int hysteria_auth_string = 1; + static constexpr int hysteria_auth_base64 = 2; + int authPayloadType = 0; + + QString authPayload = ""; + QString obfsPassword = ""; + + int uploadMbps = 100; + int downloadMbps = 100; + + qint64 streamReceiveWindow = 0; + qint64 connectionReceiveWindow = 0; + bool disableMtuDiscovery = false; + + int hopInterval = 10; + QString hopPort = ""; + + // TUIC + + QString uuid = ""; + QString password = ""; + QString congestionControl = "bbr"; + QString udpRelayMode = "native"; + bool zeroRttHandshake = false; + QString heartbeat = "10s"; + + // TLS + + bool allowInsecure = false; + QString sni = ""; + QString alpn = ""; + QString caText = ""; + bool disableSni = false; + + explicit QUICBean(int _proxy_type) : AbstractBean(0) { + proxy_type = _proxy_type; + if (proxy_type == proxy_Hysteria) { + _add(new configItem("protocol", &hyProtocol, itemType::integer)); + _add(new configItem("authPayloadType", &authPayloadType, itemType::integer)); + _add(new configItem("authPayload", &authPayload, itemType::string)); + _add(new configItem("obfsPassword", &obfsPassword, itemType::string)); + _add(new configItem("uploadMbps", &uploadMbps, itemType::integer)); + _add(new configItem("downloadMbps", &downloadMbps, itemType::integer)); + _add(new configItem("streamReceiveWindow", &streamReceiveWindow, itemType::integer64)); + _add(new configItem("connectionReceiveWindow", &connectionReceiveWindow, itemType::integer64)); + _add(new configItem("disableMtuDiscovery", &disableMtuDiscovery, itemType::boolean)); + _add(new configItem("hopInterval", &hopInterval, itemType::integer)); + _add(new configItem("hopPort", &hopPort, itemType::string)); + } else if (proxy_type == proxy_TUIC) { + _add(new configItem("uuid", &uuid, itemType::string)); + _add(new configItem("password", &password, itemType::string)); + _add(new configItem("congestionControl", &congestionControl, itemType::string)); + _add(new configItem("udpRelayMode", &udpRelayMode, itemType::string)); + _add(new configItem("zeroRttHandshake", &zeroRttHandshake, itemType::boolean)); + _add(new configItem("heartbeat", &heartbeat, itemType::string)); + } + // TLS + _add(new configItem("allowInsecure", &allowInsecure, itemType::boolean)); + _add(new configItem("sni", &sni, itemType::string)); + _add(new configItem("alpn", &alpn, itemType::string)); + _add(new configItem("caText", &caText, itemType::string)); + _add(new configItem("disableSni", &disableSni, itemType::boolean)); + }; + + QString DisplayAddress() override { + if (!hopPort.trimmed().isEmpty()) return WrapIPV6Host(serverAddress) + ":" + hopPort; + return ::DisplayAddress(serverAddress, serverPort); + } + + QString DisplayCoreType() override { return NeedExternal(true) == 0 ? software_core_name : "Hysteria"; }; + + QString DisplayType() override { return proxy_type == proxy_TUIC ? "TUIC" : "Hysteria"; }; + + int NeedExternal(bool isFirstProfile) override; + + ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; + + CoreObjOutboundBuildResult BuildCoreObjSingBox() override; + + bool TryParseLink(const QString &link); + + QString ToShareLink() override; + }; +} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/includes.h b/fmt/includes.h index b2f7ecc..cd3fe3d 100644 --- a/fmt/includes.h +++ b/fmt/includes.h @@ -6,5 +6,5 @@ #include "VMessBean.hpp" #include "TrojanVLESSBean.hpp" #include "NaiveBean.hpp" -#include "HysteriaBean.hpp" +#include "QUICBean.hpp" #include "CustomBean.hpp" diff --git a/go/cmd/nekobox_core/go.mod b/go/cmd/nekobox_core/go.mod index 3078e6c..3f416ff 100644 --- a/go/cmd/nekobox_core/go.mod +++ b/go/cmd/nekobox_core/go.mod @@ -28,6 +28,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gofrs/uuid/v5 v5.0.0 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect @@ -48,14 +49,12 @@ require ( github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-18 v0.2.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.2 // indirect - github.com/quic-go/qtls-go1-20 v0.2.2 // indirect + github.com/quic-go/qtls-go1-20 v0.3.1 // indirect github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect - github.com/sagernet/quic-go v0.0.0-20230731154841-cdc97aca6239 // indirect + github.com/sagernet/quic-go v0.0.0-20230809023643-d720ed35ac2b // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a // indirect github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 // indirect @@ -63,7 +62,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.4 // indirect github.com/sagernet/sing-shadowsocks2 v0.1.3 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.1.12-0.20230808064040-326c20ab22af // indirect + github.com/sagernet/sing-tun v0.1.12-0.20230808120247-47ab78d303db // indirect github.com/sagernet/sing-vmess v0.1.7 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect diff --git a/go/cmd/nekobox_core/go.sum b/go/cmd/nekobox_core/go.sum index e10bc45..b2e4154 100644 --- a/go/cmd/nekobox_core/go.sum +++ b/go/cmd/nekobox_core/go.sum @@ -60,6 +60,7 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= @@ -120,12 +121,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U= -github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc= -github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= -github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= -github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg= +github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM= github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I= @@ -135,8 +132,8 @@ github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTS github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/quic-go v0.0.0-20230731154841-cdc97aca6239 h1:UwKDwYDzGc9FGBYm3pr7SpaEQcjYdnfjtRDvePHtcBA= -github.com/sagernet/quic-go v0.0.0-20230731154841-cdc97aca6239/go.mod h1:5rilP6WxqIl/4ypZbMjr+MK+STxuCEvO5yVtEyYNZ6g= +github.com/sagernet/quic-go v0.0.0-20230809023643-d720ed35ac2b h1:+hpCW1zw03nnJDx+5tF9ETb6bKl2VSftv4KMGZAHC2Q= +github.com/sagernet/quic-go v0.0.0-20230809023643-d720ed35ac2b/go.mod h1:w+nln6f/ZtyPpGbFxmgd5iYFVMmgS+gpD5hu5GAqC1I= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= @@ -153,8 +150,8 @@ github.com/sagernet/sing-shadowsocks2 v0.1.3 h1:WXoLvCFi5JTFBRYorf1YePGYIQyJ/zbs github.com/sagernet/sing-shadowsocks2 v0.1.3/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.12-0.20230808064040-326c20ab22af h1:gC2vTbt2wrfPv3P33fLIrtM6YUt1w7muRrMk62g6Ykk= -github.com/sagernet/sing-tun v0.1.12-0.20230808064040-326c20ab22af/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck= +github.com/sagernet/sing-tun v0.1.12-0.20230808120247-47ab78d303db h1:jOwG+7u4NtQVwXj5pFnGeNnDoa2cv83O5x4NLKN8y/c= +github.com/sagernet/sing-tun v0.1.12-0.20230808120247-47ab78d303db/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck= github.com/sagernet/sing-vmess v0.1.7 h1:TM8FFLsXmlXH9XT8/oDgc6PC5BOzrg6OzyEe01is2r4= github.com/sagernet/sing-vmess v0.1.7/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -188,6 +185,7 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= @@ -221,6 +219,7 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -232,6 +231,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= @@ -242,6 +242,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -253,7 +254,9 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -277,6 +280,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/libs/get_source_env.sh b/libs/get_source_env.sh index 86b38f5..f8f5119 100644 --- a/libs/get_source_env.sh +++ b/libs/get_source_env.sh @@ -1,5 +1,5 @@ if [ ! -z $ENV_NEKORAY ]; then - export COMMIT_SING_BOX_EXTRA="00286b4baa9dabdce79d40ccd63a90c862a72254" + export COMMIT_SING_BOX_EXTRA="df902756f9c50257c48c580e2b5f1b5b190f89bc" export COMMIT_MATSURI_XRAY="f6843e4e1442341e88ac34c09ca2d243cdcc09f6" fi diff --git a/sub/GroupUpdater.cpp b/sub/GroupUpdater.cpp index d0efba1..d3a7339 100644 --- a/sub/GroupUpdater.cpp +++ b/sub/GroupUpdater.cpp @@ -129,7 +129,7 @@ namespace NekoGui_sub { if (str.startsWith("hysteria://")) { needFix = false; ent = NekoGui::ProfileManager::NewProxyEntity("hysteria"); - auto ok = ent->HysteriaBean()->TryParseLink(str); + auto ok = ent->QUICBean()->TryParseLink(str); if (!ok) return; } @@ -373,28 +373,26 @@ namespace NekoGui_sub { break; } } - } else if (type_clash == "hysteria") { - auto bean = ent->HysteriaBean(); + } else if (type == "hysteria") { + auto bean = ent->QUICBean(); + + bean->hopPort = Node2QString(proxy["ports"]); + if (bean->serverPort == 0) bean->hopPort = Node2QString(proxy["port"]); bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]); auto alpn = Node2QStringList(proxy["alpn"]); + bean->caText = Node2QString(proxy["ca-str"]); if (!alpn.isEmpty()) bean->alpn = alpn[0]; bean->sni = Node2QString(proxy["sni"]); - bean->name = Node2QString(proxy["name"]); - bean->serverAddress = Node2QString(proxy["server"]); - bean->serverPort = Node2Int(proxy["port"]); - bean->hopPort = Node2QString(proxy["ports"]); - if (bean->serverPort == 0) bean->hopPort = Node2QString(proxy["port"]); - auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"])); auto auth = Node2QString(proxy["auth"]); if (!auth_str.isEmpty()) { - bean->authPayloadType = NekoGui_fmt::HysteriaBean::hysteria_auth_string; + bean->authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_string; bean->authPayload = auth_str; } if (!auth.isEmpty()) { - bean->authPayloadType = NekoGui_fmt::HysteriaBean::hysteria_auth_base64; + bean->authPayloadType = NekoGui_fmt::QUICBean::hysteria_auth_base64; bean->authPayload = auth; } bean->obfsPassword = Node2QString(proxy["obfs"]); @@ -407,6 +405,30 @@ namespace NekoGui_sub { auto downMbps = Node2QString(proxy["down"]).split(" ")[0].toInt(); if (upMbps > 0) bean->uploadMbps = upMbps; if (downMbps > 0) bean->downloadMbps = downMbps; + } else if (type == "tuic") { + auto bean = ent->QUICBean(); + + 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->udpRelayMode = Node2QString(proxy["udp-relay-mode"]); + bean->congestionControl = Node2QString(proxy["congestion-controller"]); + + bean->disableSni = Node2Bool(proxy["disable-sni"]); + bean->zeroRttHandshake = Node2Bool(proxy["reduce-rtt"]); + bean->allowInsecure = Node2Bool(proxy["skip-cert-verify"]); + bean->alpn = Node2QStringList(proxy["alpn"]).join(","); + bean->caText = Node2QString(proxy["ca-str"]); + bean->sni = Node2QString(proxy["sni"]); + + if (!Node2QString(proxy["ip"]).isEmpty()) { + if (bean->sni.isEmpty()) bean->sni = bean->serverAddress; + bean->serverAddress = Node2QString(proxy["ip"]); + } } else { continue; } diff --git a/translations/fa_IR.ts b/translations/fa_IR.ts index c31d43e..228cfeb 100644 --- a/translations/fa_IR.ts +++ b/translations/fa_IR.ts @@ -856,7 +856,38 @@ This needs to be run NekoBox with administrator privileges. - EditHysteria + EditNaive + + Protocol + پروتکل + + + Password + رمزعبور + + + Extra headers + + + + SNI + + + + Username + نام کاربری + + + Certificate + گواهی + + + Insecure concurrency + همزمانی ناامن + + + + EditQUIC Certificate گواهی @@ -905,36 +936,33 @@ This needs to be run NekoBox with administrator privileges. Auth Payload - - - EditNaive - Protocol - پروتکل + Disable SNI + Password - رمزعبور - - - Extra headers - SNI + Generate UUID - Username - نام کاربری + Heartbeat + - Certificate - گواهی + Zero Rtt Handshake + - Insecure concurrency - همزمانی ناامن + Congestion Control + + + + UDP Relay Mode + diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index ebe303a..6114fcd 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -852,7 +852,38 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun - EditHysteria + EditNaive + + Protocol + Протокол + + + Password + Пароль + + + Extra headers + Дополнительные заголовки + + + SNI + SNI + + + Username + Имя пользователя + + + Certificate + Сертификат + + + Insecure concurrency + + + + + EditQUIC Auth Type Тип авторизации @@ -901,36 +932,33 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun Auth Payload - - - EditNaive - Protocol - Протокол + Disable SNI + Password - Пароль + Пароль - Extra headers - Дополнительные заголовки + Generate UUID + Сгенерировать - SNI - SNI + Heartbeat + - Username - Имя пользователя + Zero Rtt Handshake + - Certificate - Сертификат + Congestion Control + - Insecure concurrency - + UDP Relay Mode + diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 8025012..b4bb953 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -856,7 +856,38 @@ This needs to be run NekoBox with administrator privileges. - EditHysteria + EditNaive + + Protocol + 协议 + + + Password + 密码 + + + Extra headers + 附加标头 + + + SNI + + + + Username + 用户名 + + + Certificate + 证书 + + + Insecure concurrency + 不安全并发 + + + + EditQUIC Certificate 证书 @@ -905,36 +936,33 @@ This needs to be run NekoBox with administrator privileges. Auth Payload 认证有效载荷 - - - EditNaive - Protocol - 协议 + Generate UUID + 生成 UUID Password 密码 - Extra headers - 附加标头 + Zero Rtt Handshake + 0-Rtt 握手 - SNI - + UDP Relay Mode + UDP 中继模式 - Username - 用户名 + Congestion Control + 拥塞控制 - Certificate - 证书 + Heartbeat + 心跳包发送间隔 - Insecure concurrency - 不安全并发 + Disable SNI + 不发送服务器名称指示 diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp index 156423a..18244e0 100644 --- a/ui/dialog_basic_settings.cpp +++ b/ui/dialog_basic_settings.cpp @@ -169,6 +169,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent) CACHE.extraCore = QString2QJsonObject(NekoGui::dataStore->extraCore->core_map); if (!CACHE.extraCore.contains("naive")) CACHE.extraCore.insert("naive", ""); if (!CACHE.extraCore.contains("hysteria")) CACHE.extraCore.insert("hysteria", ""); + if (!CACHE.extraCore.contains("tuic")) CACHE.extraCore.insert("tuic", ""); // auto extra_core_layout = ui->extra_core_box_scrollAreaWidgetContents->layout(); for (const auto &s: CACHE.extraCore.keys()) { diff --git a/ui/edit/dialog_edit_profile.cpp b/ui/edit/dialog_edit_profile.cpp index d1af4ac..2cdceb6 100644 --- a/ui/edit/dialog_edit_profile.cpp +++ b/ui/edit/dialog_edit_profile.cpp @@ -7,7 +7,7 @@ #include "ui/edit/edit_vmess.h" #include "ui/edit/edit_trojan_vless.h" #include "ui/edit/edit_naive.h" -#include "ui/edit/edit_hysteria.h" +#include "ui/edit/edit_quic.h" #include "ui/edit/edit_custom.h" #include "fmt/includes.h" @@ -32,7 +32,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId, connect(ui->network, &QComboBox::currentTextChanged, this, [=](const QString &txt) { ui->network_box->setTitle(network_title_base.arg(txt)); // 传输设置 - if (txt == "tcp" || (!IS_NEKO_BOX && txt == "quic")) { + if (txt == "tcp") { ui->header_type->setVisible(true); ui->header_type_l->setVisible(true); ui->path->setVisible(true); @@ -121,6 +121,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId, LOAD_TYPE("vless") LOAD_TYPE("naive") LOAD_TYPE("hysteria") + LOAD_TYPE("tuic") ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal"); ui->type->addItem(tr("Custom (%1 config)").arg(software_core_name), "internal-full"); ui->type->addItem(tr("Custom (Extra Core)"), "custom"); @@ -176,8 +177,8 @@ void DialogEditProfile::typeSelected(const QString &newType) { auto _innerWidget = new EditNaive(this); innerWidget = _innerWidget; innerEditor = _innerWidget; - } else if (type == "hysteria") { - auto _innerWidget = new EditHysteria(this); + } else if (type == "hysteria" || type == "tuic") { + auto _innerWidget = new EditQUIC(this); innerWidget = _innerWidget; innerEditor = _innerWidget; } else if (type == "custom" || type == "internal" || type == "internal-full") { diff --git a/ui/edit/edit_hysteria.cpp b/ui/edit/edit_hysteria.cpp deleted file mode 100644 index e4745e2..0000000 --- a/ui/edit/edit_hysteria.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "edit_hysteria.h" -#include "ui_edit_hysteria.h" - -#include "fmt/HysteriaBean.hpp" - -#include - -EditHysteria::EditHysteria(QWidget *parent) : QWidget(parent), ui(new Ui::EditHysteria) { - ui->setupUi(this); -} - -EditHysteria::~EditHysteria() { - delete ui; -} - -void EditHysteria::onStart(std::shared_ptr _ent) { - this->ent = _ent; - auto bean = this->ent->HysteriaBean(); - - P_LOAD_STRING(hopPort); - P_LOAD_INT(hopInterval); - P_LOAD_INT(uploadMbps); - P_LOAD_INT(downloadMbps); - P_LOAD_COMBO_INT(protocol); - P_LOAD_BOOL(disableMtuDiscovery) - P_LOAD_STRING(obfsPassword); - P_LOAD_COMBO_INT(authPayloadType); - P_LOAD_STRING(authPayload); - P_LOAD_STRING(sni); - P_LOAD_STRING(alpn); - P_LOAD_BOOL(allowInsecure) - P_C_LOAD_STRING(caText); - P_LOAD_INT(streamReceiveWindow); - P_LOAD_INT(connectionReceiveWindow); -} - -bool EditHysteria::onEnd() { - auto bean = this->ent->HysteriaBean(); - - P_SAVE_STRING(hopPort); - P_SAVE_INT(hopInterval); - P_SAVE_INT(uploadMbps); - P_SAVE_INT(downloadMbps); - P_SAVE_COMBO_INT(protocol); - P_SAVE_BOOL(disableMtuDiscovery) - P_SAVE_STRING(obfsPassword); - P_SAVE_COMBO_INT(authPayloadType); - P_SAVE_STRING(authPayload); - P_SAVE_STRING(sni); - P_SAVE_STRING(alpn); - P_SAVE_BOOL(allowInsecure) - P_C_SAVE_STRING(caText); - P_SAVE_INT(streamReceiveWindow); - P_SAVE_INT(connectionReceiveWindow); - - return true; -} - -QList> EditHysteria::get_editor_cached() { - return { - {ui->certificate, CACHE.caText}, - }; -} - -void EditHysteria::on_certificate_clicked() { - bool ok; - auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.caText, &ok); - if (ok) { - CACHE.caText = txt; - editor_cache_updated(); - } -} diff --git a/ui/edit/edit_hysteria.ui b/ui/edit/edit_hysteria.ui deleted file mode 100644 index 53f71c3..0000000 --- a/ui/edit/edit_hysteria.ui +++ /dev/null @@ -1,242 +0,0 @@ - - - EditHysteria - - - - 0 - 0 - 500 - 600 - - - - EditHysteria - - - - - - Hop Port - - - - - - - - - - Hop Interval (s) - - - - - - - - - - Upload (Mbps) - - - - - - - - - - Download (Mbps) - - - - - - - - - - Protocol - - - - - - - - - - QUIC - - - - - FakeTCP - - - - - wechat-video - - - - - - - - - 0 - 0 - - - - Disable MTU Discovery - - - - - - - - - Obfs Password - - - - - - - - - - Auth Type - - - - - - - - NONE - - - - - STRING - - - - - BASE64 - - - - - - - - Auth Payload - - - - - - - - - - SNI - - - - - - - - - - Only 1 value - - - ALPN - - - - - - - - - - - - Allow Insecure - - - - - - - - - Certificate - - - - - - - PushButton - - - - - - - recv_window - - - - - - - - - - recv_window_conn - - - - - - - - - - - MyLineEdit - QLineEdit -
ui/widget/MyLineEdit.h
-
-
- - hopPort - hopInterval - uploadMbps - downloadMbps - protocol - disableMtuDiscovery - obfsPassword - authPayloadType - authPayload - sni - alpn - allowInsecure - certificate - streamReceiveWindow - connectionReceiveWindow - - - -
diff --git a/ui/edit/edit_quic.cpp b/ui/edit/edit_quic.cpp new file mode 100644 index 0000000..376c5b2 --- /dev/null +++ b/ui/edit/edit_quic.cpp @@ -0,0 +1,132 @@ +#include "edit_quic.h" +#include "ui_edit_quic.h" + +#include "fmt/QUICBean.hpp" + +#include +#include + +EditQUIC::EditQUIC(QWidget *parent) : QWidget(parent), ui(new Ui::EditQUIC) { + ui->setupUi(this); + connect(ui->uuidgen, &QPushButton::clicked, this, [=] { ui->uuid->setText(QUuid::createUuid().toString().remove("{").remove("}")); }); +} + +EditQUIC::~EditQUIC() { + delete ui; +} + +void EditQUIC::onStart(std::shared_ptr _ent) { + this->ent = _ent; + auto bean = this->ent->QUICBean(); + + if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria) { + P_LOAD_STRING(hopPort); + P_LOAD_INT(hopInterval); + P_LOAD_INT(uploadMbps); + P_LOAD_INT(downloadMbps); + P_LOAD_COMBO_INT(hyProtocol); + P_LOAD_BOOL(disableMtuDiscovery) + P_LOAD_STRING(obfsPassword); + P_LOAD_COMBO_INT(authPayloadType); + P_LOAD_STRING(authPayload); + P_LOAD_INT(streamReceiveWindow); + P_LOAD_INT(connectionReceiveWindow); + + ui->uuid->hide(); + ui->uuid_l->hide(); + ui->uuidgen->hide(); + ui->password->hide(); + ui->password_l->hide(); + ui->congestionControl->hide(); + ui->congestionControl_l->hide(); + ui->udpRelayMode->hide(); + ui->udpRelayMode_l->hide(); + ui->zeroRttHandshake->hide(); + ui->heartbeat->hide(); + ui->heartbeat_l->hide(); + } else if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_TUIC) { + P_LOAD_STRING(uuid); + P_LOAD_STRING(password); + P_LOAD_COMBO_STRING(congestionControl); + P_LOAD_COMBO_STRING(udpRelayMode); + P_LOAD_BOOL(zeroRttHandshake); + P_LOAD_STRING(heartbeat); + + ui->hopPort->hide(); + ui->hopPort_l->hide(); + ui->hopInterval->hide(); + ui->hopInterval_l->hide(); + ui->uploadMbps->hide(); + ui->uploadMbps_l->hide(); + ui->downloadMbps->hide(); + ui->downloadMbps_l->hide(); + ui->hyProtocol->hide(); + ui->hyProtocol_l->hide(); + ui->disableMtuDiscovery->hide(); + ui->obfsPassword->hide(); + ui->obfsPassword_l->hide(); + ui->authPayload->hide(); + ui->authPayload_l->hide(); + ui->authPayloadType->hide(); + ui->authPayloadType_l->hide(); + ui->streamReceiveWindow->hide(); + ui->streamReceiveWindow_l->hide(); + ui->connectionReceiveWindow->hide(); + ui->connectionReceiveWindow_l->hide(); + } + + // TLS + P_LOAD_STRING(sni); + P_LOAD_STRING(alpn); + P_C_LOAD_STRING(caText); + P_LOAD_BOOL(allowInsecure); + P_LOAD_BOOL(disableSni); +} + +bool EditQUIC::onEnd() { + auto bean = this->ent->QUICBean(); + + // Hysteria + P_SAVE_STRING(hopPort); + P_SAVE_INT(hopInterval); + P_SAVE_INT(uploadMbps); + P_SAVE_INT(downloadMbps); + P_SAVE_COMBO_INT(hyProtocol); + P_SAVE_BOOL(disableMtuDiscovery) + P_SAVE_STRING(obfsPassword); + P_SAVE_COMBO_INT(authPayloadType); + P_SAVE_STRING(authPayload); + P_SAVE_INT(streamReceiveWindow); + P_SAVE_INT(connectionReceiveWindow); + + // TUIC + P_SAVE_STRING(uuid); + P_SAVE_STRING(password); + P_SAVE_COMBO_STRING(congestionControl); + P_SAVE_COMBO_STRING(udpRelayMode); + P_SAVE_BOOL(zeroRttHandshake); + P_SAVE_STRING(heartbeat); + + // TLS + P_SAVE_STRING(sni); + P_SAVE_STRING(alpn); + P_SAVE_BOOL(allowInsecure); + P_C_SAVE_STRING(caText); + P_SAVE_BOOL(disableSni); + return true; +} + +QList> EditQUIC::get_editor_cached() { + return { + {ui->certificate, CACHE.caText}, + }; +} + +void EditQUIC::on_certificate_clicked() { + bool ok; + auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.caText, &ok); + if (ok) { + CACHE.caText = txt; + editor_cache_updated(); + } +} diff --git a/ui/edit/edit_hysteria.h b/ui/edit/edit_quic.h similarity index 68% rename from ui/edit/edit_hysteria.h rename to ui/edit/edit_quic.h index 9d36055..43996f9 100644 --- a/ui/edit/edit_hysteria.h +++ b/ui/edit/edit_quic.h @@ -1,21 +1,23 @@ #pragma once #include +#include +#include #include "profile_editor.h" QT_BEGIN_NAMESPACE namespace Ui { - class EditHysteria; + class EditQUIC; } QT_END_NAMESPACE -class EditHysteria : public QWidget, public ProfileEditor { +class EditQUIC : public QWidget, public ProfileEditor { Q_OBJECT public: - explicit EditHysteria(QWidget *parent = nullptr); + explicit EditQUIC(QWidget *parent = nullptr); - ~EditHysteria() override; + ~EditQUIC() override; void onStart(std::shared_ptr _ent) override; @@ -24,7 +26,7 @@ public: QList> get_editor_cached() override; private: - Ui::EditHysteria *ui; + Ui::EditQUIC *ui; std::shared_ptr ent; struct { diff --git a/ui/edit/edit_quic.ui b/ui/edit/edit_quic.ui new file mode 100644 index 0000000..c0cde74 --- /dev/null +++ b/ui/edit/edit_quic.ui @@ -0,0 +1,436 @@ + + + EditQUIC + + + + 0 + 0 + 500 + 600 + + + + EditHysteria + + + + + + + + + + Download (Mbps) + + + + + + + + + + + + + + Hop Port + + + + + + + + + + + + + + UDP Relay Mode + + + + + + + + native + + + + + quic + + + + + + + + + + + + Congestion Control + + + + + + + + bbr + + + + + cubic + + + + + new_reno + + + + + + + + + + + + Upload (Mbps) + + + + + + + + + + + + + + Hop Interval (s) + + + + + + + + + + + + Zero Rtt Handshake + + + + + + + + + Heartbeat + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + Obfs Password + + + + + + + Protocol + + + + + + + + + + QUIC + + + + + FakeTCP + + + + + wechat-video + + + + + + + + + 0 + 0 + + + + Disable MTU Discovery + + + + + + + + + + + + + + + + + 0 + 0 + + + + Auth Type + + + + + + + + 0 + 0 + + + + Auth Payload + + + + + + + + 0 + 0 + + + + Password + + + + + + + + 0 + 0 + + + + UUID + + + + + + + Generate UUID + + + + + + + + + + + + + + NONE + + + + + STRING + + + + + BASE64 + + + + + + + + + + + + Certificate + + + + + + + PushButton + + + + + + + + + + Disable SNI + + + + + + + + + + ALPN + + + + + + + Allow Insecure + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 40 + 20 + + + + + + + + SNI + + + + + + + + + + + recv_window + + + + + + + + + + recv_window_conn + + + + + + + + + + + + + MyLineEdit + QLineEdit +
ui/widget/MyLineEdit.h
+
+
+ + hopPort + hopInterval + uploadMbps + downloadMbps + congestionControl + udpRelayMode + heartbeat + zeroRttHandshake + hyProtocol + disableMtuDiscovery + obfsPassword + authPayloadType + authPayload + uuid + uuidgen + password + sni + disableSni + alpn + allowInsecure + certificate + streamReceiveWindow + connectionReceiveWindow + + + +