diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 784f8b4..a6ee30e 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -25,25 +25,24 @@ jobs: runs-on: ubuntu-latest steps: - name: Checking out sources - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.4 - name: Go Status run: git ls-files go | xargs cat | sha1sum > go_status - name: Cache Common Download id: cache-common - uses: actions/cache@v3 + uses: actions/cache@v4.0.2 with: path: artifacts.tgz key: CommonCache-${{ matrix.cross_os }}-${{ matrix.cross_arch }}-${{ hashFiles('libs/*.sh', 'go_status', '*.txt') }} - name: Install Golang if: steps.cache-common.outputs.cache-hit != 'true' - uses: actions/setup-go@v3 + uses: actions/setup-go@v5.0.0 with: - go-version: ^1.21 + go-version: ^1.22 - name: Build golang parts if: steps.cache-common.outputs.cache-hit != 'true' shell: bash run: | - [ ${{ matrix.cross_os }} == public_res ] || ./libs/get_source.sh [ ${{ matrix.cross_os }} == public_res ] || GOOS=${{ matrix.cross_os }} GOARCH=${{ matrix.cross_arch }} ./libs/build_go.sh [ ${{ matrix.cross_os }} == public_res ] || exit 0 ./libs/build_public_res.sh @@ -51,7 +50,7 @@ jobs: if: steps.cache-common.outputs.cache-hit != 'true' run: tar czvf artifacts.tgz ./deployment - name: Uploading Artifact - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v4.3.3 with: name: NekoRay-${{ github.sha }}-Common-${{ matrix.cross_os }}-${{ matrix.cross_arch }} path: artifacts.tgz @@ -72,7 +71,7 @@ jobs: ACTIONS_ALLOW_UNSECURE_COMMANDS: true steps: - name: Checking out sources - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.4 with: submodules: "recursive" - name: Install MSVC compiler @@ -94,7 +93,7 @@ jobs: uses: seanmiddleditch/gha-setup-ninja@v3 - name: Cache Download id: cache-deps - uses: actions/cache@v3 + uses: actions/cache@v4.0.2 with: path: libs/deps key: DepsCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('libs/build_deps_*.sh') }}-Qt${{ matrix.qt_version }} @@ -141,7 +140,7 @@ jobs: shell: bash run: tar czvf artifacts.tgz ./deployment - name: Uploading Artifact - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v4.3.3 with: name: NekoRay-${{ github.sha }}-${{ matrix.platform }}-${{ matrix.arch }}-Qt${{ matrix.qt_version }} path: artifacts.tgz @@ -154,9 +153,9 @@ jobs: - build-go steps: - name: Checking out sources - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.4 - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: path: download-artifact - name: Pack @@ -205,7 +204,7 @@ jobs: rm -rf public_res rm -rf *.pdb - name: Uploading Artifact - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v4.3.3 with: name: Deployment-${{ github.sha }} path: deployment diff --git a/.github/workflows/update-pkgbuild.yml b/.github/workflows/update-pkgbuild.yml deleted file mode 100644 index c2a59b3..0000000 --- a/.github/workflows/update-pkgbuild.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: AUR CI -on: - push: - branches: - - main - paths-ignore: - - '**.md' - - 'LICENSE' - - '!.github/workflows/**' - -jobs: - update: - runs-on: ubuntu-latest - steps: - - uses: chitang233/aur-pkgbuild-builder@main - with: - deploy_key: ${{ secrets.DEPLOY_KEY }} - package_name: 'nekoray-git' diff --git a/CMakeLists.txt b/CMakeLists.txt index eeb6ac4..05354f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,7 +166,6 @@ set(PROJECT_SOURCES db/ConfigBuilder.cpp fmt/AbstractBean.cpp - fmt/Bean2CoreObj_ray.cpp fmt/Bean2CoreObj_box.cpp fmt/Bean2External.cpp fmt/Bean2Link.cpp diff --git a/db/ConfigBuilder.cpp b/db/ConfigBuilder.cpp index 4760304..03fc5b1 100644 --- a/db/ConfigBuilder.cpp +++ b/db/ConfigBuilder.cpp @@ -66,11 +66,7 @@ namespace NekoGui { if (customBean != nullptr && customBean->core == "internal-full") { result->coreConfig = QString2QJsonObject(customBean->config_simple); } else { - if (IS_NEKO_BOX) { - BuildConfigSingBox(status); - } else { - BuildConfigV2Ray(status); - } + BuildConfigSingBox(status); } // apply custom config @@ -172,254 +168,6 @@ namespace NekoGui { status->ipListDirect += line; \ } - // V2Ray - - void BuildConfigV2Ray(const std::shared_ptr &status) { - // Log - auto logObj = QJsonObject{{"loglevel", dataStore->log_level}}; - status->result->coreConfig.insert("log", logObj); - - // Inbounds - QJsonObject sniffing{ - {"destOverride", QJsonArray{"http", "tls", "quic"}}, - {"enabled", true}, - {"metadataOnly", false}, - {"routeOnly", dataStore->routing->sniffing_mode == SniffingMode::FOR_ROUTING}, - }; - - // socks-in - if (IsValidPort(dataStore->inbound_socks_port) && !status->forTest) { - QJsonObject inboundObj; - inboundObj["tag"] = "socks-in"; - inboundObj["protocol"] = "socks"; - inboundObj["listen"] = dataStore->inbound_address; - inboundObj["port"] = dataStore->inbound_socks_port; - QJsonObject socksSettings = {{"udp", true}}; - if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) { - inboundObj["sniffing"] = sniffing; - } - if (dataStore->inbound_auth->NeedAuth()) { - socksSettings["auth"] = "password"; - socksSettings["accounts"] = QJsonArray{ - QJsonObject{ - {"user", dataStore->inbound_auth->username}, - {"pass", dataStore->inbound_auth->password}, - }, - }; - } - inboundObj["settings"] = socksSettings; - status->inbounds += inboundObj; - } - // http-in - if (IsValidPort(dataStore->inbound_http_port) && !status->forTest) { - QJsonObject inboundObj; - inboundObj["tag"] = "http-in"; - inboundObj["protocol"] = "http"; - inboundObj["listen"] = dataStore->inbound_address; - inboundObj["port"] = dataStore->inbound_http_port; - if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) { - inboundObj["sniffing"] = sniffing; - } - if (dataStore->inbound_auth->NeedAuth()) { - inboundObj["settings"] = QJsonObject{ - {"accounts", QJsonArray{ - QJsonObject{ - {"user", dataStore->inbound_auth->username}, - {"pass", dataStore->inbound_auth->password}, - }, - }}, - }; - } - status->inbounds += inboundObj; - } - - // Outbounds - auto tagProxy = BuildChain(0, status); - if (!status->result->error.isEmpty()) return; - - // direct & bypass & block - status->outbounds += QJsonObject{ - {"protocol", "freedom"}, - {"domainStrategy", dataStore->core_ray_freedom_domainStrategy}, - {"tag", "direct"}, - }; - status->outbounds += QJsonObject{ - {"protocol", "freedom"}, - {"domainStrategy", dataStore->core_ray_freedom_domainStrategy}, - {"tag", "bypass"}, - }; - status->outbounds += QJsonObject{ - {"protocol", "blackhole"}, - {"tag", "block"}, - }; - - // DNS out - if (!status->forTest) { - QJsonObject dnsOut; - dnsOut["protocol"] = "dns"; - dnsOut["tag"] = "dns-out"; - QJsonObject dnsOut_settings; - dnsOut_settings["network"] = "tcp"; - dnsOut_settings["port"] = 53; - dnsOut_settings["address"] = "8.8.8.8"; - dnsOut_settings["userLevel"] = 1; - dnsOut["settings"] = dnsOut_settings; - dnsOut["proxySettings"] = QJsonObject{{"tag", tagProxy}, - {"transportLayer", true}}; - - status->outbounds += dnsOut; - status->routingRules += QJsonObject{ - {"type", "field"}, - {"port", "53"}, - {"inboundTag", QJsonArray{"socks-in", "http-in"}}, - {"outboundTag", "dns-out"}, - }; - } - - // custom inbound - if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray()) - - status->result->coreConfig.insert("inbounds", status->inbounds); - status->result->coreConfig.insert("outbounds", status->outbounds); - - // user rule - if (!status->forTest) { - DOMAIN_USER_RULE - IP_USER_RULE - } - - // final add DNS - QJsonObject dns; - QJsonArray dnsServers; - - // Remote or FakeDNS - QJsonObject dnsServerRemote; - dnsServerRemote["address"] = dataStore->routing->remote_dns; - dnsServerRemote["domains"] = QList2QJsonArray(status->domainListDNSRemote); - dnsServerRemote["queryStrategy"] = dataStore->routing->remote_dns_strategy; - if (!status->forTest) dnsServers += dnsServerRemote; - - // Direct - auto directDnsAddress = dataStore->routing->direct_dns; - if (directDnsAddress.contains("://")) { - auto directDnsIp = SubStrBefore(SubStrAfter(directDnsAddress, "://"), "/"); - if (IsIpAddress(directDnsIp)) { - status->routingRules.push_front(QJsonObject{ - {"type", "field"}, - {"ip", QJsonArray{directDnsIp}}, - {"outboundTag", "direct"}, - }); - } else { - status->routingRules.push_front(QJsonObject{ - {"type", "field"}, - {"domain", QJsonArray{directDnsIp}}, - {"outboundTag", "direct"}, - }); - } - } else if (directDnsAddress != "localhost") { - status->routingRules.push_front(QJsonObject{ - {"type", "field"}, - {"ip", QJsonArray{directDnsAddress}}, - {"outboundTag", "direct"}, - }); - } - QJsonObject directObj{ - {"address", directDnsAddress.replace("https://", "https+local://")}, - {"queryStrategy", dataStore->routing->direct_dns_strategy}, - {"domains", QList2QJsonArray(status->domainListDNSDirect)}, - }; - if (dataStore->routing->dns_final_out == "bypass") { - dnsServers.prepend(directObj); - } else { - dnsServers.append(directObj); - } - - dns["disableFallback"] = true; - dns["servers"] = dnsServers; - dns["tag"] = "dns"; - - if (dataStore->routing->use_dns_object) { - dns = QString2QJsonObject(dataStore->routing->dns_object); - } - status->result->coreConfig.insert("dns", dns); - - // Routing - QJsonObject routing; - routing["domainStrategy"] = dataStore->routing->domain_strategy; - if (status->forTest) routing["domainStrategy"] = "AsIs"; - - // final add user rule (block) - QJsonObject routingRule_tmp; - routingRule_tmp["type"] = "field"; - routingRule_tmp["outboundTag"] = "block"; - if (!status->ipListBlock.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["ip"] = QList2QJsonArray(status->ipListBlock); - status->routingRules += tmp; - } - if (!status->domainListBlock.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["domain"] = QList2QJsonArray(status->domainListBlock); - status->routingRules += tmp; - } - - // final add user rule (proxy) - routingRule_tmp["outboundTag"] = "proxy"; - if (!status->ipListRemote.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["ip"] = QList2QJsonArray(status->ipListRemote); - status->routingRules += tmp; - } - if (!status->domainListRemote.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["domain"] = QList2QJsonArray(status->domainListRemote); - status->routingRules += tmp; - } - - // final add user rule (bypass) - routingRule_tmp["outboundTag"] = "bypass"; - if (!status->ipListDirect.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["ip"] = QList2QJsonArray(status->ipListDirect); - status->routingRules += tmp; - } - if (!status->domainListDirect.isEmpty()) { - auto tmp = routingRule_tmp; - tmp["domain"] = QList2QJsonArray(status->domainListDirect); - status->routingRules += tmp; - } - - // def_outbound - if (!status->forTest) status->routingRules += QJsonObject{ - {"type", "field"}, - {"port", "0-65535"}, - {"outboundTag", dataStore->routing->def_outbound}, - }; - - // final add routing rule - auto routingRules = QString2QJsonObject(dataStore->routing->custom)["rules"].toArray(); - if (status->forTest) routingRules = {}; - if (!status->forTest) QJSONARRAY_ADD(routingRules, QString2QJsonObject(dataStore->custom_route_global)["rules"].toArray()) - QJSONARRAY_ADD(routingRules, status->routingRules) - routing["rules"] = routingRules; - status->result->coreConfig.insert("routing", routing); - - // Policy & stats - QJsonObject policy; - QJsonObject levels; - QJsonObject level1; - level1["connIdle"] = 30; - levels["1"] = level1; - policy["levels"] = levels; - - QJsonObject policySystem; - policySystem["statsOutboundDownlink"] = true; - policySystem["statsOutboundUplink"] = true; - policy["system"] = policySystem; - status->result->coreConfig.insert("policy", policy); - status->result->coreConfig.insert("stats", QJsonObject()); - } - QString BuildChainInternal(int chainId, const QList> &ents, const std::shared_ptr &status) { QString chainTag = "c-" + Int2String(chainId); @@ -472,26 +220,16 @@ namespace NekoGui { if (IS_NEKO_BOX) { replaced["detour"] = tagOut; } else { - replaced["proxySettings"] = QJsonObject{ - {"tag", tagOut}, - {"transportLayer", true}, - }; + status->result->error = "Xray is no longer supported"; + return {}; } status->outbounds.removeLast(); status->outbounds += replaced; } else { - if (IS_NEKO_BOX) { - status->routingRules += QJsonObject{ - {"inbound", QJsonArray{pastTag + "-mapping"}}, - {"outbound", tagOut}, - }; - } else { - status->routingRules += QJsonObject{ - {"type", "field"}, - {"inboundTag", QJsonArray{pastTag + "-mapping"}}, - {"outboundTag", tagOut}, - }; - } + status->routingRules += QJsonObject{ + {"inbound", QJsonArray{pastTag + "-mapping"}}, + {"outbound", tagOut}, + }; } } else { // index == 0 means last profile in chain / not chain @@ -530,43 +268,20 @@ namespace NekoGui { if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true; if (thisExternalStat == 1) { // mapping - if (IS_NEKO_BOX) { - status->inbounds += QJsonObject{ - {"type", "direct"}, - {"tag", tagOut + "-mapping"}, - {"listen", "127.0.0.1"}, - {"listen_port", ext_mapping_port}, - {"override_address", ent->bean->serverAddress}, - {"override_port", ent->bean->serverPort}, - }; - } else { - status->inbounds += QJsonObject{ - {"protocol", "dokodemo-door"}, - {"tag", tagOut + "-mapping"}, - {"listen", "127.0.0.1"}, - {"port", ext_mapping_port}, - {"settings", QJsonObject{ - // to - {"address", ent->bean->serverAddress}, - {"port", ent->bean->serverPort}, - {"network", "tcp,udp"}, - }}, - }; - } + status->inbounds += QJsonObject{ + {"type", "direct"}, + {"tag", tagOut + "-mapping"}, + {"listen", "127.0.0.1"}, + {"listen_port", ext_mapping_port}, + {"override_address", ent->bean->serverAddress}, + {"override_port", ent->bean->serverPort}, + }; // no chain rule and not outbound, so need to set to direct if (isFirstProfile) { - if (IS_NEKO_BOX) { - status->routingRules += QJsonObject{ - {"inbound", QJsonArray{tagOut + "-mapping"}}, - {"outbound", "direct"}, - }; - } else { - status->routingRules += QJsonObject{ - {"type", "field"}, - {"inboundTag", QJsonArray{tagOut + "-mapping"}}, - {"outboundTag", "direct"}, - }; - } + status->routingRules += QJsonObject{ + {"inbound", QJsonArray{tagOut + "-mapping"}}, + {"outbound", "direct"}, + }; } } @@ -589,23 +304,11 @@ namespace NekoGui { status->result->extRs.emplace_back(std::make_shared(extR)); // SOCKS OUTBOUND - if (IS_NEKO_BOX) { - outbound["type"] = "socks"; - outbound["server"] = "127.0.0.1"; - outbound["server_port"] = ext_socks_port; - } else { - outbound["protocol"] = "socks"; - QJsonObject settings; - QJsonArray servers; - QJsonObject server; - server["address"] = "127.0.0.1"; - server["port"] = ext_socks_port; - servers.push_back(server); - settings["servers"] = servers; - outbound["settings"] = settings; - } + outbound["type"] = "socks"; + outbound["server"] = "127.0.0.1"; + outbound["server_port"] = ext_socks_port; } else { - const auto coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray(); + const auto coreR = ent->bean->BuildCoreObjSingBox(); if (coreR.outbound.isEmpty()) { status->result->error = "unsupported outbound"; return {}; @@ -624,58 +327,41 @@ namespace NekoGui { status->result->outboundStats += ent->traffic_data; // mux common - auto needMux = ent->type == "vmess" || ent->type == "trojan" || ent->type == "vless"; + auto needMux = ent->type == "vmess" || ent->type == "trojan" || ent->type == "vless" || ent->type == "shadowsocks"; needMux &= dataStore->mux_concurrency > 0; if (stream != nullptr) { - if (IS_NEKO_BOX) { - if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) { - needMux = false; - } - } else { - if (stream->network == "grpc" || stream->network == "quic") { - needMux = false; - } - } - if (stream->multiplex_status == 0) { - if (!dataStore->mux_default_on) needMux = false; - } else if (stream->multiplex_status == 1) { - needMux = true; - } else if (stream->multiplex_status == 2) { + if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) { needMux = false; } } + + auto mux_state = ent->bean->mux_state; + if (mux_state == 0) { + if (!dataStore->mux_default_on) needMux = false; + } else if (mux_state == 1) { + needMux = true; + } else if (mux_state == 2) { + needMux = false; + } + if (ent->type == "vless" && outbound["flow"] != "") { needMux = false; } // common - if (IS_NEKO_BOX) { - // apply domain_strategy - outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy; - // apply mux - if (!muxApplied && needMux) { - auto muxObj = QJsonObject{ - {"enabled", true}, - {"protocol", dataStore->mux_protocol}, - {"padding", dataStore->mux_padding}, - {"max_streams", dataStore->mux_concurrency}, - }; - outbound["multiplex"] = muxObj; - muxApplied = true; - } - } else { - // apply domain_strategy - if (!status->forTest) outbound["domainStrategy"] = dataStore->routing->outbound_domain_strategy; - // apply mux - if (!muxApplied && needMux) { - auto muxObj = QJsonObject{ - {"enabled", true}, - {"concurrency", dataStore->mux_concurrency}, - }; - outbound["mux"] = muxObj; - muxApplied = true; - } + // apply domain_strategy + outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy; + // apply mux + if (!muxApplied && needMux) { + auto muxObj = QJsonObject{ + {"enabled", true}, + {"protocol", dataStore->mux_protocol}, + {"padding", dataStore->mux_padding}, + {"max_streams", dataStore->mux_concurrency}, + }; + outbound["multiplex"] = muxObj; + muxApplied = true; } // apply custom outbound settings @@ -735,7 +421,7 @@ namespace NekoGui { } // tun-in - if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) { + if (dataStore->spmode_vpn && !status->forTest) { QJsonObject inboundObj; inboundObj["tag"] = "tun-in"; inboundObj["type"] = "tun"; @@ -885,7 +571,7 @@ namespace NekoGui { }; // Fakedns - if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) { + if (dataStore->fake_dns && dataStore->spmode_vpn && !status->forTest) { dnsServers += QJsonObject{ {"tag", "dns-fake"}, {"address", "fakeip"}, @@ -927,7 +613,7 @@ namespace NekoGui { } // fakedns rule - if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) { + if (dataStore->fake_dns && dataStore->spmode_vpn && !status->forTest) { dnsRules += QJsonObject{ {"inbound", "tun-in"}, {"server", "dns-fake"}, @@ -970,11 +656,6 @@ namespace NekoGui { add_rule_route(status->ipListDirect, true, "bypass"); // built-in rules - status->routingRules += QJsonObject{ - {"network", "udp"}, - {"port", QJsonArray{135, 137, 138, 139, 5353}}, - {"outbound", "block"}, - }; status->routingRules += QJsonObject{ {"ip_cidr", QJsonArray{"224.0.0.0/3", "ff00::/8"}}, {"outbound", "block"}, @@ -985,7 +666,7 @@ namespace NekoGui { }; // tun user rule - if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) { + if (dataStore->spmode_vpn && !status->forTest) { auto match_out = dataStore->vpn_rule_white ? "proxy" : "bypass"; QString process_name_rule = dataStore->vpn_rule_process.trimmed(); @@ -1025,7 +706,7 @@ namespace NekoGui { QJSONARRAY_ADD(routingRules, status->routingRules) auto routeObj = QJsonObject{ {"rules", routingRules}, - {"auto_detect_interface", dataStore->spmode_vpn}, + {"auto_detect_interface", true}, { "geoip", QJsonObject{ diff --git a/db/ConfigBuilder.hpp b/db/ConfigBuilder.hpp index 661c881..71f43d1 100644 --- a/db/ConfigBuilder.hpp +++ b/db/ConfigBuilder.hpp @@ -46,8 +46,6 @@ namespace NekoGui { std::shared_ptr BuildConfig(const std::shared_ptr &ent, bool forTest, bool forExport); - void BuildConfigV2Ray(const std::shared_ptr &status); - void BuildConfigSingBox(const std::shared_ptr &status); QString BuildChain(int chainId, const std::shared_ptr &status); diff --git a/fmt/AbstractBean.hpp b/fmt/AbstractBean.hpp index 56782fb..a165427 100644 --- a/fmt/AbstractBean.hpp +++ b/fmt/AbstractBean.hpp @@ -34,6 +34,7 @@ namespace NekoGui_fmt { QString custom_config = ""; QString custom_outbound = ""; + int mux_state = 0; explicit AbstractBean(int version); @@ -59,8 +60,6 @@ namespace NekoGui_fmt { virtual int NeedExternal(bool isFirstProfile) { return 0; }; - virtual CoreObjOutboundBuildResult BuildCoreObjV2Ray() { return {}; }; - virtual CoreObjOutboundBuildResult BuildCoreObjSingBox() { return {}; }; virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) { return {}; }; diff --git a/fmt/Bean2CoreObj_box.cpp b/fmt/Bean2CoreObj_box.cpp index e8307c5..b938001 100644 --- a/fmt/Bean2CoreObj_box.cpp +++ b/fmt/Bean2CoreObj_box.cpp @@ -200,10 +200,6 @@ namespace NekoGui_fmt { outbound["up_mbps"] = uploadMbps; outbound["down_mbps"] = downloadMbps; - if (!hopPort.trimmed().isEmpty()) { - outbound["hop_ports"] = hopPort; - outbound["hop_interval"] = hopInterval; - } if (authPayloadType == hysteria_auth_base64) outbound["auth"] = authPayload; if (authPayloadType == hysteria_auth_string) outbound["auth_str"] = authPayload; } else if (proxy_type == proxy_Hysteria2) { @@ -211,11 +207,7 @@ namespace NekoGui_fmt { outbound["password"] = password; outbound["up_mbps"] = uploadMbps; outbound["down_mbps"] = downloadMbps; - - if (!hopPort.trimmed().isEmpty()) { - outbound["hop_ports"] = hopPort; - outbound["hop_interval"] = hopInterval; - } + if (!obfsPassword.isEmpty()) { outbound["obfs"] = QJsonObject{ {"type", "salamander"}, diff --git a/fmt/Bean2CoreObj_ray.cpp b/fmt/Bean2CoreObj_ray.cpp deleted file mode 100644 index bb1ccf3..0000000 --- a/fmt/Bean2CoreObj_ray.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "db/ProxyEntity.hpp" -#include "fmt/includes.h" - -#define MAKE_SETTINGS_STREAM_SETTINGS \ - outbound["settings"] = settings; \ - auto streamSettings = stream->BuildStreamSettingsV2Ray(); \ - outbound["streamSettings"] = streamSettings; - -namespace NekoGui_fmt { - QJsonObject V2rayStreamSettings::BuildStreamSettingsV2Ray() { - QJsonObject streamSettings{{"network", network}}; - - if (network == "ws") { - QJsonObject ws; - if (!host.isEmpty()) ws["headers"] = QJsonObject{{"Host", host}}; - // ws path & ed - if (!path.isEmpty()) ws["path"] = path; - streamSettings["wsSettings"] = ws; - } else if (network == "http") { - QJsonObject http; - if (!path.isEmpty()) http["path"] = path; - if (!host.isEmpty()) http["host"] = QList2QJsonArray(host.split(",")); - streamSettings["httpSettings"] = http; - } else if (network == "grpc") { - QJsonObject grpc; - if (!path.isEmpty()) grpc["serviceName"] = path; - streamSettings["grpcSettings"] = grpc; - } else if (network == "quic") { - QJsonObject quic; - if (!header_type.isEmpty()) quic["header"] = QJsonObject{{"type", header_type}}; - if (!path.isEmpty()) quic["key"] = path; - if (!host.isEmpty()) quic["security"] = host; - streamSettings["quicSettings"] = quic; - } else if (network == "tcp" && !header_type.isEmpty()) { - QJsonObject header{{"type", header_type}}; - if (header_type == "http") { - header["request"] = QJsonObject{ - {"path", QList2QJsonArray(path.split(","))}, - {"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}}, - }; - } - streamSettings["tcpSettings"] = QJsonObject{{"header", header}}; - } - - if (security == "tls") { - QJsonObject tls; - if (!utlsFingerprint.isEmpty()) tls["fingerprint"] = utlsFingerprint; - if (!sni.trimmed().isEmpty()) tls["serverName"] = sni; - if (reality_pbk.trimmed().isEmpty()) { - if (allow_insecure || NekoGui::dataStore->skip_cert) tls["allowInsecure"] = true; - if (!alpn.trimmed().isEmpty()) tls["alpn"] = QList2QJsonArray(alpn.split(",")); - if (!certificate.trimmed().isEmpty()) { - tls["disableSystemRoot"] = true; - tls["certificates"] = QJsonArray{ - QJsonObject{ - {"usage", "verify"}, - {"certificate", QList2QJsonArray(SplitLines(certificate.trimmed()))}, - }, - }; - } - streamSettings["tlsSettings"] = tls; - streamSettings["security"] = "tls"; - } else { - tls["publicKey"] = reality_pbk; - tls["shortId"] = reality_sid; - tls["spiderX"] = reality_spx; - if (utlsFingerprint.isEmpty()) tls["fingerprint"] = "chrome"; - streamSettings["realitySettings"] = tls; - streamSettings["security"] = "reality"; - } - } - - return streamSettings; - } - - CoreObjOutboundBuildResult SocksHttpBean::BuildCoreObjV2Ray() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound; - outbound["protocol"] = socks_http_type == type_HTTP ? "http" : "socks"; - - QJsonObject settings; - QJsonArray servers; - QJsonObject server; - - server["address"] = serverAddress; - server["port"] = serverPort; - - QJsonArray users; - QJsonObject user; - user["user"] = username; - user["pass"] = password; - users.push_back(user); - if (!username.isEmpty() && !password.isEmpty()) server["users"] = users; - - servers.push_back(server); - settings["servers"] = servers; - - MAKE_SETTINGS_STREAM_SETTINGS - - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult ShadowSocksBean::BuildCoreObjV2Ray() { - CoreObjOutboundBuildResult result; - - QJsonObject outbound{{"protocol", "shadowsocks"}}; - - QJsonObject settings; - QJsonArray servers; - QJsonObject server; - - server["address"] = serverAddress; - server["port"] = serverPort; - server["method"] = method; - server["password"] = password; - - if (uot != 0) { - server["uot"] = true; - server["UoTVersion"] = uot; - } else { - server["uot"] = false; - } - - servers.push_back(server); - settings["servers"] = servers; - - if (!plugin.trimmed().isEmpty()) { - settings["plugin"] = SubStrBefore(plugin, ";"); - settings["pluginOpts"] = SubStrAfter(plugin, ";"); - } - - MAKE_SETTINGS_STREAM_SETTINGS - - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult VMessBean::BuildCoreObjV2Ray() { - CoreObjOutboundBuildResult result; - QJsonObject outbound{{"protocol", "vmess"}}; - - QJsonObject settings{ - {"vnext", QJsonArray{ - QJsonObject{ - {"address", serverAddress}, - {"port", serverPort}, - {"users", QJsonArray{ - QJsonObject{ - {"id", uuid.trimmed()}, - {"alterId", aid}, - {"security", security}, - }}}, - }}}}; - - MAKE_SETTINGS_STREAM_SETTINGS - - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult TrojanVLESSBean::BuildCoreObjV2Ray() { - CoreObjOutboundBuildResult result; - QJsonObject outbound{ - {"protocol", proxy_type == proxy_VLESS ? "vless" : "trojan"}, - }; - - QJsonObject settings; - if (proxy_type == proxy_VLESS) { - if (flow == "none") { - flow = ""; - } - settings = QJsonObject{ - {"vnext", QJsonArray{ - QJsonObject{ - {"address", serverAddress}, - {"port", serverPort}, - {"users", QJsonArray{ - QJsonObject{ - {"id", password.trimmed()}, - {"encryption", "none"}, - {"flow", flow}, - }}}, - }}}}; - } else { - settings = QJsonObject{ - {"servers", QJsonArray{ - QJsonObject{ - {"address", serverAddress}, - {"port", serverPort}, - {"password", password}, - }}}}; - } - - MAKE_SETTINGS_STREAM_SETTINGS - - result.outbound = outbound; - return result; - } - - CoreObjOutboundBuildResult CustomBean::BuildCoreObjV2Ray() { - CoreObjOutboundBuildResult result; - - if (core == "internal") { - result.outbound = QString2QJsonObject(config_simple); - } - - return result; - } -} // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/Bean2Link.cpp b/fmt/Bean2Link.cpp index 78e8415..7dccb34 100644 --- a/fmt/Bean2Link.cpp +++ b/fmt/Bean2Link.cpp @@ -64,6 +64,13 @@ namespace NekoGui_fmt { } } + // mux + if (mux_state == 1) { + query.addQueryItem("mux", "true"); + } else if (mux_state == 2) { + query.addQueryItem("mux", "false"); + } + // protocol if (proxy_type == proxy_VLESS) { if (!flow.isEmpty()) { @@ -92,6 +99,20 @@ namespace NekoGui_fmt { if (!name.isEmpty()) url.setFragment(name); QUrlQuery q; if (!plugin.isEmpty()) q.addQueryItem("plugin", plugin); + + // mux + if (mux_state == 1) { + q.addQueryItem("mux", "true"); + } else if (mux_state == 2) { + q.addQueryItem("mux", "false"); + } + // uot + if (uot == 1) { + q.addQueryItem("uot", "1"); + } else if (uot == 2) { + q.addQueryItem("uot", "2"); + } + if (!q.isEmpty()) url.setQuery(q); // auto link = url.toString(QUrl::FullyEncoded); @@ -164,6 +185,13 @@ namespace NekoGui_fmt { } } + // mux + if (mux_state == 1) { + query.addQueryItem("mux", "true"); + } else if (mux_state == 2) { + query.addQueryItem("mux", "false"); + } + url.setQuery(query); return url.toString(QUrl::FullyEncoded); } @@ -196,7 +224,6 @@ namespace NekoGui_fmt { 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); @@ -235,7 +262,6 @@ namespace NekoGui_fmt { q.addQueryItem("obfs", "salamander"); q.addQueryItem("obfs-password", obfsPassword); } - if (!hopPort.trimmed().isEmpty()) q.addQueryItem("mport", hopPort); if (allowInsecure) q.addQueryItem("insecure", "1"); if (!sni.isEmpty()) q.addQueryItem("sni", sni); if (!q.isEmpty()) url.setQuery(q); diff --git a/fmt/CustomBean.hpp b/fmt/CustomBean.hpp index 16efd99..35d9414 100644 --- a/fmt/CustomBean.hpp +++ b/fmt/CustomBean.hpp @@ -36,11 +36,7 @@ namespace NekoGui_fmt { QString DisplayAddress() override { if (core == "internal") { auto obj = QString2QJsonObject(config_simple); - if (IS_NEKO_BOX) { - return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt()); - } else { - return {}; - } + return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt()); } else if (core == "internal-full") { return {}; } @@ -52,7 +48,5 @@ namespace NekoGui_fmt { ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; CoreObjOutboundBuildResult BuildCoreObjSingBox() override; - - CoreObjOutboundBuildResult BuildCoreObjV2Ray() override; }; } // namespace NekoGui_fmt \ No newline at end of file diff --git a/fmt/Link2Bean.cpp b/fmt/Link2Bean.cpp index 3bbd279..202d792 100644 --- a/fmt/Link2Bean.cpp +++ b/fmt/Link2Bean.cpp @@ -100,6 +100,14 @@ namespace NekoGui_fmt { } } + // mux + auto mux_str = GetQueryValue(query, "mux", ""); + if (mux_str == "true") { + mux_state = 1; + } else if (mux_str == "false") { + mux_state = 2; + } + // protocol if (proxy_type == proxy_VLESS) { flow = GetQueryValue(query, "flow", ""); @@ -132,6 +140,13 @@ namespace NekoGui_fmt { auto query = GetQuery(url); plugin = query.queryItemValue("plugin").replace("simple-obfs;", "obfs-local;"); + auto mux_str = GetQueryValue(query, "mux", ""); + if (mux_str == "true") { + mux_state = 1; + } else if (mux_str == "false") { + mux_state = 2; + } + } else { // v2rayN DECODE_V2RAY_N_1 @@ -210,6 +225,14 @@ namespace NekoGui_fmt { stream->utlsFingerprint = NekoGui::dataStore->utlsFingerprint; } + // mux + auto mux_str = GetQueryValue(query, "mux", ""); + if (mux_str == "true") { + mux_state = 1; + } else if (mux_str == "false") { + mux_state = 2; + } + // type if (stream->network == "ws") { stream->path = GetQueryValue(query, "path", ""); @@ -263,7 +286,6 @@ namespace NekoGui_fmt { name = url.fragment(QUrl::FullyDecoded); serverAddress = url.host(); serverPort = url.port(); - hopPort = query.queryItemValue("mport"); obfsPassword = query.queryItemValue("obfsParam"); allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure")); uploadMbps = query.queryItemValue("upmbps").toInt(); @@ -308,7 +330,6 @@ namespace NekoGui_fmt { name = url.fragment(QUrl::FullyDecoded); serverAddress = url.host(); serverPort = url.port(); - hopPort = query.queryItemValue("mport"); obfsPassword = query.queryItemValue("obfs-password"); allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure")); diff --git a/fmt/ShadowSocksBean.hpp b/fmt/ShadowSocksBean.hpp index 3bf58d9..becb684 100644 --- a/fmt/ShadowSocksBean.hpp +++ b/fmt/ShadowSocksBean.hpp @@ -23,8 +23,6 @@ namespace NekoGui_fmt { QString DisplayType() override { return "Shadowsocks"; }; - CoreObjOutboundBuildResult BuildCoreObjV2Ray() override; - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; bool TryParseLink(const QString &link); diff --git a/fmt/SocksHttpBean.hpp b/fmt/SocksHttpBean.hpp index ef18326..930a5f8 100644 --- a/fmt/SocksHttpBean.hpp +++ b/fmt/SocksHttpBean.hpp @@ -26,8 +26,6 @@ namespace NekoGui_fmt { QString DisplayType() override { return socks_http_type == type_HTTP ? "HTTP" : "Socks"; }; - CoreObjOutboundBuildResult BuildCoreObjV2Ray() override; - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; bool TryParseLink(const QString &link); diff --git a/fmt/TrojanVLESSBean.hpp b/fmt/TrojanVLESSBean.hpp index e47f41d..77070a6 100644 --- a/fmt/TrojanVLESSBean.hpp +++ b/fmt/TrojanVLESSBean.hpp @@ -24,8 +24,6 @@ namespace NekoGui_fmt { QString DisplayType() override { return proxy_type == proxy_VLESS ? "VLESS" : "Trojan"; }; - CoreObjOutboundBuildResult BuildCoreObjV2Ray() override; - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; bool TryParseLink(const QString &link); diff --git a/fmt/V2RayStreamSettings.hpp b/fmt/V2RayStreamSettings.hpp index 1b5c71c..89ffcb2 100644 --- a/fmt/V2RayStreamSettings.hpp +++ b/fmt/V2RayStreamSettings.hpp @@ -26,8 +26,6 @@ namespace NekoGui_fmt { QString reality_pbk = ""; QString reality_sid = ""; QString reality_spx = ""; - // multiplex - int multiplex_status = 0; V2rayStreamSettings() : JsonStore() { _add(new configItem("net", &network, itemType::string)); @@ -46,11 +44,8 @@ namespace NekoGui_fmt { _add(new configItem("pbk", &reality_pbk, itemType::string)); _add(new configItem("sid", &reality_sid, itemType::string)); _add(new configItem("spx", &reality_spx, itemType::string)); - _add(new configItem("mux_s", &multiplex_status, itemType::integer)); } - QJsonObject BuildStreamSettingsV2Ray(); - void BuildStreamSettingsSingBox(QJsonObject *outbound); }; diff --git a/fmt/VMessBean.hpp b/fmt/VMessBean.hpp index 9bbd590..2bd81a4 100644 --- a/fmt/VMessBean.hpp +++ b/fmt/VMessBean.hpp @@ -21,8 +21,6 @@ namespace NekoGui_fmt { QString DisplayType() override { return "VMess"; }; - CoreObjOutboundBuildResult BuildCoreObjV2Ray() override; - CoreObjOutboundBuildResult BuildCoreObjSingBox() override; bool TryParseLink(const QString &link); diff --git a/libs/build_go.sh b/libs/build_go.sh index 4705fa4..86171fe 100755 --- a/libs/build_go.sh +++ b/libs/build_go.sh @@ -23,15 +23,6 @@ pushd go/cmd/updater [ "$GOOS" == "linux" ] && mv $DEST/updater $DEST/launcher || true popd -#### Go: nekoray_core #### -neko_common="github.com/matsuridayo/libneko/neko_common" -pushd ../Xray-core -Version_Xray=$(git log --pretty=format:'%h' -n 1) -popd -pushd go/cmd/nekoray_core -go build -v -o $DEST -trimpath -ldflags "-w -s -X $neko_common.Version_v2ray=$Version_Xray -X $neko_common.Version_neko=$version_standalone" -popd - #### Go: nekobox_core #### pushd go/cmd/nekobox_core go build -v -o $DEST -trimpath -ldflags "-w -s -X $neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech" diff --git a/main/Const.hpp b/main/Const.hpp index 440c847..0d7c00d 100644 --- a/main/Const.hpp +++ b/main/Const.hpp @@ -18,7 +18,6 @@ namespace NekoGui { namespace CoreType { enum CoreType { - V2RAY, SING_BOX, }; } diff --git a/main/NekoGui.hpp b/main/NekoGui.hpp index 08f9672..c894636 100644 --- a/main/NekoGui.hpp +++ b/main/NekoGui.hpp @@ -8,7 +8,7 @@ // Switch core support namespace NekoGui { - inline int coreType = CoreType::V2RAY; + inline int coreType = CoreType::SING_BOX; QString FindCoreAsset(const QString &name); diff --git a/main/main.cpp b/main/main.cpp index c67f913..c7c7e8b 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -164,19 +164,13 @@ int main(int argc, char* argv[]) { if (coreLoaded.isEmpty()) { NekoGui::coreType = -1; loadTranslate(QLocale().name()); - auto dialogFirstSetup = new DialogFirstSetup; - dialogFirstSetup->exec(); - dialogFirstSetup->deleteLater(); - if (NekoGui::coreType < 0) { - return 0; - } else { - QDir().mkdir("groups"); - QFile file; - file.setFileName("groups/coreType"); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); - file.write(Int2String(NekoGui::coreType).toUtf8()); - file.close(); - } + NekoGui::coreType = NekoGui::CoreType::SING_BOX; + QDir().mkdir("groups"); + QFile file; + file.setFileName("groups/coreType"); + file.open(QIODevice::ReadWrite | QIODevice::Truncate); + file.write(Int2String(NekoGui::coreType).toUtf8()); + file.close(); } else { NekoGui::coreType = coreLoaded.toInt(); } @@ -200,9 +194,6 @@ int main(int argc, char* argv[]) { // Load dataStore switch (NekoGui::coreType) { - case NekoGui::CoreType::V2RAY: - NekoGui::dataStore->fn = "groups/nekoray.json"; - break; case NekoGui::CoreType::SING_BOX: NekoGui::dataStore->fn = "groups/nekobox.json"; break; diff --git a/sub/GroupUpdater.cpp b/sub/GroupUpdater.cpp index 4a68dfb..d941707 100644 --- a/sub/GroupUpdater.cpp +++ b/sub/GroupUpdater.cpp @@ -278,7 +278,7 @@ namespace NekoGui_sub { // sing-mux auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; + if (Node2Bool(smux["enabled"])) bean->mux_state = 1; } else if (type == "socks" || type == "http") { auto bean = ent->SocksHTTPBean(); bean->username = Node2QString(proxy["username"]); @@ -313,7 +313,7 @@ namespace NekoGui_sub { // sing-mux auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; + if (Node2Bool(smux["enabled"])) bean->mux_state = 1; // opts auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"}); @@ -358,7 +358,7 @@ namespace NekoGui_sub { // sing-mux auto smux = NodeChild(proxy, {"smux"}); - if (Node2Bool(smux["enabled"])) bean->stream->multiplex_status = 1; + if (Node2Bool(smux["enabled"])) bean->mux_state = 1; // meta packet encoding if (Node2Bool(proxy["xudp"])) bean->stream->packet_encoding = "xudp"; diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp index bcff7e9..3f4c29c 100644 --- a/ui/dialog_basic_settings.cpp +++ b/ui/dialog_basic_settings.cpp @@ -218,31 +218,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent) }); // switch core - ui->switch_core_v2ray->setChecked(!IS_NEKO_BOX); ui->switch_core_sing_box->setChecked(IS_NEKO_BOX); - auto switch_core_on_click = [=] { - int neko_core_new; - if (sender() == ui->switch_core_sing_box) { - if (IS_NEKO_BOX) return; - neko_core_new = NekoGui::CoreType::SING_BOX; - } else { - if (!IS_NEKO_BOX) return; - neko_core_new = NekoGui::CoreType::V2RAY; - } - QString core_name_new = dynamic_cast(sender())->text(); - if (QMessageBox::question(this, tr("Confirmation"), - tr("Switching the core to %1, click \"Yes\" to complete the switch and the program will restart. This feature may be unstable, please do not switch frequently.") - .arg(core_name_new)) == QMessageBox::StandardButton::Yes) { - QFile file; - file.setFileName("groups/coreType"); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); - file.write(Int2String(neko_core_new).toUtf8()); - file.close(); - MW_dialog_message("", "RestartProgram"); - } - }; - connect(ui->switch_core_v2ray, &QRadioButton::clicked, this, switch_core_on_click); - connect(ui->switch_core_sing_box, &QRadioButton::clicked, this, switch_core_on_click); // Mux D_LOAD_INT(mux_concurrency) diff --git a/ui/dialog_first_setup.cpp b/ui/dialog_first_setup.cpp index 4fc2499..3ca16ad 100644 --- a/ui/dialog_first_setup.cpp +++ b/ui/dialog_first_setup.cpp @@ -13,10 +13,6 @@ DialogFirstSetup::~DialogFirstSetup() { void DialogFirstSetup::onButtonClicked() { auto s = sender(); - if (s == ui->v2ray) { - NekoGui::coreType = NekoGui::CoreType::V2RAY; - } else if (s == ui->singbox) { - NekoGui::coreType = NekoGui::CoreType::SING_BOX; - } + NekoGui::coreType = NekoGui::CoreType::SING_BOX; done(0); } diff --git a/ui/edit/dialog_edit_profile.cpp b/ui/edit/dialog_edit_profile.cpp index 1e9efb8..6adcf7b 100644 --- a/ui/edit/dialog_edit_profile.cpp +++ b/ui/edit/dialog_edit_profile.cpp @@ -236,7 +236,7 @@ void DialogEditProfile::typeSelected(const QString &newType) { ui->ws_early_data_length->setText(Int2String(stream->ws_early_data_length)); ui->reality_pbk->setText(stream->reality_pbk); ui->reality_sid->setText(stream->reality_sid); - ui->multiplex->setCurrentIndex(stream->multiplex_status); + ui->multiplex->setCurrentIndex(ent->bean->mux_state); CACHE.certificate = stream->certificate; } else { ui->right_all_w->setVisible(false); @@ -371,7 +371,7 @@ bool DialogEditProfile::onEnd() { stream->ws_early_data_length = ui->ws_early_data_length->text().toInt(); stream->reality_pbk = ui->reality_pbk->text(); stream->reality_sid = ui->reality_sid->text(); - stream->multiplex_status = ui->multiplex->currentIndex(); + ent->bean->mux_state = ui->multiplex->currentIndex(); stream->certificate = CACHE.certificate; } @@ -515,7 +515,7 @@ void DialogEditProfile::do_apply_to_group(const std::shared_ptr }; if (key == ui->multiplex) { - copyStream(&stream->multiplex_status); + copyStream(&ent->bean->mux_state); } else if (key == ui->sni) { copyStream(&stream->sni); } else if (key == ui->alpn) {