mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Sat Mar 7 19:46:43 CET 2026
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
+3
-3
@@ -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 }}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
Generated
+107
-107
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
-- 本地策略
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
cba7b9ac0399055aa49fbdc57c03c374f58e1597
|
||||
d181863d6a4aa2e7bb7eaf67c1d512c5e4827fde
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 租约获取的主机名。
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 租约获取的主机名。
|
||||
|
||||
|
||||
+32
-32
@@ -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
|
||||
|
||||
+64
-64
@@ -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=
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !android
|
||||
//go:build with_gvisor && !android
|
||||
|
||||
package tailscale
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !windows
|
||||
//go:build with_gvisor && !windows
|
||||
|
||||
package tailscale
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
//go:build with_gvisor && windows
|
||||
|
||||
package tailscale
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package derp
|
||||
|
||||
import (
|
||||
|
||||
@@ -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,
|
||||
-- 本地策略
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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'
|
||||
|
||||
|
||||
+1
-1
@@ -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'
|
||||
|
||||
|
||||
+1
-1
@@ -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'
|
||||
|
||||
|
||||
+1
-1
@@ -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'
|
||||
|
||||
|
||||
@@ -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<string?> childItems = [prevNode?.IndexId, node.IndexId, nextNode?.IndexId];
|
||||
var chainExtraItem = chainNode.GetProtocolExtra() with
|
||||
|
||||
@@ -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<RetResult> 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
@@ -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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Rule struct {
|
||||
RuleTag string
|
||||
Balancer *Balancer
|
||||
Condition Condition
|
||||
Webhook *WebhookNotifier
|
||||
}
|
||||
|
||||
func (r *Rule) GetTag() (string, error) {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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<string, string> headers = 3;
|
||||
}
|
||||
|
||||
message BalancingRule {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Dialer</title>
|
||||
<link rel="icon" href="data:">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
@@ -29,9 +30,37 @@
|
||||
requestInit.headers = extra.headers;
|
||||
}
|
||||
|
||||
if (extra.cookies) {
|
||||
requestInit.credentials = 'include';
|
||||
}
|
||||
|
||||
return requestInit;
|
||||
}
|
||||
|
||||
function setCookiesFromTask(task) {
|
||||
if (!task.extra.cookies) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(task.url);
|
||||
|
||||
for (const [name, value] of Object.entries(task.extra.cookies)) {
|
||||
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + '; path=' + url.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
function clearCookiesFromTask(task) {
|
||||
if (!task.extra.cookies) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(task.url);
|
||||
|
||||
for (const [name, value] of Object.entries(task.extra.cookies)) {
|
||||
document.cookie = encodeURIComponent(name) + '=; path=' + url.pathname + '; Max-Age=0';
|
||||
}
|
||||
}
|
||||
|
||||
let check = function () {
|
||||
if (clientIdleCount > 0) {
|
||||
return;
|
||||
@@ -48,116 +77,121 @@
|
||||
ws.onmessage = function (event) {
|
||||
clientIdleCount -= 1;
|
||||
let task = JSON.parse(event.data);
|
||||
switch (task.method) {
|
||||
case "WS": {
|
||||
upstreamWsCount += 1;
|
||||
console.log("Dial WS", task.url, task.extra.protocol);
|
||||
const wss = new WebSocket(task.url, task.extra.protocol);
|
||||
wss.binaryType = "arraybuffer";
|
||||
let opened = false;
|
||||
ws.onmessage = function (event) {
|
||||
wss.send(event.data)
|
||||
};
|
||||
wss.onopen = function (event) {
|
||||
opened = true;
|
||||
ws.send("ok")
|
||||
};
|
||||
wss.onmessage = function (event) {
|
||||
ws.send(event.data)
|
||||
};
|
||||
wss.onclose = function (event) {
|
||||
upstreamWsCount -= 1;
|
||||
console.log("Dial WS DONE, remaining: ", upstreamWsCount);
|
||||
ws.close()
|
||||
};
|
||||
wss.onerror = function (event) {
|
||||
!opened && ws.send("fail")
|
||||
wss.close()
|
||||
};
|
||||
ws.onclose = function (event) {
|
||||
wss.close()
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "GET": {
|
||||
(async () => {
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
|
||||
console.log("Dial GET", task.url);
|
||||
ws.send("ok");
|
||||
const controller = new AbortController();
|
||||
|
||||
/*
|
||||
Aborting a streaming response in JavaScript
|
||||
requires two levers to be pulled:
|
||||
|
||||
First, the streaming read itself has to be cancelled using
|
||||
reader.cancel(), only then controller.abort() will actually work.
|
||||
|
||||
If controller.abort() alone is called while a
|
||||
reader.read() is ongoing, it will block until the server closes the
|
||||
response, the page is refreshed or the network connection is lost.
|
||||
*/
|
||||
|
||||
let reader = null;
|
||||
ws.onclose = (event) => {
|
||||
try {
|
||||
reader && reader.cancel();
|
||||
} catch(e) {}
|
||||
|
||||
try {
|
||||
controller.abort();
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
try {
|
||||
upstreamGetCount += 1;
|
||||
|
||||
requestInit.signal = controller.signal;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
|
||||
const body = await response.body;
|
||||
reader = body.getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) ws.send(value); // don't send back "undefined" string when received nothing
|
||||
if (done) break;
|
||||
}
|
||||
} finally {
|
||||
upstreamGetCount -= 1;
|
||||
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||
ws.close();
|
||||
}
|
||||
})();
|
||||
break;
|
||||
}
|
||||
case "POST": {
|
||||
upstreamPostCount += 1;
|
||||
|
||||
if (task.method == "WS") {
|
||||
upstreamWsCount += 1;
|
||||
console.log("Dial WS", task.url, task.extra.protocol);
|
||||
const wss = new WebSocket(task.url, task.extra.protocol);
|
||||
wss.binaryType = "arraybuffer";
|
||||
let opened = false;
|
||||
ws.onmessage = function (event) {
|
||||
wss.send(event.data)
|
||||
};
|
||||
wss.onopen = function (event) {
|
||||
opened = true;
|
||||
ws.send("ok")
|
||||
};
|
||||
wss.onmessage = function (event) {
|
||||
ws.send(event.data)
|
||||
};
|
||||
wss.onclose = function (event) {
|
||||
upstreamWsCount -= 1;
|
||||
console.log("Dial WS DONE, remaining: ", upstreamWsCount);
|
||||
ws.close()
|
||||
};
|
||||
wss.onerror = function (event) {
|
||||
!opened && ws.send("fail")
|
||||
wss.close()
|
||||
};
|
||||
ws.onclose = function (event) {
|
||||
wss.close()
|
||||
};
|
||||
}
|
||||
else if (task.method == "GET" && task.streamResponse) {
|
||||
(async () => {
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
requestInit.method = "POST";
|
||||
|
||||
console.log("Dial POST", task.url);
|
||||
console.log("Dial GET", task.url);
|
||||
ws.send("ok");
|
||||
ws.onmessage = async (event) => {
|
||||
const controller = new AbortController();
|
||||
|
||||
/*
|
||||
Aborting a streaming response in JavaScript
|
||||
requires two levers to be pulled:
|
||||
|
||||
First, the streaming read itself has to be cancelled using
|
||||
reader.cancel(), only then controller.abort() will actually work.
|
||||
|
||||
If controller.abort() alone is called while a
|
||||
reader.read() is ongoing, it will block until the server closes the
|
||||
response, the page is refreshed or the network connection is lost.
|
||||
*/
|
||||
|
||||
let reader = null;
|
||||
ws.onclose = (event) => {
|
||||
try {
|
||||
requestInit.body = event.data;
|
||||
const response = await fetch(task.url, requestInit);
|
||||
if (response.ok) {
|
||||
ws.send("ok");
|
||||
} else {
|
||||
console.error("bad status code");
|
||||
ws.send("fail");
|
||||
}
|
||||
} finally {
|
||||
upstreamPostCount -= 1;
|
||||
console.log("Dial POST DONE, remaining: ", upstreamPostCount);
|
||||
ws.close();
|
||||
}
|
||||
reader && reader.cancel();
|
||||
} catch(e) {}
|
||||
|
||||
try {
|
||||
controller.abort();
|
||||
} catch(e) {}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
upstreamGetCount += 1;
|
||||
|
||||
requestInit.signal = controller.signal;
|
||||
setCookiesFromTask(task);
|
||||
const response = await fetch(task.url, requestInit);
|
||||
clearCookiesFromTask(task);
|
||||
|
||||
const body = await response.body;
|
||||
reader = body.getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) ws.send(value); // don't send back "undefined" string when received nothing
|
||||
if (done) break;
|
||||
}
|
||||
} finally {
|
||||
upstreamGetCount -= 1;
|
||||
console.log("Dial GET DONE, remaining: ", upstreamGetCount);
|
||||
ws.close();
|
||||
}
|
||||
})();
|
||||
}
|
||||
else if (!task.streamResponse) {
|
||||
upstreamPostCount += 1;
|
||||
|
||||
const requestInit = prepareRequestInit(task.extra);
|
||||
requestInit.method = task.method;
|
||||
|
||||
console.log("Dial", task.method, task.url);
|
||||
ws.send("ok");
|
||||
ws.onmessage = async (event) => {
|
||||
try {
|
||||
if (event.data.byteLength > 0) {
|
||||
requestInit.body = event.data;
|
||||
}
|
||||
setCookiesFromTask(task);
|
||||
const response = await fetch(task.url, requestInit);
|
||||
clearCookiesFromTask(task);
|
||||
if (response.ok) {
|
||||
ws.send("ok");
|
||||
} else {
|
||||
console.error("bad status code");
|
||||
ws.send("fail");
|
||||
}
|
||||
} finally {
|
||||
upstreamPostCount -= 1;
|
||||
console.log("Dial", task.method, "packet DONE, remaining: ", upstreamPostCount);
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
console.error(`Incorrect task method=${task.method} streamResponse=${task.streamResponse}.`);
|
||||
ws.close();
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
@@ -206,7 +206,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
||||
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4, 0}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5, 0}
|
||||
}
|
||||
|
||||
type TransportConfig struct {
|
||||
@@ -276,6 +276,7 @@ type StreamConfig struct {
|
||||
SecuritySettings []*serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"`
|
||||
Udpmasks []*serial.TypedMessage `protobuf:"bytes,10,rep,name=udpmasks,proto3" json:"udpmasks,omitempty"`
|
||||
Tcpmasks []*serial.TypedMessage `protobuf:"bytes,11,rep,name=tcpmasks,proto3" json:"tcpmasks,omitempty"`
|
||||
QuicParams *QuicParams `protobuf:"bytes,12,opt,name=quic_params,json=quicParams,proto3" json:"quic_params,omitempty"`
|
||||
SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -367,6 +368,13 @@ func (x *StreamConfig) GetTcpmasks() []*serial.TypedMessage {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetQuicParams() *QuicParams {
|
||||
if x != nil {
|
||||
return x.QuicParams
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
if x != nil {
|
||||
return x.SocketSettings
|
||||
@@ -374,6 +382,58 @@ func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
type QuicParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
||||
Up uint64 `protobuf:"varint,2,opt,name=up,proto3" json:"up,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *QuicParams) Reset() {
|
||||
*x = QuicParams{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *QuicParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*QuicParams) ProtoMessage() {}
|
||||
|
||||
func (x *QuicParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
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 QuicParams.ProtoReflect.Descriptor instead.
|
||||
func (*QuicParams) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetCongestion() string {
|
||||
if x != nil {
|
||||
return x.Congestion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetUp() uint64 {
|
||||
if x != nil {
|
||||
return x.Up
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
@@ -384,7 +444,7 @@ type ProxyConfig struct {
|
||||
|
||||
func (x *ProxyConfig) Reset() {
|
||||
*x = ProxyConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -396,7 +456,7 @@ func (x *ProxyConfig) String() string {
|
||||
func (*ProxyConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ProxyConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -409,7 +469,7 @@ func (x *ProxyConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ProxyConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ProxyConfig) GetTag() string {
|
||||
@@ -440,7 +500,7 @@ type CustomSockopt struct {
|
||||
|
||||
func (x *CustomSockopt) Reset() {
|
||||
*x = CustomSockopt{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -452,7 +512,7 @@ func (x *CustomSockopt) String() string {
|
||||
func (*CustomSockopt) ProtoMessage() {}
|
||||
|
||||
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -465,7 +525,7 @@ func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CustomSockopt.ProtoReflect.Descriptor instead.
|
||||
func (*CustomSockopt) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *CustomSockopt) GetSystem() string {
|
||||
@@ -547,7 +607,7 @@ type SocketConfig struct {
|
||||
|
||||
func (x *SocketConfig) Reset() {
|
||||
*x = SocketConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -559,7 +619,7 @@ func (x *SocketConfig) String() string {
|
||||
func (*SocketConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -572,7 +632,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
||||
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *SocketConfig) GetMark() int32 {
|
||||
@@ -748,7 +808,7 @@ type HappyEyeballsConfig struct {
|
||||
|
||||
func (x *HappyEyeballsConfig) Reset() {
|
||||
*x = HappyEyeballsConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -760,7 +820,7 @@ func (x *HappyEyeballsConfig) String() string {
|
||||
func (*HappyEyeballsConfig) ProtoMessage() {}
|
||||
|
||||
func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -773,7 +833,7 @@ func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HappyEyeballsConfig.ProtoReflect.Descriptor instead.
|
||||
func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool {
|
||||
@@ -811,7 +871,7 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\x1ftransport/internet/config.proto\x12\x17xray.transport.internet\x1a!common/serial/typed_message.proto\x1a\x18common/net/address.proto\"t\n" +
|
||||
"\x0fTransportConfig\x12#\n" +
|
||||
"\rprotocol_name\x18\x03 \x01(\tR\fprotocolName\x12<\n" +
|
||||
"\bsettings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\bsettings\"\x97\x04\n" +
|
||||
"\bsettings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\bsettings\"\xdd\x04\n" +
|
||||
"\fStreamConfig\x125\n" +
|
||||
"\aaddress\x18\b \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
||||
"\x04port\x18\t \x01(\rR\x04port\x12#\n" +
|
||||
@@ -821,8 +881,16 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\x11security_settings\x18\x04 \x03(\v2 .xray.common.serial.TypedMessageR\x10securitySettings\x12<\n" +
|
||||
"\budpmasks\x18\n" +
|
||||
" \x03(\v2 .xray.common.serial.TypedMessageR\budpmasks\x12<\n" +
|
||||
"\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12N\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"Q\n" +
|
||||
"\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12D\n" +
|
||||
"\vquic_params\x18\f \x01(\v2#.xray.transport.internet.QuicParamsR\n" +
|
||||
"quicParams\x12N\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"<\n" +
|
||||
"\n" +
|
||||
"QuicParams\x12\x1e\n" +
|
||||
"\n" +
|
||||
"congestion\x18\x01 \x01(\tR\n" +
|
||||
"congestion\x12\x0e\n" +
|
||||
"\x02up\x18\x02 \x01(\x04R\x02up\"Q\n" +
|
||||
"\vProxyConfig\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
||||
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
||||
@@ -911,38 +979,40 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_transport_internet_config_proto_goTypes = []any{
|
||||
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
||||
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
||||
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
||||
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
|
||||
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||
(*HappyEyeballsConfig)(nil), // 8: xray.transport.internet.HappyEyeballsConfig
|
||||
(*serial.TypedMessage)(nil), // 9: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
||||
(*QuicParams)(nil), // 5: xray.transport.internet.QuicParams
|
||||
(*ProxyConfig)(nil), // 6: xray.transport.internet.ProxyConfig
|
||||
(*CustomSockopt)(nil), // 7: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 8: xray.transport.internet.SocketConfig
|
||||
(*HappyEyeballsConfig)(nil), // 9: xray.transport.internet.HappyEyeballsConfig
|
||||
(*serial.TypedMessage)(nil), // 10: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||
9, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
10, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
11, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||
9, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
7, // 6: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 7: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 8: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
6, // 9: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 10: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
8, // 11: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
10, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
5, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams
|
||||
8, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 8: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 9: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
7, // 10: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 11: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
9, // 12: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_config_proto_init() }
|
||||
@@ -956,7 +1026,7 @@ func file_transport_internet_config_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)),
|
||||
NumEnums: 3,
|
||||
NumMessages: 6,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -59,9 +59,16 @@ message StreamConfig {
|
||||
repeated xray.common.serial.TypedMessage udpmasks = 10;
|
||||
repeated xray.common.serial.TypedMessage tcpmasks = 11;
|
||||
|
||||
QuicParams quic_params = 12;
|
||||
|
||||
SocketConfig socket_settings = 6;
|
||||
}
|
||||
|
||||
message QuicParams {
|
||||
string congestion = 1;
|
||||
uint64 up = 2;
|
||||
}
|
||||
|
||||
message ProxyConfig {
|
||||
string tag = 1;
|
||||
bool transportLayerProxy = 2;
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package finalmask
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type ConnSize interface {
|
||||
Size() int32
|
||||
}
|
||||
const (
|
||||
UDPSize = 4096 + 123
|
||||
)
|
||||
|
||||
type Udpmask interface {
|
||||
UDP()
|
||||
|
||||
WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||
WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||
WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
type UdpmaskManager struct {
|
||||
@@ -26,27 +29,23 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||
leaveSize := int32(0)
|
||||
var err error
|
||||
for i, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapPacketConnClient(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||
raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leaveSize += raw.(ConnSize).Size()
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||
leaveSize := int32(0)
|
||||
var err error
|
||||
for i, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapPacketConnServer(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||
raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leaveSize += raw.(ConnSize).Size()
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
@@ -89,3 +88,54 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *TcpmaskManager) WrapListener(l net.Listener) (net.Listener, error) {
|
||||
return NewTcpListener(m, l)
|
||||
}
|
||||
|
||||
type tcpListener struct {
|
||||
m *TcpmaskManager
|
||||
net.Listener
|
||||
}
|
||||
|
||||
func NewTcpListener(m *TcpmaskManager, l net.Listener) (net.Listener, error) {
|
||||
return &tcpListener{
|
||||
m: m,
|
||||
Listener: l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *tcpListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
newConn, err := l.m.WrapConnServer(conn)
|
||||
if err != nil {
|
||||
errors.LogDebugInner(context.Background(), err, "mask err")
|
||||
// conn.Close()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
return newConn, nil
|
||||
}
|
||||
|
||||
type TcpMaskConn interface {
|
||||
TcpMaskConn()
|
||||
RawConn() net.Conn
|
||||
Splice() bool
|
||||
}
|
||||
|
||||
func UnwrapTcpMask(conn net.Conn) net.Conn {
|
||||
for {
|
||||
if v, ok := conn.(TcpMaskConn); ok {
|
||||
if !v.Splice() {
|
||||
return conn
|
||||
}
|
||||
conn = v.RawConn()
|
||||
} else {
|
||||
return conn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package fragment
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClient(c, raw, false)
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnServer(c, raw, true)
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/fragment/config.proto
|
||||
|
||||
package fragment
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
PacketsFrom int64 `protobuf:"varint,1,opt,name=packets_from,json=packetsFrom,proto3" json:"packets_from,omitempty"`
|
||||
PacketsTo int64 `protobuf:"varint,2,opt,name=packets_to,json=packetsTo,proto3" json:"packets_to,omitempty"`
|
||||
LengthMin int64 `protobuf:"varint,3,opt,name=length_min,json=lengthMin,proto3" json:"length_min,omitempty"`
|
||||
LengthMax int64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"`
|
||||
DelayMin int64 `protobuf:"varint,5,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,6,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
MaxSplitMin int64 `protobuf:"varint,7,opt,name=max_split_min,json=maxSplitMin,proto3" json:"max_split_min,omitempty"`
|
||||
MaxSplitMax int64 `protobuf:"varint,8,opt,name=max_split_max,json=maxSplitMax,proto3" json:"max_split_max,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_fragment_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_fragment_config_proto_msgTypes[0]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_fragment_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetPacketsFrom() int64 {
|
||||
if x != nil {
|
||||
return x.PacketsFrom
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPacketsTo() int64 {
|
||||
if x != nil {
|
||||
return x.PacketsTo
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetLengthMin() int64 {
|
||||
if x != nil {
|
||||
return x.LengthMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetLengthMax() int64 {
|
||||
if x != nil {
|
||||
return x.LengthMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxSplitMin() int64 {
|
||||
if x != nil {
|
||||
return x.MaxSplitMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxSplitMax() int64 {
|
||||
if x != nil {
|
||||
return x.MaxSplitMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_fragment_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_fragment_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"2transport/internet/finalmask/fragment/config.proto\x12*xray.transport.internet.finalmask.fragment\"\x8a\x02\n" +
|
||||
"\x06Config\x12!\n" +
|
||||
"\fpackets_from\x18\x01 \x01(\x03R\vpacketsFrom\x12\x1d\n" +
|
||||
"\n" +
|
||||
"packets_to\x18\x02 \x01(\x03R\tpacketsTo\x12\x1d\n" +
|
||||
"\n" +
|
||||
"length_min\x18\x03 \x01(\x03R\tlengthMin\x12\x1d\n" +
|
||||
"\n" +
|
||||
"length_max\x18\x04 \x01(\x03R\tlengthMax\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x05 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x06 \x01(\x03R\bdelayMax\x12\"\n" +
|
||||
"\rmax_split_min\x18\a \x01(\x03R\vmaxSplitMin\x12\"\n" +
|
||||
"\rmax_split_max\x18\b \x01(\x03R\vmaxSplitMaxB\xa0\x01\n" +
|
||||
".com.xray.transport.internet.finalmask.fragmentP\x01Z?github.com/xtls/xray-core/transport/internet/finalmask/fragment\xaa\x02*Xray.Transport.Internet.Finalmask.Fragmentb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_fragment_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_fragment_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_fragment_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_fragment_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_finalmask_fragment_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.fragment.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_fragment_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_fragment_config_proto_init() }
|
||||
func file_transport_internet_finalmask_fragment_config_proto_init() {
|
||||
if File_transport_internet_finalmask_fragment_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_fragment_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_fragment_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_fragment_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_fragment_config_proto = out.File
|
||||
file_transport_internet_finalmask_fragment_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_fragment_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.fragment;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Fragment";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/fragment";
|
||||
option java_package = "com.xray.transport.internet.finalmask.fragment";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
int64 packets_from = 1;
|
||||
int64 packets_to = 2;
|
||||
int64 length_min = 3;
|
||||
int64 length_max = 4;
|
||||
int64 delay_min = 5;
|
||||
int64 delay_max = 6;
|
||||
int64 max_split_min = 7;
|
||||
int64 max_split_max = 8;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package fragment
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
||||
type fragmentConn struct {
|
||||
net.Conn
|
||||
config *Config
|
||||
count uint64
|
||||
|
||||
server bool
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.Conn, server bool) (net.Conn, error) {
|
||||
conn := &fragmentConn{
|
||||
Conn: raw,
|
||||
config: c,
|
||||
|
||||
server: server,
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.Conn, server bool) (net.Conn, error) {
|
||||
return NewConnClient(c, raw, server)
|
||||
}
|
||||
|
||||
func (c *fragmentConn) TcpMaskConn() {}
|
||||
|
||||
func (c *fragmentConn) RawConn() net.Conn {
|
||||
if c.server {
|
||||
return c
|
||||
}
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *fragmentConn) Splice() bool {
|
||||
if c.server {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *fragmentConn) Write(p []byte) (n int, err error) {
|
||||
c.count++
|
||||
|
||||
if c.config.PacketsFrom == 0 && c.config.PacketsTo == 1 {
|
||||
if c.count != 1 || len(p) <= 5 || p[0] != 22 {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
recordLen := 5 + ((int(p[3]) << 8) | int(p[4]))
|
||||
if len(p) < recordLen {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
data := p[5:recordLen]
|
||||
buff := make([]byte, 2048)
|
||||
var hello []byte
|
||||
maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax)
|
||||
var splitNum int64
|
||||
for from := 0; ; {
|
||||
to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax))
|
||||
splitNum++
|
||||
if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) {
|
||||
to = len(data)
|
||||
}
|
||||
l := to - from
|
||||
if 5+l > len(buff) {
|
||||
buff = make([]byte, 5+l)
|
||||
}
|
||||
copy(buff[:3], p)
|
||||
copy(buff[5:], data[from:to])
|
||||
from = to
|
||||
buff[3] = byte(l >> 8)
|
||||
buff[4] = byte(l)
|
||||
if c.config.DelayMax == 0 {
|
||||
hello = append(hello, buff[:5+l]...)
|
||||
} else {
|
||||
_, err := c.Conn.Write(buff[:5+l])
|
||||
time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if from == len(data) {
|
||||
if len(hello) > 0 {
|
||||
_, err := c.Conn.Write(hello)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if len(p) > recordLen {
|
||||
n, err := c.Conn.Write(p[recordLen:])
|
||||
if err != nil {
|
||||
return recordLen + n, err
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.PacketsFrom != 0 && (c.count < uint64(c.config.PacketsFrom) || c.count > uint64(c.config.PacketsTo)) {
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax)
|
||||
var splitNum int64
|
||||
for from := 0; ; {
|
||||
to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax))
|
||||
splitNum++
|
||||
if to > len(p) || (maxSplit > 0 && splitNum >= maxSplit) {
|
||||
to = len(p)
|
||||
}
|
||||
n, err := c.Conn.Write(p[from:to])
|
||||
from += n
|
||||
if err != nil {
|
||||
return from, err
|
||||
}
|
||||
time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond)
|
||||
if from >= len(p) {
|
||||
return from, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *TCPConfig) TCP() {
|
||||
}
|
||||
|
||||
func (c *TCPConfig) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClientTCP(c, raw)
|
||||
}
|
||||
|
||||
func (c *TCPConfig) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnServerTCP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) UDP() {
|
||||
}
|
||||
|
||||
func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClientUDP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServerUDP(c, raw)
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/header/custom/config.proto
|
||||
|
||||
package custom
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type TCPItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
DelayMin int64 `protobuf:"varint,1,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,2,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
Rand int32 `protobuf:"varint,3,opt,name=rand,proto3" json:"rand,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPItem) Reset() {
|
||||
*x = TCPItem{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPItem) ProtoMessage() {}
|
||||
|
||||
func (x *TCPItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0]
|
||||
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 TCPItem.ProtoReflect.Descriptor instead.
|
||||
func (*TCPItem) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetRand() int32 {
|
||||
if x != nil {
|
||||
return x.Rand
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TCPItem) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TCPSequence struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Sequence []*TCPItem `protobuf:"bytes,1,rep,name=sequence,proto3" json:"sequence,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPSequence) Reset() {
|
||||
*x = TCPSequence{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPSequence) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPSequence) ProtoMessage() {}
|
||||
|
||||
func (x *TCPSequence) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1]
|
||||
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 TCPSequence.ProtoReflect.Descriptor instead.
|
||||
func (*TCPSequence) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TCPSequence) GetSequence() []*TCPItem {
|
||||
if x != nil {
|
||||
return x.Sequence
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TCPConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Clients []*TCPSequence `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
|
||||
Servers []*TCPSequence `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"`
|
||||
Errors []*TCPSequence `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TCPConfig) Reset() {
|
||||
*x = TCPConfig{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TCPConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TCPConfig) ProtoMessage() {}
|
||||
|
||||
func (x *TCPConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2]
|
||||
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 TCPConfig.ProtoReflect.Descriptor instead.
|
||||
func (*TCPConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetClients() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Clients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetServers() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Servers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TCPConfig) GetErrors() []*TCPSequence {
|
||||
if x != nil {
|
||||
return x.Errors
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UDPItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Rand int32 `protobuf:"varint,1,opt,name=rand,proto3" json:"rand,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,2,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UDPItem) Reset() {
|
||||
*x = UDPItem{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UDPItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UDPItem) ProtoMessage() {}
|
||||
|
||||
func (x *UDPItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3]
|
||||
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 UDPItem.ProtoReflect.Descriptor instead.
|
||||
func (*UDPItem) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetRand() int32 {
|
||||
if x != nil {
|
||||
return x.Rand
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UDPItem) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UDPConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Client []*UDPItem `protobuf:"bytes,1,rep,name=client,proto3" json:"client,omitempty"`
|
||||
Server []*UDPItem `protobuf:"bytes,2,rep,name=server,proto3" json:"server,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UDPConfig) Reset() {
|
||||
*x = UDPConfig{}
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UDPConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UDPConfig) ProtoMessage() {}
|
||||
|
||||
func (x *UDPConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4]
|
||||
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 UDPConfig.ProtoReflect.Descriptor instead.
|
||||
func (*UDPConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *UDPConfig) GetClient() []*UDPItem {
|
||||
if x != nil {
|
||||
return x.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UDPConfig) GetServer() []*UDPItem {
|
||||
if x != nil {
|
||||
return x.Server
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_header_custom_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"o\n" +
|
||||
"\aTCPItem\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" +
|
||||
"\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" +
|
||||
"\x06packet\x18\x04 \x01(\fR\x06packet\"c\n" +
|
||||
"\vTCPSequence\x12T\n" +
|
||||
"\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\x91\x02\n" +
|
||||
"\tTCPConfig\x12V\n" +
|
||||
"\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" +
|
||||
"\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12T\n" +
|
||||
"\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"5\n" +
|
||||
"\aUDPItem\x12\x12\n" +
|
||||
"\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x16\n" +
|
||||
"\x06packet\x18\x02 \x01(\fR\x06packet\"\xaf\x01\n" +
|
||||
"\tUDPConfig\x12P\n" +
|
||||
"\x06client\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06client\x12P\n" +
|
||||
"\x06server\x18\x02 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06serverB\xaf\x01\n" +
|
||||
"3com.xray.transport.internet.finalmask.header.customP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/custom\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Customb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_header_custom_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_header_custom_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any{
|
||||
(*TCPItem)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPItem
|
||||
(*TCPSequence)(nil), // 1: xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
(*TCPConfig)(nil), // 2: xray.transport.internet.finalmask.header.custom.TCPConfig
|
||||
(*UDPItem)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
(*UDPConfig)(nil), // 4: xray.transport.internet.finalmask.header.custom.UDPConfig
|
||||
}
|
||||
var file_transport_internet_finalmask_header_custom_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.sequence:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem
|
||||
1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
1, // 3: xray.transport.internet.finalmask.header.custom.TCPConfig.errors:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence
|
||||
3, // 4: xray.transport.internet.finalmask.header.custom.UDPConfig.client:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
3, // 5: xray.transport.internet.finalmask.header.custom.UDPConfig.server:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_header_custom_config_proto_init() }
|
||||
func file_transport_internet_finalmask_header_custom_config_proto_init() {
|
||||
if File_transport_internet_finalmask_header_custom_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_header_custom_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_header_custom_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_header_custom_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_header_custom_config_proto = out.File
|
||||
file_transport_internet_finalmask_header_custom_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_header_custom_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.header.custom;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Custom";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/custom";
|
||||
option java_package = "com.xray.transport.internet.finalmask.header.custom";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message TCPItem {
|
||||
int64 delay_min = 1;
|
||||
int64 delay_max = 2;
|
||||
int32 rand = 3;
|
||||
bytes packet = 4;
|
||||
}
|
||||
|
||||
message TCPSequence {
|
||||
repeated TCPItem sequence = 1;
|
||||
}
|
||||
|
||||
message TCPConfig {
|
||||
repeated TCPSequence clients = 1;
|
||||
repeated TCPSequence servers = 2;
|
||||
repeated TCPSequence errors = 3;
|
||||
}
|
||||
|
||||
message UDPItem {
|
||||
int32 rand = 1;
|
||||
bytes packet = 2;
|
||||
}
|
||||
|
||||
message UDPConfig {
|
||||
repeated UDPItem client = 1;
|
||||
repeated UDPItem server = 2;
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
type tcpCustomClient struct {
|
||||
clients []*TCPSequence
|
||||
servers []*TCPSequence
|
||||
}
|
||||
|
||||
type tcpCustomClientConn struct {
|
||||
net.Conn
|
||||
header *tcpCustomClient
|
||||
|
||||
auth bool
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) {
|
||||
conn := &tcpCustomClientConn{
|
||||
Conn: raw,
|
||||
header: &tcpCustomClient{
|
||||
clients: c.Clients,
|
||||
servers: c.Servers,
|
||||
},
|
||||
}
|
||||
|
||||
conn.wg.Add(1)
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) TcpMaskConn() {}
|
||||
|
||||
func (c *tcpCustomClientConn) RawConn() net.Conn {
|
||||
// c.wg.Wait()
|
||||
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Splice() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) {
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
|
||||
func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) {
|
||||
c.once.Do(func() {
|
||||
i := 0
|
||||
j := 0
|
||||
for i = range c.header.clients {
|
||||
if !writeSequence(c.Conn, c.header.clients[i]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if j < len(c.header.servers) {
|
||||
if !readSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for j < len(c.header.servers) {
|
||||
if !readSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
c.auth = true
|
||||
c.wg.Done()
|
||||
})
|
||||
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
|
||||
type tcpCustomServer struct {
|
||||
clients []*TCPSequence
|
||||
servers []*TCPSequence
|
||||
errors []*TCPSequence
|
||||
}
|
||||
|
||||
type tcpCustomServerConn struct {
|
||||
net.Conn
|
||||
header *tcpCustomServer
|
||||
|
||||
auth bool
|
||||
wg sync.WaitGroup
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) {
|
||||
conn := &tcpCustomServerConn{
|
||||
Conn: raw,
|
||||
header: &tcpCustomServer{
|
||||
clients: c.Clients,
|
||||
servers: c.Servers,
|
||||
errors: c.Errors,
|
||||
},
|
||||
}
|
||||
|
||||
conn.wg.Add(1)
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) TcpMaskConn() {}
|
||||
|
||||
func (c *tcpCustomServerConn) RawConn() net.Conn {
|
||||
// c.wg.Wait()
|
||||
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Splice() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) {
|
||||
c.once.Do(func() {
|
||||
i := 0
|
||||
j := 0
|
||||
for i = range c.header.clients {
|
||||
if !readSequence(c.Conn, c.header.clients[i]) {
|
||||
if i < len(c.header.errors) {
|
||||
writeSequence(c.Conn, c.header.errors[i])
|
||||
}
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if j < len(c.header.servers) {
|
||||
if !writeSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
for j < len(c.header.servers) {
|
||||
if !writeSequence(c.Conn, c.header.servers[j]) {
|
||||
c.wg.Done()
|
||||
return
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
c.auth = true
|
||||
c.wg.Done()
|
||||
})
|
||||
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
|
||||
func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) {
|
||||
c.wg.Wait()
|
||||
|
||||
if !c.auth {
|
||||
return 0, errors.New("header auth failed")
|
||||
}
|
||||
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
|
||||
func readSequence(r io.Reader, sequence *TCPSequence) bool {
|
||||
for _, item := range sequence.Sequence {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
buf := make([]byte, length)
|
||||
n, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if item.Rand > 0 && n != length {
|
||||
return false
|
||||
}
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, buf[:n]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeSequence(w io.Writer, sequence *TCPSequence) bool {
|
||||
var merged []byte
|
||||
for _, item := range sequence.Sequence {
|
||||
if item.DelayMax > 0 {
|
||||
if len(merged) > 0 {
|
||||
_, err := w.Write(merged)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
merged = nil
|
||||
}
|
||||
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
||||
}
|
||||
if item.Rand > 0 {
|
||||
buf := make([]byte, item.Rand)
|
||||
common.Must2(rand.Read(buf))
|
||||
merged = append(merged, buf...)
|
||||
} else {
|
||||
merged = append(merged, item.Packet...)
|
||||
}
|
||||
}
|
||||
if len(merged) > 0 {
|
||||
_, err := w.Write(merged)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
merged = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type udpCustomClient struct {
|
||||
client []*UDPItem
|
||||
server []*UDPItem
|
||||
merged []byte
|
||||
}
|
||||
|
||||
func (h *udpCustomClient) Serialize(b []byte) {
|
||||
index := 0
|
||||
for _, item := range h.client {
|
||||
if item.Rand > 0 {
|
||||
common.Must2(rand.Read(h.merged[index : index+int(item.Rand)]))
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
copy(b, h.merged)
|
||||
}
|
||||
|
||||
func (h *udpCustomClient) Match(b []byte) bool {
|
||||
if len(b) < len(h.merged) {
|
||||
return false
|
||||
}
|
||||
|
||||
data := b
|
||||
match := true
|
||||
|
||||
for _, item := range h.server {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
type udpCustomClientConn struct {
|
||||
net.PacketConn
|
||||
header *udpCustomClient
|
||||
}
|
||||
|
||||
func NewConnClientUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &udpCustomClientConn{
|
||||
PacketConn: raw,
|
||||
header: &udpCustomClient{
|
||||
client: c.Client,
|
||||
server: c.Server,
|
||||
},
|
||||
}
|
||||
|
||||
index := 0
|
||||
for _, item := range conn.header.client {
|
||||
if item.Rand > 0 {
|
||||
conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...)
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
conn.header.merged = append(conn.header.merged, item.Packet...)
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if !c.header.Match(buf[:n]) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
if len(p) < n-len(c.header.merged) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged))
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, buf[len(c.header.merged):n])
|
||||
|
||||
return n - len(c.header.merged), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if len(c.header.merged)+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:len(c.header.merged)+len(p)]
|
||||
}
|
||||
|
||||
copy(buf[len(c.header.merged):], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
_, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type udpCustomServer struct {
|
||||
client []*UDPItem
|
||||
server []*UDPItem
|
||||
merged []byte
|
||||
}
|
||||
|
||||
func (h *udpCustomServer) Serialize(b []byte) {
|
||||
index := 0
|
||||
for _, item := range h.server {
|
||||
if item.Rand > 0 {
|
||||
common.Must2(rand.Read(h.merged[index : index+int(item.Rand)]))
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
copy(b, h.merged)
|
||||
}
|
||||
|
||||
func (h *udpCustomServer) Match(b []byte) bool {
|
||||
if len(b) < len(h.merged) {
|
||||
return false
|
||||
}
|
||||
|
||||
data := b
|
||||
match := true
|
||||
|
||||
for _, item := range h.client {
|
||||
length := max(int(item.Rand), len(item.Packet))
|
||||
|
||||
if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
type udpCustomServerConn struct {
|
||||
net.PacketConn
|
||||
header *udpCustomServer
|
||||
}
|
||||
|
||||
func NewConnServerUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &udpCustomServerConn{
|
||||
PacketConn: raw,
|
||||
header: &udpCustomServer{
|
||||
client: c.Client,
|
||||
server: c.Server,
|
||||
},
|
||||
}
|
||||
|
||||
index := 0
|
||||
for _, item := range conn.header.server {
|
||||
if item.Rand > 0 {
|
||||
conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...)
|
||||
index += int(item.Rand)
|
||||
} else {
|
||||
conn.header.merged = append(conn.header.merged, item.Packet...)
|
||||
index += len(item.Packet)
|
||||
}
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if !c.header.Match(buf[:n]) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
if len(p) < n-len(c.header.merged) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged))
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, buf[len(c.header.merged):n])
|
||||
|
||||
return n - len(c.header.merged), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if len(c.header.merged)+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:len(c.header.merged)+len(p)]
|
||||
}
|
||||
|
||||
copy(buf[len(c.header.merged):], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
_, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
func packDomainName(s string, msg []byte) (off1 int, err error) {
|
||||
@@ -81,8 +80,8 @@ type dns struct {
|
||||
header []byte
|
||||
}
|
||||
|
||||
func (h *dns) Size() int32 {
|
||||
return int32(len(h.header))
|
||||
func (h *dns) Size() int {
|
||||
return len(h.header)
|
||||
}
|
||||
|
||||
func (h *dns) Serialize(b []byte) {
|
||||
@@ -91,19 +90,11 @@ func (h *dns) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type dnsConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *dns
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
var header []byte
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query
|
||||
@@ -121,121 +112,65 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (
|
||||
header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN
|
||||
|
||||
conn := &dnsConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
PacketConn: raw,
|
||||
header: &dns{
|
||||
header: header,
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *dnsConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *dnsConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *dnsConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package dtls
|
||||
|
||||
import (
|
||||
"io"
|
||||
"context"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type dtls struct {
|
||||
@@ -16,7 +15,7 @@ type dtls struct {
|
||||
sequence uint32
|
||||
}
|
||||
|
||||
func (*dtls) Size() int32 {
|
||||
func (*dtls) Size() int {
|
||||
return 1 + 2 + 2 + 6 + 2
|
||||
}
|
||||
|
||||
@@ -42,24 +41,13 @@ func (h *dtls) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type dtlsConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *dtls
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &dtlsConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
PacketConn: raw,
|
||||
header: &dtls{
|
||||
epoch: dice.RollUint16(),
|
||||
sequence: 0,
|
||||
@@ -67,112 +55,59 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *dtlsConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *dtlsConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *dtlsConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package srtp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type srtp struct {
|
||||
@@ -16,7 +15,7 @@ type srtp struct {
|
||||
number uint16
|
||||
}
|
||||
|
||||
func (*srtp) Size() int32 {
|
||||
func (*srtp) Size() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -27,136 +26,72 @@ func (h *srtp) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type srtpConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *srtp
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &srtpConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
PacketConn: raw,
|
||||
header: &srtp{
|
||||
header: 0xB5E8,
|
||||
number: dice.RollUint16(),
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *srtpConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *srtpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *srtpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *srtpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type utp struct {
|
||||
@@ -17,7 +16,7 @@ type utp struct {
|
||||
connectionID uint16
|
||||
}
|
||||
|
||||
func (*utp) Size() int32 {
|
||||
func (*utp) Size() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -28,24 +27,13 @@ func (h *utp) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type utpConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *utp
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &utpConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
PacketConn: raw,
|
||||
header: &utp{
|
||||
header: 1,
|
||||
extension: 0,
|
||||
@@ -53,112 +41,59 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *utpConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *utpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *utpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *utpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *utpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *utpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type wechat struct {
|
||||
sn uint32
|
||||
}
|
||||
|
||||
func (*wechat) Size() int32 {
|
||||
func (*wechat) Size() int {
|
||||
return 13
|
||||
}
|
||||
|
||||
@@ -34,135 +33,71 @@ func (h *wechat) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type wechatConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *wechat
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &wechatConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
PacketConn: raw,
|
||||
header: &wechat{
|
||||
sn: uint32(dice.RollUint16()),
|
||||
},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *wechatConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *wechatConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *wechatConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wechatConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package wireguard
|
||||
|
||||
import (
|
||||
"io"
|
||||
"context"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type wireguare struct{}
|
||||
|
||||
func (*wireguare) Size() int32 {
|
||||
func (*wireguare) Size() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
@@ -23,133 +22,69 @@ func (h *wireguare) Serialize(b []byte) {
|
||||
}
|
||||
|
||||
type wireguareConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
header *wireguare
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &wireguareConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
header: &wireguare{},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
PacketConn: raw,
|
||||
header: &wireguare{},
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) Size() int32 {
|
||||
return c.header.Size()
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
copy(p, c.readBuf[c.Size():n])
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||
if n < c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err header mismatch")
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[c.Size():n])
|
||||
if len(p) < n-c.header.Size() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size())
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
copy(p, buf[c.header.Size():n])
|
||||
|
||||
return n - c.header.Size(), addr, nil
|
||||
}
|
||||
|
||||
func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.header.Size()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.header.Size()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
copy(buf[c.header.Size():], p)
|
||||
c.header.Serialize(buf)
|
||||
|
||||
func (c *wireguareConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *wireguareConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *wireguareConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,99 +1,77 @@
|
||||
package aes128gcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type aes128gcmConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
aead cipher.AEAD
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
hashedPsk := sha256.Sum256([]byte(c.Password))
|
||||
|
||||
conn := &aes128gcmConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
aead: crypto.NewAesGcm(hashedPsk[:16]),
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
PacketConn: raw,
|
||||
aead: crypto.NewAesGcm(hashedPsk[:16]),
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) Size() int32 {
|
||||
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf := make([]byte, finalmask.UDPSize)
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
if n < c.aead.NonceSize()+c.aead.Overhead() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := c.readBuf[:nonceSize]
|
||||
ciphertext := c.readBuf[nonceSize:n]
|
||||
_, err = c.aead.Open(p[:0], nonce, ciphertext, nil)
|
||||
nonce := buf[:nonceSize]
|
||||
ciphertext := buf[nonceSize:n]
|
||||
plaintext, err := c.aead.Open(p[:0], nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead open ", err)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, nil
|
||||
if len(plaintext) > len(p) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(plaintext))
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(p)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
if n < c.aead.NonceSize()+c.aead.Overhead() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
@@ -101,74 +79,40 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
ciphertext := p[nonceSize:n]
|
||||
_, err = c.aead.Open(ciphertext[:0], nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead open ", err)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, p[nonceSize:n-c.aead.Overhead()])
|
||||
|
||||
return n - int(c.Size()), addr, nil
|
||||
return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+int32(c.aead.NonceSize()):], p)
|
||||
// n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := c.writeBuf[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||
common.Must2(rand.Read(nonce))
|
||||
// copy(c.writeBuf[c.leaveSize+int32(nonceSize):], c.writeBuf[c.leaveSize+c.Size():n])
|
||||
plaintext := c.writeBuf[c.leaveSize+int32(nonceSize) : n-c.aead.Overhead()]
|
||||
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.aead.NonceSize()+c.aead.Overhead()+len(p)]
|
||||
copy(buf[c.aead.NonceSize():], p)
|
||||
p = buf[c.aead.NonceSize() : c.aead.NonceSize()+len(p)]
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
nonce := p[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||
nonce := buf[:nonceSize]
|
||||
common.Must2(rand.Read(nonce))
|
||||
copy(p[c.leaveSize+int32(nonceSize):], p[c.leaveSize+c.Size():])
|
||||
plaintext := p[c.leaveSize+int32(nonceSize) : len(p)-c.aead.Overhead()]
|
||||
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||
ciphertext := buf[nonceSize : c.aead.NonceSize()+c.aead.Overhead()+len(p)]
|
||||
_ = c.aead.Seal(ciphertext[:0], nonce, p, nil)
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.aead.NonceSize()+c.aead.Overhead()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *aes128gcmConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package original
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"net"
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type simple struct{}
|
||||
@@ -75,151 +74,77 @@ func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
type simpleConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
net.PacketConn
|
||||
aead cipher.AEAD
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &simpleConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
aead: &simple{},
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
PacketConn: raw,
|
||||
aead: &simple{},
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
}
|
||||
|
||||
func (c *simpleConn) Size() int32 {
|
||||
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
ciphertext := c.readBuf[:n]
|
||||
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
}
|
||||
|
||||
copy(p, opened)
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, nil
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||
if n < c.aead.Overhead() {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
ciphertext := p[:n]
|
||||
ciphertext := buf[:n]
|
||||
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("aead open").Base(err)
|
||||
errors.LogDebug(context.Background(), addr, " mask read err aead open ", err)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
if len(opened) > len(p) {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(opened))
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
copy(p, opened)
|
||||
|
||||
return n - int(c.Size()), addr, nil
|
||||
return n - c.aead.Overhead(), addr, nil
|
||||
}
|
||||
|
||||
func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
plaintext := c.writeBuf[c.leaveSize+c.Size() : n]
|
||||
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||
copy(c.writeBuf[c.leaveSize:], sealed)
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if c.aead.Overhead()+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
plaintext := p[c.leaveSize+c.Size():]
|
||||
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||
copy(p[c.leaveSize:], sealed)
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:c.aead.Overhead()+len(p)]
|
||||
copy(buf[c.aead.Overhead():], p)
|
||||
p = buf[c.aead.Overhead() : c.aead.Overhead()+len(p)]
|
||||
}
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
}
|
||||
_ = c.aead.Seal(buf[:0], nil, p, nil)
|
||||
|
||||
func (c *simpleConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:c.aead.Overhead()+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *simpleConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *simpleConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,22 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||
)
|
||||
|
||||
func TestSimpleSealInPlace(t *testing.T) {
|
||||
aead := original.NewSimple()
|
||||
|
||||
text := []byte("0123456789012")
|
||||
buf := make([]byte, 8192)
|
||||
|
||||
copy(buf[aead.Overhead():], text)
|
||||
plaintext := buf[aead.Overhead() : aead.Overhead()+len(text)]
|
||||
|
||||
sealed := aead.Seal(nil, nil, plaintext, nil)
|
||||
|
||||
_ = aead.Seal(buf[:0], nil, plaintext, nil)
|
||||
|
||||
assert.Equal(t, sealed, buf[:aead.Overhead()+len(text)])
|
||||
}
|
||||
|
||||
func TestOriginalBounce(t *testing.T) {
|
||||
aead := original.NewSimple()
|
||||
buf := make([]byte, aead.NonceSize()+aead.Overhead())
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package noise
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/noise/config.proto
|
||||
|
||||
package noise
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Item struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
|
||||
RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
|
||||
Packet []byte `protobuf:"bytes,3,opt,name=packet,proto3" json:"packet,omitempty"`
|
||||
DelayMin int64 `protobuf:"varint,4,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
|
||||
DelayMax int64 `protobuf:"varint,5,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Item) Reset() {
|
||||
*x = Item{}
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Item) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Item) ProtoMessage() {}
|
||||
|
||||
func (x *Item) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[0]
|
||||
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 Item.ProtoReflect.Descriptor instead.
|
||||
func (*Item) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Item) GetRandMin() int64 {
|
||||
if x != nil {
|
||||
return x.RandMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetRandMax() int64 {
|
||||
if x != nil {
|
||||
return x.RandMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetPacket() []byte {
|
||||
if x != nil {
|
||||
return x.Packet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Item) GetDelayMin() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Item) GetDelayMax() int64 {
|
||||
if x != nil {
|
||||
return x.DelayMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ResetMin int64 `protobuf:"varint,1,opt,name=reset_min,json=resetMin,proto3" json:"reset_min,omitempty"`
|
||||
ResetMax int64 `protobuf:"varint,2,opt,name=reset_max,json=resetMax,proto3" json:"reset_max,omitempty"`
|
||||
Items []*Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[1]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetResetMin() int64 {
|
||||
if x != nil {
|
||||
return x.ResetMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetResetMax() int64 {
|
||||
if x != nil {
|
||||
return x.ResetMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetItems() []*Item {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_noise_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\x8e\x01\n" +
|
||||
"\x04Item\x12\x19\n" +
|
||||
"\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" +
|
||||
"\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" +
|
||||
"\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" +
|
||||
"\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" +
|
||||
"\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" +
|
||||
"\x06Config\x12\x1b\n" +
|
||||
"\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" +
|
||||
"\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" +
|
||||
"\x05items\x18\x03 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" +
|
||||
"+com.xray.transport.internet.finalmask.noiseP\x01Z<github.com/xtls/xray-core/transport/internet/finalmask/noise\xaa\x02'Xray.Transport.Internet.Finalmask.Noiseb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_noise_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_noise_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_noise_config_proto_rawDesc), len(file_transport_internet_finalmask_noise_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_noise_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_noise_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_transport_internet_finalmask_noise_config_proto_goTypes = []any{
|
||||
(*Item)(nil), // 0: xray.transport.internet.finalmask.noise.Item
|
||||
(*Config)(nil), // 1: xray.transport.internet.finalmask.noise.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_noise_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.transport.internet.finalmask.noise.Config.items:type_name -> xray.transport.internet.finalmask.noise.Item
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_noise_config_proto_init() }
|
||||
func file_transport_internet_finalmask_noise_config_proto_init() {
|
||||
if File_transport_internet_finalmask_noise_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_noise_config_proto_rawDesc), len(file_transport_internet_finalmask_noise_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_noise_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_noise_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_noise_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_noise_config_proto = out.File
|
||||
file_transport_internet_finalmask_noise_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_noise_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.noise;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Noise";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/noise";
|
||||
option java_package = "com.xray.transport.internet.finalmask.noise";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Item {
|
||||
int64 rand_min = 1;
|
||||
int64 rand_max = 2;
|
||||
bytes packet = 3;
|
||||
int64 delay_min = 4;
|
||||
int64 delay_max = 5;
|
||||
}
|
||||
|
||||
message Config {
|
||||
int64 reset_min = 1;
|
||||
int64 reset_max = 2;
|
||||
repeated Item items = 3;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package noise
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
||||
type noiseConn struct {
|
||||
net.PacketConn
|
||||
config *Config
|
||||
m map[string]time.Time
|
||||
stop chan struct{}
|
||||
once sync.Once
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
conn := &noiseConn{
|
||||
PacketConn: raw,
|
||||
config: c,
|
||||
m: make(map[string]time.Time),
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
if conn.config.ResetMax > 0 {
|
||||
go conn.reset()
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *noiseConn) reset() {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.mutex.RLock()
|
||||
now := time.Now()
|
||||
timeOut := make([]string, 0, len(c.m))
|
||||
for key, last := range c.m {
|
||||
if now.After(last) {
|
||||
timeOut = append(timeOut, key)
|
||||
}
|
||||
}
|
||||
c.mutex.RUnlock()
|
||||
|
||||
for _, key := range timeOut {
|
||||
c.mutex.Lock()
|
||||
delete(c.m, key)
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
case <-c.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.mutex.RLock()
|
||||
_, ready := c.m[addr.String()]
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if !ready {
|
||||
c.mutex.Lock()
|
||||
_, ready = c.m[addr.String()]
|
||||
if !ready {
|
||||
for _, item := range c.config.Items {
|
||||
if item.RandMax > 0 {
|
||||
item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax))
|
||||
common.Must2(rand.Read(item.Packet))
|
||||
}
|
||||
c.PacketConn.WriteTo(item.Packet, addr)
|
||||
time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)
|
||||
}
|
||||
c.m[addr.String()] = time.Now().Add(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second)
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
return c.PacketConn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *noiseConn) Close() error {
|
||||
c.once.Do(func() {
|
||||
close(c.stop)
|
||||
})
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw, first, leaveSize)
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnServer(c, raw)
|
||||
}
|
||||
|
||||
@@ -1,147 +1,83 @@
|
||||
package salamander
|
||||
|
||||
import (
|
||||
"io"
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
type obfsPacketConn struct {
|
||||
first bool
|
||||
leaveSize int32
|
||||
|
||||
conn net.PacketConn
|
||||
type salamanderConn struct {
|
||||
net.PacketConn
|
||||
obfs *SalamanderObfuscator
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
ob, err := NewSalamanderObfuscator([]byte(c.Password))
|
||||
if err != nil {
|
||||
return nil, errors.New("salamander err").Base(err)
|
||||
}
|
||||
|
||||
conn := &obfsPacketConn{
|
||||
first: first,
|
||||
leaveSize: leaveSize,
|
||||
|
||||
conn: raw,
|
||||
obfs: ob,
|
||||
}
|
||||
|
||||
if first {
|
||||
conn.readBuf = make([]byte, 8192)
|
||||
conn.writeBuf = make([]byte, 8192)
|
||||
conn := &salamanderConn{
|
||||
PacketConn: raw,
|
||||
obfs: ob,
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw, first, leaveSize)
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) Size() int32 {
|
||||
return smSaltLen
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if c.first {
|
||||
c.readMutex.Lock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
if len(p) < n-int(c.Size()) {
|
||||
c.readMutex.Unlock()
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
}
|
||||
|
||||
c.obfs.Deobfuscate(c.readBuf[:n], p)
|
||||
|
||||
c.readMutex.Unlock()
|
||||
return n - int(c.Size()), addr, err
|
||||
func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buf := p
|
||||
if len(p) < finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
}
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
n, addr, err = c.PacketConn.ReadFrom(buf)
|
||||
if err != nil || n == 0 {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
if n < int(c.Size()) {
|
||||
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||
if n < smSaltLen {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short lenth ", n)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
c.obfs.Deobfuscate(p[:n], p)
|
||||
|
||||
return n - int(c.Size()), addr, err
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if c.first {
|
||||
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||
return 0, errors.New("too many masks")
|
||||
}
|
||||
|
||||
c.writeMutex.Lock()
|
||||
|
||||
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||
n += int(c.leaveSize) + int(c.Size())
|
||||
|
||||
c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n])
|
||||
|
||||
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||
|
||||
if err != nil {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if nn != n {
|
||||
c.writeMutex.Unlock()
|
||||
return 0, errors.New("nn != n")
|
||||
}
|
||||
|
||||
c.writeMutex.Unlock()
|
||||
return len(p), nil
|
||||
if len(p) < n-smSaltLen {
|
||||
errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-smSaltLen)
|
||||
return 0, addr, nil
|
||||
}
|
||||
|
||||
c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:])
|
||||
c.obfs.Deobfuscate(buf[:n], p)
|
||||
|
||||
return c.conn.WriteTo(p, addr)
|
||||
return n - smSaltLen, addr, nil
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if smSaltLen+len(p) > finalmask.UDPSize {
|
||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
var buf []byte
|
||||
if cap(p) != finalmask.UDPSize {
|
||||
buf = make([]byte, finalmask.UDPSize)
|
||||
} else {
|
||||
buf = p[:smSaltLen+len(p)]
|
||||
copy(buf[smSaltLen:], p)
|
||||
p = buf[smSaltLen:]
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
c.obfs.Obfuscate(p, buf)
|
||||
|
||||
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf[:smSaltLen+len(p)], addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var perm4 = [24][4]byte{
|
||||
{0, 1, 2, 3},
|
||||
{0, 1, 3, 2},
|
||||
{0, 2, 1, 3},
|
||||
{0, 2, 3, 1},
|
||||
{0, 3, 1, 2},
|
||||
{0, 3, 2, 1},
|
||||
{1, 0, 2, 3},
|
||||
{1, 0, 3, 2},
|
||||
{1, 2, 0, 3},
|
||||
{1, 2, 3, 0},
|
||||
{1, 3, 0, 2},
|
||||
{1, 3, 2, 0},
|
||||
{2, 0, 1, 3},
|
||||
{2, 0, 3, 1},
|
||||
{2, 1, 0, 3},
|
||||
{2, 1, 3, 0},
|
||||
{2, 3, 0, 1},
|
||||
{2, 3, 1, 0},
|
||||
{3, 0, 1, 2},
|
||||
{3, 0, 2, 1},
|
||||
{3, 1, 0, 2},
|
||||
{3, 1, 2, 0},
|
||||
{3, 2, 0, 1},
|
||||
{3, 2, 1, 0},
|
||||
}
|
||||
|
||||
type codec struct {
|
||||
tables []*table
|
||||
rng *rand.Rand
|
||||
paddingChance int
|
||||
tableIndex int
|
||||
}
|
||||
|
||||
func newCodec(tables []*table, pMin, pMax int) *codec {
|
||||
if len(tables) == 0 {
|
||||
tables = nil
|
||||
}
|
||||
rng := newSeededRand()
|
||||
return &codec{
|
||||
tables: tables,
|
||||
rng: rng,
|
||||
paddingChance: pickPaddingChance(rng, pMin, pMax),
|
||||
}
|
||||
}
|
||||
|
||||
func pickPaddingChance(rng *rand.Rand, pMin, pMax int) int {
|
||||
if pMin < 0 {
|
||||
pMin = 0
|
||||
}
|
||||
if pMax < pMin {
|
||||
pMax = pMin
|
||||
}
|
||||
if pMin > 100 {
|
||||
pMin = 100
|
||||
}
|
||||
if pMax > 100 {
|
||||
pMax = 100
|
||||
}
|
||||
if pMax == pMin {
|
||||
return pMin
|
||||
}
|
||||
return pMin + rng.Intn(pMax-pMin+1)
|
||||
}
|
||||
|
||||
func (c *codec) shouldPad() bool {
|
||||
if c.paddingChance <= 0 {
|
||||
return false
|
||||
}
|
||||
if c.paddingChance >= 100 {
|
||||
return true
|
||||
}
|
||||
return c.rng.Intn(100) < c.paddingChance
|
||||
}
|
||||
|
||||
func (c *codec) currentTable() *table {
|
||||
if len(c.tables) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.tables[c.tableIndex%len(c.tables)]
|
||||
}
|
||||
|
||||
func (c *codec) randomPadding(t *table) byte {
|
||||
pool := t.layout.paddingPool
|
||||
return pool[c.rng.Intn(len(pool))]
|
||||
}
|
||||
|
||||
func (c *codec) encode(in []byte) ([]byte, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
out := make([]byte, 0, len(in)*6+8)
|
||||
for _, b := range in {
|
||||
t := c.currentTable()
|
||||
if t == nil {
|
||||
return nil, fmt.Errorf("sudoku table set missing")
|
||||
}
|
||||
if c.shouldPad() {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
|
||||
enc := t.encode[b]
|
||||
if len(enc) == 0 {
|
||||
return nil, fmt.Errorf("sudoku encode table missing for byte %d", b)
|
||||
}
|
||||
|
||||
hints := enc[c.rng.Intn(len(enc))]
|
||||
perm := perm4[c.rng.Intn(len(perm4))]
|
||||
for _, idx := range perm {
|
||||
if c.shouldPad() {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
out = append(out, hints[idx])
|
||||
}
|
||||
c.tableIndex++
|
||||
}
|
||||
|
||||
if c.shouldPad() {
|
||||
if t := c.currentTable(); t != nil {
|
||||
out = append(out, c.randomPadding(t))
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func decodeBytes(tables []*table, tableIndex *int, in []byte, hintBuf []byte, out []byte) ([]byte, []byte, error) {
|
||||
if len(tables) == 0 {
|
||||
return hintBuf, out, fmt.Errorf("sudoku table set missing")
|
||||
}
|
||||
for _, b := range in {
|
||||
t := tables[*tableIndex%len(tables)]
|
||||
if !t.layout.isHint(b) {
|
||||
continue
|
||||
}
|
||||
|
||||
hintBuf = append(hintBuf, b)
|
||||
if len(hintBuf) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
keyBytes := sort4([4]byte{hintBuf[0], hintBuf[1], hintBuf[2], hintBuf[3]})
|
||||
key := packKey(keyBytes)
|
||||
decoded, ok := t.decode[key]
|
||||
if !ok {
|
||||
return hintBuf[:0], out, fmt.Errorf("invalid sudoku hint tuple")
|
||||
}
|
||||
|
||||
out = append(out, decoded)
|
||||
hintBuf = hintBuf[:0]
|
||||
*tableIndex++
|
||||
}
|
||||
|
||||
return hintBuf, out, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
// Sudoku in finalmask mode is a pure appearance transform with no standalone handshake.
|
||||
// TCP always keeps classic sudoku on uplink and uses packed downlink optimization on server writes.
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return newPackedDirectionalConn(raw, c, true)
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return newPackedDirectionalConn(raw, c, false)
|
||||
}
|
||||
|
||||
func newPackedDirectionalConn(raw net.Conn, config *Config, readPacked bool) (net.Conn, error) {
|
||||
pureReader, pureWriter, err := newPureReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packedReader, packedWriter, err := newPackedReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader, writer := pureReader, pureWriter
|
||||
if readPacked {
|
||||
reader = packedReader
|
||||
} else {
|
||||
writer = packedWriter
|
||||
}
|
||||
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
if level != levelCount {
|
||||
return nil, errors.New("sudoku udp mask must be the innermost mask in chain")
|
||||
}
|
||||
return NewUDPConn(raw, c)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
if level != levelCount {
|
||||
return nil, errors.New("sudoku udp mask must be the innermost mask in chain")
|
||||
}
|
||||
return NewUDPConn(raw, c)
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/sudoku/config.proto
|
||||
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||
Ascii string `protobuf:"bytes,2,opt,name=ascii,proto3" json:"ascii,omitempty"`
|
||||
CustomTable string `protobuf:"bytes,3,opt,name=custom_table,json=customTable,proto3" json:"custom_table,omitempty"`
|
||||
PaddingMin uint32 `protobuf:"varint,4,opt,name=padding_min,json=paddingMin,proto3" json:"padding_min,omitempty"`
|
||||
PaddingMax uint32 `protobuf:"varint,5,opt,name=padding_max,json=paddingMax,proto3" json:"padding_max,omitempty"`
|
||||
CustomTables []string `protobuf:"bytes,7,rep,name=custom_tables,json=customTables,proto3" json:"custom_tables,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_sudoku_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_sudoku_config_proto_msgTypes[0]
|
||||
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 Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_sudoku_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetAscii() string {
|
||||
if x != nil {
|
||||
return x.Ascii
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetCustomTable() string {
|
||||
if x != nil {
|
||||
return x.CustomTable
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetPaddingMin() uint32 {
|
||||
if x != nil {
|
||||
return x.PaddingMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPaddingMax() uint32 {
|
||||
if x != nil {
|
||||
return x.PaddingMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetCustomTables() []string {
|
||||
if x != nil {
|
||||
return x.CustomTables
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_sudoku_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_sudoku_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"0transport/internet/finalmask/sudoku/config.proto\x12(xray.transport.internet.finalmask.sudoku\"\xc4\x01\n" +
|
||||
"\x06Config\x12\x1a\n" +
|
||||
"\bpassword\x18\x01 \x01(\tR\bpassword\x12\x14\n" +
|
||||
"\x05ascii\x18\x02 \x01(\tR\x05ascii\x12!\n" +
|
||||
"\fcustom_table\x18\x03 \x01(\tR\vcustomTable\x12\x1f\n" +
|
||||
"\vpadding_min\x18\x04 \x01(\rR\n" +
|
||||
"paddingMin\x12\x1f\n" +
|
||||
"\vpadding_max\x18\x05 \x01(\rR\n" +
|
||||
"paddingMax\x12#\n" +
|
||||
"\rcustom_tables\x18\a \x03(\tR\fcustomTablesB\x9a\x01\n" +
|
||||
",com.xray.transport.internet.finalmask.sudokuP\x01Z=github.com/xtls/xray-core/transport/internet/finalmask/sudoku\xaa\x02(Xray.Transport.Internet.Finalmask.Sudokub\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_sudoku_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_sudoku_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_sudoku_config_proto_rawDesc), len(file_transport_internet_finalmask_sudoku_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_sudoku_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.sudoku.Config
|
||||
}
|
||||
var file_transport_internet_finalmask_sudoku_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_sudoku_config_proto_init() }
|
||||
func file_transport_internet_finalmask_sudoku_config_proto_init() {
|
||||
if File_transport_internet_finalmask_sudoku_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_sudoku_config_proto_rawDesc), len(file_transport_internet_finalmask_sudoku_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_sudoku_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_sudoku_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_sudoku_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_sudoku_config_proto = out.File
|
||||
file_transport_internet_finalmask_sudoku_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_sudoku_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.sudoku;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Sudoku";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/sudoku";
|
||||
option java_package = "com.xray.transport.internet.finalmask.sudoku";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
string password = 1;
|
||||
string ascii = 2;
|
||||
string custom_table = 3;
|
||||
uint32 padding_min = 4;
|
||||
uint32 padding_max = 5;
|
||||
repeated string custom_tables = 7;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
const ioBufferSize = 32 * 1024
|
||||
|
||||
var _ finalmask.TcpMaskConn = (*wrappedConn)(nil)
|
||||
|
||||
type streamDecoder interface {
|
||||
decodeChunk(in []byte, pending []byte) ([]byte, error)
|
||||
reset()
|
||||
}
|
||||
|
||||
type streamReader struct {
|
||||
reader *bufio.Reader
|
||||
rawBuf []byte
|
||||
pending []byte
|
||||
decode streamDecoder
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newStreamReader(raw net.Conn, decode streamDecoder) io.Reader {
|
||||
return &streamReader{
|
||||
reader: bufio.NewReaderSize(raw, ioBufferSize),
|
||||
rawBuf: make([]byte, ioBufferSize),
|
||||
pending: make([]byte, 0, 4096),
|
||||
decode: decode,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *streamReader) Read(p []byte) (int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if n, ok := drainPending(p, &r.pending); ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
for len(r.pending) == 0 {
|
||||
nr, rErr := r.reader.Read(r.rawBuf)
|
||||
if nr > 0 {
|
||||
var dErr error
|
||||
r.pending, dErr = r.decode.decodeChunk(r.rawBuf[:nr], r.pending)
|
||||
if dErr != nil {
|
||||
return 0, dErr
|
||||
}
|
||||
}
|
||||
|
||||
if rErr != nil {
|
||||
if rErr == io.EOF {
|
||||
r.decode.reset()
|
||||
if len(r.pending) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0, rErr
|
||||
}
|
||||
}
|
||||
|
||||
n, _ := drainPending(p, &r.pending)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type streamWriter struct {
|
||||
conn net.Conn
|
||||
encode func([]byte) ([]byte, error)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newStreamWriter(raw net.Conn, encode func([]byte) ([]byte, error)) io.Writer {
|
||||
return &streamWriter{
|
||||
conn: raw,
|
||||
encode: encode,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *streamWriter) Write(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
encoded, err := w.encode(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := writeAll(w.conn, encoded); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
type closeWriteConn interface {
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func newWrappedConn(raw net.Conn, reader io.Reader, writer io.Writer) net.Conn {
|
||||
return &wrappedConn{
|
||||
Conn: raw,
|
||||
reader: reader,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Read(p []byte) (int, error) {
|
||||
return c.reader.Read(p)
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Write(p []byte) (int, error) {
|
||||
return c.writer.Write(p)
|
||||
}
|
||||
|
||||
func (c *wrappedConn) TcpMaskConn() {}
|
||||
|
||||
func (c *wrappedConn) RawConn() net.Conn {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *wrappedConn) Splice() bool {
|
||||
// Sudoku transforms the entire stream; bypassing it would disable masking.
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *wrappedConn) CloseWrite() error {
|
||||
if raw, ok := c.Conn.(closeWriteConn); ok {
|
||||
return raw.CloseWrite()
|
||||
}
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
func NewTCPConn(raw net.Conn, config *Config) (net.Conn, error) {
|
||||
reader, writer, err := newPureReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func newPureReaderWriter(raw net.Conn, config *Config) (io.Reader, io.Writer, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
c := newCodec(tables, pMin, pMax)
|
||||
return newStreamReader(raw, newHintStreamDecoder(tables)), newStreamWriter(raw, c.encode), nil
|
||||
}
|
||||
|
||||
type hintStreamDecoder struct {
|
||||
tables []*table
|
||||
tableIndex int
|
||||
hintBuf []byte
|
||||
}
|
||||
|
||||
func newHintStreamDecoder(tables []*table) *hintStreamDecoder {
|
||||
return &hintStreamDecoder{
|
||||
tables: tables,
|
||||
hintBuf: make([]byte, 0, 4),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *hintStreamDecoder) decodeChunk(in []byte, pending []byte) ([]byte, error) {
|
||||
var err error
|
||||
d.hintBuf, pending, err = decodeBytes(d.tables, &d.tableIndex, in, d.hintBuf, pending)
|
||||
return pending, err
|
||||
}
|
||||
|
||||
func (d *hintStreamDecoder) reset() {}
|
||||
|
||||
func drainPending(p []byte, pending *[]byte) (int, bool) {
|
||||
if len(*pending) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
n := copy(p, *pending)
|
||||
if n >= len(*pending) {
|
||||
*pending = (*pending)[:0]
|
||||
return n, true
|
||||
}
|
||||
|
||||
remaining := len(*pending) - n
|
||||
copy(*pending, (*pending)[n:])
|
||||
*pending = (*pending)[:remaining]
|
||||
return n, true
|
||||
}
|
||||
|
||||
func writeAll(conn net.Conn, b []byte) error {
|
||||
for len(b) > 0 {
|
||||
n, err := conn.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type packedEncoder struct {
|
||||
layouts []*byteLayout
|
||||
codec *codec
|
||||
groupIndex int
|
||||
}
|
||||
|
||||
func newPackedEncoder(tables []*table, pMin, pMax int) *packedEncoder {
|
||||
layouts := make([]*byteLayout, 0, len(tables))
|
||||
for _, t := range tables {
|
||||
layouts = append(layouts, t.layout)
|
||||
}
|
||||
if len(layouts) == 0 {
|
||||
layouts = append(layouts, entropyLayout())
|
||||
}
|
||||
return &packedEncoder{
|
||||
layouts: layouts,
|
||||
codec: newCodec(nil, pMin, pMax),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *packedEncoder) encode(p []byte) ([]byte, error) {
|
||||
out := make([]byte, 0, len(p)*2+8)
|
||||
var bitBuf uint64
|
||||
var bitCount uint8
|
||||
|
||||
for _, b := range p {
|
||||
bitBuf = (bitBuf << 8) | uint64(b)
|
||||
bitCount += 8
|
||||
|
||||
for bitCount >= 6 {
|
||||
bitCount -= 6
|
||||
layout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
group := byte(bitBuf >> bitCount)
|
||||
out = e.maybePad(out, layout)
|
||||
out = append(out, layout.encodeGroup(group&0x3f))
|
||||
e.groupIndex++
|
||||
if bitCount > 0 {
|
||||
bitBuf &= (uint64(1) << bitCount) - 1
|
||||
} else {
|
||||
bitBuf = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bitCount > 0 {
|
||||
layout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
group := byte(bitBuf << (6 - bitCount))
|
||||
out = e.maybePad(out, layout)
|
||||
out = append(out, layout.encodeGroup(group&0x3f))
|
||||
e.groupIndex++
|
||||
nextLayout := e.layouts[e.groupIndex%len(e.layouts)]
|
||||
out = append(out, nextLayout.padMarker)
|
||||
}
|
||||
|
||||
out = e.maybePad(out, e.layouts[e.groupIndex%len(e.layouts)])
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (e *packedEncoder) maybePad(out []byte, layout *byteLayout) []byte {
|
||||
if !e.codec.shouldPad() {
|
||||
return out
|
||||
}
|
||||
if len(layout.paddingPool) == 1 {
|
||||
return append(out, layout.paddingPool[0])
|
||||
}
|
||||
for {
|
||||
b := layout.paddingPool[e.codec.rng.Intn(len(layout.paddingPool))]
|
||||
if b != layout.padMarker {
|
||||
return append(out, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type packedStreamDecoder struct {
|
||||
layouts []*byteLayout
|
||||
groupIndex int
|
||||
bitBuf uint64
|
||||
bitCount int
|
||||
}
|
||||
|
||||
func (d *packedStreamDecoder) decodeChunk(in []byte, pending []byte) ([]byte, error) {
|
||||
var err error
|
||||
d.bitBuf, d.bitCount, d.groupIndex, pending, err = decodePackedBytes(
|
||||
d.layouts,
|
||||
in,
|
||||
d.bitBuf,
|
||||
d.bitCount,
|
||||
d.groupIndex,
|
||||
pending,
|
||||
)
|
||||
return pending, err
|
||||
}
|
||||
|
||||
func (d *packedStreamDecoder) reset() {
|
||||
d.bitBuf = 0
|
||||
d.bitCount = 0
|
||||
}
|
||||
|
||||
func NewPackedTCPConn(raw net.Conn, config *Config) (net.Conn, error) {
|
||||
reader, writer, err := newPackedReaderWriter(raw, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWrappedConn(raw, reader, writer), nil
|
||||
}
|
||||
|
||||
func newPackedReaderWriter(raw net.Conn, config *Config) (io.Reader, io.Writer, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
encoder := newPackedEncoder(tables, pMin, pMax)
|
||||
decoder := &packedStreamDecoder{
|
||||
layouts: tablesToLayouts(tables),
|
||||
}
|
||||
return newStreamReader(raw, decoder), newStreamWriter(raw, encoder.encode), nil
|
||||
}
|
||||
|
||||
func tablesToLayouts(tables []*table) []*byteLayout {
|
||||
layouts := make([]*byteLayout, 0, len(tables))
|
||||
for _, t := range tables {
|
||||
layouts = append(layouts, t.layout)
|
||||
}
|
||||
if len(layouts) == 0 {
|
||||
layouts = append(layouts, entropyLayout())
|
||||
}
|
||||
return layouts
|
||||
}
|
||||
|
||||
func decodePackedBytes(
|
||||
layouts []*byteLayout,
|
||||
in []byte,
|
||||
bitBuf uint64,
|
||||
bitCount int,
|
||||
groupIndex int,
|
||||
out []byte,
|
||||
) (uint64, int, int, []byte, error) {
|
||||
if len(layouts) == 0 {
|
||||
return bitBuf, bitCount, groupIndex, out, fmt.Errorf("sudoku layout set missing")
|
||||
}
|
||||
for _, b := range in {
|
||||
layout := layouts[groupIndex%len(layouts)]
|
||||
if !layout.isHint(b) {
|
||||
if b == layout.padMarker {
|
||||
bitBuf = 0
|
||||
bitCount = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
group, ok := layout.decodeGroup(b)
|
||||
if !ok {
|
||||
return bitBuf, bitCount, groupIndex, out, fmt.Errorf("invalid packed sudoku byte: %d", b)
|
||||
}
|
||||
groupIndex++
|
||||
|
||||
bitBuf = (bitBuf << 6) | uint64(group)
|
||||
bitCount += 6
|
||||
|
||||
for bitCount >= 8 {
|
||||
bitCount -= 8
|
||||
out = append(out, byte(bitBuf>>bitCount))
|
||||
if bitCount > 0 {
|
||||
bitBuf &= (uint64(1) << bitCount) - 1
|
||||
} else {
|
||||
bitBuf = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitBuf, bitCount, groupIndex, out, nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type udpConn struct {
|
||||
conn net.PacketConn
|
||||
tables []*table
|
||||
pMin int
|
||||
pMax int
|
||||
|
||||
readBuf []byte
|
||||
|
||||
readMu sync.Mutex
|
||||
writeMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewUDPConn(raw net.PacketConn, config *Config) (net.PacketConn, error) {
|
||||
tables, err := getTables(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pMin, pMax := normalizedPadding(config)
|
||||
return &udpConn{
|
||||
conn: raw,
|
||||
tables: tables,
|
||||
pMin: pMin,
|
||||
pMax: pMax,
|
||||
readBuf: make([]byte, 65535),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *udpConn) Size() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
c.readMu.Lock()
|
||||
defer c.readMu.Unlock()
|
||||
|
||||
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
decoded := make([]byte, 0, n/4+1)
|
||||
hints := make([]byte, 0, 4)
|
||||
tableIndex := 0
|
||||
hints, decoded, err = decodeBytes(c.tables, &tableIndex, c.readBuf[:n], hints, decoded)
|
||||
if err != nil {
|
||||
return 0, addr, err
|
||||
}
|
||||
if len(hints) != 0 {
|
||||
return 0, addr, io.ErrUnexpectedEOF
|
||||
}
|
||||
if len(p) < len(decoded) {
|
||||
return 0, addr, io.ErrShortBuffer
|
||||
}
|
||||
copy(p, decoded)
|
||||
return len(decoded), addr, nil
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.writeMu.Lock()
|
||||
defer c.writeMu.Unlock()
|
||||
|
||||
// UDP decoding restarts at table 0 for every datagram, so encoding must do the same.
|
||||
encoded, err := newCodec(c.tables, c.pMin, c.pMax).encode(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nn, err := c.conn.WriteTo(encoded, addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if nn != len(encoded) {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *udpConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *udpConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *udpConn) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *udpConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *udpConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user