diff --git a/.github/update.log b/.github/update.log index 0fb9e65aa5..0969a2e638 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1305,3 +1305,4 @@ Update On Tue Mar 17 20:16:34 CET 2026 Update On Wed Mar 18 20:13:21 CET 2026 Update On Thu Mar 19 20:11:34 CET 2026 Update On Fri Mar 20 20:03:22 CET 2026 +Update On Sat Mar 21 19:50:06 CET 2026 diff --git a/clash-meta-android/.github/workflows/build-debug.yaml b/clash-meta-android/.github/workflows/build-debug.yaml index 5ee25b5d24..92a5fa0b9a 100644 --- a/clash-meta-android/.github/workflows/build-debug.yaml +++ b/clash-meta-android/.github/workflows/build-debug.yaml @@ -12,19 +12,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Checkout submodules run: git submodule update --init --recursive --force - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: 21 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Setup Go uses: actions/setup-go@v6 @@ -37,7 +37,7 @@ jobs: cd $(go env GOROOT) for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: | ~/.cache/go-build @@ -71,7 +71,7 @@ jobs: run: ./gradlew --no-daemon app:assembleAlphaRelease - name: Upload Aritfact (universal) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ success() }} with: name: CMFA Debug Unsigned APK (universal) @@ -79,7 +79,7 @@ jobs: app/build/outputs/apk/alpha/release/*-universal-*.apk - name: Upload Aritfact (arm64-v8a) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ success() }} with: name: CMFA Debug Unsigned APK (arm64-v8a) @@ -87,7 +87,7 @@ jobs: app/build/outputs/apk/alpha/release/*-arm64-v8a-*.apk - name: Upload Aritfact (armeabi-v7a) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ success() }} with: name: CMFA Debug Unsigned APK (armeabi-v7a) @@ -95,7 +95,7 @@ jobs: app/build/outputs/apk/alpha/release/*-armeabi-v7a-*.apk - name: Upload Aritfact (x86_64) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ success() }} with: name: CMFA Debug Unsigned APK (x86_64) @@ -103,7 +103,7 @@ jobs: app/build/outputs/apk/alpha/release/*-x86_64-*.apk - name: Upload Aritfact (x86) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ success() }} with: name: CMFA Debug Unsigned APK (x86) diff --git a/clash-meta-android/.github/workflows/build-pre-release.yaml b/clash-meta-android/.github/workflows/build-pre-release.yaml index d4715d7b79..21717939fe 100644 --- a/clash-meta-android/.github/workflows/build-pre-release.yaml +++ b/clash-meta-android/.github/workflows/build-pre-release.yaml @@ -10,19 +10,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Checkout submodules run: git submodule update --init --recursive --force - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: 21 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Setup Go uses: actions/setup-go@v6 @@ -35,7 +35,7 @@ jobs: cd $(go env GOROOT) for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: | ~/.cache/go-build diff --git a/clash-meta-android/.github/workflows/build-release.yaml b/clash-meta-android/.github/workflows/build-release.yaml index 3f9c3e9fb4..3825de96eb 100644 --- a/clash-meta-android/.github/workflows/build-release.yaml +++ b/clash-meta-android/.github/workflows/build-release.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -20,13 +20,13 @@ jobs: run: git submodule update --init --recursive --force - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: 21 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 - name: Setup Go uses: actions/setup-go@v6 @@ -39,7 +39,7 @@ jobs: cd $(go env GOROOT) for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: | ~/.cache/go-build diff --git a/clash-meta-android/.github/workflows/update-dependencies.yaml b/clash-meta-android/.github/workflows/update-dependencies.yaml index 71524443f9..e2b4897b64 100644 --- a/clash-meta-android/.github/workflows/update-dependencies.yaml +++ b/clash-meta-android/.github/workflows/update-dependencies.yaml @@ -10,13 +10,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Checkout and Update submodules run: git submodule update --init --recursive --remote --force - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: "temurin" java-version: 21 @@ -32,7 +32,7 @@ jobs: cd $(go env GOROOT) for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: | ~/.cache/go-build diff --git a/clash-meta/.github/workflows/build.yml b/clash-meta/.github/workflows/build.yml index 461a400911..c7e56b299e 100644 --- a/clash-meta/.github/workflows/build.yml +++ b/clash-meta/.github/workflows/build.yml @@ -155,7 +155,7 @@ jobs: - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' } steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Go if: ${{ matrix.jobs.goversion == '' }} @@ -386,7 +386,7 @@ jobs: shell: bash - name: Archive production artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}" path: | @@ -405,7 +405,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download all workflow run artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true @@ -463,7 +463,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: Meta fetch-depth: '0' @@ -498,7 +498,7 @@ jobs: bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} rm ./genReleaseNote.sh - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true @@ -522,11 +522,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true diff --git a/clash-meta/.github/workflows/test.yml b/clash-meta/.github/workflows/test.yml index f2862e9a86..13a08a4ad5 100644 --- a/clash-meta/.github/workflows/test.yml +++ b/clash-meta/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: # Fix mingw trying to be smart and converting paths https://github.com/moby/moby/issues/24029#issuecomment-250412919 MSYS_NO_PATHCONV: true steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 diff --git a/clash-meta/dns/resolver.go b/clash-meta/dns/resolver.go index 9184fbfb22..9295929815 100644 --- a/clash-meta/dns/resolver.go +++ b/clash-meta/dns/resolver.go @@ -165,11 +165,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e q := m.Question[0] domain := msgToDomain(m) - cacheM, expireTime, hit := r.cache.GetWithExpire(q.String()) + msg, expireTime, hit := getMsgFromCache(r.cache, q) if hit { - log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(cacheM), expireTime.Format("2006-01-02 15:04:05")) + log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(msg), expireTime.Format("2006-01-02 15:04:05")) now := time.Now() - msg = cacheM.Copy() if expireTime.Before(now) { setMsgTTL(msg, uint32(1)) // Continue fetch continueFetch = true @@ -201,14 +200,8 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M return } - msg := result - if cache { - // OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files. - msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool { - return rr.Header().Rrtype != D.TypeOPT - }) - putMsgToCache(r.cache, q.String(), q, msg) + putMsgToCache(r.cache, q, result) } }() diff --git a/clash-meta/dns/util.go b/clash-meta/dns/util.go index b7082818cf..ef57a7c24f 100644 --- a/clash-meta/dns/util.go +++ b/clash-meta/dns/util.go @@ -45,7 +45,19 @@ func updateTTL(records []D.RR, ttl uint32) { } } -func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) { +// getMsgFromCache returns a cached dns message if it exists, otherwise returns nil. +// the returned msg is a copy of the original msg, so it can be modified without affecting the original msg. +func getMsgFromCache(c dnsCache, q D.Question) (*D.Msg, time.Time, bool) { + msg, expireTime, hit := c.GetWithExpire(q.String()) + if msg != nil { + msg = msg.Copy() // never modify the original msg + } + return msg, expireTime, hit +} + +// putMsgToCache puts a dns message into the cache. +// the msg is copied before being stored in the cache, so it can be modified without affecting the original msg. +func putMsgToCache(c dnsCache, q D.Question, msg *D.Msg) { // skip dns cache for acme challenge if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") { log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) @@ -58,12 +70,20 @@ func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) { // If it does so it MUST NOT cache it for longer than five (5) minutes [...] ttl = serverFailureCacheTTL } else { - ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...)) + ttl = minimalTTL(lo.Concat(msg.Answer, msg.Ns, msg.Extra)) } if ttl == 0 { return } - c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second)) + + msg = msg.Copy() // never modify the original msg + + // OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files. + msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool { + return rr.Header().Rrtype != D.TypeOPT + }) + + c.SetWithExpire(q.String(), msg, time.Now().Add(time.Duration(ttl)*time.Second)) } func setMsgTTL(msg *D.Msg, ttl uint32) { diff --git a/clash-nyanpasu/.github/ISSUE_TEMPLATE/bug_report.yaml b/clash-nyanpasu/.github/ISSUE_TEMPLATE/bug_report.yaml index 579d02fa08..d606c6eed8 100644 --- a/clash-nyanpasu/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/clash-nyanpasu/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -60,10 +60,10 @@ body: attributes: label: 应用日志 / App logs description: | - 请确保您已移除所有敏感信息,并确保你的日志等级为 `Trace` 或 `Debug`。请参考 [FAQ - 日志目录](https://nyanpasu.elaina.moe/zh-CN/others/faq.html#_2-clash-nyanpasu-%E5%BA%94%E7%94%A8-%E6%97%A5%E8%AE%B0%E7%9B%AE%E5%BD%95%E5%9C%A8%E5%93%AA%E9%87%8C) + 请确保您已移除所有敏感信息,并确保你的日志等级为 `Trace` 或 `Debug`。请参考 [FAQ - 日志目录](https://nyanpasu.majokeiko.com/zh-CN/others/faq.html#_2-clash-nyanpasu-%E5%BA%94%E7%94%A8-%E6%97%A5%E8%AE%B0%E7%9B%AE%E5%BD%95%E5%9C%A8%E5%93%AA%E9%87%8C) 如果您可以打开主界面,可以在设置页的“日志目录”旁找到“收集日志”按钮,点击即可收集日志。(1.5.0 不可用) 如果日志过长,请使用 [Gist](https://gist.github.com/) 或 [Hastebin](https://hastebin.com/) 并附上链接。 - Please make sure you have removed all sensitive information and your log level is `Trace` or `Debug`. Please refer to [FAQ - Logs directory](https://nyanpasu.elaina.moe/others/faq.html#_2-where-is-the-clash-nyanpasu-application-logs-directory) + Please make sure you have removed all sensitive information and your log level is `Trace` or `Debug`. Please refer to [FAQ - Logs directory](https://nyanpasu.majokeiko.com/others/faq.html#_2-where-is-the-clash-nyanpasu-application-logs-directory) If you can open the main interface, you can find the "Collect logs" button next to the "Open Logs Dir" in the settings page, click to collect the logs. (Not available in 1.5.0) If the log is too long, please use [Gist](https://gist.github.com/) or [Hastebin](https://hastebin.com/) and provide the link. placeholder: | @@ -149,7 +149,7 @@ body: required: false - label: 您已知悉如果没有提供正确的系统信息,以及日志,您的 Issue 会直接被关闭 / You have known that if you don't provide correct system information and logs, your issue will be closed directly required: true - - label: 您已仔细查看并知情 [Q&A](https://nyanpasu.elaina.moe/zh-CN/others/issues) 和 [FAQ](https://nyanpasu.elaina.moe/zh-CN/others/faq) 中的内容 / You have read and understood the contents of [Q&A](https://nyanpasu.elaina.moe/others/issues) and [FAQ](https://nyanpasu.elaina.moe/others/faq) + - label: 您已仔细查看并知情 [Q&A](https://nyanpasu.majokeiko.com/zh-CN/others/issues) 和 [FAQ](https://nyanpasu.majokeiko.com/zh-CN/others/faq) 中的内容 / You have read and understood the contents of [Q&A](https://nyanpasu.majokeiko.com/others/issues) and [FAQ](https://nyanpasu.majokeiko.com/others/faq) required: true - label: 您已搜索过 [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues), No duplicate or related open issue has been found diff --git a/clash-nyanpasu/.github/ISSUE_TEMPLATE/feature_request.yaml b/clash-nyanpasu/.github/ISSUE_TEMPLATE/feature_request.yaml index 110b091f3b..2c914395f1 100644 --- a/clash-nyanpasu/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/clash-nyanpasu/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -86,7 +86,7 @@ body: - label: 如果您有足够的时间和能力,并愿意为此提交 PR,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request required: false - - label: 您已仔细查看并知情 [Q&A](https://nyanpasu.elaina.moe/zh-CN/others/issues) 中的内容 / You have checked [Q&A](https://nyanpasu.elaina.moe/others/issues) carefully + - label: 您已仔细查看并知情 [Q&A](https://nyanpasu.majokeiko.com/zh-CN/others/issues) 中的内容 / You have checked [Q&A](https://nyanpasu.majokeiko.com/others/issues) carefully required: true - label: 您已搜索过 [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues), No duplicate or related open issue has been found diff --git a/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml b/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml index 82cddb7b57..741cdbd91a 100644 --- a/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml +++ b/clash-nyanpasu/.github/workflows/deps-build-windows-nsis.yaml @@ -9,11 +9,15 @@ on: type: boolean default: false - fixed-webview: - description: 'Fixed WebView' + bundle-type: + description: 'Bundle variant' required: true - type: boolean - default: false + type: choice + default: 'standard' + options: + - standard + - fixed-webview + - both nightly: description: 'Nightly prepare' @@ -44,11 +48,11 @@ on: type: boolean default: false - fixed-webview: - description: 'Fixed WebView' - required: true - type: boolean - default: false + bundle-type: + description: 'Bundle variant: standard | fixed-webview | both' + required: false + type: string + default: 'standard' nightly: description: 'Nightly prepare' @@ -137,8 +141,114 @@ jobs: } } + - name: Build UI + run: | + pnpm -F ui build + + - name: Set file server folder path + if: ${{ inputs.nightly == true }} + shell: bash + run: | + GIT_HASH=$(git rev-parse --short HEAD) + echo "FOLDER_PATH=nightly/${GIT_HASH}" >> $GITHUB_ENV + + - name: Prepare (Windows NSIS and Portable) + if: ${{ inputs.bundle-type != 'fixed-webview' }} + run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis' || 'pnpm prepare:release --nsis' }} + + - name: Tauri build x86_64 + if: ${{ inputs.arch == 'x86_64' && inputs.bundle-type != 'fixed-webview' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} + run: | + pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json' || '-f default-meta' }} + + - name: Tauri build i686 + if: ${{ inputs.arch == 'i686' && inputs.bundle-type != 'fixed-webview' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} + run: | + pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target i686-pc-windows-msvc' || '-f default-meta --target i686-pc-windows-msvc' }} + + - name: Tauri build arm64 + if: ${{ inputs.arch == 'aarch64' && inputs.bundle-type != 'fixed-webview' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} + run: | + pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target aarch64-pc-windows-msvc' || '-f default-meta --target aarch64-pc-windows-msvc' }} + + - name: Portable Bundle + if: ${{ inputs.portable == true && inputs.bundle-type != 'fixed-webview' }} + run: | + pnpm portable + env: + RUST_ARCH: ${{ inputs.arch }} + TAG_NAME: ${{ inputs.tag }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} + VITE_WIN_PORTABLE: 1 + + - name: Upload NSIS Installer + if: ${{ inputs.bundle-type != 'fixed-webview' }} + uses: actions/upload-artifact@v7 + with: + name: Clash.Nyanpasu-windows-${{ inputs.arch }}-nsis-installer + path: | + ./backend/target/**/bundle/**/*.exe + ./backend/target/**/bundle/**/*.zip + ./backend/target/**/bundle/**/*.zip.sig + + - name: Upload portable + if: ${{ inputs.portable == true && inputs.bundle-type != 'fixed-webview' }} + uses: actions/upload-artifact@v7 + with: + name: Clash.Nyanpasu-windows-${{ inputs.arch }}-portable + path: | + ./*_portable.zip + + - name: Upload to file server + if: ${{ inputs.nightly == true && inputs.bundle-type != 'fixed-webview' }} + shell: bash + continue-on-error: true + run: | + deno run -A scripts/deno/upload-build-artifacts.ts \ + "backend/target/**/bundle/nsis/*.exe" \ + "*_portable.zip" + env: + FILE_SERVER_TOKEN: ${{ secrets.FILE_SERVER_TOKEN }} + FOLDER_PATH: ${{ env.FOLDER_PATH }} + + - name: Upload file server results + if: ${{ inputs.nightly == true && inputs.bundle-type != 'fixed-webview' }} + uses: actions/upload-artifact@v7 + with: + name: upload-results-windows-${{ inputs.arch }} + path: ./upload-results.json + if-no-files-found: ignore + + - name: Clean standard build outputs + if: ${{ inputs.bundle-type == 'both' }} + run: | + Get-ChildItem -Path "./backend/target" -Recurse -Include "*.exe", "*.zip", "*.zip.sig" | + Where-Object { $_.FullName -like "*\bundle\*" } | + Remove-Item -Force -ErrorAction SilentlyContinue + Get-ChildItem -Path "." -Filter "*_portable.zip" -ErrorAction SilentlyContinue | + Remove-Item -Force + Remove-Item -Path "./upload-results.json" -Force -ErrorAction SilentlyContinue + - name: Download fixed WebView - if: ${{ inputs.fixed-webview == true }} + if: ${{ inputs.bundle-type != 'standard' }} run: | $condition = '${{ inputs.arch }}' switch ($condition) { @@ -162,20 +272,12 @@ jobs: expand.exe $outfile -F:* ./backend/tauri echo "Extraction finished" - - name: Prepare (Windows NSIS and Portable) - if: ${{ inputs.fixed-webview == false }} - run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis' || 'pnpm prepare:release --nsis' }} - - name: Prepare (Windows NSIS and Portable) with fixed WebView - if: ${{ inputs.fixed-webview == true }} + if: ${{ inputs.bundle-type != 'standard' }} run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis --fixed-webview' || 'pnpm prepare:release --nsis --fixed-webview' }} - - name: Build UI - run: | - pnpm -F ui build - # TODO: optimize strategy - - name: Tauri build x86_64 - if: ${{ inputs.arch == 'x86_64' }} + - name: Tauri build x86_64 with fixed WebView + if: ${{ inputs.arch == 'x86_64' && inputs.bundle-type != 'standard' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} @@ -184,8 +286,8 @@ jobs: run: | pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json' || '-f default-meta' }} - - name: Tauri build i686 - if: ${{ inputs.arch == 'i686' }} + - name: Tauri build i686 with fixed WebView + if: ${{ inputs.arch == 'i686' && inputs.bundle-type != 'standard' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} @@ -193,8 +295,9 @@ jobs: NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} run: | pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target i686-pc-windows-msvc' || '-f default-meta --target i686-pc-windows-msvc' }} - - name: Tauri build arm64 - if: ${{ inputs.arch == 'aarch64' }} + + - name: Tauri build arm64 with fixed WebView + if: ${{ inputs.arch == 'aarch64' && inputs.bundle-type != 'standard' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} @@ -204,7 +307,7 @@ jobs: pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target aarch64-pc-windows-msvc' || '-f default-meta --target aarch64-pc-windows-msvc' }} - name: Rename fixed webview bundle name - if: ${{ inputs.fixed-webview == true }} + if: ${{ inputs.bundle-type != 'standard' }} run: | $files = Get-ChildItem -Path "./backend/target" -Recurse -Include "*.exe", "*.zip", "*.zip.sig" | Where-Object { $_.FullName -like "*\bundle\*" } $condition = '${{ inputs.arch }}' @@ -226,10 +329,10 @@ jobs: Rename-Item -Path $file -NewName $newname } - - name: Portable Bundle - if: ${{ inputs.portable == true }} + - name: Portable Bundle with fixed WebView + if: ${{ inputs.portable == true && inputs.bundle-type != 'standard' }} run: | - pnpm portable ${{ inputs.fixed-webview == true && '--fixed-webview' || '' }} + pnpm portable --fixed-webview env: RUST_ARCH: ${{ inputs.arch }} TAG_NAME: ${{ inputs.tag }} @@ -239,46 +342,40 @@ jobs: NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }} VITE_WIN_PORTABLE: 1 - - name: Upload NSIS Installer + - name: Upload NSIS Installer with fixed WebView + if: ${{ inputs.bundle-type != 'standard' }} uses: actions/upload-artifact@v7 with: - name: Clash.Nyanpasu-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }}-nsis-installer + name: Clash.Nyanpasu-windows-${{ inputs.arch }}-fixed-webview-nsis-installer path: | ./backend/target/**/bundle/**/*.exe ./backend/target/**/bundle/**/*.zip ./backend/target/**/bundle/**/*.zip.sig - - name: Upload portable - if: ${{ inputs.portable == true }} + - name: Upload portable with fixed WebView + if: ${{ inputs.portable == true && inputs.bundle-type != 'standard' }} uses: actions/upload-artifact@v7 with: - name: Clash.Nyanpasu-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }}-portable + name: Clash.Nyanpasu-windows-${{ inputs.arch }}-fixed-webview-portable path: | ./*_portable.zip - - name: Set file server folder path - if: ${{ inputs.nightly == true }} - shell: bash - run: | - GIT_HASH=$(git rev-parse --short HEAD) - echo "FOLDER_PATH=nightly/${GIT_HASH}" >> $GITHUB_ENV - - - name: Upload to file server - if: ${{ inputs.nightly == true }} + - name: Upload to file server with fixed WebView + if: ${{ inputs.nightly == true && inputs.bundle-type != 'standard' }} shell: bash continue-on-error: true run: | deno run -A scripts/deno/upload-build-artifacts.ts \ - "backend/target/**/bundle/**/*.exe" \ + "backend/target/**/bundle/nsis/*.exe" \ "*_portable.zip" env: FILE_SERVER_TOKEN: ${{ secrets.FILE_SERVER_TOKEN }} FOLDER_PATH: ${{ env.FOLDER_PATH }} - - name: Upload file server results - if: ${{ inputs.nightly == true }} + - name: Upload file server results with fixed WebView + if: ${{ inputs.nightly == true && inputs.bundle-type != 'standard' }} uses: actions/upload-artifact@v7 with: - name: upload-results-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }} + name: upload-results-windows-${{ inputs.arch }}-fixed-webview path: ./upload-results.json if-no-files-found: ignore diff --git a/clash-nyanpasu/.github/workflows/deps-create-updater.yaml b/clash-nyanpasu/.github/workflows/deps-create-updater.yaml index c6c5c9da0e..39769409c4 100644 --- a/clash-nyanpasu/.github/workflows/deps-create-updater.yaml +++ b/clash-nyanpasu/.github/workflows/deps-create-updater.yaml @@ -91,7 +91,6 @@ jobs: - name: Upload updater to surge.sh run: | pnpm i -g surge - surge manifest/site surge.elaina.moe surge manifest/site nyanpasu.surge.sh env: SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} diff --git a/clash-nyanpasu/.github/workflows/target-dev-build.yaml b/clash-nyanpasu/.github/workflows/target-dev-build.yaml index 2872494619..029c407d66 100644 --- a/clash-nyanpasu/.github/workflows/target-dev-build.yaml +++ b/clash-nyanpasu/.github/workflows/target-dev-build.yaml @@ -21,7 +21,7 @@ jobs: with: portable: true nightly: true - fixed-webview: false + bundle-type: 'both' arch: 'x86_64' tag: 'pre-release' secrets: inherit @@ -33,7 +33,7 @@ jobs: with: portable: true nightly: true - fixed-webview: false + bundle-type: 'both' arch: 'aarch64' tag: 'pre-release' secrets: inherit @@ -45,47 +45,11 @@ jobs: with: portable: true nightly: true - fixed-webview: false + bundle-type: 'both' arch: 'i686' tag: 'pre-release' secrets: inherit - windows_amd64_build_fixed_webview: - name: Windows x86_64 Build with Fixed WebView - if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }} - uses: ./.github/workflows/deps-build-windows-nsis.yaml - with: - portable: true - nightly: true - arch: 'x86_64' - fixed-webview: true - tag: 'pre-release' - secrets: inherit - - windows_aarch64_build_fixed_webview: - name: Windows aarch64 Build with Fixed WebView - if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }} - uses: ./.github/workflows/deps-build-windows-nsis.yaml - with: - portable: true - nightly: true - arch: 'aarch64' - fixed-webview: true - tag: 'pre-release' - secrets: inherit - - windows_i686_build_fixed_webview: - name: Windows i686 Build with Fixed WebView - if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }} - uses: ./.github/workflows/deps-build-windows-nsis.yaml - with: - portable: true - nightly: true - arch: 'i686' - fixed-webview: true - tag: 'pre-release' - secrets: inherit - linux_amd64_build: name: Linux amd64 Build if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }} @@ -163,9 +127,6 @@ jobs: windows_amd64_build, windows_i686_build, windows_aarch64_build, - windows_amd64_build_fixed_webview, - windows_i686_build_fixed_webview, - windows_aarch64_build_fixed_webview, linux_amd64_build, linux_i686_build, linux_aarch64_build, diff --git a/clash-nyanpasu/.github/workflows/target-release-build.yaml b/clash-nyanpasu/.github/workflows/target-release-build.yaml index f6f0799c2d..330df0acae 100644 --- a/clash-nyanpasu/.github/workflows/target-release-build.yaml +++ b/clash-nyanpasu/.github/workflows/target-release-build.yaml @@ -15,6 +15,7 @@ jobs: with: portable: true nightly: false + bundle-type: 'both' tag: ${{ github.event.release.tag_name }} secrets: inherit diff --git a/clash-nyanpasu/CODE_OF_CONDUCT.md b/clash-nyanpasu/CODE_OF_CONDUCT.md index b0c7f7f0a6..459e99d15b 100644 --- a/clash-nyanpasu/CODE_OF_CONDUCT.md +++ b/clash-nyanpasu/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -i@elaina.moe. +i@majokeiko.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/clash-nyanpasu/README.md b/clash-nyanpasu/README.md index 50ef9ebc79..4ce0ab90f2 100644 --- a/clash-nyanpasu/README.md +++ b/clash-nyanpasu/README.md @@ -1,5 +1,5 @@

- Clash Nyanpasu Banner + Clash Nyanpasu Banner

Clash Nyanpasu

@@ -21,22 +21,22 @@ ## Features - Built-in support [Clash Premium](https://github.com/Dreamacro/clash), [Mihomo](https://github.com/MetaCubeX/mihomo) & [Clash Rust](https://github.com/Watfaq/clash-rs). -- Profiles management and enhancement (by YAML, JavaScript & Lua). [Doc](https://nyanpasu.elaina.moe/tutorial/proxy-chain) +- Profiles management and enhancement (by YAML, JavaScript & Lua). [Doc](https://nyanpasu.majokeiko.com/tutorial/proxy-chain) - Provider management support. - Google Material You Design UI and animation support. ## Preview -![preview-light](https://nyanpasu.elaina.moe/images/screenshot/app-dashboard-light.png) +![preview-light](https://nyanpasu.majokeiko.com/images/screenshot/app-dashboard-light.png) -![preview-dark](https://nyanpasu.elaina.moe/images/screenshot/app-dashboard-dark.png) +![preview-dark](https://nyanpasu.majokeiko.com/images/screenshot/app-dashboard-dark.png) ## Links -- [Install](https://nyanpasu.elaina.moe/tutorial/install) -- [FAQ](https://nyanpasu.elaina.moe/others/faq) -- [Q&A Convention](https://nyanpasu.elaina.moe/others/issues) -- [How To Ask Questions](https://nyanpasu.elaina.moe/others/how-to-ask) +- [Install](https://nyanpasu.majokeiko.com/tutorial/install) +- [FAQ](https://nyanpasu.majokeiko.com/others/faq) +- [Q&A Convention](https://nyanpasu.majokeiko.com/others/issues) +- [How To Ask Questions](https://nyanpasu.majokeiko.com/others/how-to-ask) ## Development diff --git a/clash-nyanpasu/backend/tauri/overrides/fixed-webview2.conf.json b/clash-nyanpasu/backend/tauri/overrides/fixed-webview2.conf.json index f7cee16754..2ecb15412b 100644 --- a/clash-nyanpasu/backend/tauri/overrides/fixed-webview2.conf.json +++ b/clash-nyanpasu/backend/tauri/overrides/fixed-webview2.conf.json @@ -11,7 +11,6 @@ "plugins": { "updater": { "endpoints": [ - "https://deno.elaina.moe/updater/update-fixed-webview-proxy.json", "https://nyanpasu.surge.sh/updater/update-fixed-webview-proxy.json", "https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-fixed-webview-proxy.json", "https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-fixed-webview.json" diff --git a/clash-nyanpasu/backend/tauri/overrides/nightly.conf.json b/clash-nyanpasu/backend/tauri/overrides/nightly.conf.json index fb2c874617..59b3df7737 100644 --- a/clash-nyanpasu/backend/tauri/overrides/nightly.conf.json +++ b/clash-nyanpasu/backend/tauri/overrides/nightly.conf.json @@ -5,7 +5,6 @@ "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDlBMUM0NjMxREZCNDRGMjYKUldRbVQ3VGZNVVljbW43N0FlWjA4UkNrbTgxSWxSSXJQcExXNkZjUTlTQkIyYkJzL0tsSWF2d0cK", "endpoints": [ - "https://deno.elaina.moe/updater/update-nightly-proxy.json", "https://nyanpasu.surge.sh/updater/update-nightly-proxy.json", "https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-nightly-proxy.json", "https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-nightly.json" diff --git a/clash-nyanpasu/backend/tauri/tauri.conf.json b/clash-nyanpasu/backend/tauri/tauri.conf.json index 21b5d6c727..8948e49206 100644 --- a/clash-nyanpasu/backend/tauri/tauri.conf.json +++ b/clash-nyanpasu/backend/tauri/tauri.conf.json @@ -72,7 +72,6 @@ "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDlBMUM0NjMxREZCNDRGMjYKUldRbVQ3VGZNVVljbW43N0FlWjA4UkNrbTgxSWxSSXJQcExXNkZjUTlTQkIyYkJzL0tsSWF2d0cK", "endpoints": [ - "https://deno.elaina.moe/updater/update-proxy.json", "https://nyanpasu.surge.sh/updater/update-proxy.json", "https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-proxy.json", "https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update.json" diff --git a/clash-nyanpasu/frontend/interface/src/template/index.ts b/clash-nyanpasu/frontend/interface/src/template/index.ts index 85794a3db9..258968f023 100644 --- a/clash-nyanpasu/frontend/interface/src/template/index.ts +++ b/clash-nyanpasu/frontend/interface/src/template/index.ts @@ -1,6 +1,6 @@ // nyanpasu merge profile chain template const merge = `# Clash Nyanpasu Merge Template (YAML) -# Documentation on https://nyanpasu.elaina.moe/ +# Documentation on https://nyanpasu.majokeiko.com/ # Set the default merge strategy to recursive merge. # Enable the old mode with the override__ prefix. # Use the filter__ prefix to filter lists (removing unwanted content). @@ -9,7 +9,7 @@ const merge = `# Clash Nyanpasu Merge Template (YAML) // nyanpasu javascript profile chain template const javascript = `// Clash Nyanpasu JavaScript Template -// Documentation on https://nyanpasu.elaina.moe/ +// Documentation on https://nyanpasu.majokeiko.com/ /** @type {config} */ export default function (profile) { @@ -19,14 +19,14 @@ export default function (profile) { // nyanpasu lua profile chain template const luascript = `-- Clash Nyanpasu Lua Script Template --- Documentation on https://nyanpasu.elaina.moe/ +-- Documentation on https://nyanpasu.majokeiko.com/ return config; ` // clash profile template example const profile = `# Clash Nyanpasu Profile Template -# Documentation on https://nyanpasu.elaina.moe/ +# Documentation on https://nyanpasu.majokeiko.com/ proxies: diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index db361f37bb..43e7aeda94 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -17,7 +17,7 @@ "@dnd-kit/utilities": "3.2.2", "@emotion/styled": "11.14.1", "@hookform/resolvers": "5.2.2", - "@inlang/paraglide-js": "2.15.0", + "@inlang/paraglide-js": "2.15.1", "@juggle/resize-observer": "3.4.0", "@material/material-color-utilities": "0.4.0", "@mui/icons-material": "7.3.9", @@ -43,7 +43,7 @@ "country-emoji": "1.5.6", "dayjs": "1.11.20", "framer-motion": "12.38.0", - "i18next": "25.8.20", + "i18next": "25.9.0", "jotai": "2.18.1", "json-schema": "0.4.0", "material-react-table": "3.2.1", @@ -69,7 +69,7 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.452", + "@iconify/json": "2.2.453", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.91.3", "@tanstack/react-router": "1.167.5", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/assets/json/clash-field.json b/clash-nyanpasu/frontend/nyanpasu/src/assets/json/clash-field.json index 7434616ac1..6d9678ef13 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/assets/json/clash-field.json +++ b/clash-nyanpasu/frontend/nyanpasu/src/assets/json/clash-field.json @@ -1,55 +1,55 @@ { "default": { - "proxies": "https://nyanpasu.elaina.moe/others/field.html#proxies", - "proxy-groups": "https://nyanpasu.elaina.moe/others/field.html#proxy-groups", - "proxy-providers": "https://nyanpasu.elaina.moe/others/field.html#proxy-providers", - "rules": "https://nyanpasu.elaina.moe/others/field.html#rules", - "rule-providers": "https://nyanpasu.elaina.moe/others/field.html#rule-providers" + "proxies": "https://nyanpasu.majokeiko.com/others/field.html#proxies", + "proxy-groups": "https://nyanpasu.majokeiko.com/others/field.html#proxy-groups", + "proxy-providers": "https://nyanpasu.majokeiko.com/others/field.html#proxy-providers", + "rules": "https://nyanpasu.majokeiko.com/others/field.html#rules", + "rule-providers": "https://nyanpasu.majokeiko.com/others/field.html#rule-providers" }, "handle": { - "mode": "https://nyanpasu.elaina.moe/others/field.html#mode", - "port": "https://nyanpasu.elaina.moe/others/field.html#port", - "socks-port": "https://nyanpasu.elaina.moe/others/field.html#socks-port", - "mixed-port": "https://nyanpasu.elaina.moe/others/field.html#mixed-port", - "allow-lan": "https://nyanpasu.elaina.moe/others/field.html#allow-lan", - "log-level": "https://nyanpasu.elaina.moe/others/field.html#log-level", - "ipv6": "https://nyanpasu.elaina.moe/others/field.html#ipv6", - "secret": "https://nyanpasu.elaina.moe/others/field.html#secret", - "external-controller": "https://nyanpasu.elaina.moe/others/field.html#external-controller" + "mode": "https://nyanpasu.majokeiko.com/others/field.html#mode", + "port": "https://nyanpasu.majokeiko.com/others/field.html#port", + "socks-port": "https://nyanpasu.majokeiko.com/others/field.html#socks-port", + "mixed-port": "https://nyanpasu.majokeiko.com/others/field.html#mixed-port", + "allow-lan": "https://nyanpasu.majokeiko.com/others/field.html#allow-lan", + "log-level": "https://nyanpasu.majokeiko.com/others/field.html#log-level", + "ipv6": "https://nyanpasu.majokeiko.com/others/field.html#ipv6", + "secret": "https://nyanpasu.majokeiko.com/others/field.html#secret", + "external-controller": "https://nyanpasu.majokeiko.com/others/field.html#external-controller" }, "other": { - "dns": "https://nyanpasu.elaina.moe/others/field.html#dns", - "tun": "https://nyanpasu.elaina.moe/others/field.html#tun", - "ebpf": "https://nyanpasu.elaina.moe/others/field.html#ebpf", - "hosts": "https://nyanpasu.elaina.moe/others/field.html#hosts", - "script": "https://nyanpasu.elaina.moe/others/field.html#script", - "profile": "https://nyanpasu.elaina.moe/others/field.html#profile", - "payload": "https://nyanpasu.elaina.moe/others/field.html#payload", - "tunnels": "https://nyanpasu.elaina.moe/others/field.html#tunnels", - "auto-redir": "https://nyanpasu.elaina.moe/others/field.html#auto-redir", - "experimental": "https://nyanpasu.elaina.moe/others/field.html#experimental", - "interface-name": "https://nyanpasu.elaina.moe/others/field.html#interface-name", - "routing-mark": "https://nyanpasu.elaina.moe/others/field.html#routing-mark", - "redir-port": "https://nyanpasu.elaina.moe/others/field.html#redir-port", - "tproxy-port": "https://nyanpasu.elaina.moe/others/field.html#tproxy-port", - "iptables": "https://nyanpasu.elaina.moe/others/field.html#iptables", - "external-ui": "https://nyanpasu.elaina.moe/others/field.html#external-ui", - "bind-address": "https://nyanpasu.elaina.moe/others/field.html#bind-address", - "authentication": "https://nyanpasu.elaina.moe/others/field.html#authentication" + "dns": "https://nyanpasu.majokeiko.com/others/field.html#dns", + "tun": "https://nyanpasu.majokeiko.com/others/field.html#tun", + "ebpf": "https://nyanpasu.majokeiko.com/others/field.html#ebpf", + "hosts": "https://nyanpasu.majokeiko.com/others/field.html#hosts", + "script": "https://nyanpasu.majokeiko.com/others/field.html#script", + "profile": "https://nyanpasu.majokeiko.com/others/field.html#profile", + "payload": "https://nyanpasu.majokeiko.com/others/field.html#payload", + "tunnels": "https://nyanpasu.majokeiko.com/others/field.html#tunnels", + "auto-redir": "https://nyanpasu.majokeiko.com/others/field.html#auto-redir", + "experimental": "https://nyanpasu.majokeiko.com/others/field.html#experimental", + "interface-name": "https://nyanpasu.majokeiko.com/others/field.html#interface-name", + "routing-mark": "https://nyanpasu.majokeiko.com/others/field.html#routing-mark", + "redir-port": "https://nyanpasu.majokeiko.com/others/field.html#redir-port", + "tproxy-port": "https://nyanpasu.majokeiko.com/others/field.html#tproxy-port", + "iptables": "https://nyanpasu.majokeiko.com/others/field.html#iptables", + "external-ui": "https://nyanpasu.majokeiko.com/others/field.html#external-ui", + "bind-address": "https://nyanpasu.majokeiko.com/others/field.html#bind-address", + "authentication": "https://nyanpasu.majokeiko.com/others/field.html#authentication" }, "meta": { - "tls": "https://nyanpasu.elaina.moe/others/field.html#tls", - "sniffer": "https://nyanpasu.elaina.moe/others/field.html#sniffer", - "geox-url": "https://nyanpasu.elaina.moe/others/field.html#geox-url", - "listeners": "https://nyanpasu.elaina.moe/others/field.html#listeners", - "sub-rules": "https://nyanpasu.elaina.moe/others/field.html#sub-rules", - "geodata-mode": "https://nyanpasu.elaina.moe/others/field.html#geodata-mode", - "unified-delay": "https://nyanpasu.elaina.moe/others/field.html#unified-delay", - "tcp-concurrent": "https://nyanpasu.elaina.moe/others/field.html#tcp-concurrent", - "enable-process": "https://nyanpasu.elaina.moe/others/field.html#enable-process", - "find-process-mode": "https://nyanpasu.elaina.moe/others/field.html#find-process-mode", - "skip-auth-prefixes": "https://nyanpasu.elaina.moe/others/field.html#skip-auth-prefixes", - "external-controller-tls": "https://nyanpasu.elaina.moe/others/field.html#external-controller-tls", - "global-client-fingerprint": "https://nyanpasu.elaina.moe/others/field.html#global-client-fingerprint" + "tls": "https://nyanpasu.majokeiko.com/others/field.html#tls", + "sniffer": "https://nyanpasu.majokeiko.com/others/field.html#sniffer", + "geox-url": "https://nyanpasu.majokeiko.com/others/field.html#geox-url", + "listeners": "https://nyanpasu.majokeiko.com/others/field.html#listeners", + "sub-rules": "https://nyanpasu.majokeiko.com/others/field.html#sub-rules", + "geodata-mode": "https://nyanpasu.majokeiko.com/others/field.html#geodata-mode", + "unified-delay": "https://nyanpasu.majokeiko.com/others/field.html#unified-delay", + "tcp-concurrent": "https://nyanpasu.majokeiko.com/others/field.html#tcp-concurrent", + "enable-process": "https://nyanpasu.majokeiko.com/others/field.html#enable-process", + "find-process-mode": "https://nyanpasu.majokeiko.com/others/field.html#find-process-mode", + "skip-auth-prefixes": "https://nyanpasu.majokeiko.com/others/field.html#skip-auth-prefixes", + "external-controller-tls": "https://nyanpasu.majokeiko.com/others/field.html#external-controller-tls", + "global-client-fingerprint": "https://nyanpasu.majokeiko.com/others/field.html#global-client-fingerprint" } } diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/logs/log-item.module.d.scss.ts b/clash-nyanpasu/frontend/nyanpasu/src/components/logs/log-item.module.d.scss.ts new file mode 100644 index 0000000000..acc37df6a8 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/logs/log-item.module.d.scss.ts @@ -0,0 +1,6 @@ +declare const classNames: { + readonly item: 'item' + readonly shiki: 'shiki' + readonly dark: 'dark' +} +export default classNames diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/_modules/header-help-action.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/_modules/header-help-action.tsx index fdeac8e813..93fda03ba4 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/_modules/header-help-action.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/_modules/header-help-action.tsx @@ -13,7 +13,7 @@ import { Link } from '@tanstack/react-router' const WikiItem = () => { const handleClick = useLockFn(async () => { - await commands.openThat('https://nyanpasu.elaina.moe') + await commands.openThat('https://nyanpasu.majokeiko.com') }) return ( diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 246a888056..bf13f65f2a 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,7 +2,7 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.21", - "mihomo_alpha": "alpha-dd4eb63", + "mihomo_alpha": "alpha-ae374f6", "clash_rs": "v0.9.6", "clash_premium": "2023-09-05-gdcc8d87", "clash_rs_alpha": "0.9.6-alpha+sha.b17ba0a" @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-rs-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2026-03-19T22:22:44.922Z" + "updated_at": "2026-03-20T22:22:49.611Z" } diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index 7cbe4214cd..0488262319 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -68,7 +68,7 @@ "cross-env": "10.1.0", "dedent": "1.7.2", "globals": "17.4.0", - "knip": "5.88.1", + "knip": "6.0.1", "lint-staged": "16.4.0", "npm-run-all2": "8.0.4", "oxlint": "1.56.0", diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index 3f8c0c07b2..94814241de 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -59,8 +59,8 @@ importers: specifier: 17.4.0 version: 17.4.0 knip: - specifier: 5.88.1 - version: 5.88.1(@types/node@24.11.0)(typescript@5.9.3) + specifier: 6.0.1 + version: 6.0.1 lint-staged: specifier: 16.4.0 version: 16.4.0 @@ -183,8 +183,8 @@ importers: specifier: 5.2.2 version: 5.2.2(react-hook-form@7.71.2(react@19.2.4)) '@inlang/paraglide-js': - specifier: 2.15.0 - version: 2.15.0(babel-plugin-macros@3.1.0) + specifier: 2.15.1 + version: 2.15.1(babel-plugin-macros@3.1.0) '@juggle/resize-observer': specifier: 3.4.0 version: 3.4.0 @@ -261,8 +261,8 @@ importers: specifier: 12.38.0 version: 12.38.0(@emotion/is-prop-valid@1.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) i18next: - specifier: 25.8.20 - version: 25.8.20(typescript@5.9.3) + specifier: 25.9.0 + version: 25.9.0(typescript@5.9.3) jotai: specifier: 2.18.1 version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.14)(react@19.2.4) @@ -301,7 +301,7 @@ importers: version: 8.2.0(e309558cb1df3652b39c2e76ceb9cee4) react-i18next: specifier: 15.7.4 - version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react-markdown: specifier: 10.1.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.4) @@ -334,8 +334,8 @@ importers: specifier: 11.14.0 version: 11.14.0(@types/react@19.2.14)(react@19.2.4) '@iconify/json': - specifier: 2.2.452 - version: 2.2.452 + specifier: 2.2.453 + version: 2.2.453 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -500,7 +500,7 @@ importers: version: 6.0.0(react@19.2.4) react-i18next: specifier: 15.7.4 - version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react-use: specifier: 17.6.0 version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -1704,8 +1704,8 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@iconify/json@2.2.452': - resolution: {integrity: sha512-3gUSKSQ3UHRpuvk0GL5WdD3n+WR9MhvxjJ3cFjoBb44kD8yKC/esyxeOZfXdilRmJFvs1y8+j5DM6SIwCtMbXw==} + '@iconify/json@2.2.453': + resolution: {integrity: sha512-LL3avJyZUFtB/Ts1WbpunFZ58XnodcQn0xMLQbm9gxuuZNpnt2D89n+CstNHpqheynDz+nZVSSvKko1bFXBOTA==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -1713,16 +1713,16 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} - '@inlang/paraglide-js@2.15.0': - resolution: {integrity: sha512-2ZOa9nssVn4tjkKskqb88KP5A7cTIjo8AiM9xnPvH+vBhRIRenO+ftAbVOHhHcHjcFxy2QFcOfBAH/Cw1LIsUg==} + '@inlang/paraglide-js@2.15.1': + resolution: {integrity: sha512-7wWKbLWwLx1dkkYz55TnVp+39atKXf7rnlHnL8adSmM73UaAdB9fXDzo24GHSY/6FPGFKSkgHdT2qyJv2whWsA==} hasBin: true '@inlang/recommend-sherlock@0.2.1': resolution: {integrity: sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==} - '@inlang/sdk@2.8.0': - resolution: {integrity: sha512-w1jysvUDTMgCaONklIgOJAp9dUDl0UhLbsdqfWEwY/GIqoc9IwpuHsrP3pzC+h3DfOpkMMDnDkTpPv8kIZ98iA==} - engines: {node: '>=18.0.0'} + '@inlang/sdk@2.9.1': + resolution: {integrity: sha512-y0C3xaKo6pSGDr3p5OdreRVT3THJpgKVe1lLvG3BE4v9lskp3UfI9cPCbN8X2dpfLt/4ljtehMb5SykpMfJrMg==} + engines: {node: '>=20.0.0'} '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} @@ -1762,8 +1762,8 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@lix-js/sdk@0.4.7': - resolution: {integrity: sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==} + '@lix-js/sdk@0.4.9': + resolution: {integrity: sha512-30mDkXpx704359oRrJI42bjfCspCiaMItngVBbPkiTGypS7xX4jYbHWQkXI8XuJ7VDB69D0MsVU6xfrBAIrM4A==} engines: {node: '>=18'} '@lix-js/server-protocol-schema@0.1.1': @@ -2232,42 +2232,91 @@ packages: resolution: {integrity: sha512-16TtZXNOfH8RaRsV+iag5dTYeJvdOdZDBcpEPCULdKS3eTRJqAYxBNZPFaDJ3cx3WNyvbaQ0IxsPpnaR/tgGFA==} engines: {node: '>= 20'} + '@oxc-parser/binding-android-arm-eabi@0.120.0': + resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.120.0': + resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@oxc-parser/binding-android-arm64@0.99.0': resolution: {integrity: sha512-V4jhmKXgQQdRnm73F+r3ZY4pUEsijQeSraFeaCGng7abSNJGs76X6l82wHnmjLGFAeY00LWtjcELs7ZmbJ9+lA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@oxc-parser/binding-darwin-arm64@0.120.0': + resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@oxc-parser/binding-darwin-arm64@0.99.0': resolution: {integrity: sha512-Rp41nf9zD5FyLZciS9l1GfK8PhYqrD5kEGxyTOA2esTLeAy37rZxetG2E3xteEolAkeb2WDkVrlxPtibeAncMg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@oxc-parser/binding-darwin-x64@0.120.0': + resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@oxc-parser/binding-darwin-x64@0.99.0': resolution: {integrity: sha512-WVonp40fPPxo5Gs0POTI57iEFv485TvNKOHMwZRhigwZRhZY2accEAkYIhei9eswF4HN5B44Wybkz7Gd1Qr/5Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@oxc-parser/binding-freebsd-x64@0.120.0': + resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@oxc-parser/binding-freebsd-x64@0.99.0': resolution: {integrity: sha512-H30bjOOttPmG54gAqu6+HzbLEzuNOYO2jZYrIq4At+NtLJwvNhXz28Hf5iEAFZIH/4hMpLkM4VN7uc+5UlNW3Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-gnueabihf@0.99.0': resolution: {integrity: sha512-0Z/Th0SYqzSRDPs6tk5lQdW0i73UCupnim3dgq2oW0//UdLonV/5wIZCArfKGC7w9y4h8TxgXpgtIyD1kKzzlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.99.0': resolution: {integrity: sha512-xo0wqNd5bpbzQVNpAIFbHk1xa+SaS/FGBABCd942SRTnrpxl6GeDj/s1BFaGcTl8MlwlKVMwOcyKrw/2Kdfquw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-arm64-gnu@0.99.0': resolution: {integrity: sha512-u26I6LKoLTPTd4Fcpr0aoAtjnGf5/ulMllo+QUiBhupgbVCAlaj4RyXH/mvcjcsl2bVBv9E/gYJZz2JjxQWXBA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2275,6 +2324,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-arm64-musl@0.99.0': resolution: {integrity: sha512-qhftDo2D37SqCEl3ZTa367NqWSZNb1Ddp34CTmShLKFrnKdNiUn55RdokLnHtf1AL5ssaQlYDwBECX7XiBWOhw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2282,6 +2338,20 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-riscv64-gnu@0.99.0': resolution: {integrity: sha512-zxn/xkf519f12FKkpL5XwJipsylfSSnm36h6c1zBDTz4fbIDMGyIhHfWfwM7uUmHo9Aqw1pLxFpY39Etv398+Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2289,6 +2359,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-s390x-gnu@0.99.0': resolution: {integrity: sha512-Y1eSDKDS5E4IVC7Oxw+NbYAKRmJPMJTIjW+9xOWwteDHkFqpocKe0USxog+Q1uhzalD9M0p9eXWEWdGQCMDBMQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2296,6 +2380,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.99.0': resolution: {integrity: sha512-YVJMfk5cFWB8i2/nIrbk6n15bFkMHqWnMIWkVx7r2KwpTxHyFMfu2IpeVKo1ITDSmt5nBrGdLHD36QRlu2nDLg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2303,6 +2394,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-x64-musl@0.120.0': + resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-x64-musl@0.99.0': resolution: {integrity: sha512-2+SDPrie5f90A1b9EirtVggOgsqtsYU5raZwkDYKyS1uvJzjqHCDhG/f4TwQxHmIc5YkczdQfwvN91lwmjsKYQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2310,23 +2408,55 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-openharmony-arm64@0.120.0': + resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.120.0': + resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + '@oxc-parser/binding-wasm32-wasi@0.99.0': resolution: {integrity: sha512-DKA4j0QerUWSMADziLM5sAyM7V53Fj95CV9SjP77bPfEfT7MnvFKnneaRMqPK1cpzjAGiQF52OBUIKyk0dwOQA==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@oxc-parser/binding-win32-arm64-msvc@0.99.0': resolution: {integrity: sha512-EaB3AvsxqdNUhh9FOoAxRZ2L4PCRwDlDb//QXItwyOJrX7XS+uGK9B1KEUV4FZ/7rDhHsWieLt5e07wl2Ti5AQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-parser/binding-win32-x64-msvc@0.99.0': resolution: {integrity: sha512-sJN1Q8h7ggFOyDn0zsHaXbP/MklAVUvhrbq0LA46Qum686P3SZQHjbATqJn9yaVEvaSKXCshgl0vQ1gWkGgpcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + '@oxc-project/types@0.99.0': resolution: {integrity: sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==} @@ -5538,6 +5668,9 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + git-raw-commits@5.0.1: resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} engines: {node: '>=18'} @@ -5664,8 +5797,8 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} - i18next@25.8.20: - resolution: {integrity: sha512-xjo9+lbX/P1tQt3xpO2rfJiBppNfUnNIPKgCvNsTKsvTOCro1Qr/geXVg1N47j5ScOSaXAPq8ET93raK3Rr06A==} + i18next@25.9.0: + resolution: {integrity: sha512-mJ4rVRNWOTkqh5xnaGR6iMFT5vEw3Y2MTJhcjinR/7u8cRv6dAfC0ofuePh5fVPxoh395p6JdrJTStCcNW66gg==} peerDependencies: typescript: ^5 peerDependenciesMeta: @@ -5946,13 +6079,10 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@5.88.1: - resolution: {integrity: sha512-tpy5o7zu1MjawVkLPuahymVJekYY3kYjvzcoInhIchgePxTlo+api90tBv2KfhAIe5uXh+mez1tAfmbv8/TiZg==} - engines: {node: '>=18.18.0'} + knip@6.0.1: + resolution: {integrity: sha512-qk5m+w6IYEqfRG5546DXZJYl5AXsgFfDD6ULaDvkubqNtLye79sokBg3usURrWFjASMeQtvX19TfldU3jHkMNA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - peerDependencies: - '@types/node': '>=18' - typescript: '>=5.0.4 <7' known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} @@ -5960,9 +6090,9 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - kysely@0.27.6: - resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} - engines: {node: '>=14.0.0'} + kysely@0.28.14: + resolution: {integrity: sha512-SU3lgh0rPvq7upc6vvdVrCsSMUG1h3ChvHVOY7wJ2fw4C9QEB7X3d5eyYEyULUX7UQtxZJtZXGuT6U2US72UYA==} + engines: {node: '>=20.0.0'} less@4.2.0: resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} @@ -6454,6 +6584,10 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + oxc-parser@0.120.0: + resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-parser@0.99.0: resolution: {integrity: sha512-MpS1lbd2vR0NZn1v0drpgu7RUFu3x9Rd0kxExObZc2+F+DIrV0BOMval/RO3BYGwssIOerII6iS8EbbpCCZQpQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -9356,7 +9490,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@iconify/json@2.2.452': + '@iconify/json@2.2.453': dependencies: '@iconify/types': 2.0.0 pathe: 2.0.3 @@ -9369,10 +9503,10 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.0 - '@inlang/paraglide-js@2.15.0(babel-plugin-macros@3.1.0)': + '@inlang/paraglide-js@2.15.1(babel-plugin-macros@3.1.0)': dependencies: '@inlang/recommend-sherlock': 0.2.1 - '@inlang/sdk': 2.8.0(babel-plugin-macros@3.1.0) + '@inlang/sdk': 2.9.1(babel-plugin-macros@3.1.0) commander: 11.1.0 consola: 3.4.0 json5: 2.2.3 @@ -9385,12 +9519,12 @@ snapshots: dependencies: comment-json: 4.5.0 - '@inlang/sdk@2.8.0(babel-plugin-macros@3.1.0)': + '@inlang/sdk@2.9.1(babel-plugin-macros@3.1.0)': dependencies: - '@lix-js/sdk': 0.4.7(babel-plugin-macros@3.1.0) + '@lix-js/sdk': 0.4.9(babel-plugin-macros@3.1.0) '@sinclair/typebox': 0.31.28 - kysely: 0.27.6 - sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + kysely: 0.28.14 + sqlite-wasm-kysely: 0.3.0(kysely@0.28.14) uuid: 13.0.0 transitivePeerDependencies: - babel-plugin-macros @@ -9435,14 +9569,14 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@lix-js/sdk@0.4.7(babel-plugin-macros@3.1.0)': + '@lix-js/sdk@0.4.9(babel-plugin-macros@3.1.0)': dependencies: '@lix-js/server-protocol-schema': 0.1.1 dedent: 1.5.1(babel-plugin-macros@3.1.0) human-id: 4.1.3 js-sha256: 0.11.1 - kysely: 0.27.6 - sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + kysely: 0.28.14 + sqlite-wasm-kysely: 0.3.0(kysely@0.28.14) uuid: 10.0.0 transitivePeerDependencies: - babel-plugin-macros @@ -9989,53 +10123,117 @@ snapshots: '@octokit/request-error': 7.0.2 '@octokit/webhooks-methods': 6.0.0 + '@oxc-parser/binding-android-arm-eabi@0.120.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.120.0': + optional: true + '@oxc-parser/binding-android-arm64@0.99.0': optional: true + '@oxc-parser/binding-darwin-arm64@0.120.0': + optional: true + '@oxc-parser/binding-darwin-arm64@0.99.0': optional: true + '@oxc-parser/binding-darwin-x64@0.120.0': + optional: true + '@oxc-parser/binding-darwin-x64@0.99.0': optional: true + '@oxc-parser/binding-freebsd-x64@0.120.0': + optional: true + '@oxc-parser/binding-freebsd-x64@0.99.0': optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.99.0': optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.99.0': optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.99.0': optional: true + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + optional: true + '@oxc-parser/binding-linux-arm64-musl@0.99.0': optional: true + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + optional: true + '@oxc-parser/binding-linux-riscv64-gnu@0.99.0': optional: true + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + optional: true + '@oxc-parser/binding-linux-s390x-gnu@0.99.0': optional: true + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + optional: true + '@oxc-parser/binding-linux-x64-gnu@0.99.0': optional: true + '@oxc-parser/binding-linux-x64-musl@0.120.0': + optional: true + '@oxc-parser/binding-linux-x64-musl@0.99.0': optional: true + '@oxc-parser/binding-openharmony-arm64@0.120.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.120.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + '@oxc-parser/binding-wasm32-wasi@0.99.0': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + optional: true + '@oxc-parser/binding-win32-arm64-msvc@0.99.0': optional: true + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + optional: true + '@oxc-parser/binding-win32-x64-msvc@0.99.0': optional: true + '@oxc-project/types@0.120.0': {} + '@oxc-project/types@0.99.0': {} '@oxc-resolver/binding-android-arm-eabi@11.19.1': @@ -13155,6 +13353,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + git-raw-commits@5.0.1(conventional-commits-parser@6.3.0): dependencies: '@conventional-changelog/git-client': 2.6.0(conventional-commits-parser@6.3.0) @@ -13315,7 +13517,7 @@ snapshots: hyphenate-style-name@1.1.0: {} - i18next@25.8.20(typescript@5.9.3): + i18next@25.9.0(typescript@5.9.3): dependencies: '@babel/runtime': 7.28.6 optionalDependencies: @@ -13519,20 +13721,20 @@ snapshots: kind-of@6.0.3: {} - knip@5.88.1(@types/node@24.11.0)(typescript@5.9.3): + knip@6.0.1: dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 24.11.0 fast-glob: 3.3.3 formatly: 0.3.0 + get-tsconfig: 4.13.6 jiti: 2.6.1 minimist: 1.2.8 + oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 picomatch: 4.0.3 smol-toml: 1.6.0 strip-json-comments: 5.0.3 - typescript: 5.9.3 unbash: 2.2.0 yaml: 2.8.2 zod: 4.3.6 @@ -13541,7 +13743,7 @@ snapshots: kolorist@1.8.0: {} - kysely@0.27.6: {} + kysely@0.28.14: {} less@4.2.0: dependencies: @@ -14170,6 +14372,31 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + oxc-parser@0.120.0: + dependencies: + '@oxc-project/types': 0.120.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.120.0 + '@oxc-parser/binding-android-arm64': 0.120.0 + '@oxc-parser/binding-darwin-arm64': 0.120.0 + '@oxc-parser/binding-darwin-x64': 0.120.0 + '@oxc-parser/binding-freebsd-x64': 0.120.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 + '@oxc-parser/binding-linux-arm64-musl': 0.120.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-musl': 0.120.0 + '@oxc-parser/binding-openharmony-arm64': 0.120.0 + '@oxc-parser/binding-wasm32-wasi': 0.120.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 + '@oxc-parser/binding-win32-x64-msvc': 0.120.0 + oxc-parser@0.99.0: dependencies: '@oxc-project/types': 0.99.0 @@ -14546,11 +14773,11 @@ snapshots: dependencies: react: 19.2.4 - react-i18next@15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + react-i18next@15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: '@babel/runtime': 7.28.3 html-parse-stringify: 3.0.1 - i18next: 25.8.20(typescript@5.9.3) + i18next: 25.9.0(typescript@5.9.3) react: 19.2.4 optionalDependencies: react-dom: 19.2.4(react@19.2.4) @@ -15031,10 +15258,10 @@ snapshots: sprintf-js@1.1.3: {} - sqlite-wasm-kysely@0.3.0(kysely@0.27.6): + sqlite-wasm-kysely@0.3.0(kysely@0.28.14): dependencies: '@sqlite.org/sqlite-wasm': 3.48.0-build4 - kysely: 0.27.6 + kysely: 0.28.14 stack-generator@2.0.10: dependencies: diff --git a/clash-nyanpasu/scripts/deno/utils/cache-client.ts b/clash-nyanpasu/scripts/deno/utils/cache-client.ts index f1ca4ffe87..6b75c9c7c0 100644 --- a/clash-nyanpasu/scripts/deno/utils/cache-client.ts +++ b/clash-nyanpasu/scripts/deno/utils/cache-client.ts @@ -3,7 +3,7 @@ import { writeAll } from 'jsr:@std/io@0.225/write-all' import { CHUNK_MULTIPLIER, performChunkedUpload } from './file-server.ts' import { consola } from './logger.ts' -const CACHE_BASE_URL = 'https://file-server.elaina.moe/cache' +const CACHE_BASE_URL = 'https://file-server.majokeiko.com/cache' // --- cache chunked upload types --- diff --git a/clash-nyanpasu/scripts/deno/utils/file-server.ts b/clash-nyanpasu/scripts/deno/utils/file-server.ts index 9a7ee5e92f..dad7358e84 100644 --- a/clash-nyanpasu/scripts/deno/utils/file-server.ts +++ b/clash-nyanpasu/scripts/deno/utils/file-server.ts @@ -5,8 +5,8 @@ import { consola } from './logger.ts' // --- constants --- -export const FILE_SERVER_UPLOAD_URL = 'https://file-server.elaina.moe/upload' -export const FILE_SERVER_BIN_URL = 'https://file-server.elaina.moe/bin' +export const FILE_SERVER_UPLOAD_URL = 'https://file-server.majokeiko.com/upload' +export const FILE_SERVER_BIN_URL = 'https://file-server.majokeiko.com/bin' export const UPLOAD_CONCURRENCY = 3 export const CHUNK_RETRY_ATTEMPTS = 5 diff --git a/filebrowser/.github/workflows/ci.yaml b/filebrowser/.github/workflows/ci.yaml index 895dfe2b4b..c6e4c7b39f 100644 --- a/filebrowser/.github/workflows/ci.yaml +++ b/filebrowser/.github/workflows/ci.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v5 with: package_json_file: "frontend/package.json" - uses: actions/setup-node@v6 @@ -59,7 +59,7 @@ jobs: - uses: actions/setup-go@v6 with: go-version: '1.26' - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v5 with: package_json_file: "frontend/package.json" - uses: actions/setup-node@v6 @@ -68,7 +68,7 @@ jobs: cache: "pnpm" cache-dependency-path: "frontend/pnpm-lock.yaml" - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 - run: task build release: @@ -83,7 +83,7 @@ jobs: - uses: actions/setup-go@v6 with: go-version: '1.26' - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v5 with: package_json_file: "frontend/package.json" - uses: actions/setup-node@v6 @@ -96,7 +96,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 - run: task build:frontend - name: Login to Docker Hub uses: docker/login-action@v4 diff --git a/filebrowser/.github/workflows/docs.yml b/filebrowser/.github/workflows/docs.yml index 6dfcfe2394..d7cbf75e10 100644 --- a/filebrowser/.github/workflows/docs.yml +++ b/filebrowser/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 - name: Build site run: task docs @@ -41,7 +41,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 - name: Build site run: task docs - name: Upload static files as artifact diff --git a/mihomo/.github/workflows/build.yml b/mihomo/.github/workflows/build.yml index 461a400911..c7e56b299e 100644 --- a/mihomo/.github/workflows/build.yml +++ b/mihomo/.github/workflows/build.yml @@ -155,7 +155,7 @@ jobs: - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' } steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Go if: ${{ matrix.jobs.goversion == '' }} @@ -386,7 +386,7 @@ jobs: shell: bash - name: Archive production artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}" path: | @@ -405,7 +405,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download all workflow run artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true @@ -463,7 +463,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: Meta fetch-depth: '0' @@ -498,7 +498,7 @@ jobs: bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} rm ./genReleaseNote.sh - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true @@ -522,11 +522,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: bin/ merge-multiple: true diff --git a/mihomo/.github/workflows/test.yml b/mihomo/.github/workflows/test.yml index f2862e9a86..13a08a4ad5 100644 --- a/mihomo/.github/workflows/test.yml +++ b/mihomo/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: # Fix mingw trying to be smart and converting paths https://github.com/moby/moby/issues/24029#issuecomment-250412919 MSYS_NO_PATHCONV: true steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 diff --git a/mihomo/dns/resolver.go b/mihomo/dns/resolver.go index 9184fbfb22..9295929815 100644 --- a/mihomo/dns/resolver.go +++ b/mihomo/dns/resolver.go @@ -165,11 +165,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e q := m.Question[0] domain := msgToDomain(m) - cacheM, expireTime, hit := r.cache.GetWithExpire(q.String()) + msg, expireTime, hit := getMsgFromCache(r.cache, q) if hit { - log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(cacheM), expireTime.Format("2006-01-02 15:04:05")) + log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(msg), expireTime.Format("2006-01-02 15:04:05")) now := time.Now() - msg = cacheM.Copy() if expireTime.Before(now) { setMsgTTL(msg, uint32(1)) // Continue fetch continueFetch = true @@ -201,14 +200,8 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M return } - msg := result - if cache { - // OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files. - msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool { - return rr.Header().Rrtype != D.TypeOPT - }) - putMsgToCache(r.cache, q.String(), q, msg) + putMsgToCache(r.cache, q, result) } }() diff --git a/mihomo/dns/util.go b/mihomo/dns/util.go index b7082818cf..ef57a7c24f 100644 --- a/mihomo/dns/util.go +++ b/mihomo/dns/util.go @@ -45,7 +45,19 @@ func updateTTL(records []D.RR, ttl uint32) { } } -func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) { +// getMsgFromCache returns a cached dns message if it exists, otherwise returns nil. +// the returned msg is a copy of the original msg, so it can be modified without affecting the original msg. +func getMsgFromCache(c dnsCache, q D.Question) (*D.Msg, time.Time, bool) { + msg, expireTime, hit := c.GetWithExpire(q.String()) + if msg != nil { + msg = msg.Copy() // never modify the original msg + } + return msg, expireTime, hit +} + +// putMsgToCache puts a dns message into the cache. +// the msg is copied before being stored in the cache, so it can be modified without affecting the original msg. +func putMsgToCache(c dnsCache, q D.Question, msg *D.Msg) { // skip dns cache for acme challenge if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") { log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) @@ -58,12 +70,20 @@ func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) { // If it does so it MUST NOT cache it for longer than five (5) minutes [...] ttl = serverFailureCacheTTL } else { - ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...)) + ttl = minimalTTL(lo.Concat(msg.Answer, msg.Ns, msg.Extra)) } if ttl == 0 { return } - c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second)) + + msg = msg.Copy() // never modify the original msg + + // OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files. + msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool { + return rr.Header().Rrtype != D.TypeOPT + }) + + c.SetWithExpire(q.String(), msg, time.Now().Add(time.Duration(ttl)*time.Second)) } func setMsgTTL(msg *D.Msg, ttl uint32) { diff --git a/small/pdnsd-alt/Makefile b/small/pdnsd-alt/Makefile index f50ec1143c..3b66d14de6 100644 --- a/small/pdnsd-alt/Makefile +++ b/small/pdnsd-alt/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=pdnsd -PKG_VERSION:=1.3.1 +PKG_VERSION:=1.3.2 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/pdnsd/tar.gz/$(PKG_VERSION)? -PKG_HASH:=28929bfd7874fd86872dc1bf4cd01103d7ebbdad6a457ccd9a8947a2819440be +PKG_HASH:=ce76cecc5238ab48c0a78baa8225ce896f204fe9bda61845eeaf70f551b17478 PKG_BUILD_PARALLEL:=1 PKG_INSTALL:=1 @@ -54,4 +54,3 @@ endef $(eval $(call BuildPackage,pdnsd-alt)) - diff --git a/small/shadowsocksr-libev/Makefile b/small/shadowsocksr-libev/Makefile index f89d056d6b..694261dbf6 100644 --- a/small/shadowsocksr-libev/Makefile +++ b/small/shadowsocksr-libev/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=shadowsocksr-libev -PKG_VERSION:=2.5.9 +PKG_VERSION:=2.5.10 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/shadowsocksr-libev/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=142d62bf4a1937c96c3f0970eef7391ce8df4efb830e295338a594ddf6493630 +PKG_HASH:=7699d420bea841f6314a7b9d5c5fa996e02139812c79d39bbc4f786c65c4fffc PKG_LICENSE:=GPL-3.0 PKG_LICENSE_FILES:=LICENSE diff --git a/small/simple-obfs/Makefile b/small/simple-obfs/Makefile index be75484831..4dd4936634 100644 --- a/small/simple-obfs/Makefile +++ b/small/simple-obfs/Makefile @@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=simple-obfs -PKG_VERSION:=0.0.10 +PKG_VERSION:=0.0.11 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/simple-obfs/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=f64eb9f9013fe0798e5159e3cd108909eb4ac949d78d51804d96945af82d363e +PKG_HASH:=b5f1de228bbc3fcb7b2e1160c4736e9eb47978bd92b7bb3f0948f6a06bc979f0 PKG_LICENSE:=GPL-3.0-or-later PKG_LICENSE_FILES:=LICENSE diff --git a/small/trojan/Makefile b/small/trojan/Makefile index a46e3f6b56..de4db3a848 100644 --- a/small/trojan/Makefile +++ b/small/trojan/Makefile @@ -7,12 +7,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=trojan -PKG_VERSION:=1.16.9 +PKG_VERSION:=1.17.0 PKG_RELEASE:=1 PKG_SOURCE:=Trojan-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/trojan/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=c8c65f8227dbffc2853fad9da759391ea9c709e12cee71b78392a4cdb8aeb89c +PKG_HASH:=e6772fed325301760e7150ddd483faa77af9046a2aeedbdb51278b4d6d9ae33f PKG_BUILD_PARALLEL:=1 PKG_BUILD_DEPENDS:=openssl diff --git a/small/v2dat/Makefile b/small/v2dat/Makefile index a635e69d0c..7cf6b430ba 100644 --- a/small/v2dat/Makefile +++ b/small/v2dat/Makefile @@ -7,12 +7,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2dat -PKG_VERSION:=0.1.2 +PKG_VERSION:=0.1.3 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/v2dat/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=a85229fa6b0c9fbb4cb14c3e4734f5c6b743a779e6337bba09e144720451e1ac +PKG_HASH:=301e6c96266a512d5f0caa143c1128ac8b5b9c4bfbf96ec0c7027cc7aa5ce865 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) PKG_LICENSE:=GPL-3.0 diff --git a/v2rayn/v2rayN/ServiceLib/Enums/EConfigType.cs b/v2rayn/v2rayN/ServiceLib/Enums/EConfigType.cs index 287d7b94b1..ae4e30caa4 100644 --- a/v2rayn/v2rayN/ServiceLib/Enums/EConfigType.cs +++ b/v2rayn/v2rayN/ServiceLib/Enums/EConfigType.cs @@ -13,6 +13,7 @@ public enum EConfigType WireGuard = 9, HTTP = 10, Anytls = 11, + Naive = 12, PolicyGroup = 101, ProxyChain = 102, } diff --git a/v2rayn/v2rayN/ServiceLib/Global.cs b/v2rayn/v2rayN/ServiceLib/Global.cs index e3ed209d92..e9a5a41e57 100644 --- a/v2rayn/v2rayN/ServiceLib/Global.cs +++ b/v2rayn/v2rayN/ServiceLib/Global.cs @@ -193,6 +193,10 @@ public class Global public const string Hysteria2ProtocolShare = "hy2://"; + public const string NaiveHttpsProtocolShare = "naive+https://"; + + public const string NaiveQuicProtocolShare = "naive+quic://"; + public static readonly Dictionary ProtocolShares = new() { { EConfigType.VMess, "vmess://" }, @@ -203,7 +207,8 @@ public class Global { EConfigType.Hysteria2, "hysteria2://" }, { EConfigType.TUIC, "tuic://" }, { EConfigType.WireGuard, "wireguard://" }, - { EConfigType.Anytls, "anytls://" } + { EConfigType.Anytls, "anytls://" }, + { EConfigType.Naive, "naive://" } }; public static readonly Dictionary ProtocolTypes = new() @@ -217,7 +222,8 @@ public class Global { EConfigType.Hysteria2, "hysteria2" }, { EConfigType.TUIC, "tuic" }, { EConfigType.WireGuard, "wireguard" }, - { EConfigType.Anytls, "anytls" } + { EConfigType.Anytls, "anytls" }, + { EConfigType.Naive, "naive" } }; public static readonly List VmessSecurities = @@ -342,6 +348,7 @@ public class Global EConfigType.Hysteria2, EConfigType.TUIC, EConfigType.Anytls, + EConfigType.Naive, EConfigType.WireGuard, EConfigType.SOCKS, EConfigType.HTTP, @@ -558,6 +565,14 @@ public class Global "bbr" ]; + public static readonly List NaiveCongestionControls = + [ + "bbr", + "bbr2", + "cubic", + "reno" + ]; + public static readonly List allowSelectType = [ "selector", @@ -660,5 +675,14 @@ public class Global "" ]; + public static readonly List TunIcmpRoutingPolicies = + [ + "rule", + "direct", + "unreachable", + "drop", + "reply", + ]; + #endregion const } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index ddc98a1f9e..a8cca65412 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -322,6 +322,7 @@ public class CoreConfigContextBuilder context.ProtectDomainList.Add(address); } + // ech query server name protect if (!node.EchConfigList.IsNullOrEmpty()) { var echQuerySni = node.Sni; @@ -338,6 +339,20 @@ public class CoreConfigContextBuilder } } + // xhttp downloadSettings address protect + if (!string.IsNullOrEmpty(node.Extra) + && JsonUtils.ParseJson(node.Extra) is JsonObject extra + && extra.TryGetPropertyValue("downloadSettings", out var dsNode) + && dsNode is JsonObject downloadSettings + && downloadSettings.TryGetPropertyValue("address", out var dAddrNode) + && dAddrNode is JsonValue dAddrValue + && dAddrValue.TryGetValue(out string? dAddr) + && !string.IsNullOrEmpty(dAddr) + && Utils.IsDomain(dAddr)) + { + context.ProtectDomainList.Add(dAddr); + } + return nodeValidatorResult; } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 347586184a..8f079451b4 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -91,6 +91,7 @@ public static class ConfigHandler { EnableTun = false, Mtu = 9000, + IcmpRouting = Global.TunIcmpRoutingPolicies.First(), }; config.GuiItem ??= new(); config.MsgUIItem ??= new(); @@ -269,6 +270,7 @@ public static class ConfigHandler EConfigType.TUIC => await AddTuicServer(config, item), EConfigType.WireGuard => await AddWireguardServer(config, item), EConfigType.Anytls => await AddAnytlsServer(config, item), + EConfigType.Naive => await AddNaiveServer(config, item), _ => -1, }; return ret; @@ -804,7 +806,7 @@ public static class ConfigHandler } /// - /// Add or edit a Anytls server + /// Add or edit an Anytls server /// Validates and processes Anytls-specific settings /// /// Current configuration @@ -831,6 +833,36 @@ public static class ConfigHandler return 0; } + /// + /// Add or edit a Naive server + /// Validates and processes Naive-specific settings + /// + /// Current configuration + /// Naive profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddNaiveServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Naive; + profileItem.CoreType = ECoreType.sing_box; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Username = profileItem.Username.TrimEx(); + profileItem.Password = profileItem.Password.TrimEx(); + profileItem.Alpn = string.Empty; + profileItem.Network = string.Empty; + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Password.IsNullOrEmpty()) + { + return -1; + } + await AddServerCommon(config, profileItem, toFile); + return 0; + } + /// /// Sort the server list by the specified column /// Updates the sort order in the profile extension data @@ -1077,7 +1109,8 @@ public static class ConfigHandler if (toFile) { - profileItem.SetProtocolExtra(); + //profileItem.SetProtocolExtra(); + profileItem.SetProtocolExtra(profileItem.GetProtocolExtra()); await SQLiteHelper.Instance.ReplaceAsync(profileItem); } return 0; @@ -1105,6 +1138,7 @@ public static class ConfigHandler && AreEqual(o.Address, n.Address) && o.Port == n.Port && AreEqual(o.Password, n.Password) + && AreEqual(o.Username, n.Username) && AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption) && AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod) && AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity) @@ -1496,6 +1530,7 @@ public static class ConfigHandler EConfigType.TUIC => await AddTuicServer(config, profileItem, false), EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false), EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false), + EConfigType.Naive => await AddNaiveServer(config, profileItem, false), _ => -1, }; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 4fc251b793..611e515916 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -19,6 +19,7 @@ public class FmtHandler EConfigType.TUIC => TuicFmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item), EConfigType.Anytls => AnytlsFmt.ToUri(item), + EConfigType.Naive => NaiveFmt.ToUri(item), _ => null, }; @@ -80,6 +81,12 @@ public class FmtHandler { return AnytlsFmt.Resolve(str, out msg); } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Naive]) + || str.StartsWith(Global.NaiveHttpsProtocolShare) + || str.StartsWith(Global.NaiveQuicProtocolShare)) + { + return NaiveFmt.Resolve(str, out msg); + } else { msg = ResUI.NonvmessOrssProtocol; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs new file mode 100644 index 0000000000..5792472b44 --- /dev/null +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs @@ -0,0 +1,91 @@ +namespace ServiceLib.Handler.Fmt; + +public class NaiveFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.Naive, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var protocolExtra = item.GetProtocolExtra(); + if (parsedUrl.Scheme.Contains("quic")) + { + protocolExtra = protocolExtra with + { + NaiveQuic = true, + }; + } + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + if (rawUserInfo.Contains(':')) + { + var split = rawUserInfo.Split(':', 2); + item.Username = split[0]; + item.Password = split[1]; + } + else + { + item.Password = rawUserInfo; + } + + var query = Utils.ParseQueryString(parsedUrl.Query); + ResolveUriQuery(query, ref item); + var insecureConcurrency = int.TryParse(GetQueryValue(query, "insecure-concurrency"), out var ic) ? ic : 0; + if (insecureConcurrency > 0) + { + protocolExtra = protocolExtra with + { + InsecureConcurrency = insecureConcurrency, + }; + } + + item.SetProtocolExtra(protocolExtra); + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var userInfo = item.Username.IsNotEmpty() ? $"{Utils.UrlEncode(item.Username)}:{Utils.UrlEncode(item.Password)}" : Utils.UrlEncode(item.Password); + var dicQuery = new Dictionary(); + ToUriQuery(item, Global.None, ref dicQuery); + var protocolExtra = item.GetProtocolExtra(); + if (protocolExtra.InsecureConcurrency > 0) + { + dicQuery.Add("insecure-concurrency", protocolExtra?.InsecureConcurrency.ToString()); + } + + var query = dicQuery.Count > 0 + ? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray())) + : string.Empty; + var url = $"{userInfo}@{GetIpv6(item.Address)}:{item.Port}"; + + if (protocolExtra.NaiveQuic == true) + { + return $"{Global.NaiveQuicProtocolShare}{url}{query}{remark}"; + } + else + { + return $"{Global.NaiveHttpsProtocolShare}{url}{query}{remark}"; + } + } +} diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index 56f78ea935..d1f802b081 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -30,7 +30,10 @@ public class TuicFmt : BaseFmt var query = Utils.ParseQueryString(url.Query); ResolveUriQuery(query, ref item); - item.HeaderType = GetQueryValue(query, "congestion_control"); + item.SetProtocolExtra(item.GetProtocolExtra() with + { + CongestionControl = GetQueryValue(query, "congestion_control") + }); return item; } @@ -51,7 +54,10 @@ public class TuicFmt : BaseFmt var dicQuery = new Dictionary(); ToUriQueryLite(item, ref dicQuery); - dicQuery.Add("congestion_control", item.HeaderType); + if (!item.GetProtocolExtra().CongestionControl.IsNullOrEmpty()) + { + dicQuery.Add("congestion_control", item.GetProtocolExtra().CongestionControl); + } return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark); } diff --git a/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs index 43c5611df1..35847d71e4 100644 --- a/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/AppManager.cs @@ -305,12 +305,11 @@ public sealed class AppManager return await SQLiteHelper.Instance.TableAsync().FirstOrDefaultAsync(it => it.CoreType == eCoreType); } +#pragma warning disable CS0618 public async Task MigrateProfileExtra() { await MigrateProfileExtraGroup(); -#pragma warning disable CS0618 - const int pageSize = 100; var offset = 0; @@ -334,7 +333,6 @@ public sealed class AppManager } //await ProfileGroupItemManager.Instance.ClearAll(); -#pragma warning restore CS0618 } private async Task MigrateProfileExtraSub(List batch) @@ -380,6 +378,7 @@ public sealed class AppManager break; case EConfigType.TUIC: + extra = extra with { CongestionControl = item.HeaderType.NullIfEmpty(), }; item.Username = item.Id; item.Id = item.Security; item.Password = item.Security; @@ -436,7 +435,6 @@ public sealed class AppManager private async Task MigrateProfileExtraGroup() { -#pragma warning disable CS0618 var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); var groupItems = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); @@ -501,8 +499,8 @@ public sealed class AppManager return true; //await ProfileGroupItemManager.Instance.ClearAll(); -#pragma warning restore CS0618 } +#pragma warning restore CS0618 #endregion SqliteHelper diff --git a/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs index 741c21e5de..8583906bad 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -144,6 +144,7 @@ public class TunModeItem public string Stack { get; set; } public int Mtu { get; set; } public bool EnableIPv6Address { get; set; } + public string IcmpRouting { get; set; } } [Serializable] diff --git a/v2rayn/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayn/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index a768ba8011..03403f5b85 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -2,6 +2,9 @@ namespace ServiceLib.Models; public record ProtocolExtraItem { + public bool? Uot { get; init; } + public string? CongestionControl { get; init; } + // vmess public string? AlterId { get; init; } public string? VmessSecurity { get; init; } @@ -29,6 +32,10 @@ public record ProtocolExtraItem public string? Ports { get; init; } public string? HopInterval { get; init; } + // naiveproxy + public int? InsecureConcurrency { get; init; } + public bool? NaiveQuic { get; init; } + // group profile public string? GroupType { get; init; } public string? ChildItems { get; init; } diff --git a/v2rayn/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayn/v2rayN/ServiceLib/Models/SingboxConfig.cs index 03261c46eb..f2331cc472 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -134,10 +134,14 @@ public class Outbound4Sbox : BaseServer4Sbox public int? recv_window_conn { get; set; } public int? recv_window { get; set; } public bool? disable_mtu_discovery { get; set; } + public int? insecure_concurrency { get; set; } + public bool? udp_over_tcp { get; set; } public string? method { get; set; } public string? username { get; set; } public string? password { get; set; } public string? congestion_control { get; set; } + public bool? quic { get; set; } + public string? quic_congestion_control { get; set; } public string? version { get; set; } public string? network { get; set; } public string? packet_encoding { get; set; } diff --git a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs index 6d4eabcb85..a6e6e8c770 100644 --- a/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayn/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -179,6 +179,8 @@ public class ServersItem4Ray public string flow { get; set; } + public bool? uot { get; set; } + public List users { get; set; } } diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index c75715ae16..bd26cd9ef4 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -691,7 +691,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add Child 的本地化字符串。 + /// 查找类似 Add Child 的本地化字符串。 /// public static string menuAddChildServer { get { @@ -718,7 +718,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [Hysteria2] 的本地化字符串。 + /// 查找类似 Add [Hysteria2] 的本地化字符串。 /// public static string menuAddHysteria2Server { get { @@ -727,7 +727,16 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add Policy Group 的本地化字符串。 + /// 查找类似 Add [NaïveProxy] 的本地化字符串。 + /// + public static string menuAddNaiveServer { + get { + return ResourceManager.GetString("menuAddNaiveServer", resourceCulture); + } + } + + /// + /// 查找类似 Add Policy Group 的本地化字符串。 /// public static string menuAddPolicyGroupServer { get { @@ -772,7 +781,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [Shadowsocks] 的本地化字符串。 + /// 查找类似 Add [Shadowsocks] 的本地化字符串。 /// public static string menuAddShadowsocksServer { get { @@ -781,7 +790,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [SOCKS] 的本地化字符串。 + /// 查找类似 Add [SOCKS] 的本地化字符串。 /// public static string menuAddSocksServer { get { @@ -790,7 +799,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [Trojan] 的本地化字符串。 + /// 查找类似 Add [Trojan] 的本地化字符串。 /// public static string menuAddTrojanServer { get { @@ -799,7 +808,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [TUIC] 的本地化字符串。 + /// 查找类似 Add [TUIC] 的本地化字符串。 /// public static string menuAddTuicServer { get { @@ -808,7 +817,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [VLESS] 的本地化字符串。 + /// 查找类似 Add [VLESS] 的本地化字符串。 /// public static string menuAddVlessServer { get { @@ -817,7 +826,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [VMess] 的本地化字符串。 + /// 查找类似 Add [VMess] 的本地化字符串。 /// public static string menuAddVmessServer { get { @@ -826,7 +835,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 Add [WireGuard] 的本地化字符串。 + /// 查找类似 Add [WireGuard] 的本地化字符串。 /// public static string menuAddWireguardServer { get { @@ -3069,6 +3078,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 ICMP routing policy 的本地化字符串。 + /// + public static string TbIcmpRoutingPolicy { + get { + return ResourceManager.GetString("TbIcmpRoutingPolicy", resourceCulture); + } + } + /// /// 查找类似 UUID(id) 的本地化字符串。 /// @@ -3105,6 +3123,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Insecure Concurrency 的本地化字符串。 + /// + public static string TbInsecureConcurrency { + get { + return ResourceManager.GetString("TbInsecureConcurrency", resourceCulture); + } + } + /// /// 查找类似 Most Stable 的本地化字符串。 /// @@ -4491,6 +4518,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 UDP over TCP 的本地化字符串。 + /// + public static string TbUot { + get { + return ResourceManager.GetString("TbUot", resourceCulture); + } + } + + /// + /// 查找类似 Username 的本地化字符串。 + /// + public static string TbUsername { + get { + return ResourceManager.GetString("TbUsername", resourceCulture); + } + } + /// /// 查找类似 Validate Regional Domain IPs 的本地化字符串。 /// diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 30f663e82f..f95646a649 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Format + + UDP over TCP + + + Add NaïveProxy + + + Insecure Concurrency + + + Username + + + ICMP routing policy + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 061d6dadbe..fcdab47a54 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1677,4 +1677,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Format + + UDP over TCP + + + Ajouter [NaïveProxy] + + + Insecure Concurrency + + + Username + + + ICMP routing policy + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 4d65258717..a22a476a40 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Format + + UDP over TCP + + + [NaïveProxy] konfiguráció hozzáadása + + + Insecure Concurrency + + + Username + + + ICMP routing policy + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx index 787e7bd950..26d54e5172 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx @@ -514,19 +514,19 @@ Add a custom configuration - Add [Shadowsocks] + Add [Shadowsocks] - Add [SOCKS] + Add [SOCKS] - Add [Trojan] + Add [Trojan] - Add [VLESS] + Add [VLESS] - Add [VMess] + Add [VMess] Select all @@ -1036,7 +1036,7 @@ Domain - Add [Hysteria2] + Add [Hysteria2] Hysteria Max bandwidth (Up/Down) @@ -1045,7 +1045,7 @@ Use System Hosts - Add [TUIC] + Add [TUIC] Congestion control @@ -1075,7 +1075,7 @@ Enable IPv6 Address - Add [WireGuard] + Add [WireGuard] Private Key @@ -1495,13 +1495,13 @@ Policy Group Type - Add Policy Group + Add Policy Group Add Proxy Chain - Add Child + Add Child Remove Child @@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Format + + UDP over TCP + + + Add [NaïveProxy] + + + Insecure Concurrency + + + Username + + + ICMP routing policy + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx index ba2ef07469..aa0594bd42 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1680,4 +1680,19 @@ Format + + UDP over TCP + + + Добавить сервер [NaïveProxy] + + + Insecure Concurrency + + + Username + + + ICMP routing policy + diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 707609d0d5..484e2a1250 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -517,16 +517,16 @@ 添加 [Shadowsocks] - 添加 [SOCKS] + 添加 [SOCKS] - 添加 [Trojan] + 添加 [Trojan] - 添加 [VLESS] + 添加 [VLESS] - 添加 [VMess] + 添加 [VMess] 全选 @@ -1033,7 +1033,7 @@ Domain - 添加 [Hysteria2] + 添加 [Hysteria2] Hysteria 最大带宽 (Up/Dw) @@ -1042,7 +1042,7 @@ 使用系统 hosts - 添加 [TUIC] + 添加 [TUIC] 拥塞控制算法 @@ -1072,7 +1072,7 @@ 启用 IPv6 - 添加 [WireGuard] + 添加 [WireGuard] PrivateKey @@ -1102,7 +1102,7 @@ *grpc Authority - 添加 [HTTP] + 添加 [HTTP] 启用分片 (Fragment) @@ -1381,7 +1381,7 @@ Mldsa65Verify - 添加 [Anytls] + 添加 [Anytls] 远程 DNS @@ -1677,4 +1677,19 @@ 格式化 + + UDP over TCP + + + 添加 [NaïveProxy] + + + 不安全并发 + + + 用户名 + + + ICMP 路由策略 + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 4e41730803..8496f29341 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1677,4 +1677,19 @@ Format + + UDP over TCP + + + 新增 [NaïveProxy] 節點 + + + Insecure Concurrency + + + Username + + + ICMP routing policy + \ No newline at end of file diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index b30c9c4881..4d4bc04ef9 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -112,6 +112,7 @@ public partial class CoreConfigSingboxService outbound.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod) ? protocolExtra.SsMethod : Global.None; outbound.password = _node.Password; + outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null; if (_node.Network == nameof(ETransport.tcp) && _node.HeaderType == Global.TcpHeaderHttp) { @@ -269,7 +270,7 @@ public partial class CoreConfigSingboxService { outbound.uuid = _node.Username; outbound.password = _node.Password; - outbound.congestion_control = _node.HeaderType; + outbound.congestion_control = protocolExtra.CongestionControl; break; } case EConfigType.Anytls: @@ -277,6 +278,22 @@ public partial class CoreConfigSingboxService outbound.password = _node.Password; break; } + case EConfigType.Naive: + { + outbound.username = _node.Username; + outbound.password = _node.Password; + if (protocolExtra.NaiveQuic == true) + { + outbound.quic = true; + outbound.quic_congestion_control = protocolExtra.CongestionControl.NullIfEmpty(); + } + if (protocolExtra.InsecureConcurrency > 0) + { + outbound.insecure_concurrency = protocolExtra.InsecureConcurrency; + } + outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null; + break; + } } FillOutboundTls(outbound); @@ -727,13 +744,12 @@ public partial class CoreConfigSingboxService }, null); } var idx = echConfig.IndexOf('+'); - // NOTE: query_server_name, since sing-box 1.13.0 - //var queryServerName = idx > 0 ? echConfig[..idx] : null; + var queryServerName = idx > 0 ? echConfig[..idx] : null; var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig; return (new Ech4Sbox() { enabled = true, - query_server_name = null, + query_server_name = queryServerName, }, ParseDnsAddress(echDnsServer)); } } diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 45620c41f2..7e1af38945 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -47,6 +47,36 @@ public partial class CoreConfigSingboxService outbound = Global.DirectTag, process_name = lstDirectExe }); + + // ICMP Routing + var icmpRouting = _config.TunModeItem.IcmpRouting ?? ""; + if (!Global.TunIcmpRoutingPolicies.Contains(icmpRouting)) + { + icmpRouting = Global.TunIcmpRoutingPolicies.First(); + } + if (icmpRouting == "direct") + { + _coreConfig.route.rules.Add(new() + { + network = ["icmp"], + outbound = Global.DirectTag, + }); + } + else if (icmpRouting != "rule") + { + var rejectMethod = icmpRouting switch + { + "unreachable" => "default", + "drop" => "drop", + _ => "reply", + }; + _coreConfig.route.rules.Add(new() + { + network = ["icmp"], + action = "reject", + method = rejectMethod, + }); + } } if (_config.Inbound.First().SniffingEnabled) diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index c8862648a9..853ada7fb2 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -153,6 +153,7 @@ public partial class CoreConfigV2rayService serversItem.password = _node.Password; serversItem.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod) ? protocolExtra.SsMethod : "none"; + serversItem.uot = protocolExtra.Uot == true ? true : null; serversItem.ota = false; serversItem.level = 1; diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 6af600f7d7..e5c307c8e0 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -61,6 +61,18 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public int WgMtu { get; set; } + [Reactive] + public bool Uot { get; set; } + + [Reactive] + public string CongestionControl { get; set; } + + [Reactive] + public int? InsecureConcurrency { get; set; } + + [Reactive] + public bool NaiveQuic { get; set; } + public ReactiveCommand FetchCertCmd { get; } public ReactiveCommand FetchCertChainCmd { get; } public ReactiveCommand SaveCmd { get; } @@ -123,6 +135,10 @@ public class AddServerViewModel : MyReactiveObject WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty; WgReserved = protocolExtra?.WgReserved ?? string.Empty; WgMtu = protocolExtra?.WgMtu ?? 1280; + Uot = protocolExtra?.Uot ?? false; + CongestionControl = protocolExtra?.CongestionControl ?? string.Empty; + InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null; + NaiveQuic = protocolExtra?.NaiveQuic ?? false; } private async Task SaveServerAsync() @@ -185,6 +201,10 @@ public class AddServerViewModel : MyReactiveObject WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(), WgReserved = WgReserved.NullIfEmpty(), WgMtu = WgMtu >= 576 ? WgMtu : null, + Uot = Uot ? true : null, + CongestionControl = CongestionControl.NullIfEmpty(), + InsecureConcurrency = InsecureConcurrency > 0 ? InsecureConcurrency : null, + NaiveQuic = NaiveQuic ? true : null, }); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index ee92a0cd36..36027b79d4 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -18,6 +18,7 @@ public class MainWindowViewModel : MyReactiveObject public ReactiveCommand AddTuicServerCmd { get; } public ReactiveCommand AddWireguardServerCmd { get; } public ReactiveCommand AddAnytlsServerCmd { get; } + public ReactiveCommand AddNaiveServerCmd { get; } public ReactiveCommand AddCustomServerCmd { get; } public ReactiveCommand AddPolicyGroupServerCmd { get; } public ReactiveCommand AddProxyChainServerCmd { get; } @@ -117,6 +118,10 @@ public class MainWindowViewModel : MyReactiveObject { await AddServerAsync(EConfigType.Anytls); }); + AddNaiveServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(EConfigType.Naive); + }); AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => { await AddServerAsync(EConfigType.Custom); diff --git a/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index d936c5df3d..5df7526454 100644 --- a/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayn/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -95,6 +95,7 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public string TunStack { get; set; } [Reactive] public int TunMtu { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; } + [Reactive] public string TunIcmpRouting { get; set; } #endregion Tun mode @@ -218,6 +219,7 @@ public class OptionSettingViewModel : MyReactiveObject TunStack = _config.TunModeItem.Stack; TunMtu = _config.TunModeItem.Mtu; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; + TunIcmpRouting = _config.TunModeItem.IcmpRouting; #endregion Tun mode @@ -376,6 +378,7 @@ public class OptionSettingViewModel : MyReactiveObject _config.TunModeItem.Stack = TunStack; _config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; + _config.TunModeItem.IcmpRouting = TunIcmpRouting; //coreType await SaveCoreType(); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index e9ded8aace..4dc378b48c 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -198,6 +198,19 @@ Width="300" Margin="{StaticResource Margin4}" /> + + + + + + + + + + + + + + + + + + + + + + cmbFingerprint.SelectedValue = string.Empty; gridFinalmask.IsVisible = false; - cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; + cmbCongestionControl8.ItemsSource = Global.TuicCongestionControls; break; case EConfigType.WireGuard: @@ -94,10 +94,28 @@ public partial class AddServerWindow : WindowBase case EConfigType.Anytls: gridAnytls.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; lstStreamSecurity.Add(Global.StreamSecurityReality); cmbCoreType.IsEnabled = false; gridFinalmask.IsVisible = false; break; + + case EConfigType.Naive: + gridNaive.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; + cmbCoreType.IsEnabled = false; + gridFinalmask.IsVisible = false; + cmbFingerprint.IsEnabled = false; + cmbFingerprint.SelectedValue = string.Empty; + cmbAlpn.IsEnabled = false; + cmbAlpn.SelectedValue = string.Empty; + cmbAllowInsecure.IsEnabled = false; + cmbAllowInsecure.SelectedValue = string.Empty; + + cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls; + break; } cmbStreamSecurity.ItemsSource = lstStreamSecurity; @@ -122,6 +140,7 @@ public partial class AddServerWindow : WindowBase case EConfigType.Shadowsocks: this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables); break; @@ -156,7 +175,7 @@ public partial class AddServerWindow : WindowBase case EConfigType.TUIC: this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.SelectedValue).DisposeWith(disposables); break; case EConfigType.WireGuard: @@ -168,7 +187,17 @@ public partial class AddServerWindow : WindowBase break; case EConfigType.Anytls: - this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables); + break; + + case EConfigType.Naive: + this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.cmbCongestionControl12.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables); break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml index ca59c472bb..8d9a5f8540 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml @@ -50,6 +50,7 @@ + diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 7e96fad28c..996e7c834e 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -72,6 +72,7 @@ public partial class MainWindow : WindowBase this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 8c4f1fcc33..7d80088a50 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -824,6 +824,20 @@ Margin="{StaticResource Margin4}" HorizontalAlignment="Left" /> + + + cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs; cmbMtu.ItemsSource = Global.TunMtus; cmbStack.ItemsSource = Global.TunStacks; + cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies; cmbCoreType1.ItemsSource = Global.CoreTypes; cmbCoreType2.ItemsSource = Global.CoreTypes; @@ -114,6 +115,7 @@ public partial class OptionSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml index e78e7f9ba9..1eb28d9d92 100644 --- a/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -277,6 +277,20 @@ Margin="{StaticResource Margin4}" Style="{StaticResource DefComboBox}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables); break; @@ -151,7 +170,7 @@ public partial class AddServerWindow case EConfigType.TUIC: this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.Text).DisposeWith(disposables); break; case EConfigType.WireGuard: @@ -163,7 +182,16 @@ public partial class AddServerWindow break; case EConfigType.Anytls: - this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables); + break; + + case EConfigType.Naive: + this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables); break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml index 1c49ca24e2..f6dfe42bc2 100644 --- a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml @@ -124,6 +124,10 @@ x:Name="menuAddAnytlsServer" Height="{StaticResource MenuItemHeight}" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" /> + diff --git a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 42d8aabcc5..81f2f72030 100644 --- a/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -71,6 +71,7 @@ public partial class MainWindow this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 133b413487..41d3b8d1af 100644 --- a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1076,6 +1076,22 @@ HorizontalAlignment="Left" Style="{StaticResource DefComboBox}" /> + + + vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables); diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt index c78661caf0..909d3a3ba0 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt @@ -189,4 +189,15 @@ fun String.concatUrl(vararg paths: String): String { } return builder.toString() +} + +/** + * Helper function to match text either by Regex or literal string. + */ +fun String.matchesPattern(regex: Regex?, keyword: String?, ignoreCase: Boolean = true): Boolean { + if (keyword.isNullOrEmpty()) { + return true + } + return regex?.containsMatchIn(this) + ?: this.contains(keyword, ignoreCase = ignoreCase) } \ No newline at end of file diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/GroupServerFragment.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/GroupServerFragment.kt index 06c5d8d796..0a561e8771 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/GroupServerFragment.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/ui/GroupServerFragment.kt @@ -71,6 +71,8 @@ class GroupServerFragment : BaseFragment(), itemTouchHelper?.attachToRecyclerView(binding.recyclerView) binding.refreshLayout.setOnRefreshListener(this) + // Set the distance to trigger sync to 160dp + binding.refreshLayout.setDistanceToTriggerSync((160 * resources.displayMetrics.density).toInt()) mainViewModel.updateListAction.observe(viewLifecycleOwner) { index -> if (mainViewModel.subscriptionId != subId) { diff --git a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt index e9e07f59f2..88b6b7cc72 100644 --- a/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/v2rayng/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -19,6 +19,7 @@ import com.v2ray.ang.dto.ServersCache import com.v2ray.ang.dto.SubscriptionCache import com.v2ray.ang.dto.SubscriptionUpdateResult import com.v2ray.ang.dto.TestServiceMessage +import com.v2ray.ang.extension.matchesPattern import com.v2ray.ang.extension.serializable import com.v2ray.ang.extension.toastError import com.v2ray.ang.extension.toastSuccess @@ -35,6 +36,7 @@ import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.Collections +import java.util.regex.PatternSyntaxException class MainViewModel(application: Application) : AndroidViewModel(application) { private var serverList = mutableListOf() // MmkvManager.decodeServerList() @@ -117,7 +119,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { @Synchronized fun updateCache() { serversCache.clear() - val kw = keywordFilter.trim().lowercase() + val kw = keywordFilter.trim() + val searchRegex = try { + if (kw.isNotEmpty()) Regex(kw, setOf(RegexOption.IGNORE_CASE)) else null + } catch (e: PatternSyntaxException) { + null // Fallback to literal search if regex is invalid + } for (guid in serverList) { val profile = MmkvManager.decodeServerConfig(guid) ?: continue if (kw.isEmpty()) { @@ -125,11 +132,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { continue } - val remarks = profile.remarks.lowercase() - val description = profile.description.orEmpty().lowercase() - val server = profile.server.orEmpty().lowercase() - - if (remarks.contains(kw) || description.contains(kw) || server.contains(kw)) { + val remarks = profile.remarks + val description = profile.description.orEmpty() + val server = profile.server.orEmpty() + val protocol = profile.configType.name + if (remarks.matchesPattern(searchRegex, kw) + || description.matchesPattern(searchRegex, kw) + || server.matchesPattern(searchRegex, kw) + || protocol.matchesPattern(searchRegex, kw) + ) { serversCache.add(ServersCache(guid, profile)) } } @@ -479,4 +490,4 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } } -} \ No newline at end of file +} diff --git a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml index 0fbe243d2c..b822924606 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -297,8 +297,8 @@ Обновлено профилей: %d Обновлено профилей: %1$d (успешно: %2$d, ошибок: %3$d, пропущено: %4$d) Нет подписок - Selected server not found in current group - Unable to locate current view + Выбранный профиль не найден в текущей группе + Фрагмент недоступен Запуск службы Подтвердить diff --git a/xray-core/README.md b/xray-core/README.md index 72fe602946..2db8cc464c 100644 --- a/xray-core/README.md +++ b/xray-core/README.md @@ -10,6 +10,8 @@ [![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su) +[![BlancVPN](https://github.com/user-attachments/assets/9145ea7d-5da3-446e-8143-710dba4292c3)](https://blanc.link/VMTSDqW) + [**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668) ## Donation & NFTs diff --git a/xray-core/app/dns/nameserver_doh.go b/xray-core/app/dns/nameserver_doh.go index 2126bcd854..2849dbed3c 100644 --- a/xray-core/app/dns/nameserver_doh.go +++ b/xray-core/app/dns/nameserver_doh.go @@ -214,7 +214,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, req.Header.Add("Accept", "application/dns-message") req.Header.Add("Content-Type", "application/dns-message") - req.Header.Set("User-Agent", utils.ChromeUA) + utils.TryDefaultHeadersWith(req.Header, "fetch") req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000))) hc := s.httpClient diff --git a/xray-core/app/observatory/burst/burstobserver.go b/xray-core/app/observatory/burst/burstobserver.go index 472351cc18..9af96a1dea 100644 --- a/xray-core/app/observatory/burst/burstobserver.go +++ b/xray-core/app/observatory/burst/burstobserver.go @@ -32,6 +32,10 @@ func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { return &observatory.ObservationResult{Status: o.createResult()}, nil } +func (o *Observer) Check(tag []string) { + o.hp.Check(tag) +} + func (o *Observer) createResult() []*observatory.OutboundStatus { var result []*observatory.OutboundStatus o.hp.access.Lock() diff --git a/xray-core/app/observatory/burst/ping.go b/xray-core/app/observatory/burst/ping.go index f08adb571e..2755ec5484 100644 --- a/xray-core/app/observatory/burst/ping.go +++ b/xray-core/app/observatory/burst/ping.go @@ -62,7 +62,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) { if err != nil { return rttFailed, err } - req.Header.Set("User-Agent", utils.ChromeUA) + utils.TryDefaultHeadersWith(req.Header, "nav") start := time.Now() resp, err := s.httpClient.Do(req) diff --git a/xray-core/app/observatory/observer.go b/xray-core/app/observatory/observer.go index 0ff9ba64e0..c0b38b6e3d 100644 --- a/xray-core/app/observatory/observer.go +++ b/xray-core/app/observatory/observer.go @@ -164,7 +164,7 @@ func (o *Observer) probe(outbound string) ProbeResult { probeURL = o.config.ProbeUrl } req, _ := http.NewRequest(http.MethodGet, probeURL, nil) - req.Header.Set("User-Agent", utils.ChromeUA) + utils.TryDefaultHeadersWith(req.Header, "nav") response, err := httpClient.Do(req) if err != nil { return errors.New("outbound failed to relay connection").Base(err) diff --git a/xray-core/common/crypto/crypto.go b/xray-core/common/crypto/crypto.go index efc33a11b9..49ce164ba0 100644 --- a/xray-core/common/crypto/crypto.go +++ b/xray-core/common/crypto/crypto.go @@ -4,8 +4,11 @@ package crypto // import "github.com/xtls/xray-core/common/crypto" import ( "crypto/rand" "math/big" + + "github.com/xtls/xray-core/common" ) +// [,) func RandBetween(from int64, to int64) int64 { if from == to { return from @@ -16,3 +19,20 @@ func RandBetween(from int64, to int64) int64 { bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from)) return from + bigInt.Int64() } + +// [,] +func RandBytesBetween(b []byte, from, to byte) { + common.Must2(rand.Read(b)) + + if from > to { + from, to = to, from + } + + if to-from == 255 { + return + } + + for i := range b { + b[i] = from + b[i]%(to-from+1) + } +} diff --git a/xray-core/common/utils/browser.go b/xray-core/common/utils/browser.go index 91209f4bda..2337125a0e 100644 --- a/xray-core/common/utils/browser.go +++ b/xray-core/common/utils/browser.go @@ -4,6 +4,8 @@ import ( "math/rand" "strconv" "time" + "net/http" + "strings" "github.com/klauspost/cpuid/v2" ) @@ -24,5 +26,166 @@ func ChromeVersion() int { return version - 1 } -// ChromeUA provides default browser User-Agent based on CPU-seeded PRNG. -var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(ChromeVersion()) + ".0.0.0 Safari/537.36" +// The full Chromium brand GREASE implementation +var clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"} +var clientHintVersionNA = []string{"8", "99", "24"} +var clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}} +var clientHintShuffle4 = [][4]int{ + {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1}, + {1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0}, + {2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0}, + {3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}} +func getGreasedChInvalidBrand(seed int) string { + return "\"Not" + clientHintGreaseNA[seed % len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed + 1) % len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed % len(clientHintVersionNA)] + "\""; +} +func getGreasedChOrder(brandLength int, seed int) []int { + switch brandLength { + case 1: + return []int{0} + case 2: + return []int{seed % brandLength, (seed + 1) % brandLength} + case 3: + return clientHintShuffle3[seed % len(clientHintShuffle3)][:] + default: + return clientHintShuffle4[seed % len(clientHintShuffle4)][:] + } + return []int{} +} +func getUngreasedChUa(majorVersion int, forkName string) []string { + // Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice + baseChUa := make([]string, 0, 4) + baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion), + "\"Chromium\";v=\"" + strconv.Itoa(majorVersion) + "\"") + switch forkName { + case "chrome": + baseChUa = append(baseChUa, "\"Google Chrome\";v=\"" + strconv.Itoa(majorVersion) + "\"") + case "edge": + baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\"" + strconv.Itoa(majorVersion) + "\"") + } + return baseChUa +} +func getGreasedChUa(majorVersion int, forkName string) string { + ungreasedCh := getUngreasedChUa(majorVersion, forkName) + shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion) + shuffledCh := make([]string, len(ungreasedCh)) + for i, e := range shuffleMap { + shuffledCh[e] = ungreasedCh[i] + } + return strings.Join(shuffledCh, ", ") +} + +// It's better to pin on Firefox ESR releases, and there could be a Firefox ESR version generator later. +// However, if the Firefox fingerprint in uTLS doesn't have its update cadence match that of Firefox ESR, then it's better to update the Firefox version manually instead every time a new major ESR release is available. +var FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0" + +// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG. +var AnchoredChromeVersion = ChromeVersion() +var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36" +var ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome") +var MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0" +var MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge") + +func applyMasqueradedHeaders(header http.Header, browser string, variant string) { + // Browser-specific. + switch browser { + case "chrome": + header["Sec-CH-UA"] = []string{ChromeUACH} + header["Sec-CH-UA-Mobile"] = []string{"?0"} + header["Sec-CH-UA-Platform"] = []string{"\"Windows\""} + header["DNT"] = []string{"1"} + header.Set("User-Agent", ChromeUA) + header.Set("Accept-Language", "en-US,en;q=0.9") + case "edge": + header["Sec-CH-UA"] = []string{MSEdgeUACH} + header["Sec-CH-UA-Mobile"] = []string{"?0"} + header["Sec-CH-UA-Platform"] = []string{"\"Windows\""} + header["DNT"] = []string{"1"} + header.Set("User-Agent", MSEdgeUA) + header.Set("Accept-Language", "en-US,en;q=0.9") + case "firefox": + header.Set("User-Agent", FirefoxUA) + header["DNT"] = []string{"1"} + header.Set("Accept-Language", "en-US,en;q=0.5") + case "golang": + // Expose the default net/http header. + header.Del("User-Agent") + return + } + // Context-specific. + switch variant { + case "nav": + if header.Get("Cache-Control") == "" { + switch browser { + case "chrome", "edge": + header.Set("Cache-Control", "max-age=0") + } + } + header.Set("Upgrade-Insecure-Requests", "1") + if header.Get("Accept") == "" { + switch browser { + case "chrome", "edge": + header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7") + case "firefox": + header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + } + } + header.Set("Sec-Fetch-Site", "none") + header.Set("Sec-Fetch-Mode", "navigate") + header.Set("Sec-Fetch-User", "?1") + header.Set("Sec-Fetch-Dest", "document") + header.Set("Priority", "u=0, i") + case "ws": + header.Set("Sec-Fetch-Mode", "websocket") + header.Set("Sec-Fetch-Dest", "empty") + header.Set("Sec-Fetch-Site", "same-origin") + if header.Get("Cache-Control") == "" { + header.Set("Cache-Control", "no-cache") + } + if header.Get("Pragma") == "" { + header.Set("Pragma", "no-cache") + } + if header.Get("Accept") == "" { + header.Set("Accept", "*/*") + } + case "fetch": + header.Set("Sec-Fetch-Mode", "cors") + header.Set("Sec-Fetch-Dest", "empty") + header.Set("Sec-Fetch-Site", "same-origin") + if header.Get("Priority") == "" { + switch browser { + case "chrome", "edge": + header.Set("Priority", "u=1, i") + case "firefox": + header.Set("Priority", "u=4") + } + } + if header.Get("Cache-Control") == "" { + header.Set("Cache-Control", "no-cache") + } + if header.Get("Pragma") == "" { + header.Set("Pragma", "no-cache") + } + if header.Get("Accept") == "" { + header.Set("Accept", "*/*") + } + } +} + +func TryDefaultHeadersWith(header http.Header, variant string) { + // The global UA special value handler for transports. Used to be called HandleTransportUASettings. + // Just a FYI to whoever needing to fix this piece of code after some spontaneous event, I tried to make the two methods separate to let the code be cleaner and more organized. + if len(header.Values("User-Agent")) < 1 { + applyMasqueradedHeaders(header, "chrome", variant) + } else { + switch header.Get("User-Agent") { + case "chrome": + applyMasqueradedHeaders(header, "chrome", variant) + case "firefox": + applyMasqueradedHeaders(header, "firefox", variant) + case "edge": + applyMasqueradedHeaders(header, "edge", variant) + case "golang": + applyMasqueradedHeaders(header, "golang", variant) + } + } +} diff --git a/xray-core/features/extension/observatory.go b/xray-core/features/extension/observatory.go index 8e87103069..1b211ba39e 100644 --- a/xray-core/features/extension/observatory.go +++ b/xray-core/features/extension/observatory.go @@ -13,6 +13,11 @@ type Observatory interface { GetObservation(ctx context.Context) (proto.Message, error) } +type BurstObservatory interface { + Observatory + Check(tag []string) +} + func ObservatoryType() interface{} { return (*Observatory)(nil) } diff --git a/xray-core/infra/conf/transport_authenticators.go b/xray-core/infra/conf/transport_authenticators.go index ea286f41d4..a9590af904 100644 --- a/xray-core/infra/conf/transport_authenticators.go +++ b/xray-core/infra/conf/transport_authenticators.go @@ -44,6 +44,34 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) { Name: "User-Agent", Value: []string{utils.ChromeUA}, }, + { + Name: "Sec-CH-UA", + Value: []string{utils.ChromeUACH}, + }, + { + Name: "Sec-CH-UA-Mobile", + Value: []string{"?0"}, + }, + { + Name: "Sec-CH-UA-Platform", + Value: []string{"Windows"}, + }, + { + Name: "Sec-Fetch-Mode", + Value: []string{"no-cors", "cors", "same-origin"}, + }, + { + Name: "Sec-Fetch-Dest", + Value: []string{"empty"}, + }, + { + Name: "Sec-Fetch-Site", + Value: []string{"none"}, + }, + { + Name: "Sec-Fetch-User", + Value: []string{"?1"}, + }, { Name: "Accept-Encoding", Value: []string{"gzip, deflate"}, diff --git a/xray-core/infra/conf/transport_internet.go b/xray-core/infra/conf/transport_internet.go index cdd6157b01..7a302aecce 100644 --- a/xray-core/infra/conf/transport_internet.go +++ b/xray-core/infra/conf/transport_internet.go @@ -909,6 +909,12 @@ func (c *REALITYConfig) Build() (proto.Message, error) { } } + for _, sn := range config.ServerNames { + if strings.Contains(sn, "apple") || strings.Contains(sn, "icloud") { + errors.LogWarning(context.Background(), `REALITY: Choosing apple, icloud, etc. as the target may get your IP blocked by the GFW`) + } + } + config.LimitFallbackUpload = new(reality.LimitFallback) config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec @@ -1254,10 +1260,11 @@ var ( ) type TCPItem struct { - Delay Int32Range `json:"delay"` - Rand int32 `json:"rand"` - Type string `json:"type"` - Packet json.RawMessage `json:"packet"` + Delay Int32Range `json:"delay"` + Rand int32 `json:"rand"` + RandRange *Int32Range `json:"randRange"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` } type HeaderCustomTCP struct { @@ -1289,10 +1296,18 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { } } + errInvalidRange := errors.New("invalid randRange") + clients := make([]*custom.TCPSequence, len(c.Clients)) for i, value := range c.Clients { clients[i] = &custom.TCPSequence{} for _, item := range value { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errInvalidRange + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err @@ -1301,6 +1316,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), Rand: item.Rand, + RandMin: item.RandRange.From, + RandMax: item.RandRange.To, Packet: item.Packet, }) } @@ -1310,6 +1327,12 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { for i, value := range c.Servers { servers[i] = &custom.TCPSequence{} for _, item := range value { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errInvalidRange + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err @@ -1318,6 +1341,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), Rand: item.Rand, + RandMin: item.RandRange.From, + RandMax: item.RandRange.To, Packet: item.Packet, }) } @@ -1327,6 +1352,12 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { for i, value := range c.Errors { errors[i] = &custom.TCPSequence{} for _, item := range value { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errInvalidRange + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err @@ -1335,6 +1366,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), Rand: item.Rand, + RandMin: item.RandRange.From, + RandMax: item.RandRange.To, Packet: item.Packet, }) } @@ -1433,9 +1466,10 @@ func (c *NoiseMask) Build() (proto.Message, error) { } type UDPItem struct { - Rand int32 `json:"rand"` - Type string `json:"type"` - Packet json.RawMessage `json:"packet"` + Rand int32 `json:"rand"` + RandRange *Int32Range `json:"randRange"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` } type HeaderCustomUDP struct { @@ -1457,25 +1491,41 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) { client := make([]*custom.UDPItem, 0, len(c.Client)) for _, item := range c.Client { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errors.New("invalid randRange") + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err } client = append(client, &custom.UDPItem{ - Rand: item.Rand, - Packet: item.Packet, + Rand: item.Rand, + RandMin: item.RandRange.From, + RandMax: item.RandRange.To, + Packet: item.Packet, }) } server := make([]*custom.UDPItem, 0, len(c.Server)) for _, item := range c.Server { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errors.New("invalid randRange") + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err } server = append(server, &custom.UDPItem{ - Rand: item.Rand, - Packet: item.Packet, + Rand: item.Rand, + RandMin: item.RandRange.From, + RandMax: item.RandRange.To, + Packet: item.Packet, }) } diff --git a/xray-core/infra/conf/xray.go b/xray-core/infra/conf/xray.go index 15e4a191bf..a2410244be 100644 --- a/xray-core/infra/conf/xray.go +++ b/xray-core/infra/conf/xray.go @@ -174,6 +174,10 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { return nil, err } receiverSettings.StreamSettings = ss + if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil || + len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) { + errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`) + } } if c.SniffingConfig != nil { s, err := c.SniffingConfig.Build() diff --git a/xray-core/proxy/http/client.go b/xray-core/proxy/http/client.go index f79ce54737..0e50edba3b 100644 --- a/xray-core/proxy/http/client.go +++ b/xray-core/proxy/http/client.go @@ -220,9 +220,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u for _, h := range header { req.Header.Set(h.Key, h.Value) } - if req.Header.Get("User-Agent") == "" { - req.Header.Set("User-Agent", utils.ChromeUA) - } + utils.TryDefaultHeadersWith(req.Header, "nav") connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) { req.Header.Set("Proxy-Connection", "Keep-Alive") diff --git a/xray-core/proxy/vless/inbound/inbound.go b/xray-core/proxy/vless/inbound/inbound.go index d12495b407..02dc947dea 100644 --- a/xray-core/proxy/vless/inbound/inbound.go +++ b/xray-core/proxy/vless/inbound/inbound.go @@ -27,7 +27,9 @@ import ( "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/dns" + "github.com/xtls/xray-core/features/extension" feature_inbound "github.com/xtls/xray-core/features/inbound" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/policy" @@ -78,6 +80,7 @@ type Handler struct { validator vless.Validator decryption *encryption.ServerInstance outboundHandlerManager outbound.Manager + observer features.Feature defaultDispatcher routing.Dispatcher ctx context.Context fallbacks map[string]map[string]map[string]*Fallback // or nil @@ -93,6 +96,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val stats: v.GetFeature(stats.ManagerType()).(stats.Manager), validator: validator, outboundHandlerManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), + observer: v.GetFeature(extension.ObservatoryType()), defaultDispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher), ctx: ctx, } @@ -623,7 +627,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if err != nil { return err } - return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter})) + return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter}), h.observer) } if err := dispatch.DispatchLink(ctx, request.Destination(), &transport.Link{ @@ -645,7 +649,7 @@ func (r *Reverse) Tag() string { return r.tag } -func (r *Reverse) NewMux(ctx context.Context, link *transport.Link) error { +func (r *Reverse) NewMux(ctx context.Context, link *transport.Link, observer features.Feature) error { muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{}) if err != nil { return errors.New("failed to create mux client worker").Base(err).AtWarning() @@ -655,6 +659,9 @@ func (r *Reverse) NewMux(ctx context.Context, link *transport.Link) error { return errors.New("failed to create portal worker").Base(err).AtWarning() } r.picker.AddWorker(worker) + if burstObs, ok := observer.(extension.BurstObservatory); ok { + go burstObs.Check([]string{r.Tag()}) + } select { case <-ctx.Done(): case <-muxClient.WaitClosed(): diff --git a/xray-core/transport/internet/finalmask/finalmask.go b/xray-core/transport/internet/finalmask/finalmask.go index 3eee635a5a..a98aa3fc3c 100644 --- a/xray-core/transport/internet/finalmask/finalmask.go +++ b/xray-core/transport/internet/finalmask/finalmask.go @@ -3,14 +3,11 @@ package finalmask import ( "context" "net" + "sync" "github.com/xtls/xray-core/common/errors" ) -const ( - UDPSize = 4096 + 123 -) - type Udpmask interface { UDP() @@ -29,27 +26,165 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager { } func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) { - var err error + var sizes []int + var conns []net.PacketConn for i, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1) - if err != nil { - return nil, err + if _, ok := mask.(headerConn); ok { + conn, err := mask.WrapPacketConnClient(nil, i, len(m.udpmasks)-1) + if err != nil { + return nil, err + } + sizes = append(sizes, conn.(headerSize).Size()) + conns = append(conns, conn) + } else { + if len(conns) > 0 { + raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw} + sizes = nil + conns = nil + } + var err error + raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1) + if err != nil { + return nil, err + } } } + + if len(conns) > 0 { + raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw} + sizes = nil + conns = nil + } return raw, nil } func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) { - var err error + var sizes []int + var conns []net.PacketConn for i, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1) - if err != nil { - return nil, err + if _, ok := mask.(headerConn); ok { + conn, err := mask.WrapPacketConnServer(nil, i, len(m.udpmasks)-1) + if err != nil { + return nil, err + } + sizes = append(sizes, conn.(headerSize).Size()) + conns = append(conns, conn) + } else { + if len(conns) > 0 { + raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw} + sizes = nil + conns = nil + } + var err error + raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1) + if err != nil { + return nil, err + } } } + + if len(conns) > 0 { + raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw} + sizes = nil + conns = nil + } return raw, nil } +const ( + UDPSize = 4096 +) + +type headerConn interface { + HeaderConn() +} + +type headerSize interface { + Size() int +} + +type headerManagerConn struct { + sizes []int + conns []net.PacketConn + net.PacketConn + m sync.Mutex + writeBuf [UDPSize]byte +} + +func (c *headerManagerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + buf := p + if len(buf) < UDPSize { + buf = make([]byte, UDPSize) + } + + n, addr, err = c.PacketConn.ReadFrom(buf) + if n == 0 || err != nil { + return 0, addr, err + } + newBuf := buf[:n] + + sum := 0 + for _, size := range c.sizes { + sum += size + } + + if n < sum { + errors.LogDebug(context.Background(), addr, " mask read err short length") + return 0, addr, nil + } + + for i := range c.conns { + n, _, err = c.conns[i].ReadFrom(newBuf) + if n == 0 || err != nil { + errors.LogDebug(context.Background(), addr, " mask read err ", err) + return 0, addr, nil + } + newBuf = newBuf[c.sizes[i] : n+c.sizes[i]] + } + + if len(p) < n { + errors.LogDebug(context.Background(), addr, " mask read err short buffer") + return 0, addr, nil + } + + copy(p, buf[sum:sum+n]) + + return n, addr, nil +} + +func (c *headerManagerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.m.Lock() + defer c.m.Unlock() + + sum := 0 + for _, size := range c.sizes { + sum += size + } + + if sum+len(p) > UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write") + return 0, nil + } + + n = copy(c.writeBuf[sum:], p) + + for i := len(c.conns) - 1; i >= 0; i-- { + n, err = c.conns[i].WriteTo(c.writeBuf[sum-c.sizes[i]:n+sum], nil) + if n == 0 || err != nil { + errors.LogDebug(context.Background(), addr, " mask write err ", err) + return 0, nil + } + sum -= c.sizes[i] + } + + n, err = c.PacketConn.WriteTo(c.writeBuf[:n], addr) + if n == 0 || err != nil { + return n, err + } + + return len(p), nil +} + type Tcpmask interface { TCP() diff --git a/xray-core/transport/internet/finalmask/fragment/conn.go b/xray-core/transport/internet/finalmask/fragment/conn.go index 1cadf5f0ae..91822ace34 100644 --- a/xray-core/transport/internet/finalmask/fragment/conn.go +++ b/xray-core/transport/internet/finalmask/fragment/conn.go @@ -33,9 +33,6 @@ func NewConnServer(c *Config, raw net.Conn, server bool) (net.Conn, error) { func (c *fragmentConn) TcpMaskConn() {} func (c *fragmentConn) RawConn() net.Conn { - if c.server { - return c - } return c.Conn } diff --git a/xray-core/transport/internet/finalmask/header/custom/config.go b/xray-core/transport/internet/finalmask/header/custom/config.go index 1d72e336c0..be094f216b 100644 --- a/xray-core/transport/internet/finalmask/header/custom/config.go +++ b/xray-core/transport/internet/finalmask/header/custom/config.go @@ -25,3 +25,6 @@ func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCou func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServerUDP(c, raw) } + +func (c *UDPConfig) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/custom/config.pb.go b/xray-core/transport/internet/finalmask/header/custom/config.pb.go index ff06eb2914..c8db3e1abd 100644 --- a/xray-core/transport/internet/finalmask/header/custom/config.pb.go +++ b/xray-core/transport/internet/finalmask/header/custom/config.pb.go @@ -26,7 +26,9 @@ type TCPItem struct { DelayMin int64 `protobuf:"varint,1,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` DelayMax int64 `protobuf:"varint,2,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` Rand int32 `protobuf:"varint,3,opt,name=rand,proto3" json:"rand,omitempty"` - Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"` + RandMin int32 `protobuf:"varint,4,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"` + RandMax int32 `protobuf:"varint,5,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"` + Packet []byte `protobuf:"bytes,6,opt,name=packet,proto3" json:"packet,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -82,6 +84,20 @@ func (x *TCPItem) GetRand() int32 { return 0 } +func (x *TCPItem) GetRandMin() int32 { + if x != nil { + return x.RandMin + } + return 0 +} + +func (x *TCPItem) GetRandMax() int32 { + if x != nil { + return x.RandMax + } + return 0 +} + func (x *TCPItem) GetPacket() []byte { if x != nil { return x.Packet @@ -196,7 +212,9 @@ func (x *TCPConfig) GetErrors() []*TCPSequence { type UDPItem struct { state protoimpl.MessageState `protogen:"open.v1"` Rand int32 `protobuf:"varint,1,opt,name=rand,proto3" json:"rand,omitempty"` - Packet []byte `protobuf:"bytes,2,opt,name=packet,proto3" json:"packet,omitempty"` + RandMin int32 `protobuf:"varint,2,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"` + RandMax int32 `protobuf:"varint,3,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"` + Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -238,6 +256,20 @@ func (x *UDPItem) GetRand() int32 { return 0 } +func (x *UDPItem) GetRandMin() int32 { + if x != nil { + return x.RandMin + } + return 0 +} + +func (x *UDPItem) GetRandMax() int32 { + if x != nil { + return x.RandMax + } + return 0 +} + func (x *UDPItem) GetPacket() []byte { if x != nil { return x.Packet @@ -301,21 +333,25 @@ var File_transport_internet_finalmask_header_custom_config_proto protoreflect.Fi const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" + "\n" + - "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"o\n" + + "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"\xa5\x01\n" + "\aTCPItem\x12\x1b\n" + "\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" + "\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" + - "\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" + - "\x06packet\x18\x04 \x01(\fR\x06packet\"c\n" + + "\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x19\n" + + "\brand_min\x18\x04 \x01(\x05R\arandMin\x12\x19\n" + + "\brand_max\x18\x05 \x01(\x05R\arandMax\x12\x16\n" + + "\x06packet\x18\x06 \x01(\fR\x06packet\"c\n" + "\vTCPSequence\x12T\n" + "\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\x91\x02\n" + "\tTCPConfig\x12V\n" + "\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" + "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12T\n" + - "\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"5\n" + + "\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"k\n" + "\aUDPItem\x12\x12\n" + - "\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x16\n" + - "\x06packet\x18\x02 \x01(\fR\x06packet\"\xaf\x01\n" + + "\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x19\n" + + "\brand_min\x18\x02 \x01(\x05R\arandMin\x12\x19\n" + + "\brand_max\x18\x03 \x01(\x05R\arandMax\x12\x16\n" + + "\x06packet\x18\x04 \x01(\fR\x06packet\"\xaf\x01\n" + "\tUDPConfig\x12P\n" + "\x06client\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06client\x12P\n" + "\x06server\x18\x02 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06serverB\xaf\x01\n" + diff --git a/xray-core/transport/internet/finalmask/header/custom/config.proto b/xray-core/transport/internet/finalmask/header/custom/config.proto index 34314dee9e..cbf498efb5 100644 --- a/xray-core/transport/internet/finalmask/header/custom/config.proto +++ b/xray-core/transport/internet/finalmask/header/custom/config.proto @@ -10,7 +10,9 @@ message TCPItem { int64 delay_min = 1; int64 delay_max = 2; int32 rand = 3; - bytes packet = 4; + int32 rand_min = 4; + int32 rand_max = 5; + bytes packet = 6; } message TCPSequence { @@ -25,7 +27,9 @@ message TCPConfig { message UDPItem { int32 rand = 1; - bytes packet = 2; + int32 rand_min = 2; + int32 rand_max = 3; + bytes packet = 4; } message UDPConfig { diff --git a/xray-core/transport/internet/finalmask/header/custom/tcp.go b/xray-core/transport/internet/finalmask/header/custom/tcp.go index 9f3e893a0e..ef6319840f 100644 --- a/xray-core/transport/internet/finalmask/header/custom/tcp.go +++ b/xray-core/transport/internet/finalmask/header/custom/tcp.go @@ -2,13 +2,11 @@ package custom import ( "bytes" - "crypto/rand" "io" "net" "sync" "time" - "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" ) @@ -231,7 +229,7 @@ func writeSequence(w io.Writer, sequence *TCPSequence) bool { } if item.Rand > 0 { buf := make([]byte, item.Rand) - common.Must2(rand.Read(buf)) + crypto.RandBytesBetween(buf, byte(item.RandMin), byte(item.RandMax)) merged = append(merged, buf...) } else { merged = append(merged, item.Packet...) diff --git a/xray-core/transport/internet/finalmask/header/custom/udp.go b/xray-core/transport/internet/finalmask/header/custom/udp.go index efecf90b12..033351e1a8 100644 --- a/xray-core/transport/internet/finalmask/header/custom/udp.go +++ b/xray-core/transport/internet/finalmask/header/custom/udp.go @@ -2,13 +2,10 @@ package custom import ( "bytes" - "context" - "crypto/rand" "net" - "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type udpCustomClient struct { @@ -21,7 +18,7 @@ func (h *udpCustomClient) Serialize(b []byte) { index := 0 for _, item := range h.client { if item.Rand > 0 { - common.Must2(rand.Read(h.merged[index : index+int(item.Rand)])) + crypto.RandBytesBetween(h.merged[index:index+int(item.Rand)], byte(item.RandMin), byte(item.RandMax)) index += int(item.Rand) } else { index += len(item.Packet) @@ -80,52 +77,20 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) return conn, nil } +func (c *udpCustomClientConn) Size() int { + return len(c.header.merged) +} + func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) + if !c.header.Match(p) { + return 0, addr, errors.New("header mismatch") } - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if !c.header.Match(buf[:n]) { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-len(c.header.merged) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) - return 0, addr, nil - } - - copy(p, buf[len(c.header.merged):n]) - - return n - len(c.header.merged), addr, nil + return len(p) - len(c.header.merged), addr, nil } func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if len(c.header.merged)+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:len(c.header.merged)+len(p)] - } - - copy(buf[len(c.header.merged):], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } @@ -140,7 +105,7 @@ func (h *udpCustomServer) Serialize(b []byte) { index := 0 for _, item := range h.server { if item.Rand > 0 { - common.Must2(rand.Read(h.merged[index : index+int(item.Rand)])) + crypto.RandBytesBetween(h.merged[index:index+int(item.Rand)], byte(item.RandMin), byte(item.RandMax)) index += int(item.Rand) } else { index += len(item.Packet) @@ -199,52 +164,20 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) return conn, nil } +func (c *udpCustomServerConn) Size() int { + return len(c.header.merged) +} + func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) + if !c.header.Match(p) { + return 0, addr, errors.New("header mismatch") } - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if !c.header.Match(buf[:n]) { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-len(c.header.merged) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) - return 0, addr, nil - } - - copy(p, buf[len(c.header.merged):n]) - - return n - len(c.header.merged), addr, nil + return len(p) - len(c.header.merged), addr, nil } func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if len(c.header.merged)+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:len(c.header.merged)+len(p)] - } - - copy(buf[len(c.header.merged):], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/dns/config.go b/xray-core/transport/internet/finalmask/header/dns/config.go index 7be9eb9aae..48ede1267a 100644 --- a/xray-core/transport/internet/finalmask/header/dns/config.go +++ b/xray-core/transport/internet/finalmask/header/dns/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/dns/conn.go b/xray-core/transport/internet/finalmask/header/dns/conn.go index a60aa2e4f5..263ca7efc0 100644 --- a/xray-core/transport/internet/finalmask/header/dns/conn.go +++ b/xray-core/transport/internet/finalmask/header/dns/conn.go @@ -1,13 +1,11 @@ package dns import ( - "context" "encoding/binary" "net" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) func packDomainName(s string, msg []byte) (off1 int, err error) { @@ -125,52 +123,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *dnsConn) Size() int { + return c.header.Size() +} + func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/dtls/config.go b/xray-core/transport/internet/finalmask/header/dtls/config.go index 02d102d06e..decb69e305 100644 --- a/xray-core/transport/internet/finalmask/header/dtls/config.go +++ b/xray-core/transport/internet/finalmask/header/dtls/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/dtls/conn.go b/xray-core/transport/internet/finalmask/header/dtls/conn.go index 0f7c16e8e3..3f875c2e1a 100644 --- a/xray-core/transport/internet/finalmask/header/dtls/conn.go +++ b/xray-core/transport/internet/finalmask/header/dtls/conn.go @@ -1,12 +1,9 @@ package dtls import ( - "context" "net" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type dtls struct { @@ -62,52 +59,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *dtlsConn) Size() int { + return c.header.Size() +} + func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/srtp/config.go b/xray-core/transport/internet/finalmask/header/srtp/config.go index 875bf899f2..006964d969 100644 --- a/xray-core/transport/internet/finalmask/header/srtp/config.go +++ b/xray-core/transport/internet/finalmask/header/srtp/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/srtp/conn.go b/xray-core/transport/internet/finalmask/header/srtp/conn.go index 8dabcd5f04..94de0f9f0f 100644 --- a/xray-core/transport/internet/finalmask/header/srtp/conn.go +++ b/xray-core/transport/internet/finalmask/header/srtp/conn.go @@ -1,13 +1,10 @@ package srtp import ( - "context" "encoding/binary" "net" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type srtp struct { @@ -46,52 +43,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *srtpConn) Size() int { + return c.header.Size() +} + func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/utp/config.go b/xray-core/transport/internet/finalmask/header/utp/config.go index 804f462ace..45d9a20c5e 100644 --- a/xray-core/transport/internet/finalmask/header/utp/config.go +++ b/xray-core/transport/internet/finalmask/header/utp/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/utp/conn.go b/xray-core/transport/internet/finalmask/header/utp/conn.go index 7647e0490f..59005325ea 100644 --- a/xray-core/transport/internet/finalmask/header/utp/conn.go +++ b/xray-core/transport/internet/finalmask/header/utp/conn.go @@ -1,13 +1,10 @@ package utp import ( - "context" "encoding/binary" "net" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type utp struct { @@ -48,52 +45,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *utpConn) Size() int { + return c.header.Size() +} + func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/wechat/config.go b/xray-core/transport/internet/finalmask/header/wechat/config.go index 8cd6ad3f9b..a433318eda 100644 --- a/xray-core/transport/internet/finalmask/header/wechat/config.go +++ b/xray-core/transport/internet/finalmask/header/wechat/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/wechat/conn.go b/xray-core/transport/internet/finalmask/header/wechat/conn.go index 157c194711..cb1fff62a1 100644 --- a/xray-core/transport/internet/finalmask/header/wechat/conn.go +++ b/xray-core/transport/internet/finalmask/header/wechat/conn.go @@ -1,13 +1,10 @@ package wechat import ( - "context" "encoding/binary" "net" "github.com/xtls/xray-core/common/dice" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type wechat struct { @@ -52,52 +49,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *wechatConn) Size() int { + return c.header.Size() +} + func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/header/wireguard/config.go b/xray-core/transport/internet/finalmask/header/wireguard/config.go index b159f438a9..dd3609d8b2 100644 --- a/xray-core/transport/internet/finalmask/header/wireguard/config.go +++ b/xray-core/transport/internet/finalmask/header/wireguard/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/header/wireguard/conn.go b/xray-core/transport/internet/finalmask/header/wireguard/conn.go index 2d310d282f..4a38969f8f 100644 --- a/xray-core/transport/internet/finalmask/header/wireguard/conn.go +++ b/xray-core/transport/internet/finalmask/header/wireguard/conn.go @@ -1,11 +1,7 @@ package wireguard import ( - "context" "net" - - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type wireguare struct{} @@ -39,52 +35,16 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *wireguareConn) Size() int { + return c.header.Size() +} + func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - return 0, addr, nil - } - - if len(p) < n-c.header.Size() { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, nil - } - - copy(p, buf[c.header.Size():n]) - - return n - c.header.Size(), addr, nil + return len(p) - c.header.Size(), addr, nil } func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.header.Size()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.header.Size()+len(p)] - } - - copy(buf[c.header.Size():], p) - c.header.Serialize(buf) - - _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) - if err != nil { - return 0, err - } + c.header.Serialize(p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/mkcp/aes128gcm/config.go b/xray-core/transport/internet/finalmask/mkcp/aes128gcm/config.go index da3459c6d1..a7160e2b0a 100644 --- a/xray-core/transport/internet/finalmask/mkcp/aes128gcm/config.go +++ b/xray-core/transport/internet/finalmask/mkcp/aes128gcm/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/xray-core/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 89fd90317b..055803af2b 100644 --- a/xray-core/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/xray-core/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -1,7 +1,6 @@ package aes128gcm import ( - "context" "crypto/cipher" "crypto/rand" "crypto/sha256" @@ -33,86 +32,36 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *aes128gcmConn) Size() int { + return c.aead.NonceSize() +} + func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if len(p) < finalmask.UDPSize { - buf := make([]byte, finalmask.UDPSize) - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.aead.NonceSize()+c.aead.Overhead() { - errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) - return 0, addr, nil - } - - nonceSize := c.aead.NonceSize() - nonce := buf[:nonceSize] - ciphertext := buf[nonceSize:n] - plaintext, err := c.aead.Open(p[:0], nonce, ciphertext, nil) - if err != nil { - errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) - return 0, addr, nil - } - - if len(plaintext) > len(p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(plaintext)) - return 0, addr, nil - } - - return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil - } - - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.aead.NonceSize()+c.aead.Overhead() { - errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) - return 0, addr, nil + if len(p) < c.aead.NonceSize()+c.aead.Overhead() { + return 0, addr, errors.New("aead short lenth") } nonceSize := c.aead.NonceSize() nonce := p[:nonceSize] - ciphertext := p[nonceSize:n] + ciphertext := p[nonceSize:] _, err = c.aead.Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { - errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) - return 0, addr, nil + return 0, addr, errors.New("aead open").Base(err) } - copy(p, p[nonceSize:n-c.aead.Overhead()]) - - return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil + return len(p) - c.aead.NonceSize() - c.aead.Overhead(), addr, nil } func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.aead.NonceSize()+c.aead.Overhead()+len(p)] - copy(buf[c.aead.NonceSize():], p) - p = buf[c.aead.NonceSize() : c.aead.NonceSize()+len(p)] + if c.aead.Overhead()+len(p) > finalmask.UDPSize { + return 0, errors.New("aead short write") } nonceSize := c.aead.NonceSize() - nonce := buf[:nonceSize] + nonce := p[:nonceSize] common.Must2(rand.Read(nonce)) - ciphertext := buf[nonceSize : c.aead.NonceSize()+c.aead.Overhead()+len(p)] - _ = c.aead.Seal(ciphertext[:0], nonce, p, nil) + plaintext := p[nonceSize:] + _ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil) - _, err = c.PacketConn.WriteTo(buf[:c.aead.NonceSize()+c.aead.Overhead()+len(p)], addr) - if err != nil { - return 0, err - } - - return len(p), nil + return len(p) + c.aead.Overhead(), nil } diff --git a/xray-core/transport/internet/finalmask/mkcp/original/config.go b/xray-core/transport/internet/finalmask/mkcp/original/config.go index 19db413835..d18b139185 100644 --- a/xray-core/transport/internet/finalmask/mkcp/original/config.go +++ b/xray-core/transport/internet/finalmask/mkcp/original/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/mkcp/original/conn.go b/xray-core/transport/internet/finalmask/mkcp/original/conn.go index 898541bdc6..15abe99be2 100644 --- a/xray-core/transport/internet/finalmask/mkcp/original/conn.go +++ b/xray-core/transport/internet/finalmask/mkcp/original/conn.go @@ -1,7 +1,6 @@ package original import ( - "context" "crypto/cipher" "encoding/binary" "hash/fnv" @@ -9,7 +8,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type simple struct{} @@ -91,60 +89,21 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *simpleConn) Size() int { + return c.aead.Overhead() +} + func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } - - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < c.aead.Overhead() { - errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) - return 0, addr, nil - } - - ciphertext := buf[:n] - opened, err := c.aead.Open(nil, nil, ciphertext, nil) + _, err = c.aead.Open(p[:0], nil, p, nil) if err != nil { - errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) - return 0, addr, nil + return 0, addr, errors.New("aead open").Base(err) } - if len(opened) > len(p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(opened)) - return 0, addr, nil - } - - copy(p, opened) - - return n - c.aead.Overhead(), addr, nil + return len(p) - c.aead.Overhead(), addr, nil } func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.aead.Overhead()+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:c.aead.Overhead()+len(p)] - copy(buf[c.aead.Overhead():], p) - p = buf[c.aead.Overhead() : c.aead.Overhead()+len(p)] - } - - _ = c.aead.Seal(buf[:0], nil, p, nil) - - _, err = c.PacketConn.WriteTo(buf[:c.aead.Overhead()+len(p)], addr) - if err != nil { - return 0, err - } + _ = c.aead.Seal(p[:0], nil, p[c.aead.Overhead():], nil) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/salamander/config.go b/xray-core/transport/internet/finalmask/salamander/config.go index 371b528c61..8df1285d36 100644 --- a/xray-core/transport/internet/finalmask/salamander/config.go +++ b/xray-core/transport/internet/finalmask/salamander/config.go @@ -14,3 +14,6 @@ func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { return NewConnServer(c, raw) } + +func (c *Config) HeaderConn() { +} diff --git a/xray-core/transport/internet/finalmask/salamander/conn.go b/xray-core/transport/internet/finalmask/salamander/conn.go index f693ef07ad..bfb4934ac2 100644 --- a/xray-core/transport/internet/finalmask/salamander/conn.go +++ b/xray-core/transport/internet/finalmask/salamander/conn.go @@ -1,11 +1,9 @@ package salamander import ( - "context" "net" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask" ) type salamanderConn struct { @@ -31,53 +29,18 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { return NewConnClient(c, raw) } +func (c *salamanderConn) Size() int { + return smSaltLen +} + func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := p - if len(p) < finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } + c.obfs.Deobfuscate(p, p[smSaltLen:]) - n, addr, err = c.PacketConn.ReadFrom(buf) - if err != nil || n == 0 { - return n, addr, err - } - - if n < smSaltLen { - errors.LogDebug(context.Background(), addr, " mask read err short lenth ", n) - return 0, addr, nil - } - - if len(p) < n-smSaltLen { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-smSaltLen) - return 0, addr, nil - } - - c.obfs.Deobfuscate(buf[:n], p) - - return n - smSaltLen, addr, nil + return len(p) - smSaltLen, addr, nil } func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if smSaltLen+len(p) > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize) - return 0, nil - } - - var buf []byte - if cap(p) != finalmask.UDPSize { - buf = make([]byte, finalmask.UDPSize) - } else { - buf = p[:smSaltLen+len(p)] - copy(buf[smSaltLen:], p) - p = buf[smSaltLen:] - } - - c.obfs.Obfuscate(p, buf) - - _, err = c.PacketConn.WriteTo(buf[:smSaltLen+len(p)], addr) - if err != nil { - return 0, err - } + c.obfs.Obfuscate(p[smSaltLen:], p) return len(p), nil } diff --git a/xray-core/transport/internet/finalmask/sudoku/conn_udp.go b/xray-core/transport/internet/finalmask/sudoku/conn_udp.go index 0a77468288..c2b2f4dda5 100644 --- a/xray-core/transport/internet/finalmask/sudoku/conn_udp.go +++ b/xray-core/transport/internet/finalmask/sudoku/conn_udp.go @@ -35,10 +35,6 @@ func NewUDPConn(raw net.PacketConn, config *Config) (net.PacketConn, error) { }, nil } -func (c *udpConn) Size() int32 { - return 0 -} - func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMu.Lock() defer c.readMu.Unlock() diff --git a/xray-core/transport/internet/grpc/dial.go b/xray-core/transport/internet/grpc/dial.go index b8f77c0ea8..c8b8423c65 100644 --- a/xray-core/transport/internet/grpc/dial.go +++ b/xray-core/transport/internet/grpc/dial.go @@ -10,8 +10,8 @@ import ( c "github.com/xtls/xray-core/common/ctx" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/utils" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/grpc/encoding" "github.com/xtls/xray-core/transport/internet/reality" @@ -191,8 +191,16 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in ) if err == nil { userAgent := grpcSettings.UserAgent - if userAgent == "" { + // It's NOT recommended to set the UA of gRPC connections to that of real browsers, as they are fundamentally incapable of initiating real gRPC connections. + switch userAgent { + case "chrome", "": userAgent = utils.ChromeUA + case "firefox": + userAgent = utils.FirefoxUA + case "edge": + userAgent = utils.MSEdgeUA + case "golang": + userAgent = "" } setUserAgent(conn, userAgent) conn.Connect() diff --git a/xray-core/transport/internet/httpupgrade/dialer.go b/xray-core/transport/internet/httpupgrade/dialer.go index eacbded4ed..571797f617 100644 --- a/xray-core/transport/internet/httpupgrade/dialer.go +++ b/xray-core/transport/internet/httpupgrade/dialer.go @@ -96,9 +96,7 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings * for key, value := range transportConfiguration.Header { AddHeader(req.Header, key, value) } - if req.Header.Get("User-Agent") == "" { - req.Header.Set("User-Agent", utils.ChromeUA) - } + utils.TryDefaultHeadersWith(req.Header, "ws") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "websocket") diff --git a/xray-core/transport/internet/reality/reality.go b/xray-core/transport/internet/reality/reality.go index 1f6de2b5b1..50b2e02f7d 100644 --- a/xray-core/transport/internet/reality/reality.go +++ b/xray-core/transport/internet/reality/reality.go @@ -223,7 +223,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati if req == nil { return } - req.Header.Set("User-Agent", utils.ChromeUA) + utils.TryDefaultHeadersWith(req.Header, "nav") if first && config.Show { fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent()) } diff --git a/xray-core/transport/internet/splithttp/browser_client.go b/xray-core/transport/internet/splithttp/browser_client.go index 1ae3ae9541..a70447f2d4 100644 --- a/xray-core/transport/internet/splithttp/browser_client.go +++ b/xray-core/transport/internet/splithttp/browser_client.go @@ -5,6 +5,7 @@ import ( "io" "net/http" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet/browser_dialer" @@ -41,21 +42,20 @@ func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, sessio return websocket.NewConnection(conn, dummyAddr, nil, 0), conn.RemoteAddr(), conn.LocalAddr(), nil } -func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, sessionId string, seqStr string, body io.Reader, contentLength int64) error { +func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, sessionId string, seqStr string, payload buf.MultiBuffer) error { method := c.transportConfig.GetNormalizedUplinkHTTPMethod() - request, err := http.NewRequest(method, url, body) + request, err := http.NewRequest(method, url, nil) if err != nil { return err } - request.ContentLength = contentLength - err = c.transportConfig.FillPacketRequest(request, sessionId, seqStr) + err = c.transportConfig.FillPacketRequest(request, sessionId, seqStr, payload) if err != nil { return err } var bytes []byte - if (request.Body != nil) { + if request.Body != nil { bytes, err = io.ReadAll(request.Body) if err != nil { return err diff --git a/xray-core/transport/internet/splithttp/client.go b/xray-core/transport/internet/splithttp/client.go index 38990c1ad6..c156509a0c 100644 --- a/xray-core/transport/internet/splithttp/client.go +++ b/xray-core/transport/internet/splithttp/client.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/signal/done" @@ -23,7 +24,7 @@ type DialerClient interface { OpenStream(context.Context, string, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error) // ctx, url, sessionId, seqStr, body, contentLength - PostPacket(context.Context, string, string, string, io.Reader, int64) error + PostPacket(context.Context, string, string, string, buf.MultiBuffer) error } // implements splithttp.DialerClient in terms of direct network connections @@ -89,14 +90,13 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessio return } -func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, sessionId string, seqStr string, body io.Reader, contentLength int64) error { +func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, sessionId string, seqStr string, payload buf.MultiBuffer) error { method := c.transportConfig.GetNormalizedUplinkHTTPMethod() - req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body) + req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, nil) if err != nil { return err } - req.ContentLength = contentLength - c.transportConfig.FillPacketRequest(req, sessionId, seqStr) + c.transportConfig.FillPacketRequest(req, sessionId, seqStr, payload) if c.httpVersion != "1.1" { resp, err := c.client.Do(req) @@ -117,6 +117,7 @@ func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, sessio // times, the body is already drained after the first // request requestBuff := new(bytes.Buffer) + requestBuff.Grow(512 + int(req.ContentLength)) common.Must(req.Write(requestBuff)) var uploadConn any diff --git a/xray-core/transport/internet/splithttp/config.go b/xray-core/transport/internet/splithttp/config.go index 3954f8f0d7..61f861a33c 100644 --- a/xray-core/transport/internet/splithttp/config.go +++ b/xray-core/transport/internet/splithttp/config.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/utils" "github.com/xtls/xray-core/transport/internet" @@ -51,13 +52,10 @@ func (c *Config) GetRequestHeader() http.Header { for k, v := range c.Headers { header.Add(k, v) } - if header.Get("User-Agent") == "" { - header.Set("User-Agent", utils.ChromeUA) - } + utils.TryDefaultHeadersWith(header, "fetch") return header } - func (c *Config) GetRequestHeaderWithPayload(payload []byte) http.Header { header := c.GetRequestHeader() @@ -102,9 +100,9 @@ func (c *Config) WriteResponseHeader(writer http.ResponseWriter, requestMethod s } if c.GetNormalizedSessionPlacement() == PlacementCookie || - c.GetNormalizedSeqPlacement() == PlacementCookie || - c.XPaddingPlacement == PlacementCookie || - c.GetNormalizedUplinkDataPlacement() == PlacementCookie { + c.GetNormalizedSeqPlacement() == PlacementCookie || + c.XPaddingPlacement == PlacementCookie || + c.GetNormalizedUplinkDataPlacement() == PlacementCookie { writer.Header().Set("Access-Control-Allow-Credentials", "true") } @@ -324,22 +322,17 @@ func (c *Config) FillStreamRequest(request *http.Request, sessionId string, seqS } } -func (c *Config) FillPacketRequest(request *http.Request, sessionId string, seqStr string) error { +func (c *Config) FillPacketRequest(request *http.Request, sessionId string, seqStr string, payload buf.MultiBuffer) error { dataPlacement := c.GetNormalizedUplinkDataPlacement() if dataPlacement == PlacementBody || dataPlacement == PlacementAuto { request.Header = c.GetRequestHeader() + request.Body = io.NopCloser(&buf.MultiBufferContainer{MultiBuffer: payload}) + request.ContentLength = int64(payload.Len()) } else { - var data []byte - var err error - if request.Body != nil { - data, err = io.ReadAll(request.Body) - if err != nil { - return err - } - } - request.Body = nil - request.ContentLength = 0 + data := make([]byte, payload.Len()) + payload.Copy(data) + buf.ReleaseMulti(payload) switch dataPlacement { case PlacementHeader: request.Header = c.GetRequestHeaderWithPayload(data) diff --git a/xray-core/transport/internet/splithttp/dialer.go b/xray-core/transport/internet/splithttp/dialer.go index 71888a73ac..0c351a5ae7 100644 --- a/xray-core/transport/internet/splithttp/dialer.go +++ b/xray-core/transport/internet/splithttp/dialer.go @@ -562,8 +562,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me requestURL.String(), sessionId, seqStr, - &buf.MultiBufferContainer{MultiBuffer: chunk}, - int64(chunk.Len()), + chunk, ) wroteRequest.Close() if err != nil { diff --git a/xray-core/transport/internet/splithttp/hub.go b/xray-core/transport/internet/splithttp/hub.go index c2ab8fbf22..1ffdf6f247 100644 --- a/xray-core/transport/internet/splithttp/hub.go +++ b/xray-core/transport/internet/splithttp/hub.go @@ -18,6 +18,7 @@ import ( "github.com/apernet/quic-go/http3" goreality "github.com/xtls/reality" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" http_proto "github.com/xtls/xray-core/common/protocol/http" @@ -293,15 +294,36 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req var bodyPayload []byte if dataPlacement == PlacementAuto || dataPlacement == PlacementBody { - bodyPayload, err = io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1)) - if err != nil { - errors.LogInfoInner(context.Background(), err, "failed to upload (ReadAll)") - writer.WriteHeader(http.StatusInternalServerError) + var readErr error + if request.ContentLength > int64(scMaxEachPostBytes) { + errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request size exceed it. Adjust scMaxEachPostBytes on the server to be at least as large as client.") + writer.WriteHeader(http.StatusRequestEntityTooLarge) + return + } + if request.ContentLength > 0 { + bodyPayload = make([]byte, request.ContentLength) + _, readErr = io.ReadFull(request.Body, bodyPayload) + } else { + bodyPayload, readErr = buf.ReadAllToBytes(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1)) + } + if readErr != nil { + errors.LogInfoInner(context.Background(), readErr, "failed to read body payload") + writer.WriteHeader(http.StatusBadRequest) return } } - payload := slices.Concat(headerPayload, cookiePayload, bodyPayload) + var payload []byte + switch dataPlacement { + case PlacementHeader: + payload = headerPayload + case PlacementCookie: + payload = cookiePayload + case PlacementBody: + payload = bodyPayload + case PlacementAuto: + payload = slices.Concat(headerPayload, cookiePayload, bodyPayload) + } if len(payload) > scMaxEachPostBytes { errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request size exceed it. Adjust scMaxEachPostBytes on the server to be at least as large as client.") diff --git a/xray-core/transport/internet/tls/ech.go b/xray-core/transport/internet/tls/ech.go index 26721bc18e..8cfb12512d 100644 --- a/xray-core/transport/internet/tls/ech.go +++ b/xray-core/transport/internet/tls/ech.go @@ -253,7 +253,7 @@ func dnsQuery(server string, domain string, sockopt *internet.SocketConfig) ([]b } req.Header.Set("Accept", "application/dns-message") req.Header.Set("Content-Type", "application/dns-message") - req.Header.Set("User-Agent", utils.ChromeUA) + utils.TryDefaultHeadersWith(req.Header, "fetch") req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000))) resp, err := client.Do(req) diff --git a/xray-core/transport/internet/websocket/config.go b/xray-core/transport/internet/websocket/config.go index bd38cd4fe8..1778c960b8 100644 --- a/xray-core/transport/internet/websocket/config.go +++ b/xray-core/transport/internet/websocket/config.go @@ -24,9 +24,7 @@ func (c *Config) GetRequestHeader() http.Header { for k, v := range c.Header { header.Add(k, v) } - if header.Get("User-Agent") == "" { - header.Set("User-Agent", utils.ChromeUA) - } + utils.TryDefaultHeadersWith(header, "ws") return header }