diff --git a/.github/update.log b/.github/update.log index 2c7ce9ece7..7d8989cbcb 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1291,3 +1291,4 @@ Update On Mon Mar 2 20:01:19 CET 2026 Update On Wed Mar 4 20:06:13 CET 2026 Update On Thu Mar 5 20:25:40 CET 2026 Update On Fri Mar 6 20:05:10 CET 2026 +Update On Sat Mar 7 19:46:34 CET 2026 diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 6ebc5511da..b623ef5268 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,10 +2,10 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.20", - "mihomo_alpha": "alpha-b7b05e0", + "mihomo_alpha": "alpha-7f772de", "clash_rs": "v0.9.4", "clash_premium": "2023-09-05-gdcc8d87", - "clash_rs_alpha": "0.9.4-alpha+sha.e11288b" + "clash_rs_alpha": "latest" }, "arch_template": { "mihomo": { @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2026-03-04T22:36:39.634Z" + "updated_at": "2026-03-06T22:22:44.355Z" } diff --git a/filebrowser/.github/workflows/ci.yaml b/filebrowser/.github/workflows/ci.yaml index ddbb8962a4..895dfe2b4b 100644 --- a/filebrowser/.github/workflows/ci.yaml +++ b/filebrowser/.github/workflows/ci.yaml @@ -92,14 +92,14 @@ jobs: cache: "pnpm" cache-dependency-path: "frontend/pnpm-lock.yaml" - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Install Task uses: go-task/setup-task@v1 - run: task build:frontend - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/filebrowser/.github/workflows/docs.yml b/filebrowser/.github/workflows/docs.yml index 5b21ccba79..6dfcfe2394 100644 --- a/filebrowser/.github/workflows/docs.yml +++ b/filebrowser/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Install Task uses: go-task/setup-task@v1 - name: Build site @@ -39,7 +39,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Install Task uses: go-task/setup-task@v1 - name: Build site diff --git a/filebrowser/frontend/pnpm-lock.yaml b/filebrowser/frontend/pnpm-lock.yaml index bbdee7df01..1446cc027c 100644 --- a/filebrowser/frontend/pnpm-lock.yaml +++ b/filebrowser/frontend/pnpm-lock.yaml @@ -101,7 +101,7 @@ importers: devDependencies: '@intlify/unplugin-vue-i18n': specifier: ^11.0.1 - version: 11.0.7(@vue/compiler-dom@3.5.29)(eslint@10.0.2)(rollup@4.57.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.29)(eslint@10.0.3)(rollup@4.57.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)) '@tsconfig/node24': specifier: ^24.0.2 version: 24.0.4 @@ -113,7 +113,7 @@ importers: version: 24.12.0 '@typescript-eslint/eslint-plugin': specifier: ^8.37.0 - version: 8.56.1(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(typescript@5.9.3) + version: 8.56.1(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3) '@vitejs/plugin-legacy': specifier: ^7.2.1 version: 7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2)) @@ -122,10 +122,10 @@ importers: version: 6.0.4(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) '@vue/eslint-config-prettier': specifier: ^10.2.0 - version: 10.2.0(eslint@10.0.2)(prettier@3.8.1) + version: 10.2.0(eslint@10.0.3)(prettier@3.8.1) '@vue/eslint-config-typescript': specifier: ^14.6.0 - version: 14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)))(eslint@10.0.2)(typescript@5.9.3) + version: 14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(vue-eslint-parser@10.4.0(eslint@10.0.3)))(eslint@10.0.3)(typescript@5.9.3) '@vue/tsconfig': specifier: ^0.9.0 version: 0.9.0(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3)) @@ -134,16 +134,16 @@ importers: version: 10.4.27(postcss@8.5.8) eslint: specifier: ^10.0.0 - version: 10.0.2 + version: 10.0.3 eslint-config-prettier: specifier: ^10.1.5 - version: 10.1.8(eslint@10.0.2) + version: 10.1.8(eslint@10.0.3) eslint-plugin-prettier: specifier: ^5.5.1 - version: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.2))(eslint@10.0.2)(prettier@3.8.1) + version: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.3))(eslint@10.0.3)(prettier@3.8.1) eslint-plugin-vue: specifier: ^10.5.1 - version: 10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)) + version: 10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(vue-eslint-parser@10.4.0(eslint@10.0.3)) postcss: specifier: ^8.5.6 version: 8.5.8 @@ -990,24 +990,24 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.23.2': - resolution: {integrity: sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==} + '@eslint/config-array@0.23.3': + resolution: {integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/config-helpers@0.5.2': - resolution: {integrity: sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==} + '@eslint/config-helpers@0.5.3': + resolution: {integrity: sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@1.1.0': - resolution: {integrity: sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==} + '@eslint/core@1.1.1': + resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/object-schema@3.0.2': - resolution: {integrity: sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==} + '@eslint/object-schema@3.0.3': + resolution: {integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.6.0': - resolution: {integrity: sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==} + '@eslint/plugin-kit@0.6.1': + resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@humanfs/core@0.19.1': @@ -1882,8 +1882,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-scope@9.1.1: - resolution: {integrity: sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: @@ -1894,8 +1894,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.0.2: - resolution: {integrity: sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==} + eslint@10.0.3: + resolution: {integrity: sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -1908,8 +1908,8 @@ packages: resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} engines: {node: '>=0.10'} - espree@11.1.1: - resolution: {integrity: sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==} + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} espree@9.6.1: @@ -1997,8 +1997,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flatted@3.3.4: + resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} focus-trap@8.0.0: resolution: {integrity: sha512-Aa84FOGHs99vVwufDMdq2qgOwXPC2e9U66GcqBhn1/jEHPDhJaP8PYhkIbqG9lhfL5Kddk/567lj46LLHYCRUw==} @@ -2236,8 +2236,8 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} mitt@3.0.1: @@ -3595,34 +3595,34 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.0.2)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.0.3)': dependencies: - eslint: 10.0.2 + eslint: 10.0.3 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.23.2': + '@eslint/config-array@0.23.3': dependencies: - '@eslint/object-schema': 3.0.2 + '@eslint/object-schema': 3.0.3 debug: 4.4.3 minimatch: 10.2.4 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.5.2': + '@eslint/config-helpers@0.5.3': dependencies: - '@eslint/core': 1.1.0 + '@eslint/core': 1.1.1 - '@eslint/core@1.1.0': + '@eslint/core@1.1.1': dependencies: '@types/json-schema': 7.0.15 - '@eslint/object-schema@3.0.2': {} + '@eslint/object-schema@3.0.3': {} - '@eslint/plugin-kit@0.6.0': + '@eslint/plugin-kit@0.6.1': dependencies: - '@eslint/core': 1.1.0 + '@eslint/core': 1.1.1 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -3662,9 +3662,9 @@ snapshots: '@intlify/shared@11.2.8': {} - '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.29)(eslint@10.0.2)(rollup@4.57.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.29)(eslint@10.0.3)(rollup@4.57.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) '@intlify/bundle-utils': 11.0.7(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3))) '@intlify/shared': 11.2.8 '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.8)(@vue/compiler-dom@3.5.29)(vue-i18n@11.2.8(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3)) @@ -3845,15 +3845,15 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.0.3)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.56.0 - '@typescript-eslint/type-utils': 8.56.0(eslint@10.0.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.56.0(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.0(eslint@10.0.3)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.0 - eslint: 10.0.2 + eslint: 10.0.3 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -3861,15 +3861,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.0.3)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.1 - eslint: 10.0.2 + eslint: 10.0.3 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -3877,14 +3877,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.56.0 '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.0 debug: 4.4.3 - eslint: 10.0.2 + eslint: 10.0.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3925,25 +3925,25 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.56.0(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.56.0(eslint@10.0.3)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.0(eslint@10.0.3)(typescript@5.9.3) debug: 4.4.3 - eslint: 10.0.2 + eslint: 10.0.3 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.56.1(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.56.1(eslint@10.0.3)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) debug: 4.4.3 - eslint: 10.0.2 + eslint: 10.0.3 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -3960,7 +3960,7 @@ snapshots: '@typescript-eslint/types': 8.56.0 '@typescript-eslint/visitor-keys': 8.56.0 debug: 4.4.3 - minimatch: 9.0.5 + minimatch: 9.0.9 semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -3983,24 +3983,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.56.0(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.56.0(eslint@10.0.3)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) '@typescript-eslint/scope-manager': 8.56.0 '@typescript-eslint/types': 8.56.0 '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - eslint: 10.0.2 + eslint: 10.0.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.56.1(eslint@10.0.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.56.1(eslint@10.0.3)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - eslint: 10.0.2 + eslint: 10.0.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4182,23 +4182,23 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/eslint-config-prettier@10.2.0(eslint@10.0.2)(prettier@3.8.1)': + '@vue/eslint-config-prettier@10.2.0(eslint@10.0.3)(prettier@3.8.1)': dependencies: - eslint: 10.0.2 - eslint-config-prettier: 10.1.8(eslint@10.0.2) - eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.2))(eslint@10.0.2)(prettier@3.8.1) + eslint: 10.0.3 + eslint-config-prettier: 10.1.8(eslint@10.0.3) + eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.3))(eslint@10.0.3)(prettier@3.8.1) prettier: 3.8.1 transitivePeerDependencies: - '@types/eslint' - '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)))(eslint@10.0.2)(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(vue-eslint-parser@10.4.0(eslint@10.0.3)))(eslint@10.0.3)(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.56.0(eslint@10.0.2)(typescript@5.9.3) - eslint: 10.0.2 - eslint-plugin-vue: 10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)) + '@typescript-eslint/utils': 8.56.0(eslint@10.0.3)(typescript@5.9.3) + eslint: 10.0.3 + eslint-plugin-vue: 10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(vue-eslint-parser@10.4.0(eslint@10.0.3)) fast-glob: 3.3.3 - typescript-eslint: 8.56.0(eslint@10.0.2)(typescript@5.9.3) - vue-eslint-parser: 10.4.0(eslint@10.0.2) + typescript-eslint: 8.56.0(eslint@10.0.3)(typescript@5.9.3) + vue-eslint-parser: 10.4.0(eslint@10.0.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -4557,33 +4557,33 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.8(eslint@10.0.2): + eslint-config-prettier@10.1.8(eslint@10.0.3): dependencies: - eslint: 10.0.2 + eslint: 10.0.3 - eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.2))(eslint@10.0.2)(prettier@3.8.1): + eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@10.0.3))(eslint@10.0.3)(prettier@3.8.1): dependencies: - eslint: 10.0.2 + eslint: 10.0.3 prettier: 3.8.1 prettier-linter-helpers: 1.0.1 synckit: 0.11.12 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@10.0.2) + eslint-config-prettier: 10.1.8(eslint@10.0.3) - eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)): + eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(vue-eslint-parser@10.4.0(eslint@10.0.3)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2) - eslint: 10.0.2 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) + eslint: 10.0.3 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 semver: 7.7.4 - vue-eslint-parser: 10.4.0(eslint@10.0.2) + vue-eslint-parser: 10.4.0(eslint@10.0.3) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.0.3)(typescript@5.9.3) - eslint-scope@9.1.1: + eslint-scope@9.1.2: dependencies: '@types/esrecurse': 4.3.1 '@types/estree': 1.0.8 @@ -4594,14 +4594,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.0.2: + eslint@10.0.3: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.2 - '@eslint/config-helpers': 0.5.2 - '@eslint/core': 1.1.0 - '@eslint/plugin-kit': 0.6.0 + '@eslint/config-array': 0.23.3 + '@eslint/config-helpers': 0.5.3 + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -4610,9 +4610,9 @@ snapshots: cross-spawn: 7.0.6 debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 9.1.1 + eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 - espree: 11.1.1 + espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -4636,7 +4636,7 @@ snapshots: event-emitter: 0.3.5 type: 2.7.3 - espree@11.1.1: + espree@11.2.0: dependencies: acorn: 8.16.0 acorn-jsx: 5.3.2(acorn@8.16.0) @@ -4716,10 +4716,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.3 + flatted: 3.3.4 keyv: 4.5.4 - flatted@3.3.3: {} + flatted@3.3.4: {} focus-trap@8.0.0: dependencies: @@ -4931,7 +4931,7 @@ snapshots: dependencies: brace-expansion: 5.0.4 - minimatch@9.0.5: + minimatch@9.0.9: dependencies: brace-expansion: 2.0.2 @@ -5253,13 +5253,13 @@ snapshots: type@2.7.3: {} - typescript-eslint@8.56.0(eslint@10.0.2)(typescript@5.9.3): + typescript-eslint@8.56.0(eslint@10.0.3)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.0(eslint@10.0.2)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.0(eslint@10.0.3)(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@10.0.2)(typescript@5.9.3) - eslint: 10.0.2 + '@typescript-eslint/utils': 8.56.0(eslint@10.0.3)(typescript@5.9.3) + eslint: 10.0.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5376,13 +5376,13 @@ snapshots: vscode-uri@3.1.0: {} - vue-eslint-parser@10.4.0(eslint@10.0.2): + vue-eslint-parser@10.4.0(eslint@10.0.3): dependencies: debug: 4.4.3 - eslint: 10.0.2 - eslint-scope: 9.1.1 + eslint: 10.0.3 + eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 - espree: 11.1.1 + espree: 11.2.0 esquery: 1.7.0 semver: 7.7.4 transitivePeerDependencies: diff --git a/mieru/pkg/metrics/counter.go b/mieru/pkg/metrics/counter.go index 71b3f8a309..48079ccbb5 100644 --- a/mieru/pkg/metrics/counter.go +++ b/mieru/pkg/metrics/counter.go @@ -183,6 +183,10 @@ func (c *Counter) doRollUp(fromLabel, toLabel pb.RollUpLabel, rollUpDuration, tr for _, h := range c.history { // case 1: h should not be rolled up if h.GetRollUp() != fromLabel { + if last != nil { + newHistory = append(newHistory, last) + last = nil + } newHistory = append(newHistory, h) continue } diff --git a/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_xray.lua b/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_xray.lua index 69ff385b95..49b054640f 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -749,7 +749,6 @@ function gen_config(var) local fakedns = nil local routing = nil local observatory = nil - local burstObservatory = nil local strategy = nil local inbounds = {} local outbounds = {} @@ -975,29 +974,19 @@ function gen_config(var) fallbackTag = fallback_node_tag, strategy = strategy }) - if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then - if _node.balancingStrategy == "leastLoad" then - if not burstObservatory then - burstObservatory = { - subjectSelector = { "blc-" }, - pingConfig = { - destination = _node.useCustomProbeUrl and _node.probeUrl or nil, - interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m", - sampling = 3, - timeout = "5s" - } - } - end - else - if not observatory then - observatory = { - subjectSelector = { "blc-" }, - probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil, - probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m", - enableConcurrency = true - } - end + if not observatory and (_node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag) then + local t = api.format_go_time(_node.probeInterval) + if t == "0s" then + t = "60s" + elseif not t:find("[hm]") and tonumber(t:match("%d+")) < 10 then + t = "10s" end + observatory = { + subjectSelector = { "blc-" }, + probeUrl = _node.useCustomProbeUrl and _node.probeUrl or "https://www.google.com/generate_204", + probeInterval = t, + enableConcurrency = true + } end local loopback_outbound = gen_loopback(loopback_tag, loopback_dst) local inbound_tag = loopback_outbound.settings.inboundTag @@ -1652,8 +1641,7 @@ function gen_config(var) -- 传出连接 outbounds = outbounds, -- 连接观测 - observatory = (not burstObservatory) and observatory or nil, - burstObservatory = burstObservatory, + observatory = observatory, -- 路由 routing = routing, -- 本地策略 diff --git a/sing-box/.github/CRONET_GO_VERSION b/sing-box/.github/CRONET_GO_VERSION index 2838ee072b..cc20e651e7 100644 --- a/sing-box/.github/CRONET_GO_VERSION +++ b/sing-box/.github/CRONET_GO_VERSION @@ -1 +1 @@ -cba7b9ac0399055aa49fbdc57c03c374f58e1597 +d181863d6a4aa2e7bb7eaf67c1d512c5e4827fde diff --git a/sing-box/clients/android/version.properties b/sing-box/clients/android/version.properties index 66d15dfd1c..5eee21757d 100644 --- a/sing-box/clients/android/version.properties +++ b/sing-box/clients/android/version.properties @@ -1,5 +1,5 @@ -VERSION_CODE=631 -VERSION_NAME=1.13.1 -GO_VERSION=go1.25.7 +VERSION_CODE=632 +VERSION_NAME=1.13.2 +GO_VERSION=go1.25.8 diff --git a/sing-box/clients/apple/ApplicationLibrary/Views/Connections/ConnectionListView.swift b/sing-box/clients/apple/ApplicationLibrary/Views/Connections/ConnectionListView.swift index 2f5ef8ba21..84510f3e87 100644 --- a/sing-box/clients/apple/ApplicationLibrary/Views/Connections/ConnectionListView.swift +++ b/sing-box/clients/apple/ApplicationLibrary/Views/Connections/ConnectionListView.swift @@ -60,10 +60,15 @@ public struct ConnectionListView: View { #endif .alert($viewModel.alert) .onAppear { + if !environments.connectionSearchText.isEmpty { + viewModel.searchText = environments.connectionSearchText + viewModel.isSearching = true + } viewModel.connect() commandClient.connect() } .onDisappear { + environments.connectionSearchText = viewModel.searchText viewModel.disconnect() commandClient.disconnect() } diff --git a/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogView.swift b/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogView.swift index f9e29d2d24..de2b118854 100644 --- a/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogView.swift +++ b/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogView.swift @@ -15,15 +15,16 @@ public struct LogView: View { public init() {} public var body: some View { - LogViewContent(commandClient: environments.commandClient) + LogViewContent(commandClient: environments.commandClient, initialSearchText: environments.logSearchText) } } private struct LogViewContent: View { + @EnvironmentObject private var environments: ExtensionEnvironments @StateObject private var viewModel: LogViewModel - init(commandClient: CommandClient) { - _viewModel = StateObject(wrappedValue: LogViewModel(commandClient: commandClient)) + init(commandClient: CommandClient, initialSearchText: String = "") { + _viewModel = StateObject(wrappedValue: LogViewModel(commandClient: commandClient, searchText: initialSearchText)) } var body: some View { @@ -35,6 +36,9 @@ private struct LogViewContent: View { toolbarButtons } } + .onDisappear { + environments.logSearchText = viewModel.searchText + } .alert($viewModel.alert) .background( LogExportView( diff --git a/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogViewModel.swift b/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogViewModel.swift index 0d45c1775d..4bf34ca505 100644 --- a/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogViewModel.swift +++ b/sing-box/clients/apple/ApplicationLibrary/Views/Log/LogViewModel.swift @@ -18,6 +18,8 @@ public class LogDataModel: ObservableObject { private let commandClient: CommandClient private weak var viewModel: LogViewModel? + private var pausedLogSnapshot: [LogEntry]? + private var lastPaused = false private var lastProcessedLogCount = 0 private var lastEffectiveLevel: Int? private var lastSearchText = "" @@ -48,38 +50,59 @@ public class LogDataModel: ObservableObject { let debouncedSearchText = viewModel.$searchText .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) - Publishers.CombineLatest4( - commandClient.$logList, - commandClient.$defaultLogLevel, - viewModel.$selectedLogLevel, - debouncedSearchText + Publishers.CombineLatest( + Publishers.CombineLatest4( + commandClient.$logList, + commandClient.$defaultLogLevel, + viewModel.$selectedLogLevel, + debouncedSearchText + ), + viewModel.$isPaused ) .receive(on: DispatchQueue.main) - .sink { [weak self] logList, defaultLogLevel, selectedLogLevel, searchText in + .sink { [weak self] combined, isPaused in guard let self else { return } + let (logList, defaultLogLevel, selectedLogLevel, searchText) = combined let effectiveLevel = selectedLogLevel ?? defaultLogLevel + if isPaused, !self.lastPaused { + self.pausedLogSnapshot = logList + self.lastProcessedLogCount = 0 + } else if !isPaused, self.lastPaused { + self.pausedLogSnapshot = nil + self.lastProcessedLogCount = 0 + } + self.lastPaused = isPaused + + let sourceList = self.pausedLogSnapshot ?? logList + + if isPaused, effectiveLevel == self.lastEffectiveLevel, searchText == self.lastSearchText, + self.lastProcessedLogCount > 0 + { + return + } + let canIncrement = self.lastProcessedLogCount > 0 && - logList.count > self.lastProcessedLogCount && + sourceList.count > self.lastProcessedLogCount && effectiveLevel == self.lastEffectiveLevel && searchText == self.lastSearchText if canIncrement { - let newLogs = logList[self.lastProcessedLogCount...] + let newLogs = sourceList[self.lastProcessedLogCount...] let newFilteredLogs = newLogs.filter { log in log.level <= effectiveLevel && (searchText.isEmpty || log.message.contains(searchText)) } self.filteredLogs.append(contentsOf: newFilteredLogs) } else { - self.filteredLogs = logList.filter { log in + self.filteredLogs = sourceList.filter { log in log.level <= effectiveLevel && (searchText.isEmpty || log.message.contains(searchText)) } } self.updateVisibleLogs() - self.lastProcessedLogCount = logList.count + self.lastProcessedLogCount = sourceList.count self.lastEffectiveLevel = effectiveLevel self.lastSearchText = searchText } @@ -88,6 +111,8 @@ public class LogDataModel: ObservableObject { public func clearLogs() { viewModel?.isPaused = false + pausedLogSnapshot = nil + lastPaused = false lastProcessedLogCount = 0 lastEffectiveLevel = nil lastSearchText = "" @@ -151,8 +176,10 @@ public class LogViewModel: BaseViewModel { public let commandClient: CommandClient public private(set) var dataModel: LogDataModel! - public init(commandClient: CommandClient) { + public init(commandClient: CommandClient, searchText: String = "") { self.commandClient = commandClient + self.searchText = searchText + self.isSearching = !searchText.isEmpty super.init() dataModel = LogDataModel(commandClient: commandClient, viewModel: self) } diff --git a/sing-box/clients/apple/ApplicationLibrary/Views/Setting/CoreView.swift b/sing-box/clients/apple/ApplicationLibrary/Views/Setting/CoreView.swift index 8dfda54798..86afc0d101 100644 --- a/sing-box/clients/apple/ApplicationLibrary/Views/Setting/CoreView.swift +++ b/sing-box/clients/apple/ApplicationLibrary/Views/Setting/CoreView.swift @@ -19,6 +19,7 @@ public struct CoreView: View { @State private var version = "" @State private var dataSize: String? + @State private var dataSizeLoaded = false #if os(macOS) @State private var helperUnavailable = false @@ -38,6 +39,12 @@ public struct CoreView: View { FormTextItem("Version", version) if let dataSize { FormTextItem("Data Size", dataSize) + } else if !dataSizeLoaded { + HStack { + Text("Data Size") + Spacer() + ProgressView() + } } else { #if os(macOS) HStack { @@ -124,11 +131,6 @@ public struct CoreView: View { } else { await MainActor.run { version = LibboxVersion() - #if os(macOS) - if Variant.useSystemExtension { - helperUnavailable = HelperServiceManager.rootHelperStatus != .enabled - } - #endif isLoading = false } await loadSettingsBackground() @@ -173,6 +175,7 @@ public struct CoreView: View { self.helperUnavailable = helperUnavailable #endif self.dataSize = dataSize + self.dataSizeLoaded = true } } diff --git a/sing-box/clients/apple/ApplicationLibrary/Views/Setting/MacAppView.swift b/sing-box/clients/apple/ApplicationLibrary/Views/Setting/MacAppView.swift index c17915e754..fccf76c605 100644 --- a/sing-box/clients/apple/ApplicationLibrary/Views/Setting/MacAppView.swift +++ b/sing-box/clients/apple/ApplicationLibrary/Views/Setting/MacAppView.swift @@ -29,6 +29,7 @@ public struct AppView: View { @Environment(\.showMenuBarExtra) private var showMenuBarExtra @Environment(\.menuBarExtraSpeedMode) private var menuBarExtraSpeedMode @State private var menuBarExtraInBackground = false + @State private var helperStatusLoaded = false @State private var rootHelperRegistrationStatus: SMAppService.Status = .notRegistered #endif @@ -108,7 +109,9 @@ public struct AppView: View { } Section { - if rootHelperRegistrationStatus == .enabled { + if !helperStatusLoaded { + ProgressView() + } else if rootHelperRegistrationStatus == .enabled { FormButton { Task { do { @@ -171,11 +174,14 @@ public struct AppView: View { #if os(macOS) startAtLogin = SMAppService.mainApp.status == .enabled menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get() - if Variant.useSystemExtension { - refreshHelperStatus() - } #endif isLoading = false + #if os(macOS) + if Variant.useSystemExtension { + refreshHelperStatus() + helperStatusLoaded = true + } + #endif } private static func currentLanguage() -> String? { diff --git a/sing-box/clients/apple/Library/Network/ExtensionEnvironments.swift b/sing-box/clients/apple/Library/Network/ExtensionEnvironments.swift index 7d7def71c0..d099d2e53d 100644 --- a/sing-box/clients/apple/Library/Network/ExtensionEnvironments.swift +++ b/sing-box/clients/apple/Library/Network/ExtensionEnvironments.swift @@ -180,6 +180,9 @@ public class ExtensionEnvironments: ObservableObject { @Published public var emptyProfiles = false @Published public var pendingImportRemoteProfile: ImportRemoteProfileRequest? + public var logSearchText = "" + public var connectionSearchText = "" + public let profileUpdate = ObjectWillChangePublisher() public let selectedProfileUpdate = ObjectWillChangePublisher() public let openSettings = ObjectWillChangePublisher() diff --git a/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj b/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj index d84ec2152d..d5493d177a 100644 --- a/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj +++ b/sing-box/clients/apple/sing-box.xcodeproj/project.pbxproj @@ -2235,7 +2235,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -2269,7 +2269,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -2662,7 +2662,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2704,7 +2704,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2744,7 +2744,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; OTHER_CODE_SIGN_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2783,7 +2783,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; OTHER_CODE_SIGN_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt; PRODUCT_NAME = "sing-box"; @@ -2925,7 +2925,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2973,7 +2973,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3016,7 +3016,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.standalone; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3058,7 +3058,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.13.1"; + MARKETING_VERSION = "1.13.2"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfavt.standalone; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/sing-box/docs/changelog.md b/sing-box/docs/changelog.md index 00dc31673c..6c3d8759c2 100644 --- a/sing-box/docs/changelog.md +++ b/sing-box/docs/changelog.md @@ -2,6 +2,35 @@ icon: material/alert-decagram --- +#### 1.14.0-alpha.1 + +* Add `source_mac_address` and `source_hostname` rule items **1** +* Add `include_mac_address` and `exclude_mac_address` TUN options **2** +* Update NaiveProxy to 145.0.7632.159 **3** +* Fixes and improvements + +**1**: + +New rule items for matching LAN devices by MAC address and hostname via neighbor resolution. +Supported on Linux, macOS, or in graphical clients on Android and macOS. + +See [Route Rule](/configuration/route/rule/#source_mac_address), [DNS Rule](/configuration/dns/rule/#source_mac_address) and [Neighbor Resolution](/configuration/shared/neighbor/). + +**2**: + +Limit or exclude devices from TUN routing by MAC address. +Only supported on Linux with `auto_route` and `auto_redirect` enabled. + +See [TUN](/configuration/inbound/tun/#include_mac_address). + +**3**: + +This is not an official update from NaiveProxy. Instead, it's a Chromium codebase update maintained by Project S. + +#### 1.13.2 + +* Fixes and improvements + #### 1.13.1 * Fixes and improvements diff --git a/sing-box/docs/configuration/dns/rule.md b/sing-box/docs/configuration/dns/rule.md index c682bc8ad4..97a4a7b3d5 100644 --- a/sing-box/docs/configuration/dns/rule.md +++ b/sing-box/docs/configuration/dns/rule.md @@ -425,7 +425,7 @@ Match default interface address. !!! quote "" - Only supported on Linux and macOS, or in graphical clients on Android. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. + Only supported on Linux, macOS, or in graphical clients on Android and macOS. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. Match source device MAC address. @@ -435,7 +435,7 @@ Match source device MAC address. !!! quote "" - Only supported on Linux and macOS, or in graphical clients on Android. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. + Only supported on Linux, macOS, or in graphical clients on Android and macOS. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. Match source device hostname from DHCP leases. diff --git a/sing-box/docs/configuration/dns/rule.zh.md b/sing-box/docs/configuration/dns/rule.zh.md index d472d92825..e1288bb69e 100644 --- a/sing-box/docs/configuration/dns/rule.zh.md +++ b/sing-box/docs/configuration/dns/rule.zh.md @@ -424,7 +424,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. !!! quote "" - 仅支持 Linux 和 macOS,或在 Android 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 + 仅支持 Linux、macOS,或在 Android 和 macOS 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 匹配源设备 MAC 地址。 @@ -434,7 +434,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`. !!! quote "" - 仅支持 Linux 和 macOS,或在 Android 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 + 仅支持 Linux、macOS,或在 Android 和 macOS 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 匹配源设备从 DHCP 租约获取的主机名。 diff --git a/sing-box/docs/configuration/route/rule.md b/sing-box/docs/configuration/route/rule.md index 2ba71380e5..767e9ef756 100644 --- a/sing-box/docs/configuration/route/rule.md +++ b/sing-box/docs/configuration/route/rule.md @@ -466,7 +466,7 @@ Match specified outbounds' preferred routes. !!! quote "" - Only supported on Linux and macOS, or in graphical clients on Android. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. + Only supported on Linux, macOS, or in graphical clients on Android and macOS. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. Match source device MAC address. @@ -476,7 +476,7 @@ Match source device MAC address. !!! quote "" - Only supported on Linux and macOS, or in graphical clients on Android. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. + Only supported on Linux, macOS, or in graphical clients on Android and macOS. See [Neighbor Resolution](/configuration/shared/neighbor/) for setup. Match source device hostname from DHCP leases. diff --git a/sing-box/docs/configuration/route/rule.zh.md b/sing-box/docs/configuration/route/rule.zh.md index 4b6d94f569..e581ae995d 100644 --- a/sing-box/docs/configuration/route/rule.zh.md +++ b/sing-box/docs/configuration/route/rule.zh.md @@ -463,7 +463,7 @@ icon: material/new-box !!! quote "" - 仅支持 Linux 和 macOS,或在 Android 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 + 仅支持 Linux、macOS,或在 Android 和 macOS 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 匹配源设备 MAC 地址。 @@ -473,7 +473,7 @@ icon: material/new-box !!! quote "" - 仅支持 Linux 和 macOS,或在 Android 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 + 仅支持 Linux、macOS,或在 Android 和 macOS 图形客户端中支持。参阅 [邻居解析](/configuration/shared/neighbor/) 了解设置方法。 匹配源设备从 DHCP 租约获取的主机名。 diff --git a/sing-box/go.mod b/sing-box/go.mod index dda122fc85..a0b60fd8bc 100644 --- a/sing-box/go.mod +++ b/sing-box/go.mod @@ -29,13 +29,13 @@ require ( github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cors v1.2.1 - github.com/sagernet/cronet-go v0.0.0-20260303101018-cba7b9ac0399 - github.com/sagernet/cronet-go/all v0.0.0-20260303101018-cba7b9ac0399 + github.com/sagernet/cronet-go v0.0.0-20260306075351-e5943141aa40 + github.com/sagernet/cronet-go/all v0.0.0-20260306075351-e5943141aa40 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/gomobile v0.1.12 github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 - github.com/sagernet/sing v0.8.1 + github.com/sagernet/sing v0.8.2 github.com/sagernet/sing-mux v0.3.4 github.com/sagernet/sing-quic v0.6.0 github.com/sagernet/sing-shadowsocks v0.2.8 @@ -105,35 +105,35 @@ require ( github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/safchain/ethtool v0.3.0 // indirect - github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260303100323-125d0d93b3e6 // indirect - github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260303100323-125d0d93b3e6 // indirect + github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260306074725-2e4f95b376d3 // indirect + github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260306074725-2e4f95b376d3 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/spf13/pflag v1.0.9 // indirect diff --git a/sing-box/go.sum b/sing-box/go.sum index 26d2a1cf2e..6f5c06f8af 100644 --- a/sing-box/go.sum +++ b/sing-box/go.sum @@ -162,68 +162,68 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= -github.com/sagernet/cronet-go v0.0.0-20260303101018-cba7b9ac0399 h1:x3tVYQHdqqnKbEd9/H4KIGhtHTjA+KfiiaXedI3/w8Q= -github.com/sagernet/cronet-go v0.0.0-20260303101018-cba7b9ac0399/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw= -github.com/sagernet/cronet-go/all v0.0.0-20260303101018-cba7b9ac0399 h1:mD3ehudpYf1IFgCTv25d/B6KnBc/lLFq1jmSQIK24y0= -github.com/sagernet/cronet-go/all v0.0.0-20260303101018-cba7b9ac0399/go.mod h1:MbYagcGGIaRo9tNrgafbCTO+Qc7eVEh32ZWMprSB8b0= -github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260303100323-125d0d93b3e6 h1:ghRKgSaswefPwQF8AYtUlNyumILOB0ptJWxgZ8MFrEE= -github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw= -github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260303100323-125d0d93b3e6 h1:Behr7YCnQP2dsvzAJDIoMd5nTVU9/d6MMtk/S3MctwA= -github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM= -github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260303100323-125d0d93b3e6 h1:6UL9XdGU/44oTHj36e+EBDJ0RonFoObmd299NG/qQCU= -github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc= -github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:Q9apxjtkj6iMIBQlTo71QsOTrNlhHneaXQb1Q0IshU8= -github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ= -github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260303100323-125d0d93b3e6 h1:0N+xlnMkFEeqgFe3X/PEvHt+/t+BPgxmbx7wzNcYppg= -github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs= -github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:7f2vTXtePikBSV1bdD0zs5/WuZM+bRuej3mREpWL/qQ= -github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0= -github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6 h1:HMlnhEYs+axOa0tAJ79se3QsYB8CpRCQo9mewWWFeeg= -github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0= -github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:Ux/U6vF+1AoGLSJK3jVa9Kqkn64MX4Ivv7fy0ikDrpQ= -github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4= -github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6 h1:5Dhuere2bQFzfGvKxA7TFgA5MoTtgcZMmJQuKwQKlyA= -github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo= -github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260303100323-125d0d93b3e6 h1:aMRcLow4UpZWZ28fR9FjveTL/4okrigZySIkEVZnlgA= -github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ= -github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260303100323-125d0d93b3e6 h1:y4g8oNtEfSdcKrBKsH5vMAjzGthvhHFNU80sanYDQEM= -github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU= -github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260303100323-125d0d93b3e6 h1:CXN6OPILi5trwffmYiiJ9rqJL3XAWx1menLrBBwA0gU= -github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI= -github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260303100323-125d0d93b3e6 h1:ZphFHQeFOTpqCWPwFcQRnrePXajml8LbKlYFJ5n0isU= -github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ= -github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260303100323-125d0d93b3e6 h1:nKzFK84oANHz7I6bab+25bBY+pdpAbO0b3NJroyLldo= -github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0= -github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:HqqZUGRXcWvvwlbuvjk/efo8TKW1H/aHdqQTde+Xs9Q= -github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s= -github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260303100323-125d0d93b3e6 h1:D2v9lZZG5sm4x/CkG7uqc6ZU3YlhFQ+GmJfvZMK0h/s= -github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ= -github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260303100323-125d0d93b3e6 h1:TWveNeXHrA5r8XOlf+vw7U2b2M0ip6GNF89jcUi1ogw= -github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow= -github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260303100323-125d0d93b3e6 h1:DVCBoXOZI4PNG0cbCLg8lrphRXoLFcAIDLNmzsCVg3I= -github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk= -github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260303100323-125d0d93b3e6 h1:7s5xqNlBUWkIXdruPYi3/txXekQhGWxrYxbnB0cnARo= -github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E= -github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260303100323-125d0d93b3e6 h1:eyEb+Q7VH4hpE1nV+EmEnN2XX5WilgBpIsfCw4C/7no= -github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8= -github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260303100323-125d0d93b3e6 h1:9F1W7+z1hHST6GSzdpQ8Q0NCkneAL18dkRA1HfxH09A= -github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w= -github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260303100323-125d0d93b3e6 h1:MmQIR3iJsdvw1ONBP3geK57i9c3+v9dXPMNdZYcYGKw= -github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0= -github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260303100323-125d0d93b3e6 h1:j6Pk1Wsl+PCbKRXtp7a912D2D6zqX5Nk51wDQU9TEDc= -github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs= -github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260303100323-125d0d93b3e6 h1:0DnFhbRfNqwguNCxiinA7BowQ/RaFt627sjW09JNp80= -github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc= -github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6 h1:3CZmlEk2/WW5UHLFJZxXPJ9IJxX3td8U3PyqWSGMl3c= -github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4= -github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:eHkVRptoZf3BuuskkjcclO2dwQrX4zluoVGODMrX7n0= -github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc= -github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6 h1:UgFmE0cZo9euu8/7sTAhj1G8lldavwXBdcPNyTE29CQ= -github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc= -github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260303100323-125d0d93b3e6 h1:xbg3ZB9tLMGDQe4+aewG0Z4bEP/2pLtYBcDzILv5eEc= -github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8= -github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260303100323-125d0d93b3e6 h1:M0bTSTSTnSMlPY2WaZT6fL5TFICqk8v4cm+QVf8Fcao= -github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260303100323-125d0d93b3e6/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw= +github.com/sagernet/cronet-go v0.0.0-20260306075351-e5943141aa40 h1:A9P5YN0Tq+quO9vISIOL+PkExbGWAroyNIk9pI309ls= +github.com/sagernet/cronet-go v0.0.0-20260306075351-e5943141aa40/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw= +github.com/sagernet/cronet-go/all v0.0.0-20260306075351-e5943141aa40 h1:0W9yjyRZ/9peX7jFlruJgOhydBzqj0u7uRY+NUFlbCE= +github.com/sagernet/cronet-go/all v0.0.0-20260306075351-e5943141aa40/go.mod h1:U54HWP2v0xDyTEpAcof98Y923Lr1ymOvFWpa8aVBBAk= +github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260306074725-2e4f95b376d3 h1:Par4t1sZVTJodVxVoGoaSi4MTojaDrraHXCK5Xjt/rM= +github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw= +github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260306074725-2e4f95b376d3 h1:Wg7qunP2EtGnQSHaAL2a/shion6Y5QatyFtAoMcZjdg= +github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM= +github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260306074725-2e4f95b376d3 h1:JZSGrRe1y5yR+REJLK2X1ZxHcUnXc110m7rEuqkhurk= +github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc= +github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:DwgYmuUd36tXSJuu3wK1HntOifcRPifDc/s6X6LdVSQ= +github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ= +github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260306074725-2e4f95b376d3 h1:jjjSy31cytxMRYLoNlwA98YasRAe0P5EEsw5c4Pwvv0= +github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs= +github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:2b/N8xhl+MBRIg70sHYuJ/3V3gJu3F4aVTndxFnbICU= +github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0= +github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3 h1:CysHa5F+LqLumG3HUfUbQzWIbG13QMTUMkkc2DTHclU= +github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0= +github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:Lf9FtR/87jNgc+0yeCCxlvlu2RLSrlaaYfVlYCJeFq0= +github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4= +github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3 h1:1X6PNucfXzZB21EOP0aBn+m06UgL6e4oJZJ2bcqrbtM= +github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo= +github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260306074725-2e4f95b376d3 h1:XvHeLlblB6nXilTqfDI+SxyIuR2FUkpNkL9mXNt/wNg= +github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ= +github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260306074725-2e4f95b376d3 h1:ACHr8UvOHs/+S29L7UcCrTe3P53NuZbKzHmwCpteyoo= +github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU= +github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260306074725-2e4f95b376d3 h1:MzSFaCUaGn/a4jAGw7Qnm0t5ssnx1z87YEqwvG1ZhRU= +github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI= +github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260306074725-2e4f95b376d3 h1:i7lFKCd4AcKut4Co/jEzvb9d1d10K3t4un9NarqAyo0= +github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ= +github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260306074725-2e4f95b376d3 h1:aLBHE3UGmBf+f+Vf5ceYDzsKPufDfYoMILrMhqwsJYI= +github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0= +github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:5CZoDiP1u3REF7LcBYoQgBuWacnBcxWeERU5UrQDqHg= +github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s= +github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260306074725-2e4f95b376d3 h1:4W7D6UUZH5/636fE2VMHJ+YLofmYWaBhAlvaj23C20I= +github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ= +github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260306074725-2e4f95b376d3 h1:zK/9ebQ3Ykcvomc+JEIou8rgIxbU1O6bBB7z2A3irO4= +github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow= +github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260306074725-2e4f95b376d3 h1:q79ByUHlbxPcADvOZ2G8ayCnLBlF/fzHtvLennf2clo= +github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk= +github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260306074725-2e4f95b376d3 h1:3RNNwgX1rltXu7gIGD12gxlIJc1s8e2stB2BzMtl9tE= +github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E= +github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260306074725-2e4f95b376d3 h1:/hD/Vk7/Jlg07Ic1atNjU1mXii91ziN6e3zxFYTKqio= +github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8= +github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260306074725-2e4f95b376d3 h1:d7Z63bQ/U7ZmB1MkC1dtAtIn6h40WrHey9S/vnfDb5g= +github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w= +github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260306074725-2e4f95b376d3 h1:1c6ZqstM62BrbTFrCA4vINFTCooCM8uph6uIGfAEfqQ= +github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0= +github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260306074725-2e4f95b376d3 h1:ZTHDXreHG+9XT0hD+MIu1etqPQAfKBApFS8Z1XMT7Nw= +github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs= +github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260306074725-2e4f95b376d3 h1:ry0S9V5pSNTg2wXra1rBajSITvXRufgw0u3w/mE0GB4= +github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc= +github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3 h1:iO5cm5MiqvKQB7QkY2b8QFgnMt3jDdOiDopX2aNsFOM= +github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4= +github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:DC03qT5UTbDgUzJ78xajYXq5UYcFHBLHKIoH+PRpCf0= +github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc= +github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3 h1:uLqZSA2OAynMxrokxVO2pW3unWA8DNjion/I4ihX/84= +github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc= +github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260306074725-2e4f95b376d3 h1:WHTBryhjXaniv5fMjSr/FvWKyAhdomD7rLagh4ano10= +github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8= +github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260306074725-2e4f95b376d3 h1:TmikX4Xtalpv2Jts/MuB5qwg+KmTKbrpPf5deZGLIqA= +github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260306074725-2e4f95b376d3/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg= @@ -236,8 +236,8 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o= github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4= -github.com/sagernet/sing v0.8.1 h1:Li+zg4xdiMsvdX4j50TPqmSG8LF/TB9US2qlAN40izU= -github.com/sagernet/sing v0.8.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.8.2 h1:kX1IH9SWJv4S0T9M8O+HNahWgbOuY1VauxbF7NU5lOg= +github.com/sagernet/sing v0.8.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s= github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk= github.com/sagernet/sing-quic v0.6.0 h1:dhrFnP45wgVKEOT1EvtsToxdzRnHIDIAgj6WHV9pLyM= diff --git a/sing-box/protocol/tailscale/dns_transport.go b/sing-box/protocol/tailscale/dns_transport.go index 521bb55146..1c227db7a7 100644 --- a/sing-box/protocol/tailscale/dns_transport.go +++ b/sing-box/protocol/tailscale/dns_transport.go @@ -1,3 +1,5 @@ +//go:build with_gvisor + package tailscale import ( diff --git a/sing-box/protocol/tailscale/endpoint.go b/sing-box/protocol/tailscale/endpoint.go index ff82ef86e4..a178901caa 100644 --- a/sing-box/protocol/tailscale/endpoint.go +++ b/sing-box/protocol/tailscale/endpoint.go @@ -1,3 +1,5 @@ +//go:build with_gvisor + package tailscale import ( diff --git a/sing-box/protocol/tailscale/protect_android.go b/sing-box/protocol/tailscale/protect_android.go index 37dd33bdc4..63be868d73 100644 --- a/sing-box/protocol/tailscale/protect_android.go +++ b/sing-box/protocol/tailscale/protect_android.go @@ -1,3 +1,5 @@ +//go:build with_gvisor + package tailscale import ( diff --git a/sing-box/protocol/tailscale/protect_nonandroid.go b/sing-box/protocol/tailscale/protect_nonandroid.go index f315c2eaf0..c2f39f1fc0 100644 --- a/sing-box/protocol/tailscale/protect_nonandroid.go +++ b/sing-box/protocol/tailscale/protect_nonandroid.go @@ -1,4 +1,4 @@ -//go:build !android +//go:build with_gvisor && !android package tailscale diff --git a/sing-box/protocol/tailscale/tun_device_unix.go b/sing-box/protocol/tailscale/tun_device_unix.go index 77f2955b21..a8d237abf3 100644 --- a/sing-box/protocol/tailscale/tun_device_unix.go +++ b/sing-box/protocol/tailscale/tun_device_unix.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build with_gvisor && !windows package tailscale diff --git a/sing-box/protocol/tailscale/tun_device_windows.go b/sing-box/protocol/tailscale/tun_device_windows.go index 3b0e3440ed..8c9e87cee8 100644 --- a/sing-box/protocol/tailscale/tun_device_windows.go +++ b/sing-box/protocol/tailscale/tun_device_windows.go @@ -1,4 +1,4 @@ -//go:build windows +//go:build with_gvisor && windows package tailscale diff --git a/sing-box/service/derp/service.go b/sing-box/service/derp/service.go index 6cc1b9b6bc..02dac60bfa 100644 --- a/sing-box/service/derp/service.go +++ b/sing-box/service/derp/service.go @@ -1,3 +1,5 @@ +//go:build with_gvisor + package derp import ( diff --git a/small/luci-app-passwall/luasrc/passwall/util_xray.lua b/small/luci-app-passwall/luasrc/passwall/util_xray.lua index 69ff385b95..49b054640f 100644 --- a/small/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/small/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -749,7 +749,6 @@ function gen_config(var) local fakedns = nil local routing = nil local observatory = nil - local burstObservatory = nil local strategy = nil local inbounds = {} local outbounds = {} @@ -975,29 +974,19 @@ function gen_config(var) fallbackTag = fallback_node_tag, strategy = strategy }) - if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then - if _node.balancingStrategy == "leastLoad" then - if not burstObservatory then - burstObservatory = { - subjectSelector = { "blc-" }, - pingConfig = { - destination = _node.useCustomProbeUrl and _node.probeUrl or nil, - interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m", - sampling = 3, - timeout = "5s" - } - } - end - else - if not observatory then - observatory = { - subjectSelector = { "blc-" }, - probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil, - probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m", - enableConcurrency = true - } - end + if not observatory and (_node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag) then + local t = api.format_go_time(_node.probeInterval) + if t == "0s" then + t = "60s" + elseif not t:find("[hm]") and tonumber(t:match("%d+")) < 10 then + t = "10s" end + observatory = { + subjectSelector = { "blc-" }, + probeUrl = _node.useCustomProbeUrl and _node.probeUrl or "https://www.google.com/generate_204", + probeInterval = t, + enableConcurrency = true + } end local loopback_outbound = gen_loopback(loopback_tag, loopback_dst) local inbound_tag = loopback_outbound.settings.inboundTag @@ -1652,8 +1641,7 @@ function gen_config(var) -- 传出连接 outbounds = outbounds, -- 连接观测 - observatory = (not burstObservatory) and observatory or nil, - burstObservatory = burstObservatory, + observatory = observatory, -- 路由 routing = routing, -- 本地策略 diff --git a/small/sing-box/Makefile b/small/sing-box/Makefile index ada15a5918..b4c3e9b7e5 100644 --- a/small/sing-box/Makefile +++ b/small/sing-box/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=sing-box -PKG_VERSION:=1.13.1 +PKG_VERSION:=1.13.2 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=62624d4c11e318606b0dc181d1da4b2b4d7e110f67c6fb15e1ba14bb88377f69 +PKG_HASH:=04b72fcd355c36a85eb028f47986894e9cf4dadbea3fee79f6891481cabeb692 PKG_LICENSE:=GPL-3.0-or-later PKG_LICENSE_FILES:=LICENSE diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index 5425a04fa0..387129f706 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -21,13 +21,13 @@ define Download/geoip HASH:=c6c1d1be0d28defef55b153e87cb430f94fb480c8f523bf901c5e4ca18d58a00 endef -GEOSITE_VER:=20260306064900 +GEOSITE_VER:=20260307033015 GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) define Download/geosite URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ URL_FILE:=dlc.dat FILE:=$(GEOSITE_FILE) - HASH:=a599cbd88ee567d3e35346785924bf31d29a1eb4de798f6cf1fbf3ea55c996a1 + HASH:=6e432a65a6deefa816555eb2c6bc83138479ead2bc88b01d2a51ccea6a9e7315 endef GEOSITE_IRAN_VER:=202603020055 diff --git a/v2rayn/.github/workflows/build-linux.yml b/v2rayn/.github/workflows/build-linux.yml index a814b8e482..88cbb3885e 100644 --- a/v2rayn/.github/workflows/build-linux.yml +++ b/v2rayn/.github/workflows/build-linux.yml @@ -37,7 +37,7 @@ jobs: fetch-depth: '0' - name: Setup .NET - uses: actions/setup-dotnet@v5.0.1 + uses: actions/setup-dotnet@v5.2.0 with: dotnet-version: '8.0.x' diff --git a/v2rayn/.github/workflows/build-osx.yml b/v2rayn/.github/workflows/build-osx.yml index f16f28bf0a..d79295cc59 100644 --- a/v2rayn/.github/workflows/build-osx.yml +++ b/v2rayn/.github/workflows/build-osx.yml @@ -32,7 +32,7 @@ jobs: fetch-depth: '0' - name: Setup - uses: actions/setup-dotnet@v5.0.1 + uses: actions/setup-dotnet@v5.2.0 with: dotnet-version: '8.0.x' diff --git a/v2rayn/.github/workflows/build-windows-desktop.yml b/v2rayn/.github/workflows/build-windows-desktop.yml index 1a85e546ca..99c7ef328b 100644 --- a/v2rayn/.github/workflows/build-windows-desktop.yml +++ b/v2rayn/.github/workflows/build-windows-desktop.yml @@ -32,7 +32,7 @@ jobs: fetch-depth: '0' - name: Setup - uses: actions/setup-dotnet@v5.0.1 + uses: actions/setup-dotnet@v5.2.0 with: dotnet-version: '8.0.x' diff --git a/v2rayn/.github/workflows/build-windows.yml b/v2rayn/.github/workflows/build-windows.yml index 4c1ce3ea9c..7d202370b9 100644 --- a/v2rayn/.github/workflows/build-windows.yml +++ b/v2rayn/.github/workflows/build-windows.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v6.0.2 - name: Setup - uses: actions/setup-dotnet@v5.0.1 + uses: actions/setup-dotnet@v5.2.0 with: dotnet-version: '8.0.x' diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index 4894b334bb..9a3aea8409 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -268,7 +268,7 @@ public class CoreConfigContextBuilder { IndexId = $"inner-{Utils.GetGuid(false)}", ConfigType = EConfigType.ProxyChain, - CoreType = node.CoreType ?? ECoreType.Xray, + CoreType = AppManager.Instance.GetCoreType(node, node.ConfigType), }; List childItems = [prevNode?.IndexId, node.IndexId, nextNode?.IndexId]; var chainExtraItem = chainNode.GetProtocolExtra() with diff --git a/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 474234e4f1..99f6566258 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -21,7 +21,7 @@ public static class CoreConfigHandler _ => await GenerateClientCustomConfig(node, fileName) }; } - else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + else if (context.RunCoreType == ECoreType.sing_box) { result = new CoreConfigSingboxService(context).GenerateClientConfigContent(); } @@ -128,12 +128,11 @@ public static class CoreConfigHandler public static async Task GenerateClientSpeedtestConfig(Config config, CoreConfigContext context, ServerTestItem testItem, string fileName) { var result = new RetResult(); - var node = context.Node; var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest); var port = Utils.GetFreePort(initPort + testItem.QueueNum); testItem.Port = port; - if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + if (context.RunCoreType == ECoreType.sing_box) { result = new CoreConfigSingboxService(context).GenerateClientSpeedtestConfig(port); } diff --git a/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs index a55be232ba..14a9ae50aa 100644 --- a/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayn/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -132,7 +132,7 @@ public class CoreManager return null; } - var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); + var coreType = context.RunCoreType; var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); return await RunProcess(coreInfo, fileName, true, false); } diff --git a/xray-core/.github/workflows/docker.yml b/xray-core/.github/workflows/docker.yml index 7601290f42..38fff2bef0 100644 --- a/xray-core/.github/workflows/docker.yml +++ b/xray-core/.github/workflows/docker.yml @@ -68,13 +68,13 @@ jobs: uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -82,7 +82,7 @@ jobs: - name: Build Docker image (main architectures) id: build_main_arches - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: .github/docker/Dockerfile @@ -97,7 +97,7 @@ jobs: - name: Build Docker image (additional architectures) id: build_additional_arches - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: .github/docker/Dockerfile.usa diff --git a/xray-core/app/dispatcher/default.go b/xray-core/app/dispatcher/default.go index 03722e8a89..e6f89657e2 100644 --- a/xray-core/app/dispatcher/default.go +++ b/xray-core/app/dispatcher/default.go @@ -183,12 +183,9 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran if p.Stats.UserOnline { name := "user>>>" + user.Email + ">>>online" if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil { - sessionInbounds := session.InboundFromContext(ctx) - userIP := sessionInbounds.Source.Address.String() + userIP := sessionInbound.Source.Address.String() om.AddIP(userIP) - // log Online user with ips - // errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List()) - + context.AfterFunc(ctx, func() { om.RemoveIP(userIP) }) } } } @@ -225,11 +222,9 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st if p.Stats.UserOnline { name := "user>>>" + user.Email + ">>>online" if om, _ := stats.GetOrRegisterOnlineMap(statsManager, name); om != nil { - sessionInbounds := session.InboundFromContext(ctx) - userIP := sessionInbounds.Source.Address.String() + userIP := sessionInbound.Source.Address.String() om.AddIP(userIP) - // log Online user with ips - // errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List()) + context.AfterFunc(ctx, func() { om.RemoveIP(userIP) }) } } } diff --git a/xray-core/app/router/config.go b/xray-core/app/router/config.go index 4288f2af30..4acbaf4187 100644 --- a/xray-core/app/router/config.go +++ b/xray-core/app/router/config.go @@ -18,6 +18,7 @@ type Rule struct { RuleTag string Balancer *Balancer Condition Condition + Webhook *WebhookNotifier } func (r *Rule) GetTag() (string, error) { diff --git a/xray-core/app/router/config.pb.go b/xray-core/app/router/config.pb.go index 4ee7165b83..4067602440 100644 --- a/xray-core/app/router/config.pb.go +++ b/xray-core/app/router/config.pb.go @@ -129,7 +129,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use Config_DomainStrategy.Descriptor instead. func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{10, 0} + return file_app_router_config_proto_rawDescGZIP(), []int{11, 0} } // Domain for routing decision. @@ -483,6 +483,7 @@ type RoutingRule struct { LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"` VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"` Process []string `protobuf:"bytes,21,rep,name=process,proto3" json:"process,omitempty"` + Webhook *WebhookConfig `protobuf:"bytes,22,opt,name=webhook,proto3" json:"webhook,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -647,6 +648,13 @@ func (x *RoutingRule) GetProcess() []string { return nil } +func (x *RoutingRule) GetWebhook() *WebhookConfig { + if x != nil { + return x.Webhook + } + return nil +} + type isRoutingRule_TargetTag interface { isRoutingRule_TargetTag() } @@ -665,6 +673,66 @@ func (*RoutingRule_Tag) isRoutingRule_TargetTag() {} func (*RoutingRule_BalancingTag) isRoutingRule_TargetTag() {} +type WebhookConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + Deduplication uint32 `protobuf:"varint,2,opt,name=deduplication,proto3" json:"deduplication,omitempty"` + Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WebhookConfig) Reset() { + *x = WebhookConfig{} + mi := &file_app_router_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WebhookConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WebhookConfig) ProtoMessage() {} + +func (x *WebhookConfig) ProtoReflect() protoreflect.Message { + mi := &file_app_router_config_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WebhookConfig.ProtoReflect.Descriptor instead. +func (*WebhookConfig) Descriptor() ([]byte, []int) { + return file_app_router_config_proto_rawDescGZIP(), []int{7} +} + +func (x *WebhookConfig) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *WebhookConfig) GetDeduplication() uint32 { + if x != nil { + return x.Deduplication + } + return 0 +} + +func (x *WebhookConfig) GetHeaders() map[string]string { + if x != nil { + return x.Headers + } + return nil +} + type BalancingRule struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` @@ -678,7 +746,7 @@ type BalancingRule struct { func (x *BalancingRule) Reset() { *x = BalancingRule{} - mi := &file_app_router_config_proto_msgTypes[7] + mi := &file_app_router_config_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -690,7 +758,7 @@ func (x *BalancingRule) String() string { func (*BalancingRule) ProtoMessage() {} func (x *BalancingRule) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[7] + mi := &file_app_router_config_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -703,7 +771,7 @@ func (x *BalancingRule) ProtoReflect() protoreflect.Message { // Deprecated: Use BalancingRule.ProtoReflect.Descriptor instead. func (*BalancingRule) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{7} + return file_app_router_config_proto_rawDescGZIP(), []int{8} } func (x *BalancingRule) GetTag() string { @@ -752,7 +820,7 @@ type StrategyWeight struct { func (x *StrategyWeight) Reset() { *x = StrategyWeight{} - mi := &file_app_router_config_proto_msgTypes[8] + mi := &file_app_router_config_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -764,7 +832,7 @@ func (x *StrategyWeight) String() string { func (*StrategyWeight) ProtoMessage() {} func (x *StrategyWeight) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[8] + mi := &file_app_router_config_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -777,7 +845,7 @@ func (x *StrategyWeight) ProtoReflect() protoreflect.Message { // Deprecated: Use StrategyWeight.ProtoReflect.Descriptor instead. func (*StrategyWeight) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{8} + return file_app_router_config_proto_rawDescGZIP(), []int{9} } func (x *StrategyWeight) GetRegexp() bool { @@ -819,7 +887,7 @@ type StrategyLeastLoadConfig struct { func (x *StrategyLeastLoadConfig) Reset() { *x = StrategyLeastLoadConfig{} - mi := &file_app_router_config_proto_msgTypes[9] + mi := &file_app_router_config_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -831,7 +899,7 @@ func (x *StrategyLeastLoadConfig) String() string { func (*StrategyLeastLoadConfig) ProtoMessage() {} func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[9] + mi := &file_app_router_config_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -844,7 +912,7 @@ func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use StrategyLeastLoadConfig.ProtoReflect.Descriptor instead. func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{9} + return file_app_router_config_proto_rawDescGZIP(), []int{10} } func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight { @@ -893,7 +961,7 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_app_router_config_proto_msgTypes[10] + mi := &file_app_router_config_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -905,7 +973,7 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[10] + mi := &file_app_router_config_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -918,7 +986,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_app_router_config_proto_rawDescGZIP(), []int{10} + return file_app_router_config_proto_rawDescGZIP(), []int{11} } func (x *Config) GetDomainStrategy() Config_DomainStrategy { @@ -956,7 +1024,7 @@ type Domain_Attribute struct { func (x *Domain_Attribute) Reset() { *x = Domain_Attribute{} - mi := &file_app_router_config_proto_msgTypes[11] + mi := &file_app_router_config_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -968,7 +1036,7 @@ func (x *Domain_Attribute) String() string { func (*Domain_Attribute) ProtoMessage() {} func (x *Domain_Attribute) ProtoReflect() protoreflect.Message { - mi := &file_app_router_config_proto_msgTypes[11] + mi := &file_app_router_config_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1066,7 +1134,7 @@ const file_app_router_config_proto_rawDesc = "" + "\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12/\n" + "\x06domain\x18\x02 \x03(\v2\x17.xray.app.router.DomainR\x06domain\"=\n" + "\vGeoSiteList\x12.\n" + - "\x05entry\x18\x01 \x03(\v2\x18.xray.app.router.GeoSiteR\x05entry\"\x82\a\n" + + "\x05entry\x18\x01 \x03(\v2\x18.xray.app.router.GeoSiteR\x05entry\"\xbc\a\n" + "\vRoutingRule\x12\x12\n" + "\x03tag\x18\x01 \x01(\tH\x00R\x03tag\x12%\n" + "\rbalancing_tag\x18\f \x01(\tH\x00R\fbalancingTag\x12\x19\n" + @@ -1090,12 +1158,20 @@ const file_app_router_config_proto_rawDesc = "" + "localGeoip\x12A\n" + "\x0flocal_port_list\x18\x12 \x01(\v2\x19.xray.common.net.PortListR\rlocalPortList\x12C\n" + "\x10vless_route_list\x18\x14 \x01(\v2\x19.xray.common.net.PortListR\x0evlessRouteList\x12\x18\n" + - "\aprocess\x18\x15 \x03(\tR\aprocess\x1a=\n" + + "\aprocess\x18\x15 \x03(\tR\aprocess\x128\n" + + "\awebhook\x18\x16 \x01(\v2\x1e.xray.app.router.WebhookConfigR\awebhook\x1a=\n" + "\x0fAttributesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\f\n" + "\n" + - "target_tag\"\xdc\x01\n" + + "target_tag\"\xca\x01\n" + + "\rWebhookConfig\x12\x10\n" + + "\x03url\x18\x01 \x01(\tR\x03url\x12$\n" + + "\rdeduplication\x18\x02 \x01(\rR\rdeduplication\x12E\n" + + "\aheaders\x18\x03 \x03(\v2+.xray.app.router.WebhookConfig.HeadersEntryR\aheaders\x1a:\n" + + "\fHeadersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xdc\x01\n" + "\rBalancingRule\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12+\n" + "\x11outbound_selector\x18\x02 \x03(\tR\x10outboundSelector\x12\x1a\n" + @@ -1136,7 +1212,7 @@ func file_app_router_config_proto_rawDescGZIP() []byte { } var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_app_router_config_proto_goTypes = []any{ (Domain_Type)(0), // 0: xray.app.router.Domain.Type (Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy @@ -1147,43 +1223,47 @@ var file_app_router_config_proto_goTypes = []any{ (*GeoSite)(nil), // 6: xray.app.router.GeoSite (*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList (*RoutingRule)(nil), // 8: xray.app.router.RoutingRule - (*BalancingRule)(nil), // 9: xray.app.router.BalancingRule - (*StrategyWeight)(nil), // 10: xray.app.router.StrategyWeight - (*StrategyLeastLoadConfig)(nil), // 11: xray.app.router.StrategyLeastLoadConfig - (*Config)(nil), // 12: xray.app.router.Config - (*Domain_Attribute)(nil), // 13: xray.app.router.Domain.Attribute - nil, // 14: xray.app.router.RoutingRule.AttributesEntry - (*net.PortList)(nil), // 15: xray.common.net.PortList - (net.Network)(0), // 16: xray.common.net.Network - (*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage + (*WebhookConfig)(nil), // 9: xray.app.router.WebhookConfig + (*BalancingRule)(nil), // 10: xray.app.router.BalancingRule + (*StrategyWeight)(nil), // 11: xray.app.router.StrategyWeight + (*StrategyLeastLoadConfig)(nil), // 12: xray.app.router.StrategyLeastLoadConfig + (*Config)(nil), // 13: xray.app.router.Config + (*Domain_Attribute)(nil), // 14: xray.app.router.Domain.Attribute + nil, // 15: xray.app.router.RoutingRule.AttributesEntry + nil, // 16: xray.app.router.WebhookConfig.HeadersEntry + (*net.PortList)(nil), // 17: xray.common.net.PortList + (net.Network)(0), // 18: xray.common.net.Network + (*serial.TypedMessage)(nil), // 19: xray.common.serial.TypedMessage } var file_app_router_config_proto_depIdxs = []int32{ 0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type - 13, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute + 14, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute 3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR 4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP 2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain 6, // 5: xray.app.router.GeoSiteList.entry:type_name -> xray.app.router.GeoSite 2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain 4, // 7: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP - 15, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList - 16, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network + 17, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList + 18, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network 4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP - 15, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList - 14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry + 17, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList + 15, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry 4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP - 15, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList - 15, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList - 17, // 16: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage - 10, // 17: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight - 1, // 18: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy - 8, // 19: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule - 9, // 20: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule - 21, // [21:21] is the sub-list for method output_type - 21, // [21:21] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 17, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList + 17, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList + 9, // 16: xray.app.router.RoutingRule.webhook:type_name -> xray.app.router.WebhookConfig + 16, // 17: xray.app.router.WebhookConfig.headers:type_name -> xray.app.router.WebhookConfig.HeadersEntry + 19, // 18: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage + 11, // 19: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight + 1, // 20: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy + 8, // 21: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule + 10, // 22: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name } func init() { file_app_router_config_proto_init() } @@ -1195,7 +1275,7 @@ func file_app_router_config_proto_init() { (*RoutingRule_Tag)(nil), (*RoutingRule_BalancingTag)(nil), } - file_app_router_config_proto_msgTypes[11].OneofWrappers = []any{ + file_app_router_config_proto_msgTypes[12].OneofWrappers = []any{ (*Domain_Attribute_BoolValue)(nil), (*Domain_Attribute_IntValue)(nil), } @@ -1205,7 +1285,7 @@ func file_app_router_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_config_proto_rawDesc), len(file_app_router_config_proto_rawDesc)), NumEnums: 2, - NumMessages: 13, + NumMessages: 15, NumExtensions: 0, NumServices: 0, }, diff --git a/xray-core/app/router/config.proto b/xray-core/app/router/config.proto index 20da23ba6d..07fe4c515f 100644 --- a/xray-core/app/router/config.proto +++ b/xray-core/app/router/config.proto @@ -114,6 +114,13 @@ message RoutingRule { xray.common.net.PortList vless_route_list = 20; repeated string process = 21; + WebhookConfig webhook = 22; +} + +message WebhookConfig { + string url = 1; + uint32 deduplication = 2; + map headers = 3; } message BalancingRule { diff --git a/xray-core/app/router/router.go b/xray-core/app/router/router.go index 790bf8e23c..df770a3a4d 100644 --- a/xray-core/app/router/router.go +++ b/xray-core/app/router/router.go @@ -57,6 +57,7 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out for _, rule := range config.Rule { cond, err := rule.BuildCondition() if err != nil { + r.closeWebhooks() return err } rr := &Rule{ @@ -64,10 +65,22 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out Tag: rule.GetTag(), RuleTag: rule.GetRuleTag(), } + if wh := rule.GetWebhook(); wh != nil { + notifier, err := NewWebhookNotifier(wh) + if err != nil { + r.closeWebhooks() + return err + } + rr.Webhook = notifier + } btag := rule.GetBalancingTag() if len(btag) > 0 { brule, found := r.balancers[btag] if !found { + if rr.Webhook != nil { + rr.Webhook.Close() + } + r.closeWebhooks() return errors.New("balancer ", btag, " not found") } rr.Balancer = brule @@ -80,6 +93,7 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out // PickRoute implements routing.Router. func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) { + originalCtx := ctx rule, ctx, err := r.pickRouteInternal(ctx) if err != nil { return nil, err @@ -88,6 +102,9 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) { if err != nil { return nil, err } + if rule.Webhook != nil { + rule.Webhook.Fire(originalCtx, tag) + } return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil } @@ -109,6 +126,11 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error { defer r.mu.Unlock() if !shouldAppend { + for _, rule := range r.rules { + if rule.Webhook != nil { + rule.Webhook.Close() + } + } r.balancers = make(map[string]*Balancer, len(config.BalancingRule)) r.rules = make([]*Rule, 0, len(config.Rule)) } @@ -125,12 +147,24 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error { r.balancers[rule.Tag] = balancer } + startIdx := len(r.rules) + closeNewWebhooks := func() { + for i := startIdx; i < len(r.rules); i++ { + if r.rules[i].Webhook != nil { + r.rules[i].Webhook.Close() + } + } + r.rules = r.rules[:startIdx] + } + for _, rule := range config.Rule { if r.RuleExists(rule.GetRuleTag()) { + closeNewWebhooks() return errors.New("duplicate ruleTag ", rule.GetRuleTag()) } cond, err := rule.BuildCondition() if err != nil { + closeNewWebhooks() return err } rr := &Rule{ @@ -138,10 +172,22 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error { Tag: rule.GetTag(), RuleTag: rule.GetRuleTag(), } + if wh := rule.GetWebhook(); wh != nil { + notifier, err := NewWebhookNotifier(wh) + if err != nil { + closeNewWebhooks() + return err + } + rr.Webhook = notifier + } btag := rule.GetBalancingTag() if len(btag) > 0 { brule, found := r.balancers[btag] if !found { + if rr.Webhook != nil { + rr.Webhook.Close() + } + closeNewWebhooks() return errors.New("balancer ", btag, " not found") } rr.Balancer = brule @@ -173,6 +219,8 @@ func (r *Router) RemoveRule(tag string) error { for _, rule := range r.rules { if rule.RuleTag != tag { newRules = append(newRules, rule) + } else if rule.Webhook != nil { + rule.Webhook.Close() } } r.rules = newRules @@ -233,8 +281,20 @@ func (r *Router) Start() error { return nil } +// closeWebhooks closes all webhook notifiers in the current rule set. +func (r *Router) closeWebhooks() { + for _, rule := range r.rules { + if rule.Webhook != nil { + rule.Webhook.Close() + } + } +} + // Close implements common.Closable. func (r *Router) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + r.closeWebhooks() return nil } diff --git a/xray-core/app/router/webhook.go b/xray-core/app/router/webhook.go new file mode 100644 index 0000000000..32ae28872f --- /dev/null +++ b/xray-core/app/router/webhook.go @@ -0,0 +1,287 @@ +package router + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net" + "net/http" + "path/filepath" + "runtime" + "strings" + "sync" + "syscall" + "time" + + "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/features/routing" + routing_session "github.com/xtls/xray-core/features/routing/session" +) + +// parseURL splits a webhook URL into an HTTP URL and an optional Unix socket +// path. For regular http/https URLs the input is returned unchanged with an +// empty socketPath. For Unix sockets the format is: +// +// /path/to/socket.sock:/http/path +// @abstract:/http/path +// @@padded:/http/path +// +// The :/ separator after the socket path delimits the HTTP request path. +// If omitted, "/" is used. +func parseURL(raw string) (httpURL, socketPath string) { + if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') { + return raw, "" + } + if idx := strings.Index(raw, ":/"); idx >= 0 { + return "http://localhost" + raw[idx+1:], raw[:idx] + } + return "http://localhost/", raw +} + +// resolveSocketPath applies platform-specific transformations to a Unix +// socket path, matching the behaviour of the listen side in +// transport/internet/system_listener.go. +// +// For abstract sockets (prefix @) on Linux/Android: +// - single @ — used as-is (lock-free abstract socket) +// - double @@ — stripped to single @ and padded to +// syscall.RawSockaddrUnix{}.Path length (HAProxy compat) +func resolveSocketPath(path string) string { + if len(path) == 0 || path[0] != '@' { + return path + } + if runtime.GOOS != "linux" && runtime.GOOS != "android" { + return path + } + if len(path) > 1 && path[1] == '@' { + fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) + copy(fullAddr, path[1:]) + return string(fullAddr) + } + return path +} + +func ptr[T any](v T) *T { return &v } + +type event struct { + Email *string `json:"email"` + Level *uint32 `json:"level"` + Protocol *string `json:"protocol"` + Network *string `json:"network"` + Source *string `json:"source"` + Destination *string `json:"destination"` + OriginalTarget *string `json:"originalTarget"` + RouteTarget *string `json:"routeTarget"` + InboundTag *string `json:"inboundTag"` + InboundName *string `json:"inboundName"` + InboundLocal *string `json:"inboundLocal"` + OutboundTag *string `json:"outboundTag"` + Timestamp int64 `json:"ts"` +} + +type WebhookNotifier struct { + url string + headers map[string]string + deduplication uint32 + client *http.Client + seen sync.Map + done chan struct{} + wg sync.WaitGroup + closeOnce sync.Once +} + +func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) { + if cfg == nil || cfg.Url == "" { + return nil, nil + } + + httpURL, socketPath := parseURL(cfg.Url) + h := &WebhookNotifier{ + url: httpURL, + deduplication: cfg.Deduplication, + client: &http.Client{ + Timeout: 5 * time.Second, + }, + done: make(chan struct{}), + } + + if socketPath != "" { + dialAddr := resolveSocketPath(socketPath) + h.client.Transport = &http.Transport{ + DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, "unix", dialAddr) + }, + } + } + + if len(cfg.Headers) > 0 { + h.headers = make(map[string]string, len(cfg.Headers)) + for k, v := range cfg.Headers { + h.headers[k] = v + } + } + + if h.deduplication > 0 { + h.wg.Add(1) + go h.cleanupLoop() + } + + return h, nil +} + +func (h *WebhookNotifier) Fire(ctx routing.Context, outboundTag string) { + ev := buildEvent(ctx, outboundTag) + + email := "" + if ev.Email != nil { + email = *ev.Email + } + if h.isDuplicate(email) { + return + } + + h.wg.Add(1) + select { + case <-h.done: + h.wg.Done() + return + default: + } + go func() { + defer h.wg.Done() + h.post(ev) + }() +} + +func buildEvent(ctx routing.Context, outboundTag string) *event { + ev := &event{ + Timestamp: time.Now().Unix(), + OutboundTag: ptr(outboundTag), + InboundTag: ptr(ctx.GetInboundTag()), + Protocol: ptr(ctx.GetProtocol()), + Network: ptr(ctx.GetNetwork().SystemString()), + } + + if user := ctx.GetUser(); user != "" { + ev.Email = ptr(user) + } + + if srcIPs := ctx.GetSourceIPs(); len(srcIPs) > 0 { + srcPort := ctx.GetSourcePort() + ev.Source = ptr(net.JoinHostPort(srcIPs[0].String(), srcPort.String())) + } + + targetPort := ctx.GetTargetPort() + if domain := ctx.GetTargetDomain(); domain != "" { + ev.Destination = ptr(net.JoinHostPort(domain, targetPort.String())) + } else if targetIPs := ctx.GetTargetIPs(); len(targetIPs) > 0 { + ev.Destination = ptr(net.JoinHostPort(targetIPs[0].String(), targetPort.String())) + } + + if localIPs := ctx.GetLocalIPs(); len(localIPs) > 0 { + localPort := ctx.GetLocalPort() + ev.InboundLocal = ptr(net.JoinHostPort(localIPs[0].String(), localPort.String())) + } + + if sctx, ok := ctx.(*routing_session.Context); ok { + enrichFromSession(ev, sctx) + } + + return ev +} + +func enrichFromSession(ev *event, sctx *routing_session.Context) { + if sctx.Inbound != nil { + ev.InboundName = ptr(sctx.Inbound.Name) + if sctx.Inbound.User != nil { + ev.Level = ptr(sctx.Inbound.User.Level) + } + } + if sctx.Outbound != nil { + if sctx.Outbound.OriginalTarget.Address != nil { + ev.OriginalTarget = ptr(sctx.Outbound.OriginalTarget.String()) + } + if sctx.Outbound.RouteTarget.Address != nil { + ev.RouteTarget = ptr(sctx.Outbound.RouteTarget.String()) + } + } +} + +func (h *WebhookNotifier) post(ev *event) { + body, err := json.Marshal(ev) + if err != nil { + errors.LogWarning(context.Background(), "webhook: marshal failed: ", err) + return + } + + req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, h.url, bytes.NewReader(body)) + if err != nil { + errors.LogWarning(context.Background(), "webhook: request build failed: ", err) + return + } + req.Header.Set("Content-Type", "application/json") + + for k, v := range h.headers { + req.Header.Set(k, v) + } + + resp, err := h.client.Do(req) + if err != nil { + errors.LogInfo(context.Background(), "webhook: POST failed: ", err) + return + } + defer func() { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + }() + if resp.StatusCode >= 400 { + errors.LogWarning(context.Background(), "webhook: POST returned status ", resp.StatusCode) + } +} + +func (h *WebhookNotifier) isDuplicate(email string) bool { + if h.deduplication == 0 || email == "" { + return false + } + ttl := time.Duration(h.deduplication) * time.Second + now := time.Now() + if v, loaded := h.seen.LoadOrStore(email, now); loaded { + if now.Sub(v.(time.Time)) < ttl { + return true + } + h.seen.Store(email, now) + } + return false +} + +func (h *WebhookNotifier) cleanupLoop() { + defer h.wg.Done() + ttl := time.Duration(h.deduplication) * time.Second + ticker := time.NewTicker(ttl) + defer ticker.Stop() + for { + select { + case <-h.done: + return + case <-ticker.C: + now := time.Now() + h.seen.Range(func(key, value any) bool { + if now.Sub(value.(time.Time)) >= ttl { + h.seen.Delete(key) + } + return true + }) + } + } +} + +func (h *WebhookNotifier) Close() error { + h.closeOnce.Do(func() { + close(h.done) + }) + h.wg.Wait() + h.client.CloseIdleConnections() + return nil +} diff --git a/xray-core/app/stats/command/command.go b/xray-core/app/stats/command/command.go index a4e113741d..079df1ecae 100644 --- a/xray-core/app/stats/command/command.go +++ b/xray-core/app/stats/command/command.go @@ -70,7 +70,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat } ips := make(map[string]int64) - for ip, t := range c.IpTimeMap() { + for ip, t := range c.IPTimeMap() { ips[ip] = t.Unix() } diff --git a/xray-core/app/stats/online_map.go b/xray-core/app/stats/online_map.go index a7933fdb9c..cba625983c 100644 --- a/xray-core/app/stats/online_map.go +++ b/xray-core/app/stats/online_map.go @@ -2,84 +2,98 @@ package stats import ( "sync" + "sync/atomic" "time" ) -// OnlineMap is an implementation of stats.OnlineMap. -type OnlineMap struct { - ipList map[string]time.Time - access sync.RWMutex - lastCleanup time.Time - cleanupPeriod time.Duration +const ( + localhostIPv4 = "127.0.0.1" + localhostIPv6 = "[::1]" +) + +type ipEntry struct { + refCount int + lastSeen time.Time } -// NewOnlineMap creates a new instance of OnlineMap. +// OnlineMap is a refcount-based implementation of stats.OnlineMap. +// IPs are tracked by reference counting: AddIP increments, RemoveIP decrements. +// An IP is removed from the map when its reference count reaches zero. +type OnlineMap struct { + entries map[string]*ipEntry + access sync.Mutex + count atomic.Int64 +} + +// NewOnlineMap creates a new OnlineMap instance. func NewOnlineMap() *OnlineMap { return &OnlineMap{ - ipList: make(map[string]time.Time), - lastCleanup: time.Now(), - cleanupPeriod: 10 * time.Second, + entries: make(map[string]*ipEntry), + } +} + +// AddIP implements stats.OnlineMap. +func (om *OnlineMap) AddIP(ip string) { + if ip == localhostIPv4 || ip == localhostIPv6 { + return + } + + om.access.Lock() + defer om.access.Unlock() + + if e, ok := om.entries[ip]; ok { + e.refCount++ + e.lastSeen = time.Now() + } else { + om.entries[ip] = &ipEntry{ + refCount: 1, + lastSeen: time.Now(), + } + om.count.Add(1) + } +} + +// RemoveIP implements stats.OnlineMap. +func (om *OnlineMap) RemoveIP(ip string) { + om.access.Lock() + defer om.access.Unlock() + + e, ok := om.entries[ip] + if !ok { + return + } + e.refCount-- + if e.refCount <= 0 { + delete(om.entries, ip) + om.count.Add(-1) } } // Count implements stats.OnlineMap. -func (c *OnlineMap) Count() int { - c.access.RLock() - defer c.access.RUnlock() - - return len(c.ipList) +func (om *OnlineMap) Count() int { + return int(om.count.Load()) } // List implements stats.OnlineMap. -func (c *OnlineMap) List() []string { - return c.GetKeys() -} +func (om *OnlineMap) List() []string { + om.access.Lock() + defer om.access.Unlock() -// AddIP implements stats.OnlineMap. -func (c *OnlineMap) AddIP(ip string) { - if ip == "127.0.0.1" { - return - } - - c.access.Lock() - c.ipList[ip] = time.Now() - c.access.Unlock() - - if time.Since(c.lastCleanup) > c.cleanupPeriod { - c.RemoveExpiredIPs() - c.lastCleanup = time.Now() - } -} - -func (c *OnlineMap) GetKeys() []string { - c.access.RLock() - defer c.access.RUnlock() - - keys := []string{} - for k := range c.ipList { - keys = append(keys, k) + keys := make([]string, 0, len(om.entries)) + for ip := range om.entries { + keys = append(keys, ip) } return keys } -func (c *OnlineMap) RemoveExpiredIPs() { - c.access.Lock() - defer c.access.Unlock() +// IPTimeMap implements stats.OnlineMap. +func (om *OnlineMap) IPTimeMap() map[string]time.Time { + om.access.Lock() + defer om.access.Unlock() - now := time.Now() - for k, t := range c.ipList { - diff := now.Sub(t) - if diff.Seconds() > 20 { - delete(c.ipList, k) - } + result := make(map[string]time.Time, len(om.entries)) + for ip, e := range om.entries { + result[ip] = e.lastSeen } -} - -func (c *OnlineMap) IpTimeMap() map[string]time.Time { - if time.Since(c.lastCleanup) > c.cleanupPeriod { - c.RemoveExpiredIPs() - c.lastCleanup = time.Now() - } - - return c.ipList + return result } diff --git a/xray-core/app/stats/stats.go b/xray-core/app/stats/stats.go index cc988029e7..fc12fa23a0 100644 --- a/xray-core/app/stats/stats.go +++ b/xray-core/app/stats/stats.go @@ -163,12 +163,12 @@ func (m *Manager) GetChannel(name string) stats.Channel { // GetAllOnlineUsers implements stats.Manager. func (m *Manager) GetAllOnlineUsers() []string { - m.access.Lock() - defer m.access.Unlock() + m.access.RLock() + defer m.access.RUnlock() usersOnline := make([]string, 0, len(m.onlineMap)) for user, onlineMap := range m.onlineMap { - if len(onlineMap.IpTimeMap()) > 0 { + if onlineMap.Count() > 0 { usersOnline = append(usersOnline, user) } } diff --git a/xray-core/common/net/cnc/connection.go b/xray-core/common/net/cnc/connection.go index bdae540967..519918fd47 100644 --- a/xray-core/common/net/cnc/connection.go +++ b/xray-core/common/net/cnc/connection.go @@ -10,46 +10,46 @@ import ( "github.com/xtls/xray-core/common/signal/done" ) -type ConnectionOption func(*connection) +type ConnectionOption func(*Connection) func ConnectionLocalAddr(a net.Addr) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.local = a } } func ConnectionRemoteAddr(a net.Addr) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.remote = a } } func ConnectionInput(writer io.Writer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.writer = buf.NewWriter(writer) } } func ConnectionInputMulti(writer buf.Writer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.writer = writer } } func ConnectionOutput(reader io.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)} } } func ConnectionOutputMulti(reader buf.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{Reader: reader} } } func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{ Reader: reader, Splitter: buf.SplitFirstBytes, @@ -58,13 +58,13 @@ func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { } func ConnectionOnClose(n io.Closer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.onClose = n } } func NewConnection(opts ...ConnectionOption) net.Conn { - c := &connection{ + c := &Connection{ done: done.New(), local: &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, @@ -83,7 +83,7 @@ func NewConnection(opts ...ConnectionOption) net.Conn { return c } -type connection struct { +type Connection struct { reader *buf.BufferedReader writer buf.Writer done *done.Instance @@ -92,17 +92,17 @@ type connection struct { remote net.Addr } -func (c *connection) Read(b []byte) (int, error) { +func (c *Connection) Read(b []byte) (int, error) { return c.reader.Read(b) } // ReadMultiBuffer implements buf.Reader. -func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) { +func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) { return c.reader.ReadMultiBuffer() } // Write implements net.Conn.Write(). -func (c *connection) Write(b []byte) (int, error) { +func (c *Connection) Write(b []byte) (int, error) { if c.done.Done() { return 0, io.ErrClosedPipe } @@ -113,7 +113,7 @@ func (c *connection) Write(b []byte) (int, error) { return l, c.writer.WriteMultiBuffer(mb) } -func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { +func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { if c.done.Done() { buf.ReleaseMulti(mb) return io.ErrClosedPipe @@ -123,7 +123,7 @@ func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { } // Close implements net.Conn.Close(). -func (c *connection) Close() error { +func (c *Connection) Close() error { common.Must(c.done.Close()) common.Interrupt(c.reader) common.Close(c.writer) @@ -135,26 +135,26 @@ func (c *connection) Close() error { } // LocalAddr implements net.Conn.LocalAddr(). -func (c *connection) LocalAddr() net.Addr { +func (c *Connection) LocalAddr() net.Addr { return c.local } // RemoteAddr implements net.Conn.RemoteAddr(). -func (c *connection) RemoteAddr() net.Addr { +func (c *Connection) RemoteAddr() net.Addr { return c.remote } // SetDeadline implements net.Conn.SetDeadline(). -func (c *connection) SetDeadline(t time.Time) error { +func (c *Connection) SetDeadline(t time.Time) error { return nil } // SetReadDeadline implements net.Conn.SetReadDeadline(). -func (c *connection) SetReadDeadline(t time.Time) error { +func (c *Connection) SetReadDeadline(t time.Time) error { return nil } // SetWriteDeadline implements net.Conn.SetWriteDeadline(). -func (c *connection) SetWriteDeadline(t time.Time) error { +func (c *Connection) SetWriteDeadline(t time.Time) error { return nil } diff --git a/xray-core/features/stats/stats.go b/xray-core/features/stats/stats.go index 0ed910e38f..abea7459f7 100644 --- a/xray-core/features/stats/stats.go +++ b/xray-core/features/stats/stats.go @@ -25,14 +25,16 @@ type Counter interface { // // xray:api:stable type OnlineMap interface { - // Count is the current value of the OnlineMap. + // Count returns the number of unique online IPs. Count() int - // AddIP adds a ip to the current OnlineMap. + // AddIP increments the reference count for the given IP. AddIP(string) - // List is the current OnlineMap ip list. + // RemoveIP decrements the reference count for the given IP. Deletes at zero. + RemoveIP(string) + // List returns all currently online IPs. List() []string - // IpTimeMap return client ips and their last access time. - IpTimeMap() map[string]time.Time + // IPTimeMap returns a snapshot copy of IPs to their last-seen times. + IPTimeMap() map[string]time.Time } // Channel is the interface for stats channel. diff --git a/xray-core/infra/conf/freedom.go b/xray-core/infra/conf/freedom.go index 65930a2e8a..82d2c9a8bc 100644 --- a/xray-core/infra/conf/freedom.go +++ b/xray-core/infra/conf/freedom.go @@ -10,8 +10,8 @@ import ( v2net "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/proxy/freedom" - "google.golang.org/protobuf/proto" "github.com/xtls/xray-core/transport/internet" + "google.golang.org/protobuf/proto" ) type FreedomConfig struct { diff --git a/xray-core/infra/conf/router.go b/xray-core/infra/conf/router.go index bc32461088..a488d3972a 100644 --- a/xray-core/infra/conf/router.go +++ b/xray-core/infra/conf/router.go @@ -522,25 +522,32 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) { return geoipList, nil } +type WebhookRuleConfig struct { + URL string `json:"url"` + Deduplication uint32 `json:"deduplication"` + Headers map[string]string `json:"headers"` +} + func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { type RawFieldRule struct { RouterRule - Domain *StringList `json:"domain"` - Domains *StringList `json:"domains"` - IP *StringList `json:"ip"` - Port *PortList `json:"port"` - Network *NetworkList `json:"network"` - SourceIP *StringList `json:"sourceIP"` - Source *StringList `json:"source"` - SourcePort *PortList `json:"sourcePort"` - User *StringList `json:"user"` - VlessRoute *PortList `json:"vlessRoute"` - InboundTag *StringList `json:"inboundTag"` - Protocols *StringList `json:"protocol"` - Attributes map[string]string `json:"attrs"` - LocalIP *StringList `json:"localIP"` - LocalPort *PortList `json:"localPort"` - Process *StringList `json:"process"` + Domain *StringList `json:"domain"` + Domains *StringList `json:"domains"` + IP *StringList `json:"ip"` + Port *PortList `json:"port"` + Network *NetworkList `json:"network"` + SourceIP *StringList `json:"sourceIP"` + Source *StringList `json:"source"` + SourcePort *PortList `json:"sourcePort"` + User *StringList `json:"user"` + VlessRoute *PortList `json:"vlessRoute"` + InboundTag *StringList `json:"inboundTag"` + Protocols *StringList `json:"protocol"` + Attributes map[string]string `json:"attrs"` + LocalIP *StringList `json:"localIP"` + LocalPort *PortList `json:"localPort"` + Process *StringList `json:"process"` + Webhook *WebhookRuleConfig `json:"webhook"` } rawFieldRule := new(RawFieldRule) err := json.Unmarshal(msg, rawFieldRule) @@ -657,6 +664,14 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { rule.Process = *rawFieldRule.Process } + if rawFieldRule.Webhook != nil && rawFieldRule.Webhook.URL != "" { + rule.Webhook = &router.WebhookConfig{ + Url: rawFieldRule.Webhook.URL, + Deduplication: rawFieldRule.Webhook.Deduplication, + Headers: rawFieldRule.Webhook.Headers, + } + } + return rule, nil } diff --git a/xray-core/infra/conf/transport_internet.go b/xray-core/infra/conf/transport_internet.go index b4458096b4..bf6d7a43c7 100644 --- a/xray-core/infra/conf/transport_internet.go +++ b/xray-core/infra/conf/transport_internet.go @@ -18,6 +18,8 @@ import ( "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask/fragment" + "github.com/xtls/xray-core/transport/internet/finalmask/header/custom" "github.com/xtls/xray-core/transport/internet/finalmask/header/dns" "github.com/xtls/xray-core/transport/internet/finalmask/header/dtls" "github.com/xtls/xray-core/transport/internet/finalmask/header/srtp" @@ -26,7 +28,9 @@ import ( "github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard" "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm" "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original" + "github.com/xtls/xray-core/transport/internet/finalmask/noise" "github.com/xtls/xray-core/transport/internet/finalmask/salamander" + finalsudoku "github.com/xtls/xray-core/transport/internet/finalmask/sudoku" "github.com/xtls/xray-core/transport/internet/finalmask/xdns" "github.com/xtls/xray-core/transport/internet/finalmask/xicmp" "github.com/xtls/xray-core/transport/internet/httpupgrade" @@ -72,7 +76,7 @@ func (c *KCPConfig) Build() (proto.Message, error) { } if c.Tti != nil { tti := *c.Tti - if tti < 10 || tti > 100 { + if tti < 10 || tti > 5000 { return nil, errors.New("invalid mKCP TTI: ", tti).AtError() } config.Tti = &kcp.TTI{Value: tti} @@ -230,13 +234,14 @@ type SplitHTTPConfig struct { SeqKey string `json:"seqKey"` UplinkDataPlacement string `json:"uplinkDataPlacement"` UplinkDataKey string `json:"uplinkDataKey"` - UplinkChunkSize uint32 `json:"uplinkChunkSize"` + UplinkChunkSize Int32Range `json:"uplinkChunkSize"` NoGRPCHeader bool `json:"noGRPCHeader"` NoSSEHeader bool `json:"noSSEHeader"` ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"` ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"` ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"` ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"` + ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"` Xmux XmuxConfig `json:"xmux"` DownloadSettings *StreamConfig `json:"downloadSettings"` Extra json.RawMessage `json:"extra"` @@ -316,9 +321,9 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) { switch c.UplinkDataPlacement { case "": - c.UplinkDataPlacement = "body" - case "body": - case "cookie", "header": + c.UplinkDataPlacement = splithttp.PlacementAuto + case splithttp.PlacementAuto, splithttp.PlacementBody: + case splithttp.PlacementCookie, splithttp.PlacementHeader: if c.Mode != "packet-up" { return nil, errors.New("UplinkDataPlacement can be " + c.UplinkDataPlacement + " only in packet-up mode") } @@ -347,9 +352,6 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) { case "": c.SeqPlacement = "path" case "path", "cookie", "header", "query": - if c.SessionPlacement == "path" { - return nil, errors.New("SeqPlacement must be path when SessionPlacement is path") - } default: return nil, errors.New("unsupported seq placement: " + c.SeqPlacement) } @@ -372,24 +374,17 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) { } } - if c.UplinkDataPlacement != "body" && c.UplinkDataKey == "" { + if c.UplinkDataPlacement != splithttp.PlacementBody && c.UplinkDataKey == "" { switch c.UplinkDataPlacement { - case "cookie": + case splithttp.PlacementCookie: c.UplinkDataKey = "x_data" - case "header": + case splithttp.PlacementAuto, splithttp.PlacementHeader: c.UplinkDataKey = "X-Data" } } - if c.UplinkChunkSize == 0 { - switch c.UplinkDataPlacement { - case "cookie": - c.UplinkChunkSize = 3 * 1024 // 3KB - case "header": - c.UplinkChunkSize = 4 * 1024 // 4KB - } - } else if c.UplinkChunkSize < 64 { - c.UplinkChunkSize = 64 + if c.ServerMaxHeaderBytes < 0 { + return nil, errors.New("invalid negative value of maxHeaderBytes") } if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 { @@ -422,13 +417,14 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) { SeqKey: c.SeqKey, UplinkDataPlacement: c.UplinkDataPlacement, UplinkDataKey: c.UplinkDataKey, - UplinkChunkSize: c.UplinkChunkSize, + UplinkChunkSize: newRangeConfig(c.UplinkChunkSize), NoGRPCHeader: c.NoGRPCHeader, NoSSEHeader: c.NoSSEHeader, ScMaxEachPostBytes: newRangeConfig(c.ScMaxEachPostBytes), ScMinPostsIntervalMs: newRangeConfig(c.ScMinPostsIntervalMs), ScMaxBufferedPosts: c.ScMaxBufferedPosts, ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs), + ServerMaxHeaderBytes: c.ServerMaxHeaderBytes, Xmux: &splithttp.XmuxConfig{ MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency), MaxConnections: newRangeConfig(c.Xmux.MaxConnections), @@ -725,6 +721,11 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) { return certificate, nil } +type QuicParamsConfig struct { + Congestion string `json:"congestion"` + Up Bandwidth `json:"up"` +} + type TLSConfig struct { AllowInsecure bool `json:"allowInsecure"` Certs []*TLSCertConfig `json:"certificates"` @@ -1276,8 +1277,49 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { }, nil } +func PraseByteSlice(data json.RawMessage, typ string) ([]byte, error) { + switch strings.ToLower(typ) { + case "", "array": + if len(data) == 0 { + return data, nil + } + var packet []byte + if err := json.Unmarshal(data, &packet); err != nil { + return nil, err + } + return packet, nil + case "str": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return []byte(str), nil + case "hex": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return hex.DecodeString(str) + case "base64": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return base64.StdEncoding.DecodeString(str) + default: + return nil, errors.New("unknown type") + } +} + var ( + tcpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ + "header-custom": func() interface{} { return new(HeaderCustomTCP) }, + "fragment": func() interface{} { return new(FragmentMask) }, + "sudoku": func() interface{} { return new(Sudoku) }, + }, "type", "settings") + udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ + "header-custom": func() interface{} { return new(HeaderCustomUDP) }, "header-dns": func() interface{} { return new(Dns) }, "header-dtls": func() interface{} { return new(Dtls) }, "header-srtp": func() interface{} { return new(Srtp) }, @@ -1286,12 +1328,246 @@ var ( "header-wireguard": func() interface{} { return new(Wireguard) }, "mkcp-original": func() interface{} { return new(Original) }, "mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) }, + "noise": func() interface{} { return new(NoiseMask) }, "salamander": func() interface{} { return new(Salamander) }, + "sudoku": func() interface{} { return new(Sudoku) }, "xdns": func() interface{} { return new(Xdns) }, "xicmp": func() interface{} { return new(Xicmp) }, }, "type", "settings") ) +type TCPItem struct { + Delay Int32Range `json:"delay"` + Rand int32 `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` +} + +type HeaderCustomTCP struct { + Clients [][]TCPItem `json:"clients"` + Servers [][]TCPItem `json:"servers"` + Errors [][]TCPItem `json:"errors"` +} + +func (c *HeaderCustomTCP) Build() (proto.Message, error) { + for _, value := range c.Clients { + for _, item := range value { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + } + for _, value := range c.Servers { + for _, item := range value { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + } + for _, value := range c.Errors { + for _, item := range value { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + } + + clients := make([]*custom.TCPSequence, len(c.Clients)) + for i, value := range c.Clients { + clients[i] = &custom.TCPSequence{} + for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } + clients[i].Sequence = append(clients[i].Sequence, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) + } + } + + servers := make([]*custom.TCPSequence, len(c.Servers)) + for i, value := range c.Servers { + servers[i] = &custom.TCPSequence{} + for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } + servers[i].Sequence = append(servers[i].Sequence, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) + } + } + + errors := make([]*custom.TCPSequence, len(c.Errors)) + for i, value := range c.Errors { + errors[i] = &custom.TCPSequence{} + for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } + errors[i].Sequence = append(errors[i].Sequence, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) + } + } + + return &custom.TCPConfig{ + Clients: clients, + Servers: servers, + Errors: errors, + }, nil +} + +type FragmentMask struct { + Packets string `json:"packets"` + Length Int32Range `json:"length"` + Delay Int32Range `json:"delay"` + MaxSplit Int32Range `json:"maxSplit"` +} + +func (c *FragmentMask) Build() (proto.Message, error) { + config := &fragment.Config{} + + switch strings.ToLower(c.Packets) { + case "tlshello": + config.PacketsFrom = 0 + config.PacketsTo = 1 + case "": + config.PacketsFrom = 0 + config.PacketsTo = 0 + default: + from, to, err := ParseRangeString(c.Packets) + if err != nil { + return nil, errors.New("Invalid PacketsFrom").Base(err) + } + config.PacketsFrom = int64(from) + config.PacketsTo = int64(to) + if config.PacketsFrom == 0 { + return nil, errors.New("PacketsFrom can't be 0") + } + } + + config.LengthMin = int64(c.Length.From) + config.LengthMax = int64(c.Length.To) + if config.LengthMin == 0 { + return nil, errors.New("LengthMin can't be 0") + } + + config.DelayMin = int64(c.Delay.From) + config.DelayMax = int64(c.Delay.To) + + config.MaxSplitMin = int64(c.MaxSplit.From) + config.MaxSplitMax = int64(c.MaxSplit.To) + + return config, nil +} + +type NoiseItem struct { + Rand Int32Range `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` + Delay Int32Range `json:"delay"` +} + +type NoiseMask struct { + Reset Int32Range `json:"reset"` + Noise []NoiseItem `json:"noise"` +} + +func (c *NoiseMask) Build() (proto.Message, error) { + for _, item := range c.Noise { + if len(item.Packet) > 0 && item.Rand.To > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand.To > 0") + } + } + + noiseSlice := make([]*noise.Item, 0, len(c.Noise)) + for _, item := range c.Noise { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } + noiseSlice = append(noiseSlice, &noise.Item{ + RandMin: int64(item.Rand.From), + RandMax: int64(item.Rand.To), + Packet: item.Packet, + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + }) + } + + return &noise.Config{ + ResetMin: int64(c.Reset.From), + ResetMax: int64(c.Reset.To), + Items: noiseSlice, + }, nil +} + +type UDPItem struct { + Rand int32 `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` +} + +type HeaderCustomUDP struct { + Client []UDPItem `json:"client"` + Server []UDPItem `json:"server"` +} + +func (c *HeaderCustomUDP) Build() (proto.Message, error) { + for _, item := range c.Client { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + for _, item := range c.Server { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + + client := make([]*custom.UDPItem, 0, len(c.Client)) + for _, item := range c.Client { + 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, + }) + } + + server := make([]*custom.UDPItem, 0, len(c.Server)) + for _, item := range c.Server { + 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, + }) + } + + return &custom.UDPConfig{ + Client: client, + Server: server, + }, nil +} + type Dns struct { Domain string `json:"domain"` } @@ -1363,6 +1639,50 @@ func (c *Salamander) Build() (proto.Message, error) { return config, nil } +type Sudoku struct { + Password string `json:"password"` + ASCII string `json:"ascii"` + + CustomTable string `json:"customTable"` + LegacyCustomTable string `json:"custom_table"` + CustomTables []string `json:"customTables"` + LegacyCustomSets []string `json:"custom_tables"` + + PaddingMin uint32 `json:"paddingMin"` + LegacyPaddingMin uint32 `json:"padding_min"` + PaddingMax uint32 `json:"paddingMax"` + LegacyPaddingMax uint32 `json:"padding_max"` +} + +func (c *Sudoku) Build() (proto.Message, error) { + customTable := c.CustomTable + if customTable == "" { + customTable = c.LegacyCustomTable + } + customTables := c.CustomTables + if len(customTables) == 0 { + customTables = c.LegacyCustomSets + } + + paddingMin := c.PaddingMin + if paddingMin == 0 { + paddingMin = c.LegacyPaddingMin + } + paddingMax := c.PaddingMax + if paddingMax == 0 { + paddingMax = c.LegacyPaddingMax + } + + return &finalsudoku.Config{ + Password: c.Password, + Ascii: c.ASCII, + CustomTable: customTable, + CustomTables: customTables, + PaddingMin: paddingMin, + PaddingMax: paddingMax, + }, nil +} + type Xdns struct { Domain string `json:"domain"` } @@ -1403,7 +1723,7 @@ type Mask struct { func (c *Mask) Build(tcp bool) (proto.Message, error) { loader := udpmaskLoader if tcp { - return nil, errors.New("") + loader = tcpmaskLoader } settings := []byte("{}") @@ -1422,8 +1742,9 @@ func (c *Mask) Build(tcp bool) (proto.Message, error) { } type FinalMask struct { - Tcp []Mask `json:"tcp"` - Udp []Mask `json:"udp"` + Tcp []Mask `json:"tcp"` + Udp []Mask `json:"udp"` + QuicParams *QuicParamsConfig `json:"quicParams"` } type StreamConfig struct { @@ -1596,6 +1917,28 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { } config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u)) } + if c.FinalMask.QuicParams != nil { + up, err := c.FinalMask.QuicParams.Up.Bps() + if err != nil { + return nil, err + } + if up > 0 && up < 65536 { + return nil, errors.New("Up must be at least 65536 bytes per second") + } + switch c.FinalMask.QuicParams.Congestion { + case "", "bbr", "reno": + case "force-brutal": + if up == 0 { + return nil, errors.New("force-brutal requires up") + } + default: + return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: bbr, reno, force-brutal") + } + config.QuicParams = &internet.QuicParams{ + Congestion: c.FinalMask.QuicParams.Congestion, + Up: up, + } + } } return config, nil diff --git a/xray-core/proxy/proxy.go b/xray-core/proxy/proxy.go index 29548d9fb1..ea8c3b0618 100644 --- a/xray-core/proxy/proxy.go +++ b/xray-core/proxy/proxy.go @@ -28,6 +28,7 @@ import ( "github.com/xtls/xray-core/proxy/vless/encryption" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -659,7 +660,7 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte } } -// UnwrapRawConn support unwrap encryption, stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it +// UnwrapRawConn support unwrap encryption, stats, mask wrappers, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { var readCounter, writerCounter stats.Counter if conn != nil { @@ -676,6 +677,7 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { readCounter = statConn.ReadCounter writerCounter = statConn.WriteCounter } + if !isEncryption { // avoids double penetration if xc, ok := conn.(*tls.Conn); ok { conn = xc.NetConn() @@ -687,6 +689,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { conn = realityUConn.NetConn() } } + + conn = finalmask.UnwrapTcpMask(conn) + if pc, ok := conn.(*proxyproto.Conn); ok { conn = pc.Raw() // 8192 > 4096, there is no need to process pc's bufReader @@ -788,6 +793,7 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { iConn := stat.TryUnwrapStatsConn(conn) + iConn = finalmask.UnwrapTcpMask(iConn) _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) diff --git a/xray-core/transport/internet/browser_dialer/dialer.go b/xray-core/transport/internet/browser_dialer/dialer.go index 1991284d60..be2f137edc 100644 --- a/xray-core/transport/internet/browser_dialer/dialer.go +++ b/xray-core/transport/internet/browser_dialer/dialer.go @@ -22,6 +22,7 @@ type task struct { Method string `json:"method"` URL string `json:"url"` Extra any `json:"extra,omitempty"` + StreamResponse bool `json:"streamResponse"` } var conns chan *websocket.Conn @@ -52,6 +53,7 @@ func init() { } } } else { + w.Header().Set("Access-Control-Allow-Origin", "*"); w.Write(webpage) } })) @@ -70,6 +72,7 @@ func DialWS(uri string, ed []byte) (*websocket.Conn, error) { task := task{ Method: "WS", URL: uri, + StreamResponse: true, } if ed != nil { @@ -84,9 +87,10 @@ func DialWS(uri string, ed []byte) (*websocket.Conn, error) { type httpExtra struct { Referrer string `json:"referrer,omitempty"` Headers map[string]string `json:"headers,omitempty"` + Cookies map[string]string `json:"cookies,omitempty"` } -func httpExtraFromHeaders(headers http.Header) *httpExtra { +func httpExtraFromHeadersAndCookies(headers http.Header, cookies []*http.Cookie) *httpExtra { if len(headers) == 0 { return nil } @@ -104,24 +108,37 @@ func httpExtraFromHeaders(headers http.Header) *httpExtra { } } + if len(cookies) > 0 { + extra.Cookies = make(map[string]string) + for _, cookie := range cookies { + extra.Cookies[cookie.Name] = cookie.Value + } + } + return &extra } -func DialGet(uri string, headers http.Header) (*websocket.Conn, error) { +func DialGet(uri string, headers http.Header, cookies []*http.Cookie) (*websocket.Conn, error) { task := task{ Method: "GET", URL: uri, - Extra: httpExtraFromHeaders(headers), + Extra: httpExtraFromHeadersAndCookies(headers, cookies), + StreamResponse: true, } return dialTask(task) } -func DialPost(uri string, headers http.Header, payload []byte) error { +func DialPacket(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error { + return dialWithBody(method, uri, headers, cookies, payload) +} + +func dialWithBody(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error { task := task{ - Method: "POST", + Method: method, URL: uri, - Extra: httpExtraFromHeaders(headers), + Extra: httpExtraFromHeadersAndCookies(headers, cookies), + StreamResponse: false, } conn, err := dialTask(task) diff --git a/xray-core/transport/internet/browser_dialer/dialer.html b/xray-core/transport/internet/browser_dialer/dialer.html index c62135ae6b..5a0df4892b 100644 --- a/xray-core/transport/internet/browser_dialer/dialer.html +++ b/xray-core/transport/internet/browser_dialer/dialer.html @@ -2,6 +2,7 @@ Browser Dialer +