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
-
-
-
-
- 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
+
+
+
+
+ hopPort
+ hopInterval
+ uploadMbps
+ downloadMbps
+ congestionControl
+ udpRelayMode
+ heartbeat
+ zeroRttHandshake
+ hyProtocol
+ disableMtuDiscovery
+ obfsPassword
+ authPayloadType
+ authPayload
+ uuid
+ uuidgen
+ password
+ sni
+ disableSni
+ alpn
+ allowInsecure
+ certificate
+ streamReceiveWindow
+ connectionReceiveWindow
+
+
+
+