From 50387db15e1247e85f90d1174ea1a47c76f3cd24 Mon Sep 17 00:00:00 2001 From: arm64v8a <48624112+arm64v8a@users.noreply.github.com> Date: Mon, 1 May 2023 20:28:14 +0900 Subject: [PATCH] refactor vpn mode --- 3rdparty/WinCommander.cpp | 4 +- 3rdparty/WinCommander.hpp | 6 +- .../v2/ui/widgets/editors/w_JsonEditor.cpp | 5 + db/ConfigBuilder.cpp | 122 +++++++++-- fmt/AbstractBean.hpp | 2 +- fmt/Bean2External.cpp | 42 ++-- fmt/CustomBean.hpp | 4 +- fmt/HysteriaBean.hpp | 4 +- fmt/NaiveBean.hpp | 2 +- go/cmd/nekobox_core/main.go | 3 - libs/package_appimage.sh | 4 +- main/Const.hpp | 8 - main/NekoRay.cpp | 8 +- main/NekoRay.hpp | 1 + main/NekoRay_DataStore.hpp | 9 +- translations/fa_IR.ts | 75 +++---- translations/zh_CN.ts | 74 ++----- ui/dialog_basic_settings.cpp | 8 - ui/dialog_manage_routes.cpp | 6 +- ui/dialog_manage_routes.ui | 122 +++-------- ui/dialog_vpn_settings.cpp | 9 +- ui/dialog_vpn_settings.ui | 55 +++-- ui/mainwindow.cpp | 196 ++++++++++-------- ui/mainwindow.h | 4 +- 24 files changed, 391 insertions(+), 382 deletions(-) diff --git a/3rdparty/WinCommander.cpp b/3rdparty/WinCommander.cpp index 09cac07..062fc24 100644 --- a/3rdparty/WinCommander.cpp +++ b/3rdparty/WinCommander.cpp @@ -48,7 +48,7 @@ Returns the return value of the executed command uint WinCommander::runProcessElevated(const QString &path, const QStringList ¶meters, const QString &workingDir, - bool hide, bool aWait) { + int nShow, bool aWait) { uint result = 0; #ifdef Q_OS_WIN @@ -79,7 +79,7 @@ uint WinCommander::runProcessElevated(const QString &path, shex.lpParameters = pszParameters; shex.lpDirectory = pszDirectory; // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow - shex.nShow = hide ? SW_HIDE : SW_SHOWMINIMIZED; + shex.nShow = nShow; ShellExecuteEx(&shex); if (shex.hProcess) diff --git a/3rdparty/WinCommander.hpp b/3rdparty/WinCommander.hpp index 0324513..4aca562 100644 --- a/3rdparty/WinCommander.hpp +++ b/3rdparty/WinCommander.hpp @@ -28,10 +28,14 @@ class WinCommander { public: + static const int SW_HIDE = 0; + static const int SW_NORMAL = 1; + static const int SW_SHOWMINIMIZED = 2; + static uint runProcessElevated(const QString &path, const QStringList ¶meters = QStringList(), const QString &workingDir = QString(), - bool hide = false, bool aWait = true); + int nShow = SW_SHOWMINIMIZED, bool aWait = true); }; #endif // WINCOMMANDER_H \ No newline at end of file diff --git a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp b/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp index 942b628..55dfa63 100644 --- a/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp +++ b/3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp @@ -40,6 +40,11 @@ QJsonObject JsonEditor::OpenEditor() { auto string = jsonEditor->toPlainText(); while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) { + if (string.isEmpty()) { + resultCode = QDialog::Accepted; + final = {}; + break; + } QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("You must correct these errors before continuing.")); resultCode = this->exec(); diff --git a/db/ConfigBuilder.cpp b/db/ConfigBuilder.cpp index 035c6fd..02b7ffa 100644 --- a/db/ConfigBuilder.cpp +++ b/db/ConfigBuilder.cpp @@ -13,6 +13,24 @@ namespace NekoRay { + QStringList getAutoBypassExternalProcessPaths(const QSharedPointer &result) { + QStringList paths; + for (const auto &ext: result->exts) { + auto path = ext.first.program; + if (path.trimmed().isEmpty()) continue; + paths << path.replace("\\", "/"); + } + return paths; + } + + QString genTunName() { + auto tun_name = "nekoray-tun"; +#ifdef Q_OS_MACOS + tun_name = "utun9"; +#endif + return tun_name; + } + void MergeJson(const QJsonObject &custom, QJsonObject &outbound) { // 合并 if (custom.isEmpty()) return; @@ -215,8 +233,8 @@ namespace NekoRay { {"tag", "block"}, }; - // DNS Routing - if (dataStore->dns_routing && !status->forTest) { + // DNS out + if (!status->forTest) { QJsonObject dnsOut; dnsOut["protocol"] = "dns"; dnsOut["tag"] = "dns-out"; @@ -300,7 +318,7 @@ namespace NekoRay { // Routing QJsonObject routing; routing["domainStrategy"] = dataStore->domain_strategy; - routing["domainMatcher"] = dataStore->domain_matcher == DomainMatcher::MPH ? "mph" : "linear"; + routing["domainMatcher"] = "mph"; if (status->forTest) routing["domainStrategy"] = "AsIs"; // final add user rule (block) @@ -457,7 +475,12 @@ namespace NekoRay { // chain rules: this auto ext_mapping_port = 0; auto ext_socks_port = 0; - auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile, dataStore->running_spmode == SystemProxyMode::VPN); + auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile); + if (thisExternalStat < 0) { + status->result->error = "This configuration cannot be set automatically, please try another."; + return {}; + } + // determine port if (thisExternalStat > 0) { if (ent->type == "custom") { @@ -658,9 +681,28 @@ namespace NekoRay { }, }; } - // apply domain_strategy - inboundObj["domain_strategy"] = dataStore->outbound_domain_strategy; - // + inboundObj["domain_strategy"] = dataStore->domain_strategy; + status->inbounds += inboundObj; + } + + // tun-in + if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn) { + QJsonObject inboundObj; + inboundObj["tag"] = "tun-in"; + inboundObj["type"] = "tun"; + inboundObj["interface_name"] = genTunName(); + inboundObj["auto_route"] = true; + inboundObj["endpoint_independent_nat"] = true; + inboundObj["mtu"] = dataStore->vpn_mtu; + inboundObj["stack"] = Preset::SingBox::VpnImplementation.value(dataStore->vpn_implementation); + inboundObj["strict_route"] = dataStore->vpn_strict_route; + inboundObj["inet4_address"] = "172.19.0.1/28"; + if (dataStore->vpn_ipv6) inboundObj["inet4_address"] = "fdfe:dcba:9876::1/126"; + if (dataStore->sniffing_mode != SniffingMode::DISABLE) { + inboundObj["sniff"] = true; + inboundObj["sniff_override_destination"] = dataStore->sniffing_mode == SniffingMode::FOR_DESTINATION; + } + inboundObj["domain_strategy"] = dataStore->domain_strategy; status->inbounds += inboundObj; } @@ -805,7 +847,12 @@ namespace NekoRay { // Routing // dns hijack - if (!status->forTest) status->routingRules += QJsonObject{{"protocol", "dns"}, {"outbound", "dns-out"}}; + if (!status->forTest) { + status->routingRules += QJsonObject{ + {"protocol", "dns"}, + {"outbound", "dns-out"}, + }; + } // sing-box routing rule object auto add_rule_route = [&](const QStringList &list, bool isIP, const QString &out) { @@ -823,6 +870,49 @@ namespace NekoRay { add_rule_route(status->domainListRemote, false, tagProxy); add_rule_route(status->domainListDirect, false, "bypass"); + // built-in rules + status->routingRules += QJsonObject{ + {"network", "udp"}, + {"port", QJsonArray{135, 137, 138, 5353}}, + {"outbound", "block"}, + }; + status->routingRules += QJsonObject{ + {"ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}}, + {"outbound", "block"}, + }; + status->routingRules += QJsonObject{ + {"source_ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}}, + {"outbound", "block"}, + }; + + // tun user rule + if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn) { + auto match_out = NekoRay::dataStore->vpn_rule_white ? "proxy" : "bypass"; + + QString process_name_rule = dataStore->vpn_rule_process.trimmed(); + if (!process_name_rule.isEmpty()) { + auto arr = SplitLinesSkipSharp(process_name_rule); + QJsonObject rule{{"outbound", match_out}, + {"process_name", QList2QJsonArray(arr)}}; + status->routingRules += rule; + } + + QString cidr_rule = dataStore->vpn_rule_cidr.trimmed(); + if (!cidr_rule.isEmpty()) { + auto arr = SplitLinesSkipSharp(cidr_rule); + QJsonObject rule{{"outbound", match_out}, + {"ip_cidr", QList2QJsonArray(arr)}}; + status->routingRules += rule; + } + + auto autoBypassExternalProcessPaths = getAutoBypassExternalProcessPaths(status->result); + if (!autoBypassExternalProcessPaths.isEmpty()) { + QJsonObject rule{{"outbound", "bypass"}, + {"process_name", QList2QJsonArray(autoBypassExternalProcessPaths)}}; + status->routingRules += rule; + } + } + // geopath auto geoip = FindCoreAsset("geoip.db"); auto geosite = FindCoreAsset("geosite.db"); @@ -836,7 +926,7 @@ namespace NekoRay { QJSONARRAY_ADD(routingRules, status->routingRules) auto routeObj = QJsonObject{ {"rules", routingRules}, - {"auto_detect_interface", NekoRay::dataStore->core_box_auto_detect_interface}, + {"auto_detect_interface", dataStore->spmode_vpn}, { "geoip", QJsonObject{ @@ -873,9 +963,10 @@ namespace NekoRay { } QString WriteVPNSingBoxConfig() { + // tun user rule auto match_out = NekoRay::dataStore->vpn_rule_white ? "nekoray-socks" : "direct"; auto no_match_out = NekoRay::dataStore->vpn_rule_white ? "direct" : "nekoray-socks"; - // user rule + QString process_name_rule = dataStore->vpn_rule_process.trimmed(); if (!process_name_rule.isEmpty()) { auto arr = SplitLinesSkipSharp(process_name_rule); @@ -883,6 +974,7 @@ namespace NekoRay { {"process_name", QList2QJsonArray(arr)}}; process_name_rule = "," + QJsonObject2QString(rule, false); } + QString cidr_rule = dataStore->vpn_rule_cidr.trimmed(); if (!cidr_rule.isEmpty()) { auto arr = SplitLinesSkipSharp(cidr_rule); @@ -890,11 +982,9 @@ namespace NekoRay { {"ip_cidr", QList2QJsonArray(arr)}}; cidr_rule = "," + QJsonObject2QString(rule, false); } - // tun name - auto tun_name = "nekoray-tun"; -#ifdef Q_OS_MACOS - tun_name = "utun9"; -#endif + + // TODO bypass ext core process path? + // auth QString socks_user_pass; if (dataStore->inbound_auth->NeedAuth()) { @@ -910,7 +1000,7 @@ namespace NekoRay { .replace("%STACK%", Preset::SingBox::VpnImplementation.value(dataStore->vpn_implementation)) .replace("%PROCESS_NAME_RULE%", process_name_rule) .replace("%CIDR_RULE%", cidr_rule) - .replace("%TUN_NAME%", tun_name) + .replace("%TUN_NAME%", genTunName()) .replace("%STRICT_ROUTE%", dataStore->vpn_strict_route ? "true" : "false") .replace("%SOCKS_USER_PASS%", socks_user_pass) .replace("%FINAL_OUT%", no_match_out) diff --git a/fmt/AbstractBean.hpp b/fmt/AbstractBean.hpp index e8a3c69..88aec9b 100644 --- a/fmt/AbstractBean.hpp +++ b/fmt/AbstractBean.hpp @@ -54,7 +54,7 @@ namespace NekoRay::fmt { // - virtual int NeedExternal(bool isFirstProfile, bool isVPN) { return 0; }; + virtual int NeedExternal(bool isFirstProfile) { return 0; }; virtual CoreObjOutboundBuildResult BuildCoreObjV2Ray() { return {}; }; diff --git a/fmt/Bean2External.cpp b/fmt/Bean2External.cpp index d7d194b..dc9c7b6 100644 --- a/fmt/Bean2External.cpp +++ b/fmt/Bean2External.cpp @@ -20,38 +20,50 @@ auto TempFile = QFileInfo(f).absoluteFilePath(); namespace NekoRay::fmt { + // -1: Cannot use this config // 0: Internal // 1: Mapping External // 2: Direct External - int NaiveBean::NeedExternal(bool isFirstProfile, bool isVPN) { - if (isFirstProfile && !isVPN) { + int NaiveBean::NeedExternal(bool isFirstProfile) { + if (isFirstProfile) { + if (dataStore->spmode_vpn) { + return 1; + } return 2; } return 1; } - int HysteriaBean::NeedExternal(bool isFirstProfile, bool isVPN) { + int HysteriaBean::NeedExternal(bool isFirstProfile) { + auto hysteriaCore = [=] { + if (isFirstProfile) { + if (dataStore->spmode_vpn && protocol != hysteria_protocol_facktcp && hopPort.trimmed().isEmpty()) { + return 1; + } + return 2; + } else { + if (protocol == hysteria_protocol_facktcp || !hopPort.trimmed().isEmpty()) { + return -1; + } + } + return 1; + }; + if (IS_NEKO_BOX) { if (protocol == hysteria_protocol_udp && hopPort.trimmed().isEmpty()) { // sing-box support return 0; } else { // hysteria core support - if (isFirstProfile && !isVPN) { - return 2; - } - return 1; + return hysteriaCore(); } } else { - if (isFirstProfile && !isVPN) { - return 2; - } - return 1; + return hysteriaCore(); } } - int CustomBean::NeedExternal(bool isFirstProfile, bool isVPN) { + int CustomBean::NeedExternal(bool isFirstProfile) { if (core == "internal" || core == "internal-full") return 0; return 1; } @@ -99,8 +111,8 @@ namespace NekoRay::fmt { // determine server format auto is_direct = external_stat == 2; - auto sni2 = sni; - if (sni.isEmpty() && is_direct) sni2 = serverAddress; + auto sniGen = sni; + if (sni.isEmpty() && !IsIpAddress(serverAddress)) sniGen = serverAddress; auto server = serverAddress; if (!hopPort.trimmed().isEmpty()) { @@ -130,7 +142,7 @@ namespace NekoRay::fmt { if (protocol == hysteria_protocol_facktcp) config["protocol"] = "faketcp"; if (protocol == hysteria_protocol_wechat_video) config["protocol"] = "wechat-video"; - if (!sni2.isEmpty()) config["server_name"] = sni2; + if (!sniGen.isEmpty()) config["server_name"] = sniGen; if (!alpn.isEmpty()) config["alpn"] = alpn; if (!caText.trimmed().isEmpty()) { diff --git a/fmt/CustomBean.hpp b/fmt/CustomBean.hpp index 4466eee..80e2fb0 100644 --- a/fmt/CustomBean.hpp +++ b/fmt/CustomBean.hpp @@ -31,7 +31,7 @@ namespace NekoRay::fmt { return core; }; - QString DisplayCoreType() override { return NeedExternal(false, false) ? core : software_core_name; }; + QString DisplayCoreType() override { return NeedExternal(false) ? core : software_core_name; }; QString DisplayAddress() override { if (core == "internal") { @@ -47,7 +47,7 @@ namespace NekoRay::fmt { return AbstractBean::DisplayAddress(); }; - int NeedExternal(bool isFirstProfile, bool isVPN) override; + int NeedExternal(bool isFirstProfile) override; ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; diff --git a/fmt/HysteriaBean.hpp b/fmt/HysteriaBean.hpp index 0aed776..04ec7fa 100644 --- a/fmt/HysteriaBean.hpp +++ b/fmt/HysteriaBean.hpp @@ -63,11 +63,11 @@ namespace NekoRay::fmt { return ::DisplayAddress(serverAddress, serverPort); } - QString DisplayCoreType() override { return NeedExternal(false, false) == 0 ? software_core_name : "Hysteria"; }; + QString DisplayCoreType() override { return NeedExternal(false) == 0 ? software_core_name : "Hysteria"; }; QString DisplayType() override { return "Hysteria"; }; - int NeedExternal(bool isFirstProfile, bool isVPN) override; + int NeedExternal(bool isFirstProfile) override; ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; diff --git a/fmt/NaiveBean.hpp b/fmt/NaiveBean.hpp index c9a7fbd..fa8bf31 100644 --- a/fmt/NaiveBean.hpp +++ b/fmt/NaiveBean.hpp @@ -27,7 +27,7 @@ namespace NekoRay::fmt { QString DisplayType() override { return "Naive"; }; - int NeedExternal(bool isFirstProfile, bool isVPN) override; + int NeedExternal(bool isFirstProfile) override; ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; diff --git a/go/cmd/nekobox_core/main.go b/go/cmd/nekobox_core/main.go index 87ed779..5777d58 100644 --- a/go/cmd/nekobox_core/main.go +++ b/go/cmd/nekobox_core/main.go @@ -17,9 +17,6 @@ func main() { fmt.Println("sing-box-extra:", boxbox.Version(), "NekoBox:", neko_common.Version_neko) fmt.Println() - // local DNS transport - _ = os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",netdns=go") - // nekobox_core if len(os.Args) > 1 && os.Args[1] == "nekobox" { neko_common.RunMode = neko_common.RunMode_NekoBox_Core diff --git a/libs/package_appimage.sh b/libs/package_appimage.sh index bbcd9f0..b785b8a 100644 --- a/libs/package_appimage.sh +++ b/libs/package_appimage.sh @@ -1,6 +1,6 @@ #!/bin/bash -sudo apt install fuse -y +sudo apt-get install fuse -y cp -r linux64 nekoray.AppDir @@ -25,7 +25,7 @@ chmod +x nekoray.AppDir/AppRun # build -curl -L -O https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage +curl -fLSO https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage chmod +x appimagetool-x86_64.AppImage ./appimagetool-x86_64.AppImage nekoray.AppDir diff --git a/main/Const.hpp b/main/Const.hpp index 7cf0633..2d52d92 100644 --- a/main/Const.hpp +++ b/main/Const.hpp @@ -16,14 +16,6 @@ namespace NekoRay { }; } - namespace SystemProxyMode { - enum SystemProxyMode { - DISABLE, - SYSTEM_PROXY, - VPN, - }; - } - namespace CoreType { enum CoreType { V2RAY, diff --git a/main/NekoRay.cpp b/main/NekoRay.cpp index 9d02ca3..6d0dab8 100644 --- a/main/NekoRay.cpp +++ b/main/NekoRay.cpp @@ -30,7 +30,6 @@ namespace NekoRay { _add(new configItem("remote_dns_strategy", &remote_dns_strategy, itemType::string)); _add(new configItem("direct_dns", &direct_dns, itemType::string)); _add(new configItem("direct_dns_strategy", &direct_dns_strategy, itemType::string)); - _add(new configItem("domain_matcher", &domain_matcher, itemType::integer)); _add(new configItem("domain_strategy", &domain_strategy, itemType::string)); _add(new configItem("outbound_domain_strategy", &outbound_domain_strategy, itemType::string)); _add(new configItem("sniffing_mode", &sniffing_mode, itemType::integer)); @@ -46,7 +45,7 @@ namespace NekoRay { _add(new configItem("remember_id", &remember_id, itemType::integer)); _add(new configItem("remember_enable", &remember_enable, itemType::boolean)); _add(new configItem("language", &language, itemType::integer)); - _add(new configItem("spmode", &remember_spmode, itemType::integer)); + _add(new configItem("spmode2", &remember_spmode, itemType::stringList)); _add(new configItem("skip_cert", &skip_cert, itemType::boolean)); _add(new configItem("hk_mw", &hotkey_mainwindow, itemType::string)); _add(new configItem("hk_group", &hotkey_group, itemType::string)); @@ -74,14 +73,13 @@ namespace NekoRay { _add(new configItem("max_log_line", &max_log_line, itemType::integer)); _add(new configItem("splitter_state", &splitter_state, itemType::string)); _add(new configItem("utlsFingerprint", &utlsFingerprint, itemType::string)); - _add(new configItem("core_box_auto_detect_interface", &core_box_auto_detect_interface, itemType::boolean)); _add(new configItem("core_box_clash_api", &core_box_clash_api, itemType::integer)); _add(new configItem("core_box_clash_api_secret", &core_box_clash_api_secret, itemType::string)); _add(new configItem("core_box_underlying_dns", &core_box_underlying_dns, itemType::string)); _add(new configItem("core_ray_direct_dns", &core_ray_direct_dns, itemType::boolean)); -#ifndef Q_OS_WIN + _add(new configItem("vpn_internal_tun", &vpn_internal_tun, itemType::boolean)); +#ifdef Q_OS_WIN _add(new configItem("core_ray_windows_disable_auto_interface", &core_ray_windows_disable_auto_interface, itemType::boolean)); - _add(new configItem("vpn_already_admin", &vpn_already_admin, itemType::boolean)); #endif } diff --git a/main/NekoRay.hpp b/main/NekoRay.hpp index 282c37b..2629b89 100644 --- a/main/NekoRay.hpp +++ b/main/NekoRay.hpp @@ -14,5 +14,6 @@ namespace NekoRay { } // namespace NekoRay #define IS_NEKO_BOX (NekoRay::coreType == NekoRay::CoreType::SING_BOX) +#define IS_NEKO_BOX_INTERNAL_TUN (IS_NEKO_BOX && NekoRay::dataStore->vpn_internal_tun) #define ROUTES_PREFIX_NAME QString(IS_NEKO_BOX ? "routes_box" : "routes") #define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/") diff --git a/main/NekoRay_DataStore.hpp b/main/NekoRay_DataStore.hpp index 5fcbd25..99b3d8f 100644 --- a/main/NekoRay_DataStore.hpp +++ b/main/NekoRay_DataStore.hpp @@ -54,7 +54,8 @@ namespace NekoRay { int started_id = -1919; bool core_running = false; bool core_prepare_exit = false; - int running_spmode = NekoRay::SystemProxyMode::DISABLE; + bool spmode_vpn = false; + bool spmode_system_proxy = false; bool need_keep_vpn_off = false; QStringList ignoreConnTag = {}; @@ -104,7 +105,7 @@ namespace NekoRay { QString utlsFingerprint = ""; // Remember - int remember_spmode = NekoRay::SystemProxyMode::DISABLE; + QStringList remember_spmode = {}; int remember_id = -1919; bool remember_enable = false; @@ -127,18 +128,17 @@ namespace NekoRay { QString domain_strategy = "AsIs"; QString outbound_domain_strategy = "AsIs"; int sniffing_mode = SniffingMode::FOR_ROUTING; - int domain_matcher = DomainMatcher::MPH; QString custom_route_global = "{\"rules\": []}"; QString active_routing = "Default"; // VPN + bool vpn_internal_tun = true; int vpn_implementation = 0; int vpn_mtu = 9000; bool vpn_ipv6 = false; bool vpn_hide_console = false; bool vpn_strict_route = false; bool vpn_rule_white = false; - bool vpn_already_admin = false; // not saved on Windows QString vpn_rule_process = ""; QString vpn_rule_cidr = ""; @@ -149,7 +149,6 @@ namespace NekoRay { QString hotkey_system_proxy_menu = ""; // Core - bool core_box_auto_detect_interface = true; int core_box_clash_api = -9090; QString core_box_clash_api_secret = ""; QString core_box_underlying_dns = ""; diff --git a/translations/fa_IR.ts b/translations/fa_IR.ts index 5d3847b..66c4e5e 100644 --- a/translations/fa_IR.ts +++ b/translations/fa_IR.ts @@ -91,19 +91,11 @@ Use proxy when updating subscription استفاده از پروکسی زمانی که اشتراک را بروزرسانی می کنید - - Language - زبان - Security امنیت security امنیت - - Insecure hint - اشاره ناامن - Statistics refresh rate نرخ تازه سازی آمار ترافیک @@ -357,10 +349,6 @@ For NekoBox, this rewrites the underlying(localhost) DNS in VPN mode, normal mod Network Settings (%1) تنظیمات شبکه (1%) - - Security Settings - تنظیمات امنیت - When enabled, V2Ray will not check the validity of the TLS certificate provided by the remote host (the security is equivalent to plaintext) @@ -503,14 +491,6 @@ These settings can be changed later. Disable غیرفعال کردن - - The sniffing result is used for routing - - - - The sniffing result is used for destination - - Remote DNS @@ -523,14 +503,6 @@ These settings can be changed later. Enable DNS Routing فعال کردن مسیریابی DNS - - V2Ray Domain Strategy - استراتژی دامنه - - - Matcher - - Block @@ -607,16 +579,28 @@ These settings can be changed later. Default Outbound - - Query Strategy - - Remote - Inbound & Outbound Domain Strategy + DNS Query Strategy + + + + Domain Strategy + + + + Server Address Strategy + + + + Sniff result for routing + + + + Sniff result for destination @@ -682,11 +666,12 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun لغو کردن - Don't ask for privilege elevation + Internal Tun - Already Admin + Add a tun inbound to the profile startup, instead of using two processes. +This needs to be run NekoBox with administrator privileges. @@ -1336,14 +1321,6 @@ Split by line. Clear پاک کردن - - End - پایان - - - Active - فعال - Start: %1 End: %2 @@ -1373,10 +1350,6 @@ End: %2 Starting profile %1 اغاز پروفایل %1 - - Profile is insecure: %1 - پروفایل ناامن می باشد: %1 - Stopping profile %1 متوقف کردن پروفایل %1 @@ -1405,13 +1378,13 @@ End: %2 Restart nekoray to take effect. برای اعمال تغییرات nekoray را مجددا راه اندازی کنید. + + Please run NekoBox as admin + + ProxyItem - - Remove - حذف کردن - Confirmation تائیدیه diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 5b9a742..caa2474 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -95,10 +95,6 @@ Security 安全 - - Insecure hint - 提示不安全的配置 - Statistics refresh rate 流量统计刷新率 @@ -494,11 +490,11 @@ These settings can be changed later. 流量探测 - The sniffing result is used for routing + Sniff result for routing 探测结果用于路由判断 - The sniffing result is used for destination + Sniff result for destination 探测结果用于目标地址 @@ -513,14 +509,6 @@ These settings can be changed later. Enable DNS Routing 启用 DNS 路由 - - V2Ray Domain Strategy - 域名策略 - - - Matcher - 域名匹配器 - Block 阻止 @@ -597,17 +585,21 @@ These settings can be changed later. Default Outbound 默认出站 - - Query Strategy - DNS 查询策略 - Remote 远程 - Inbound & Outbound Domain Strategy - 出入站域名策略 + DNS Query Strategy + DNS 查询策略 + + + Domain Strategy + 域名策略 + + + Server Address Strategy + 服务器地址策略 @@ -675,12 +667,14 @@ https://matsuridayo.github.io/n-configuration/#vpn-tun 取消 - Don't ask for privilege elevation - 不请求特权提升 + Internal Tun + 内部 Tun - Already Admin - 已是管理员 + Add a tun inbound to the profile startup, instead of using two processes. +This needs to be run NekoBox with administrator privileges. + 在配置文件启动时添加一个tun inbound,而不是使用两个进程。 +这需要以管理员权限运行NekoBox。 @@ -1189,10 +1183,6 @@ End: %2 Move %1 item(s) 移动 %1 个项目 - - Profile is insecure: %1 - 配置不安全: %1 - Remove Unavailable 删除不可用的配置 @@ -1395,6 +1385,10 @@ Split by line. Restart nekoray to take effect. 重启 nekoray 生效。 + + Please run NekoBox as admin + 请以管理员权限运行 NekoBox + ProxyItem @@ -1498,26 +1492,6 @@ Release note: Chain Proxy 链式代理 - - The configuration (insecure) can be detected and identified, the transmission is fully visible to the censor and is not resistant to man-in-the-middle tampering with the content of the communication. - 该配置 (不安全) 能够被检测识别,传输的内容对审查者完全可见,并且无法抵抗中间人篡改通讯内容. - - - This configuration (Shadowsocks streaming cipher) can be accurately proactively detected and decrypted by censors without requiring a password, and cannot be mitigated by turning on IV replay filters on the server side. - -Learn more: https://github.com/net4people/bbs/issues/24 - 该配置 (Shadowsocks 流式密码) 可以被准确地主动探测、在不需要密码的情况下被审查者解密流量, 且服务端开启 IV 重放过滤器也无法缓解. - -了解更多: https://github.com/net4people/bbs/issues/24 - - - This configuration (VMess MD5 authentication) has been deprecated by upstream because of its questionable resistance to tampering and concealment. - -As of January 1, 2022, compatibility with MD5 authentication information will be disabled on the server side by default. Any client using MD5 authentication information will not be able to connect to a server with VMess MD5 authentication information disabled. - 该配置 (VMess MD5 认证) 抗篡改能力存疑, 隐蔽性存疑, 已被上游废弃. - -自 2022 年 1 月 1 日起, 服务器端将默认禁用对于 MD5 认证信息 的兼容. 任何使用 MD5 认证信息的客户端将无法连接到禁用 VMess MD5 认证信息的服务器端. - Requesting subscription: %1 正在请求订阅: %1 @@ -1534,10 +1508,6 @@ As of January 1, 2022, compatibility with MD5 authentication information will be Change of %1: %1 变化: - - This profile is cleartext, don't use it if the server is not in your local network. - 该配置为明文传输,如果服务器不在本地局域网,请不要使用。 - Select 选择 diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp index e97b93d..b6b4595 100644 --- a/ui/dialog_basic_settings.cpp +++ b/ui/dialog_basic_settings.cpp @@ -392,7 +392,6 @@ void DialogBasicSettings::on_core_settings_clicked() { w->setLayout(layout); // auto line = -1; - QCheckBox *core_box_auto_detect_interface; QCheckBox *core_box_enable_clash_api; MyLineEdit *core_box_clash_api; MyLineEdit *core_box_clash_api_secret; @@ -412,12 +411,6 @@ void DialogBasicSettings::on_core_settings_clicked() { layout->addWidget(core_box_underlying_dns, line, 1); // if (IS_NEKO_BOX) { - auto core_box_auto_detect_interface_l = new QLabel("auto_detect_interface"); - core_box_auto_detect_interface = new QCheckBox; - core_box_auto_detect_interface->setChecked(NekoRay::dataStore->core_box_auto_detect_interface); - layout->addWidget(core_box_auto_detect_interface_l, ++line, 0); - layout->addWidget(core_box_auto_detect_interface, line, 1); - // auto core_box_enable_clash_api_l = new QLabel("Enable Clash API"); core_box_enable_clash_api = new QCheckBox; core_box_enable_clash_api->setChecked(NekoRay::dataStore->core_box_clash_api > 0); @@ -460,7 +453,6 @@ void DialogBasicSettings::on_core_settings_clicked() { connect(box, &QDialogButtonBox::accepted, w, [=] { NekoRay::dataStore->core_box_underlying_dns = core_box_underlying_dns->text(); if (IS_NEKO_BOX) { - NekoRay::dataStore->core_box_auto_detect_interface = core_box_auto_detect_interface->isChecked(); NekoRay::dataStore->core_box_clash_api = core_box_clash_api->text().toInt() * (core_box_enable_clash_api->isChecked() ? 1 : -1); NekoRay::dataStore->core_box_clash_api_secret = core_box_clash_api_secret->text(); } else { diff --git a/ui/dialog_manage_routes.cpp b/ui/dialog_manage_routes.cpp index 5f289f4..5964944 100644 --- a/ui/dialog_manage_routes.cpp +++ b/ui/dialog_manage_routes.cpp @@ -31,16 +31,15 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(ne title_base = windowTitle(); if (IS_NEKO_BOX) { - ui->domain_v2ray->setVisible(false); ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy); + ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy); } else { - ui->domain_v2ray->setVisible(true); ui->outbound_domain_strategy->addItems({"AsIs", "UseIPv4", "UseIPv6", "PreferIPv4", "PreferIPv6"}); + ui->domainStrategyCombo->addItems({"AsIs", "IPIfNonMatch", "IPOnDemand"}); } // ui->sniffing_mode->setCurrentIndex(NekoRay::dataStore->sniffing_mode); ui->outbound_domain_strategy->setCurrentText(NekoRay::dataStore->outbound_domain_strategy); - ui->domainMatcherCombo->setCurrentIndex(NekoRay::dataStore->domain_matcher); ui->domainStrategyCombo->setCurrentText(NekoRay::dataStore->domain_strategy); ui->dns_routing->setChecked(NekoRay::dataStore->dns_routing); ui->dns_remote->setText(NekoRay::dataStore->remote_dns); @@ -88,7 +87,6 @@ DialogManageRoutes::~DialogManageRoutes() { void DialogManageRoutes::accept() { NekoRay::dataStore->sniffing_mode = ui->sniffing_mode->currentIndex(); - NekoRay::dataStore->domain_matcher = ui->domainMatcherCombo->currentIndex(); NekoRay::dataStore->domain_strategy = ui->domainStrategyCombo->currentText(); NekoRay::dataStore->outbound_domain_strategy = ui->outbound_domain_strategy->currentText(); NekoRay::dataStore->dns_routing = ui->dns_routing->isChecked(); diff --git a/ui/dialog_manage_routes.ui b/ui/dialog_manage_routes.ui index f3282f5..93b56e8 100644 --- a/ui/dialog_manage_routes.ui +++ b/ui/dialog_manage_routes.ui @@ -21,12 +21,6 @@ - - - 0 - 0 - - Sniffing Mode @@ -41,33 +35,38 @@ - The sniffing result is used for routing + Sniff result for routing - The sniffing result is used for destination + Sniff result for destination - - - Qt::Vertical + + + For V2Ray, it sets routing.domainStrategy +For sing-box, it sets inbound.domain_strategy + + + Domain Strategy + + + + + + + false - - - 0 - 0 - - - Inbound & Outbound Domain Strategy + Server Address Strategy @@ -111,12 +110,6 @@ - - - 0 - 0 - - Enable DNS Routing @@ -124,81 +117,19 @@ + + + 0 + 0 + + - Query Strategy + DNS Query Strategy - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - V2Ray Domain Strategy - - - - - - - false - - - - AsIs - - - - - IPIfNonMatch - - - - - IPOnDemand - - - - - - - - Matcher - - - - - - - - Original - - - - - Minimal Perfect Hash Matcher - - - - - - - @@ -461,13 +392,8 @@ sniffing_mode - outbound_domain_strategy dns_remote dns_direct - dns_routing - queryStrategy - domainStrategyCombo - domainMatcherCombo preset custom_route_edit def_outbound diff --git a/ui/dialog_vpn_settings.cpp b/ui/dialog_vpn_settings.cpp index b58c7a5..1275332 100644 --- a/ui/dialog_vpn_settings.cpp +++ b/ui/dialog_vpn_settings.cpp @@ -18,13 +18,12 @@ DialogVPNSettings::DialogVPNSettings(QWidget *parent) : QDialog(parent), ui(new ui->vpn_mtu->setCurrentText(Int2String(NekoRay::dataStore->vpn_mtu)); ui->vpn_ipv6->setChecked(NekoRay::dataStore->vpn_ipv6); ui->hide_console->setChecked(NekoRay::dataStore->vpn_hide_console); - ui->vpn_already_admin->setChecked(NekoRay::dataStore->vpn_already_admin); -#ifdef Q_OS_WIN - ui->vpn_already_admin->setVisible(false); -#else +#ifndef Q_OS_WIN ui->hide_console->setVisible(false); #endif ui->strict_route->setChecked(NekoRay::dataStore->vpn_strict_route); + ui->single_core->setVisible(IS_NEKO_BOX); + ui->single_core->setChecked(NekoRay::dataStore->vpn_internal_tun); // D_LOAD_STRING(vpn_rule_cidr) D_LOAD_STRING(vpn_rule_process) @@ -56,7 +55,7 @@ void DialogVPNSettings::accept() { NekoRay::dataStore->vpn_hide_console = ui->hide_console->isChecked(); NekoRay::dataStore->vpn_strict_route = ui->strict_route->isChecked(); NekoRay::dataStore->vpn_rule_white = ui->whitelist_mode->isChecked(); - NekoRay::dataStore->vpn_already_admin = ui->vpn_already_admin->isChecked(); + NekoRay::dataStore->vpn_internal_tun = ui->single_core->isChecked(); // D_SAVE_STRING_QTEXTEDIT(vpn_rule_cidr) D_SAVE_STRING_QTEXTEDIT(vpn_rule_process) diff --git a/ui/dialog_vpn_settings.ui b/ui/dialog_vpn_settings.ui index e45c0c4..3deb617 100644 --- a/ui/dialog_vpn_settings.ui +++ b/ui/dialog_vpn_settings.ui @@ -86,13 +86,6 @@ - - - - Hide Console - - - @@ -100,13 +93,6 @@ - - - - FakeDNS - - - @@ -115,12 +101,34 @@ - + + + FakeDNS + + + + + + + Qt::Vertical + + + + + + + Hide Console + + + + + - Don't ask for privilege elevation + Add a tun inbound to the profile startup, instead of using two processes. +This needs to be run NekoBox with administrator privileges. - Already Admin + Internal Tun @@ -198,6 +206,19 @@ + + vpn_implementation + vpn_mtu + vpn_ipv6 + strict_route + fake_dns + hide_console + single_core + vpn_rule_cidr + vpn_rule_process + whitelist_mode + troubleshooting + diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index e511d64..351dd45 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -94,12 +94,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi software_name = "NekoBox"; software_core_name = "sing-box"; // replace default values - if (NekoRay::dataStore->log_level == "warning") { - NekoRay::dataStore->log_level = "info"; - } - if (!Preset::SingBox::DomainStrategy.contains(NekoRay::dataStore->outbound_domain_strategy)) { - NekoRay::dataStore->outbound_domain_strategy = ""; - } + if (NekoRay::dataStore->log_level == "warning") NekoRay::dataStore->log_level = "info"; + if (!Preset::SingBox::DomainStrategy.contains(NekoRay::dataStore->domain_strategy)) NekoRay::dataStore->domain_strategy = ""; + if (!Preset::SingBox::DomainStrategy.contains(NekoRay::dataStore->outbound_domain_strategy)) NekoRay::dataStore->outbound_domain_strategy = ""; // if (QDir("dashboard").isEmpty()) { QDir().mkdir("dashboard"); @@ -334,20 +331,19 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi MW_dialog_message("", "UpdateDataStore"); }); // - connect(ui->checkBox_VPN, &QCheckBox::clicked, this, [=](bool checked) { - neko_set_spmode(checked ? NekoRay::SystemProxyMode::VPN : NekoRay::SystemProxyMode::DISABLE); - }); - connect(ui->checkBox_SystemProxy, &QCheckBox::clicked, this, [=](bool checked) { - neko_set_spmode(checked ? NekoRay::SystemProxyMode::SYSTEM_PROXY : NekoRay::SystemProxyMode::DISABLE); - }); + connect(ui->checkBox_VPN, &QCheckBox::clicked, this, [=](bool checked) { neko_set_spmode_vpn(checked); }); + connect(ui->checkBox_SystemProxy, &QCheckBox::clicked, this, [=](bool checked) { neko_set_spmode_system_proxy(checked); }); connect(ui->menu_spmode, &QMenu::aboutToShow, this, [=]() { - ui->menu_spmode_disabled->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::DISABLE); - ui->menu_spmode_system_proxy->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY); - ui->menu_spmode_vpn->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN); + ui->menu_spmode_disabled->setChecked(!(NekoRay::dataStore->spmode_system_proxy || NekoRay::dataStore->spmode_vpn)); + ui->menu_spmode_system_proxy->setChecked(NekoRay::dataStore->spmode_system_proxy); + ui->menu_spmode_vpn->setChecked(NekoRay::dataStore->spmode_vpn); + }); + connect(ui->menu_spmode_system_proxy, &QAction::triggered, this, [=](bool checked) { neko_set_spmode_system_proxy(checked); }); + connect(ui->menu_spmode_vpn, &QAction::triggered, this, [=](bool checked) { neko_set_spmode_vpn(checked); }); + connect(ui->menu_spmode_disabled, &QAction::triggered, this, [=]() { + neko_set_spmode_system_proxy(false); + neko_set_spmode_vpn(false); }); - connect(ui->menu_spmode_system_proxy, &QAction::triggered, this, [=]() { neko_set_spmode(NekoRay::SystemProxyMode::SYSTEM_PROXY); }); - connect(ui->menu_spmode_vpn, &QAction::triggered, this, [=]() { neko_set_spmode(NekoRay::SystemProxyMode::VPN); }); - connect(ui->menu_spmode_disabled, &QAction::triggered, this, [=]() { neko_set_spmode(NekoRay::SystemProxyMode::DISABLE); }); connect(ui->menu_qr, &QAction::triggered, this, [=]() { display_qr_link(false); }); connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0); }); connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1); }); @@ -417,8 +413,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // Start last if (NekoRay::dataStore->remember_enable) { - if (NekoRay::dataStore->remember_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { - neko_set_spmode(NekoRay::dataStore->remember_spmode, false); + if (NekoRay::dataStore->remember_spmode.contains("system_proxy")) { + neko_set_spmode_system_proxy(true, false); + } + if (NekoRay::dataStore->remember_spmode.contains("vpn")) { + neko_set_spmode_vpn(true, false); } if (NekoRay::dataStore->remember_id >= 0) { runOnUiThread([=] { neko_start(NekoRay::dataStore->remember_id); }); @@ -514,7 +513,7 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info) auto changed = NekoRay::dataStore->Save(); if (info.contains("RouteChanged")) changed = true; refresh_proxy_list(); - if (info.contains("VPNChanged") && NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) { + if (info.contains("VPNChanged") && NekoRay::dataStore->spmode_vpn) { MessageBoxWarning(tr("VPN settings changed"), tr("Restart VPN to take effect.")); } else if (changed && NekoRay::dataStore->started_id >= 0 && QMessageBox::question(GetMessageBoxParent(), tr("Confirmation"), tr("Settings changed, restart proxy?")) == QMessageBox::StandardButton::Yes) { @@ -624,8 +623,9 @@ void MainWindow::on_commitDataRequest() { } void MainWindow::on_menu_exit_triggered() { - neko_set_spmode(NekoRay::SystemProxyMode::DISABLE, false); - if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) return; + neko_set_spmode_system_proxy(false, false); + neko_set_spmode_vpn(false, false); + if (NekoRay::dataStore->spmode_vpn) return; RegisterHotkey(true); // on_commitDataRequest(); @@ -638,13 +638,21 @@ void MainWindow::on_menu_exit_triggered() { if (exit_reason == 1) { QDir::setCurrent(QApplication::applicationDirPath()); QProcess::startDetached("./updater", QStringList{}); - } else if (exit_reason == 2) { + } else if (exit_reason == 2 || exit_reason == 3) { QDir::setCurrent(QApplication::applicationDirPath()); auto arguments = NekoRay::dataStore->argv; if (arguments.length() > 0) arguments.removeFirst(); auto isLauncher = qEnvironmentVariable("NKR_FROM_LAUNCHER") == "1"; if (isLauncher) arguments.prepend("--"); - QProcess::startDetached(isLauncher ? "./launcher" : QApplication::applicationFilePath(), arguments); + auto program = isLauncher ? "./launcher" : QApplication::applicationFilePath(); + + if (exit_reason == 3) { // restart as admin +#ifdef Q_OS_WIN + WinCommander::runProcessElevated(program, arguments, "", WinCommander::SW_NORMAL, false); +#endif + } else { + QProcess::startDetached(program, arguments); + } } tray->hide(); QCoreApplication::quit(); @@ -654,23 +662,11 @@ void MainWindow::on_menu_exit_triggered() { refresh_status(); \ return; -void MainWindow::neko_set_spmode(int mode, bool save) { - if (mode != NekoRay::dataStore->running_spmode) { - // DISABLE - - if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { - ClearSystemProxy(); - } else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) { - if (!StopVPNProcess()) { - neko_set_spmode_FAILED - } - } - - // ENABLE - - if (mode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { +void MainWindow::neko_set_spmode_system_proxy(bool enable, bool save) { + if (enable != NekoRay::dataStore->spmode_system_proxy) { + if (enable) { #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) - if (mode == NekoRay::SystemProxyMode::SYSTEM_PROXY && !IS_NEKO_BOX && !IsValidPort(NekoRay::dataStore->inbound_http_port)) { + if (!IS_NEKO_BOX && !IsValidPort(NekoRay::dataStore->inbound_http_port)) { auto btn = QMessageBox::warning(this, software_name, tr("Http inbound is not enabled, can't set system proxy."), "OK", tr("Settings"), "", 0, 0); @@ -682,29 +678,72 @@ void MainWindow::neko_set_spmode(int mode, bool save) { #endif auto socks_port = NekoRay::dataStore->inbound_socks_port; auto http_port = NekoRay::dataStore->inbound_http_port; - if (IS_NEKO_BOX) { - http_port = socks_port; - } + if (IS_NEKO_BOX) http_port = socks_port; SetSystemProxy(http_port, socks_port); - } else if (mode == NekoRay::SystemProxyMode::VPN) { - if (NekoRay::dataStore->need_keep_vpn_off) { - MessageBoxWarning(software_name, - tr("Current server is incompatible with VPN. Please stop the server first, enable VPN mode, and then restart.")); - neko_set_spmode_FAILED + } else { + ClearSystemProxy(); + } + } + + if (save) { + NekoRay::dataStore->remember_spmode.removeAll("system_proxy"); + if (enable) { + NekoRay::dataStore->remember_spmode.append("system_proxy"); + } + NekoRay::dataStore->Save(); + } + + NekoRay::dataStore->spmode_system_proxy = enable; + refresh_status(); +} + +void MainWindow::neko_set_spmode_vpn(bool enable, bool save) { + if (enable != NekoRay::dataStore->spmode_vpn) { + if (enable) { + if (IS_NEKO_BOX_INTERNAL_TUN) { +#ifdef Q_OS_WIN + if (!Windows_IsInAdmin()) { + auto n = QMessageBox::warning(GetMessageBoxParent(), software_name, tr("Please run NekoBox as admin"), QMessageBox::Yes | QMessageBox::No); + if (n == QMessageBox::Yes) { + this->exit_reason = 3; + on_menu_exit_triggered(); + } + neko_set_spmode_FAILED + } +#endif + // TODO check permission for Linux + } else { + if (NekoRay::dataStore->need_keep_vpn_off) { + MessageBoxWarning(software_name, tr("Current server is incompatible with VPN. Please stop the server first, enable VPN mode, and then restart.")); + neko_set_spmode_FAILED + } + if (!StartVPNProcess()) { + neko_set_spmode_FAILED + } } - if (!StartVPNProcess()) { - neko_set_spmode_FAILED + } else { + if (IS_NEKO_BOX_INTERNAL_TUN) { + // current core is sing-box + } else { + if (!StopVPNProcess()) { + neko_set_spmode_FAILED + } } } } if (save) { - NekoRay::dataStore->remember_spmode = mode; + NekoRay::dataStore->remember_spmode.removeAll("vpn"); + if (enable) { + NekoRay::dataStore->remember_spmode.append("vpn"); + } NekoRay::dataStore->Save(); } - NekoRay::dataStore->running_spmode = mode; + NekoRay::dataStore->spmode_vpn = enable; refresh_status(); + + if (NekoRay::dataStore->started_id >= 0) neko_start(NekoRay::dataStore->started_id); } void MainWindow::refresh_status(const QString &traffic_update) { @@ -736,8 +775,8 @@ void MainWindow::refresh_status(const QString &traffic_update) { if (IS_NEKO_BOX) inbound_txt = QString("Mixed: %1").arg(display_socks); ui->label_inbound->setText(inbound_txt); // - ui->checkBox_VPN->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN); - ui->checkBox_SystemProxy->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY); + ui->checkBox_VPN->setChecked(NekoRay::dataStore->spmode_vpn); + ui->checkBox_SystemProxy->setChecked(NekoRay::dataStore->spmode_system_proxy); if (select_mode) ui->label_running->setText("[" + tr("Select") + "]"); auto make_title = [=](bool isTray) { @@ -747,11 +786,8 @@ void MainWindow::refresh_status(const QString &traffic_update) { #endif if (select_mode) tt << "[" + tr("Select") + "]"; if (!title_error.isEmpty()) tt << "[" + title_error + "]"; - if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { - tt << "[" + tr("System Proxy") + "]"; - } else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) { - tt << "[" + tr("VPN Mode") + "]"; - } + if (NekoRay::dataStore->spmode_vpn) tt << "[VPN]"; + if (NekoRay::dataStore->spmode_system_proxy) tt << "[" + tr("System Proxy") + "]"; tt << software_name; if (!isTray) tt << "(" + QString(NKR_VERSION) + ")"; if (!NekoRay::dataStore->active_routing.isEmpty() && NekoRay::dataStore->active_routing != "Default") { @@ -762,12 +798,15 @@ void MainWindow::refresh_status(const QString &traffic_update) { }; auto icon_status_new = Icon::NONE; - if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { - icon_status_new = Icon::SYSTEM_PROXY; - } else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) { - icon_status_new = Icon::VPN; - } else if (!running.isNull()) { - icon_status_new = Icon::RUNNING; + + if (!running.isNull()) { + if (NekoRay::dataStore->spmode_vpn) { + icon_status_new = Icon::VPN; + } else if (NekoRay::dataStore->spmode_system_proxy) { + icon_status_new = Icon::SYSTEM_PROXY; + } else { + icon_status_new = Icon::RUNNING; + } } // refresh title & window icon @@ -1677,11 +1716,10 @@ bool MainWindow::StartVPNProcess() { runOnNewThread([=] { vpn_pid = 1; // TODO get pid? WinCommander::runProcessElevated(QApplication::applicationDirPath() + "/nekobox_core.exe", - {"--disable-color", "run", "-c", configPath}, - "", - NekoRay::dataStore->vpn_hide_console); // blocking + {"--disable-color", "run", "-c", configPath}, "", + NekoRay::dataStore->vpn_hide_console ? WinCommander::SW_HIDE : WinCommander::SW_SHOWMINIMIZED); // blocking vpn_pid = 0; - runOnUiThread([=] { neko_set_spmode(NekoRay::SystemProxyMode::DISABLE); }); + runOnUiThread([=] { neko_set_spmode_vpn(false); }); }); #else QFile::remove(protectPath); @@ -1695,7 +1733,7 @@ bool MainWindow::StartVPNProcess() { if (state == QProcess::NotRunning) { vpn_pid = 0; vpn_process->deleteLater(); - GetMainWindow()->neko_set_spmode(NekoRay::SystemProxyMode::DISABLE); + GetMainWindow()->neko_set_spmode_vpn(false); } }); // @@ -1704,11 +1742,7 @@ bool MainWindow::StartVPNProcess() { vpn_process->start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges") .arg("bash " + scriptPath)}); #else - if (NekoRay::dataStore->vpn_already_admin) { - vpn_process->start("bash", {scriptPath}); - } else { - vpn_process->start("pkexec", {"bash", scriptPath}); - } + vpn_process->start("pkexec", {"bash", scriptPath}); #endif vpn_process->waitForStarted(); vpn_pid = vpn_process->processId(); // actually it's pkexec or bash PID @@ -1731,14 +1765,10 @@ bool MainWindow::StopVPNProcess(bool unconditional) { p.start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges") .arg("pkill -2 -U 0 nekobox_core")}); #else - if (NekoRay::dataStore->vpn_already_admin) { - p.start("bash", {"kill", "-2", Int2String(vpn_pid)}); + if (unconditional) { + p.start("pkexec", {"killall", "-2", "nekobox_core"}); } else { - if (unconditional) { - p.start("pkexec", {"killall", "-2", "nekobox_core"}); - } else { - p.start("pkexec", {"pkill", "-2", "-P", Int2String(vpn_pid)}); - } + p.start("pkexec", {"pkill", "-2", "-P", Int2String(vpn_pid)}); } #endif p.waitForFinished(); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index bd9f97f..3b7f557 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -51,7 +51,9 @@ public: void neko_stop(bool crash = false); - void neko_set_spmode(int mode, bool save = true); + void neko_set_spmode_system_proxy(bool enable, bool save = true); + + void neko_set_spmode_vpn(bool enable, bool save = true); void show_log_impl(const QString &log);