From aad4063ef730f7f40eb5b6fcfd94d806dfbb8da4 Mon Sep 17 00:00:00 2001 From: HystericalDragon <138737572+HystericalDragon@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:50:09 +0800 Subject: [PATCH] =?UTF-8?q?wip:=20hysteria2=20=E4=BB=85=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E4=B8=8D=E4=BB=A3=E8=A1=A8=E8=83=BD=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: xchacha20-poly1305 <139959885+xchacha20-poly1305@users.noreply.github.com> --- README.md | 1 + README_fa.md | 1 + db/Database.cpp | 2 + fmt/Bean2CoreObj_box.cpp | 14 +++++- fmt/Bean2External.cpp | 84 +++++++++++++++++++++++++++++++++ fmt/QUICBean.hpp | 15 ++++-- ui/dialog_basic_settings.cpp | 1 + ui/edit/dialog_edit_profile.cpp | 3 +- ui/edit/edit_quic.cpp | 22 +++++++-- ui/edit/edit_quic.ui | 28 +++++++---- 10 files changed, 154 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9232683..0436cb8 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ https://matsuridayo.github.io * TUIC ( sing-box ) * NaïveProxy ( Custom Core ) * Hysteria ( Custom Core or sing-box ) +* Hysteria2 ( Custom Core or sing-box ) * Custom Outbound * Custom Config * Custom Core diff --git a/README_fa.md b/README_fa.md index 80118e0..834f8b1 100644 --- a/README_fa.md +++ b/README_fa.md @@ -39,6 +39,7 @@ https://matsuridayo.github.io * Trojan * NaïveProxy ( Custom Core ) * Hysteria ( Custom Core or sing-box ) +* Hysteria2 ( Custom Core or sing-box ) * Custom Outbound * Custom Core diff --git a/db/Database.cpp b/db/Database.cpp index dea8698..0ff3f3c 100644 --- a/db/Database.cpp +++ b/db/Database.cpp @@ -186,6 +186,8 @@ namespace NekoGui { bean = new NekoGui_fmt::NaiveBean(); } else if (type == "hysteria") { bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria); + } else if (type == "hysteria2" ) { + bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_Hysteria2); } else if (type == "tuic") { bean = new NekoGui_fmt::QUICBean(NekoGui_fmt::QUICBean::proxy_TUIC); } else if (type == "custom") { diff --git a/fmt/Bean2CoreObj_box.cpp b/fmt/Bean2CoreObj_box.cpp index fa4f4eb..e344943 100644 --- a/fmt/Bean2CoreObj_box.cpp +++ b/fmt/Bean2CoreObj_box.cpp @@ -199,13 +199,25 @@ namespace NekoGui_fmt { 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_Hysteria2) { + outbound["type"] = "hysteria2"; + outbound["password"] = authPayload; + + if (uploadMbps > 0) outbound["up_mbps"] = uploadMbps; + if (downloadMbps > 0) outbound["down_mbps"] = downloadMbps; + if (!obfsPassword.isEmpty()) { + outbound["obfs"] = QJsonObject{ + {"type", "salamander"}, + {"password", obfsPassword}, + }; + } } else if (proxy_type == proxy_TUIC) { outbound["type"] = "tuic"; outbound["uuid"] = uuid; outbound["password"] = password; outbound["congestion_control"] = congestionControl; if (uos) { - outbound["udp_over_stream"]= true; + outbound["udp_over_stream"] = true; } else { outbound["udp_relay_mode"] = udpRelayMode; } diff --git a/fmt/Bean2External.cpp b/fmt/Bean2External.cpp index 34bb6ca..808d9d0 100644 --- a/fmt/Bean2External.cpp +++ b/fmt/Bean2External.cpp @@ -50,6 +50,16 @@ namespace NekoGui_fmt { return 1; }; + auto hysteria2Core = [=] { + if (isFirstProfile) { + if (NekoGui::dataStore->spmode_vpn || !hopPort.trimmed().isEmpty()) { + return 1; + } + return 2; + } + return 1; + }; + auto tuicCore = [=] { if (isFirstProfile) { if (NekoGui::dataStore->spmode_vpn) { @@ -70,6 +80,8 @@ namespace NekoGui_fmt { } } else if (proxy_type == proxy_TUIC) { return tuicCore(); + } else if (proxy_type == proxy_Hysteria2) { + return hysteria2Core(); } else { return hysteriaCore(); } @@ -162,6 +174,78 @@ namespace NekoGui_fmt { result.arguments = QStringList{"-c", TempFile}; return result; + } else if (proxy_type == proxy_Hysteria2) { + ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria2")}; + + QJsonObject config; + + auto server = serverAddress; + if (!hopPort.trimmed().isEmpty()) { + server = WrapIPV6Host(server) + ":" + hopPort; + } else { + server = WrapIPV6Host(server) + ":" + Int2String(serverPort); + } + + QJsonObject transport; + transport["type"] = "udp"; + transport["udp"] = QJsonObject{ + {"hopInterval", hopInterval}, + }; + config["transport"] = transport; + + config["server"] = server; + config["socks5"] = QJsonObject{ + {"listen", "127.0.0.1:" + Int2String(socks_port)}, + {"disableUDP", false}, + }; + if (username.isEmpty()) { + config["auth"] = authPayload; + } else { + config["auth"] = username + ":" + authPayload; + } + + QJsonObject bandwidth; + if (uploadMbps > 0) bandwidth["up"] = Int2String(uploadMbps) + " mbps"; + if (downloadMbps > 0) bandwidth["down"] = Int2String(downloadMbps) + " mbps"; + config["bandwidth"] = bandwidth; + + QJsonObject quic; + if (streamReceiveWindow > 0) quic["initStreamReceiveWindow"] = streamReceiveWindow; + if (connectionReceiveWindow > 0) quic["initConnReceiveWindow"] = connectionReceiveWindow; + if (disableMtuDiscovery) quic["disablePathMTUDiscovery"] = true; + + config["fastopen"] = true; + config["lazy"] = true; + + if (!obfsPassword.isEmpty()) { + QJsonObject obfs; + obfs["type"] = "salamander"; + obfs["salamander"] = QJsonObject{ + {"password", obfsPassword}, + }; + + config["obfs"] = obfs; + } + + QJsonObject tls; + auto sniGen = sni; + if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress; + tls["sni"] = sniGen; + if (allowInsecure) tls["insecure"] = true; + if (!caText.trimmed().isEmpty()) { + WriteTempFile("hysteria2_" + GetRandomString(10) + ".crt", caText.toUtf8()); + QJsonArray certificate; + certificate.append(TempFile); + tls["certificates"] = certificate; + } + config["tls"] = tls; + + result.config_export = QJsonObject2QString(config, false); + WriteTempFile("hysteria2_" + GetRandomString(10) + ".json", result.config_export.toUtf8()); + result.arguments = QStringList{"-c", TempFile}; + + return result; + } else { // Hysteria ExternalBuildResult result{NekoGui::dataStore->extraCore->Get("hysteria")}; diff --git a/fmt/QUICBean.hpp b/fmt/QUICBean.hpp index e64fa41..49d47e5 100644 --- a/fmt/QUICBean.hpp +++ b/fmt/QUICBean.hpp @@ -7,6 +7,7 @@ namespace NekoGui_fmt { public: static constexpr int proxy_Hysteria = 0; static constexpr int proxy_TUIC = 1; + static constexpr int proxy_Hysteria2 = 3; int proxy_type = proxy_Hysteria; // Hysteria @@ -34,6 +35,9 @@ namespace NekoGui_fmt { int hopInterval = 10; QString hopPort = ""; + // Hysteria 2 (Something same as hy1) + QString username = ""; + // TUIC QString uuid = ""; @@ -54,9 +58,7 @@ namespace NekoGui_fmt { 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)); + if (proxy_type == proxy_Hysteria || proxy_type == proxy_Hysteria2) { _add(new configItem("authPayload", &authPayload, itemType::string)); _add(new configItem("obfsPassword", &obfsPassword, itemType::string)); _add(new configItem("uploadMbps", &uploadMbps, itemType::integer)); @@ -66,6 +68,13 @@ namespace NekoGui_fmt { _add(new configItem("disableMtuDiscovery", &disableMtuDiscovery, itemType::boolean)); _add(new configItem("hopInterval", &hopInterval, itemType::integer)); _add(new configItem("hopPort", &hopPort, itemType::string)); + if (proxy_type == proxy_Hysteria2) { + _add(new configItem("authPayloadType", &authPayloadType, itemType::integer)); + _add(new configItem("protocol", &hyProtocol, itemType::integer)); + } else { + _add(new configItem("username", &username, itemType::string)); + } + } else if (proxy_type == proxy_TUIC) { _add(new configItem("uuid", &uuid, itemType::string)); _add(new configItem("password", &password, itemType::string)); diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp index 9d34e6d..152d1f9 100644 --- a/ui/dialog_basic_settings.cpp +++ b/ui/dialog_basic_settings.cpp @@ -170,6 +170,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("hysteria2")) CACHE.extraCore.insert("hysteria2", ""); if (!CACHE.extraCore.contains("tuic")) CACHE.extraCore.insert("tuic", ""); // auto extra_core_layout = ui->extra_core_box_scrollAreaWidgetContents->layout(); diff --git a/ui/edit/dialog_edit_profile.cpp b/ui/edit/dialog_edit_profile.cpp index 4f43a28..4eb524f 100644 --- a/ui/edit/dialog_edit_profile.cpp +++ b/ui/edit/dialog_edit_profile.cpp @@ -121,6 +121,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId, LOAD_TYPE("vless") LOAD_TYPE("naive") LOAD_TYPE("hysteria") + LOAD_TYPE("hysteria2") 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"); @@ -177,7 +178,7 @@ void DialogEditProfile::typeSelected(const QString &newType) { auto _innerWidget = new EditNaive(this); innerWidget = _innerWidget; innerEditor = _innerWidget; - } else if (type == "hysteria" || type == "tuic") { + } else if (type == "hysteria" || type == "hysteria2" || type == "tuic") { auto _innerWidget = new EditQUIC(this); innerWidget = _innerWidget; innerEditor = _innerWidget; diff --git a/ui/edit/edit_quic.cpp b/ui/edit/edit_quic.cpp index 1c1ebe4..616f85f 100644 --- a/ui/edit/edit_quic.cpp +++ b/ui/edit/edit_quic.cpp @@ -19,15 +19,13 @@ void EditQUIC::onStart(std::shared_ptr _ent) { this->ent = _ent; auto bean = this->ent->QUICBean(); - if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria) { + if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria || bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria2) { 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); @@ -45,6 +43,21 @@ void EditQUIC::onStart(std::shared_ptr _ent) { ui->heartbeat->hide(); ui->heartbeat_l->hide(); ui->uos->hide(); + + if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_Hysteria) { + P_LOAD_COMBO_INT(hyProtocol); + P_LOAD_COMBO_INT(authPayloadType); + + ui->username_l->hide(); + ui->username->hide(); + } else { + P_LOAD_STRING(username); + + ui->hyProtocol->hide(); + ui->hyProtocol_l->hide(); + ui->hyProtocol->hide(); + ui->hyProtocol_l->hide(); + } } else if (bean->proxy_type == NekoGui_fmt::QUICBean::proxy_TUIC) { P_LOAD_STRING(uuid); P_LOAD_STRING(password); @@ -104,6 +117,9 @@ bool EditQUIC::onEnd() { P_SAVE_INT(streamReceiveWindow); P_SAVE_INT(connectionReceiveWindow); + // Hysteria2 + P_SAVE_STRING(username); + // TUIC P_SAVE_STRING(uuid); P_SAVE_STRING(password); diff --git a/ui/edit/edit_quic.ui b/ui/edit/edit_quic.ui index 3a1c863..64ff71d 100644 --- a/ui/edit/edit_quic.ui +++ b/ui/edit/edit_quic.ui @@ -166,23 +166,16 @@ - + - + Obfs Password - - - - Protocol - - - @@ -219,6 +212,23 @@ + + + + Protocol + + + + + + + Username + + + + + +