mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
284 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f6c15a09b3 | ||
![]() |
0a099d972b | ||
![]() |
837d7d885f | ||
![]() |
18e5b0963f | ||
![]() |
90d915ea05 | ||
![]() |
d9994538bc | ||
![]() |
69aa3f48cc | ||
![]() |
ca32496a38 | ||
![]() |
d3060c28f8 | ||
![]() |
ac8109eef8 | ||
![]() |
197bc78ea1 | ||
![]() |
039e5f2078 | ||
![]() |
242f3b0e0b | ||
![]() |
b4c1a56026 | ||
![]() |
9f8e9e8e64 | ||
![]() |
06c9e50c52 | ||
![]() |
4f601530fa | ||
![]() |
b33b0bc89d | ||
![]() |
01b7e5e9be | ||
![]() |
24a2be43ef | ||
![]() |
29d7865d78 | ||
![]() |
05d24d6827 | ||
![]() |
76b27a37cb | ||
![]() |
15cf31f30a | ||
![]() |
54ad0e96a0 | ||
![]() |
be23d5d3b7 | ||
![]() |
67affe3753 | ||
![]() |
2c0a89f7dc | ||
![]() |
a4d1509c23 | ||
![]() |
f4ab8d7e8b | ||
![]() |
beb603af06 | ||
![]() |
a0d06f3a97 | ||
![]() |
526c6789ed | ||
![]() |
6872be5cc3 | ||
![]() |
c6b78318cb | ||
![]() |
f89998fc77 | ||
![]() |
0573760346 | ||
![]() |
172f353bd7 | ||
![]() |
55efac7236 | ||
![]() |
f57ec13880 | ||
![]() |
f1e35ad9d4 | ||
![]() |
d6801ab031 | ||
![]() |
c3322294be | ||
![]() |
836e84b851 | ||
![]() |
4a0b45d1ff | ||
![]() |
c04c333afc | ||
![]() |
9e5bc07bf2 | ||
![]() |
4c8ee0af50 | ||
![]() |
25ea69fc3a | ||
![]() |
a4790133d2 | ||
![]() |
ccba465590 | ||
![]() |
6526e74d49 | ||
![]() |
7b54255cc1 | ||
![]() |
43bc929030 | ||
![]() |
fbc7c1cf84 | ||
![]() |
cc4be239cf | ||
![]() |
2d898480be | ||
![]() |
55dc26f228 | ||
![]() |
9401d65ef1 | ||
![]() |
c38179a67f | ||
![]() |
a5b297f968 | ||
![]() |
d208fd31c9 | ||
![]() |
2e201c57cc | ||
![]() |
336b2daeb9 | ||
![]() |
c8b4580869 | ||
![]() |
03b8c094de | ||
![]() |
267d93f7bd | ||
![]() |
7f16f4ccd9 | ||
![]() |
9e07d8304d | ||
![]() |
9d3de59d3f | ||
![]() |
4d5c3195d2 | ||
![]() |
c7358a32f5 | ||
![]() |
e1cd1fd33e | ||
![]() |
82003f28b2 | ||
![]() |
4d2e2b24d3 | ||
![]() |
15999e5c2a | ||
![]() |
48ff0d92c9 | ||
![]() |
229e2513b5 | ||
![]() |
9046eda5ce | ||
![]() |
f32921df30 | ||
![]() |
c3faa8b7ac | ||
![]() |
00c9576118 | ||
![]() |
fa7300e910 | ||
![]() |
b57d3fa869 | ||
![]() |
53833c2323 | ||
![]() |
f176ec54ee | ||
![]() |
bf35e9dcd6 | ||
![]() |
dc72cf2c78 | ||
![]() |
74416570d4 | ||
![]() |
b70912799b | ||
![]() |
15bb23e4ec | ||
![]() |
915690b9ef | ||
![]() |
f571aa72df | ||
![]() |
fb212905bd | ||
![]() |
3fb67f065a | ||
![]() |
77d2f9edd7 | ||
![]() |
7added2693 | ||
![]() |
f536359367 | ||
![]() |
0d5c62e44d | ||
![]() |
620eb63c1b | ||
![]() |
32ce7cd730 | ||
![]() |
717518cb5f | ||
![]() |
7b8ff01114 | ||
![]() |
ed960cc885 | ||
![]() |
1bf3a632ca | ||
![]() |
ff5ce767df | ||
![]() |
8c0d3c0257 | ||
![]() |
9bc1564b0a | ||
![]() |
6a85682716 | ||
![]() |
6f61021f7a | ||
![]() |
c0ceebe709 | ||
![]() |
eaf401eda9 | ||
![]() |
11ec77bc76 | ||
![]() |
3b2ff95a9b | ||
![]() |
3db7d44fc2 | ||
![]() |
c4fbdf1b78 | ||
![]() |
c9b6fc0104 | ||
![]() |
d7ac6946d2 | ||
![]() |
48a75fc340 | ||
![]() |
a55cf1d0bf | ||
![]() |
f35ded79ad | ||
![]() |
f3104b8684 | ||
![]() |
bc4de6a026 | ||
![]() |
3e4e050313 | ||
![]() |
b8e8229242 | ||
![]() |
a8fa5bf516 | ||
![]() |
4a3f3ef775 | ||
![]() |
5858726233 | ||
![]() |
b13c3f053a | ||
![]() |
2e30093ffd | ||
![]() |
1d7c40d728 | ||
![]() |
143229b148 | ||
![]() |
79eda46c62 | ||
![]() |
d87758d46f | ||
![]() |
e5e9e58d66 | ||
![]() |
0565589b8b | ||
![]() |
3cf1b7e601 | ||
![]() |
e18b52a5df | ||
![]() |
691b2b1c73 | ||
![]() |
494a10971b | ||
![]() |
34b3f0204a | ||
![]() |
18fd768166 | ||
![]() |
07d4cfd81a | ||
![]() |
d3efd2d24f | ||
![]() |
aa846b8420 | ||
![]() |
8006430c15 | ||
![]() |
04278a8940 | ||
![]() |
48f7cc2132 | ||
![]() |
8e75e9d763 | ||
![]() |
c7f7e07821 | ||
![]() |
ff2cdcdaaa | ||
![]() |
095e6a714b | ||
![]() |
15d66974cc | ||
![]() |
8ef609ff46 | ||
![]() |
fffd908db2 | ||
![]() |
9b293013e6 | ||
![]() |
5e695327b1 | ||
![]() |
341d317d0c | ||
![]() |
87c1c426d5 | ||
![]() |
2637970976 | ||
![]() |
da0b13cca0 | ||
![]() |
1f93cbbc5d | ||
![]() |
149e2247e8 | ||
![]() |
51859425d7 | ||
![]() |
93c7ebe382 | ||
![]() |
ed9b99cfc8 | ||
![]() |
825fb9f8bd | ||
![]() |
8117b66949 | ||
![]() |
8cf23f1947 | ||
![]() |
43477109f5 | ||
![]() |
632cb34116 | ||
![]() |
79fdc5f6f7 | ||
![]() |
6bb735d103 | ||
![]() |
c21595a937 | ||
![]() |
92795677f9 | ||
![]() |
debd2e3ba8 | ||
![]() |
84537e98c4 | ||
![]() |
ed9ffa82c9 | ||
![]() |
b55b6bb5e0 | ||
![]() |
35d5a7fe93 | ||
![]() |
71a9a6dd55 | ||
![]() |
2096821c07 | ||
![]() |
4140bcd11a | ||
![]() |
59602db02d | ||
![]() |
76638d793c | ||
![]() |
b67314796f | ||
![]() |
340234166b | ||
![]() |
50b5ea5a54 | ||
![]() |
5e323958b6 | ||
![]() |
2b46178ff9 | ||
![]() |
c835622b37 | ||
![]() |
05483cc729 | ||
![]() |
b4e11e1856 | ||
![]() |
f956b142d8 | ||
![]() |
7d52ded2a3 | ||
![]() |
e459daaaf6 | ||
![]() |
00230a74d5 | ||
![]() |
9480bc0379 | ||
![]() |
0eed604ba3 | ||
![]() |
52930a16b2 | ||
![]() |
0f2a6f2088 | ||
![]() |
9f365b7b45 | ||
![]() |
a809596829 | ||
![]() |
7d946562eb | ||
![]() |
d4f18b1342 | ||
![]() |
ba4ce4c24f | ||
![]() |
a1c3aed9d3 | ||
![]() |
ec8904066a | ||
![]() |
def30a0882 | ||
![]() |
bd0cf955c7 | ||
![]() |
e91f033c01 | ||
![]() |
f1b70b4155 | ||
![]() |
cc67e83a8f | ||
![]() |
ea9246ec7f | ||
![]() |
0c9bd21b59 | ||
![]() |
34aab75484 | ||
![]() |
c3505632fd | ||
![]() |
6f93ef7736 | ||
![]() |
c4a307e84d | ||
![]() |
f1d753f069 | ||
![]() |
91ce752405 | ||
![]() |
f0b58d9ee0 | ||
![]() |
d56f38d38e | ||
![]() |
7b72e19e16 | ||
![]() |
4e5752f93e | ||
![]() |
36906d018d | ||
![]() |
79f3057687 | ||
![]() |
c375b144f8 | ||
![]() |
1edce576ca | ||
![]() |
cf7e675c45 | ||
![]() |
b6391cbbe1 | ||
![]() |
398375d76f | ||
![]() |
3b77e26fa7 | ||
![]() |
22706041d1 | ||
![]() |
087f0d1240 | ||
![]() |
3f64f3206c | ||
![]() |
f046feb9ca | ||
![]() |
778992eeb9 | ||
![]() |
5f3949a838 | ||
![]() |
95af983154 | ||
![]() |
63895caf60 | ||
![]() |
dcba88e511 | ||
![]() |
244db57398 | ||
![]() |
3bfd6853f4 | ||
![]() |
00c4b6f44f | ||
![]() |
2f86c7c795 | ||
![]() |
5e18ae68b7 | ||
![]() |
8caf690680 | ||
![]() |
b413066012 | ||
![]() |
c9df755426 | ||
![]() |
11518fe089 | ||
![]() |
4fb4dacae7 | ||
![]() |
99f45a2d7f | ||
![]() |
24a0ae0ea9 | ||
![]() |
e8400e65f0 | ||
![]() |
393d211d1e | ||
![]() |
c6550aecfc | ||
![]() |
430235a1cf | ||
![]() |
755268b7d4 | ||
![]() |
70306c4ec8 | ||
![]() |
35eb165f63 | ||
![]() |
91ffb7617d | ||
![]() |
09ca5c7341 | ||
![]() |
97b7460786 | ||
![]() |
fd508e92ca | ||
![]() |
441e770b96 | ||
![]() |
3d3801fc25 | ||
![]() |
7f00df4f02 | ||
![]() |
94c249a8c8 | ||
![]() |
c1a54ae58e | ||
![]() |
03ade23022 | ||
![]() |
8bff676fb0 | ||
![]() |
d78d1f119c | ||
![]() |
cd93e10d58 | ||
![]() |
7dcf08c5ef | ||
![]() |
22e46b846c | ||
![]() |
496b2c02c5 | ||
![]() |
cf1ee095a2 | ||
![]() |
b3ab94ef5b | ||
![]() |
b6f77e4944 | ||
![]() |
d4f38ac339 | ||
![]() |
ebf2873146 | ||
![]() |
41ce6ccf9f | ||
![]() |
42284a757c |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -9,3 +9,7 @@ updates:
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
21
.github/docker/Dockerfile
vendored
Normal file
21
.github/docker/Dockerfile
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
FROM --platform=${TARGETPLATFORM} alpine:latest
|
||||
WORKDIR /root
|
||||
COPY .github/docker/files/config.json /etc/xray/config.json
|
||||
COPY --from=build /src/xray /usr/bin/xray
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache tzdata ca-certificates \
|
||||
&& mkdir -p /var/log/xray /usr/share/xray \
|
||||
&& chmod +x /usr/bin/xray \
|
||||
&& wget -O /usr/share/xray/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
|
||||
&& wget -O /usr/share/xray/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
|
||||
VOLUME /etc/xray
|
||||
ENV TZ=Asia/Shanghai
|
||||
CMD [ "/usr/bin/xray", "-config", "/etc/xray/config.json" ]
|
19
.github/docker/files/config.json
vendored
Normal file
19
.github/docker/files/config.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"inbounds": [{
|
||||
"port": 9000,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "1eb6e917-774b-4a84-aff6-b058577c60a5",
|
||||
"level": 1,
|
||||
"alterId": 64
|
||||
}
|
||||
]
|
||||
}
|
||||
}],
|
||||
"outbounds": [{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
}]
|
||||
}
|
45
.github/workflows/docker.yml
vendored
Normal file
45
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Build docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
||||
flavor: latest=true
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- # Add support for more platforms with QEMU (optional)
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
file: .github/docker/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
95
.github/workflows/release.yml
vendored
95
.github/workflows/release.yml
vendored
@@ -20,7 +20,53 @@ on:
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Restore Cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
|
||||
- name: Update Geodat
|
||||
id: update
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
timeout_minutes: 60
|
||||
retry_wait_seconds: 60
|
||||
max_attempts: 60
|
||||
command: |
|
||||
[ -d 'resources' ] || mkdir resources
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
|
||||
continue
|
||||
else
|
||||
echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..."
|
||||
curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
echo "unhit=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Save Cache
|
||||
uses: actions/cache/save@v3
|
||||
if: ${{ steps.update.outputs.unhit }}
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
@@ -110,20 +156,19 @@ jobs:
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Show workflow information
|
||||
id: get_filename
|
||||
run: |
|
||||
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.17.6
|
||||
go-version: '1.20'
|
||||
check-latest: true
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
@@ -143,6 +188,11 @@ jobs:
|
||||
mkdir -p build_assets
|
||||
go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
- name: Build background Xray on Windows
|
||||
if: matrix.goos == 'windows'
|
||||
run: |
|
||||
go build -v -o build_assets/wxray.exe -trimpath -ldflags "-s -w -H windowsgui -buildid=" ./main
|
||||
|
||||
- name: Build Mips softfloat Xray
|
||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||
run: |
|
||||
@@ -154,31 +204,26 @@ jobs:
|
||||
cd ./build_assets || exit 1
|
||||
mv xray xray.exe
|
||||
|
||||
- name: Prepare to release
|
||||
- name: Restore Cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
|
||||
- name: Copy README.md & LICENSE
|
||||
run: |
|
||||
mv -f resources/* build_assets
|
||||
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Downloading ${FILE_NAME}..."
|
||||
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
done
|
||||
|
||||
- name: Create ZIP archive
|
||||
shell: bash
|
||||
run: |
|
||||
pushd build_assets || exit 1
|
||||
touch -mt $(date +%Y01010000) *
|
||||
zip -9vr ../Xray-$ASSET_NAME.zip .
|
||||
zip -9vr ../Xray-${{ env.ASSET_NAME }}.zip .
|
||||
popd || exit 1
|
||||
FILE=./Xray-$ASSET_NAME.zip
|
||||
FILE=./Xray-${{ env.ASSET_NAME }}.zip
|
||||
DGST=$FILE.dgst
|
||||
for METHOD in {"md5","sha1","sha256","sha512"}
|
||||
do
|
||||
@@ -187,20 +232,20 @@ jobs:
|
||||
|
||||
- name: Change the name
|
||||
run: |
|
||||
mv build_assets Xray-$ASSET_NAME
|
||||
mv build_assets Xray-${{ env.ASSET_NAME }}
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Xray-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||
name: Xray-${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}/*
|
||||
./Xray-${{ env.ASSET_NAME }}/*
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*
|
||||
file: ./Xray-${{ env.ASSET_NAME }}.zip*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
||||
|
28
.github/workflows/test.yml
vendored
28
.github/workflows/test.yml
vendored
@@ -19,6 +19,8 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -26,23 +28,17 @@ jobs:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.17.6
|
||||
go-version: '1.20'
|
||||
check-latest: true
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare geo*dat
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
wget -O ./resources/geoip.dat https://github.com/v2fly/geoip/releases/latest/download/geoip.dat
|
||||
wget -O ./resources/geosite.dat https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat
|
||||
- name: Prepare geo*dat for Windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/geoip/releases/latest/download/geoip.dat" -OutFile "./resources/geoip.dat"
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" -OutFile "./resources/geosite.dat"
|
||||
uses: actions/checkout@v3
|
||||
- name: Restore Cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: resources
|
||||
key: xray-geodat-
|
||||
enableCrossOsArchive: true
|
||||
- name: Test
|
||||
run: go test -timeout 1h -v ./...
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,8 +18,7 @@
|
||||
.idea
|
||||
*.zip
|
||||
*.tar.gz
|
||||
v2ray
|
||||
v2ctl
|
||||
xray
|
||||
mockgen
|
||||
vprotogen
|
||||
!infra/vprotogen/
|
||||
|
107
README.md
107
README.md
@@ -1,58 +1,105 @@
|
||||
# Project X
|
||||
|
||||
[Project X](https://github.com/XTLS) originates from XTLS protocol, provides a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [Xray-flutter](https://github.com/XTLS/Xray-flutter).
|
||||
[Project X](https://github.com/XTLS) originates from XTLS protocol, providing a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [REALITY](https://github.com/XTLS/REALITY).
|
||||
|
||||
[README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls).
|
||||
|
||||
## License
|
||||
|
||||
[Mozilla Public License Version 2.0](https://github.com/XTLS/Xray-core/blob/main/LICENSE)
|
||||
|
||||
## Documentation
|
||||
|
||||
[Project X Official Website](https://xtls.github.io)
|
||||
|
||||
## Telegram
|
||||
|
||||
[Project X](https://t.me/projectXray)
|
||||
|
||||
[Project X Channel](https://t.me/projectXtls)
|
||||
|
||||
## Installation
|
||||
|
||||
- Linux Script
|
||||
- [Xray-install](https://github.com/XTLS/Xray-install)
|
||||
- [Xray-script](https://github.com/kirin10000/Xray-script)
|
||||
- [XTLS/Xray-install](https://github.com/XTLS/Xray-install)
|
||||
- Docker
|
||||
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
||||
- Xray-docker
|
||||
- Web Panel
|
||||
- [X-UI](https://github.com/FranzKafkaYu/x-ui), [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui)
|
||||
- [Xray-UI](https://github.com/qist/xray-ui), [X-UI](https://github.com/sing-web/x-ui)
|
||||
- [Hiddify](https://github.com/hiddify/hiddify-config)
|
||||
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||
- [Libertea](https://github.com/VZiChoushaDui/Libertea)
|
||||
- One Click
|
||||
- [ProxySU](https://github.com/proxysu/ProxySU)
|
||||
- [v2ray-agent](https://github.com/mack-a/v2ray-agent)
|
||||
- [Xray-yes](https://github.com/jiuqi9997/Xray-yes)
|
||||
- [Xray_onekey](https://github.com/wulabing/Xray_onekey)
|
||||
- [Xray-script](https://github.com/kirin10000/Xray-script), [Xray-REALITY](https://github.com/zxcvos/Xray-script), [LetsXray](https://github.com/tdjnodj/LetsXray)
|
||||
- [XTool](https://github.com/LordPenguin666/XTool), [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [xray-reality](https://github.com/sajjaddg/xray-reality)
|
||||
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
|
||||
- Magisk
|
||||
- [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk)
|
||||
- [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
|
||||
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||
- Homebrew
|
||||
- `brew install xray`
|
||||
- [(Tap) Repository 0](https://github.com/N4FA/homebrew-xray)
|
||||
- [(Tap) Repository 1](https://github.com/xiruizhao/homebrew-xray)
|
||||
|
||||
## Contributing
|
||||
[Code Of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
## Usage
|
||||
|
||||
[Xray-examples](https://github.com/XTLS/Xray-examples) / [VLESS-TCP-XTLS-WHATEVER](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-WHATEVER)
|
||||
- Example
|
||||
- [VLESS-XTLS-uTLS-REALITY](https://github.com/XTLS/REALITY#readme)
|
||||
- [VLESS-TCP-XTLS-Vision](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-Vision)
|
||||
- [All-in-One-fallbacks-Nginx](https://github.com/XTLS/Xray-examples/tree/main/All-in-One-fallbacks-Nginx)
|
||||
- Xray-examples
|
||||
- [XTLS/Xray-examples](https://github.com/XTLS/Xray-examples)
|
||||
- [chika0801/Xray-examples](https://github.com/chika0801/Xray-examples)
|
||||
- [lxhao61/integrated-examples](https://github.com/lxhao61/integrated-examples)
|
||||
- Tutorial
|
||||
- [XTLS Vision](https://github.com/chika0801/Xray-install)
|
||||
- [REALITY (English)](https://cscot.pages.dev/2023/03/02/Xray-REALITY-tutorial/)
|
||||
- [XTLS-Iran-Reality (English)](https://github.com/SasukeFreestyle/XTLS-Iran-Reality)
|
||||
|
||||
## GUI Clients
|
||||
|
||||
- OpenWrt
|
||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall)
|
||||
- [Hello World](https://github.com/jerrykuku/luci-app-vssr)
|
||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
||||
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||
- Windows
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive)
|
||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
||||
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
||||
- Android
|
||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||
- [AnXray](https://github.com/XTLS/AnXray)
|
||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
||||
- iOS & macOS (with M1 chip)
|
||||
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||
- iOS & macOS arm64
|
||||
- [Mango](https://github.com/arror/Mango)
|
||||
- [Wings X](https://apps.apple.com/app/wings-x/id6446119727)
|
||||
- macOS arm64 & x64
|
||||
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
||||
- [Wings X](https://apps.apple.com/app/wings-x/id6446119727)
|
||||
|
||||
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||
|
||||
- iOS & macOS arm64
|
||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||
- macOS (Intel chip & M1 chip)
|
||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive)
|
||||
- [Stash](https://apps.apple.com/app/stash/id1596063349)
|
||||
- Xray Wrapper
|
||||
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
||||
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
||||
- [XrayKit](https://github.com/arror/XrayKit)
|
||||
- [libxray](https://github.com/KouYiGuo/libxray)
|
||||
- [XrayR](https://github.com/XrayR-project/XrayR)
|
||||
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
|
||||
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
|
||||
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
|
||||
- [Clash Verge](https://github.com/zzzgydi/clash-verge)
|
||||
- [clashN](https://github.com/2dust/clashN)
|
||||
- [Clash Meta for Android](https://github.com/MetaCubeX/ClashMetaForAndroid)
|
||||
- [meta_for_ios](https://t.me/meta_for_ios)
|
||||
- [sing-box](https://github.com/SagerNet/sing-box)
|
||||
- [installReality](https://github.com/BoxXt/installReality)
|
||||
- [sbox-reality](https://github.com/Misaka-blog/sbox-reality)
|
||||
- [sing-box-for-ios](https://github.com/SagerNet/sing-box-for-ios)
|
||||
|
||||
## Contributing
|
||||
|
||||
[Code of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -61,13 +108,17 @@ This repo relies on the following third-party projects:
|
||||
- Special thanks:
|
||||
- [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core)
|
||||
- In production:
|
||||
- [ghodss/yaml](https://github.com/ghodss/yaml)
|
||||
- [gorilla/websocket](https://github.com/gorilla/websocket)
|
||||
- [lucas-clemente/quic-go](https://github.com/lucas-clemente/quic-go)
|
||||
- [quic-go/quic-go](https://github.com/quic-go/quic-go)
|
||||
- [pelletier/go-toml](https://github.com/pelletier/go-toml)
|
||||
- [pires/go-proxyproto](https://github.com/pires/go-proxyproto)
|
||||
- [refraction-networking/utls](https://github.com/refraction-networking/utls)
|
||||
- [seiflotfy/cuckoofilter](https://github.com/seiflotfy/cuckoofilter)
|
||||
- [google/starlark-go](https://github.com/google/starlark-go)
|
||||
- For testing only:
|
||||
- [miekg/dns](https://github.com/miekg/dns)
|
||||
- [stretchr/testify](https://github.com/stretchr/testify)
|
||||
- [h12w/socks](https://github.com/h12w/socks)
|
||||
|
||||
## Compilation
|
||||
@@ -84,12 +135,6 @@ go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
```
|
||||
|
||||
## Telegram
|
||||
|
||||
[Project X](https://t.me/projectXray)
|
||||
|
||||
[Project X Channel](https://t.me/projectXtls)
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/XTLS/Xray-core)
|
||||
|
@@ -7,12 +7,11 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Commander is a Xray feature that provides gRPC methods to external clients.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/commander/config.proto
|
||||
|
||||
package commander
|
||||
|
@@ -3,10 +3,9 @@ package commander
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
)
|
||||
|
||||
// Service is a Commander service.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/dispatcher/config.proto
|
||||
|
||||
package dispatcher
|
||||
|
@@ -4,6 +4,7 @@ package dispatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -29,10 +30,15 @@ var errSniffingTimeout = newError("timeout on sniffing")
|
||||
|
||||
type cachedReader struct {
|
||||
sync.Mutex
|
||||
reader *pipe.Reader
|
||||
reader timeoutReader
|
||||
cache buf.MultiBuffer
|
||||
}
|
||||
|
||||
type timeoutReader interface {
|
||||
buf.Reader
|
||||
buf.TimeoutReader
|
||||
}
|
||||
|
||||
func (r *cachedReader) Cache(b *buf.Buffer) {
|
||||
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
|
||||
r.Lock()
|
||||
@@ -83,7 +89,7 @@ func (r *cachedReader) Interrupt() {
|
||||
r.cache = buf.ReleaseMulti(r.cache)
|
||||
}
|
||||
r.Unlock()
|
||||
r.reader.Interrupt()
|
||||
common.Interrupt(r.reader)
|
||||
}
|
||||
|
||||
// DefaultDispatcher is a default implementation of Dispatcher.
|
||||
@@ -92,13 +98,17 @@ type DefaultDispatcher struct {
|
||||
router routing.Router
|
||||
policy policy.Manager
|
||||
stats stats.Manager
|
||||
hosts dns.HostsLookup
|
||||
dns dns.Client
|
||||
fdns dns.FakeDNSEngine
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
d := new(DefaultDispatcher)
|
||||
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
||||
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||
d.fdns = fdns
|
||||
})
|
||||
return d.Init(config.(*Config), om, router, pm, sm, dc)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
@@ -108,14 +118,12 @@ func init() {
|
||||
}
|
||||
|
||||
// Init initializes DefaultDispatcher.
|
||||
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
||||
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
|
||||
d.ohm = om
|
||||
d.router = router
|
||||
d.policy = pm
|
||||
d.stats = sm
|
||||
if hosts, ok := dc.(dns.HostsLookup); ok {
|
||||
d.hosts = hosts
|
||||
}
|
||||
d.dns = dns
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -132,10 +140,77 @@ func (*DefaultDispatcher) Start() error {
|
||||
// Close implements common.Closable.
|
||||
func (*DefaultDispatcher) Close() error { return nil }
|
||||
|
||||
func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link) {
|
||||
opt := pipe.OptionsFromContext(ctx)
|
||||
uplinkReader, uplinkWriter := pipe.New(opt...)
|
||||
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link) {
|
||||
downOpt := pipe.OptionsFromContext(ctx)
|
||||
upOpt := downOpt
|
||||
|
||||
if network == net.Network_UDP {
|
||||
var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns
|
||||
// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.
|
||||
// When target replies, server will restore the domain and send back to client.
|
||||
// Note: this map is not global but per connection context
|
||||
upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
||||
for i, buffer := range mb {
|
||||
if buffer.UDP == nil {
|
||||
continue
|
||||
}
|
||||
addr := buffer.UDP.Address
|
||||
if addr.Family().IsIP() {
|
||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {
|
||||
domain := fkr0.GetDomainFromFakeDNS(addr)
|
||||
if len(domain) > 0 {
|
||||
buffer.UDP.Address = net.DomainAddress(domain)
|
||||
newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
} else {
|
||||
newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ip2domain == nil {
|
||||
ip2domain = new(sync.Map)
|
||||
newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
domain := addr.Domain()
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false})
|
||||
if err == nil {
|
||||
for _, ip := range ips {
|
||||
ip2domain.Store(ip.String(), domain)
|
||||
}
|
||||
newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
} else {
|
||||
newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mb
|
||||
}))
|
||||
downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
||||
for i, buffer := range mb {
|
||||
if buffer.UDP == nil {
|
||||
continue
|
||||
}
|
||||
addr := buffer.UDP.Address
|
||||
if addr.Family().IsIP() {
|
||||
if ip2domain == nil {
|
||||
continue
|
||||
}
|
||||
if domain, found := ip2domain.Load(addr.IP().String()); found {
|
||||
buffer.UDP.Address = net.DomainAddress(domain.(string))
|
||||
newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
} else {
|
||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {
|
||||
fakeIp := fkr0.GetFakeIPForDomain(addr.Domain())
|
||||
buffer.UDP.Address = fakeIp[0]
|
||||
newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mb
|
||||
}))
|
||||
}
|
||||
uplinkReader, uplinkWriter := pipe.New(upOpt...)
|
||||
downlinkReader, downlinkWriter := pipe.New(downOpt...)
|
||||
|
||||
inboundLink := &transport.Link{
|
||||
Reader: downlinkReader,
|
||||
@@ -178,17 +253,16 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
||||
return inboundLink, outboundLink
|
||||
}
|
||||
|
||||
func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
|
||||
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
|
||||
domain := result.Domain()
|
||||
if domain == "" {
|
||||
return false
|
||||
}
|
||||
for _, d := range request.ExcludeForDomain {
|
||||
if strings.ToLower(domain) == d {
|
||||
return false
|
||||
}
|
||||
}
|
||||
var fakeDNSEngine dns.FakeDNSEngine
|
||||
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||
fakeDNSEngine = fdns
|
||||
})
|
||||
protocolString := result.Protocol()
|
||||
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||
protocolString = resComp.ProtocolForDomainResult()
|
||||
@@ -197,7 +271,7 @@ func shouldOverride(ctx context.Context, result SniffResult, request session.Sni
|
||||
if strings.HasPrefix(protocolString, p) {
|
||||
return true
|
||||
}
|
||||
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
||||
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
||||
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
||||
return true
|
||||
@@ -221,45 +295,27 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
Target: destination,
|
||||
}
|
||||
ctx = session.ContextWithOutbound(ctx, ob)
|
||||
|
||||
inbound, outbound := d.getLink(ctx)
|
||||
content := session.ContentFromContext(ctx)
|
||||
if content == nil {
|
||||
content = new(session.Content)
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
}
|
||||
|
||||
sniffingRequest := content.SniffingRequest
|
||||
switch {
|
||||
case !sniffingRequest.Enabled:
|
||||
inbound, outbound := d.getLink(ctx, destination.Network, sniffingRequest)
|
||||
if !sniffingRequest.Enabled {
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
case destination.Network != net.Network_TCP:
|
||||
// Only metadata sniff will be used for non tcp connection
|
||||
result, err := sniffer(ctx, nil, true)
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
if shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||
ob.RouteTarget = destination
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
}
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
default:
|
||||
} else {
|
||||
go func() {
|
||||
cReader := &cachedReader{
|
||||
reader: outbound.Reader.(*pipe.Reader),
|
||||
}
|
||||
outbound.Reader = cReader
|
||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
|
||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
}
|
||||
if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
@@ -290,53 +346,34 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
}
|
||||
sniffingRequest := content.SniffingRequest
|
||||
switch {
|
||||
case !sniffingRequest.Enabled:
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
case destination.Network != net.Network_TCP:
|
||||
// Only metadata sniff will be used for non tcp connection
|
||||
result, err := sniffer(ctx, nil, true)
|
||||
if !sniffingRequest.Enabled {
|
||||
d.routedDispatch(ctx, outbound, destination)
|
||||
} else {
|
||||
cReader := &cachedReader{
|
||||
reader: outbound.Reader.(timeoutReader),
|
||||
}
|
||||
outbound.Reader = cReader
|
||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
if shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||
ob.RouteTarget = destination
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||
ob.RouteTarget = destination
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
go d.routedDispatch(ctx, outbound, destination)
|
||||
default:
|
||||
go func() {
|
||||
cReader := &cachedReader{
|
||||
reader: outbound.Reader.(*pipe.Reader),
|
||||
}
|
||||
outbound.Reader = cReader
|
||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
}
|
||||
if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||
ob.RouteTarget = destination
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
d.routedDispatch(ctx, outbound, destination)
|
||||
}()
|
||||
d.routedDispatch(ctx, outbound, destination)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
|
||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
|
||||
payload := buf.New()
|
||||
defer payload.Release()
|
||||
|
||||
@@ -362,7 +399,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
|
||||
|
||||
cReader.Cache(payload)
|
||||
if !payload.IsEmpty() {
|
||||
result, err := sniffer.Sniff(ctx, payload.Bytes())
|
||||
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
|
||||
if err != common.ErrNoClue {
|
||||
return result, err
|
||||
}
|
||||
@@ -384,8 +421,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
|
||||
|
||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||
ob := session.OutboundFromContext(ctx)
|
||||
if d.hosts != nil && destination.Address.Family().IsDomain() {
|
||||
proxied := d.hosts.LookupHosts(ob.Target.String())
|
||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
||||
proxied := hosts.LookupHosts(ob.Target.String())
|
||||
if proxied != nil {
|
||||
ro := ob.RouteTarget == destination
|
||||
destination.Address = *proxied
|
||||
@@ -399,9 +436,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
|
||||
var handler outbound.Handler
|
||||
|
||||
routingLink := routing_session.AsRoutingContext(ctx)
|
||||
inTag := routingLink.GetInboundTag()
|
||||
isPickRoute := 0
|
||||
if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
|
||||
ctx = session.SetForcedOutboundTagToContext(ctx, "")
|
||||
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
|
||||
isPickRoute = 1
|
||||
newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||
handler = h
|
||||
} else {
|
||||
@@ -411,13 +452,14 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
return
|
||||
}
|
||||
} else if d.router != nil {
|
||||
if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil {
|
||||
tag := route.GetOutboundTag()
|
||||
if h := d.ohm.GetHandler(tag); h != nil {
|
||||
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||
if route, err := d.router.PickRoute(routingLink); err == nil {
|
||||
outTag := route.GetOutboundTag()
|
||||
if h := d.ohm.GetHandler(outTag); h != nil {
|
||||
isPickRoute = 2
|
||||
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||
handler = h
|
||||
} else {
|
||||
newError("non existing outTag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
} else {
|
||||
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||
@@ -437,7 +479,15 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
|
||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||
if tag := handler.Tag(); tag != "" {
|
||||
accessMessage.Detour = tag
|
||||
if inTag == "" {
|
||||
accessMessage.Detour = tag
|
||||
} else if isPickRoute == 1 {
|
||||
accessMessage.Detour = inTag + " ==> " + tag
|
||||
} else if isPickRoute == 2 {
|
||||
accessMessage.Detour = inTag + " -> " + tag
|
||||
} else {
|
||||
accessMessage.Detour = inTag + " >> " + tag
|
||||
}
|
||||
}
|
||||
log.Record(accessMessage)
|
||||
}
|
||||
|
@@ -14,12 +14,13 @@ import (
|
||||
// newFakeDNSSniffer Creates a Fake DNS metadata sniffer
|
||||
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
|
||||
var fakeDNSEngine dns.FakeDNSEngine
|
||||
err := core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||
fakeDNSEngine = fdns
|
||||
})
|
||||
if err != nil {
|
||||
return protocolSnifferWithMetadata{}, err
|
||||
{
|
||||
fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil))
|
||||
if fakeDNSEngineFeat != nil {
|
||||
fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine)
|
||||
}
|
||||
}
|
||||
|
||||
if fakeDNSEngine == nil {
|
||||
errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
|
||||
return protocolSnifferWithMetadata{}, errNotInit
|
||||
@@ -84,7 +85,8 @@ func (f DNSThenOthersSniffResult) Domain() string {
|
||||
}
|
||||
|
||||
func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (
|
||||
protocolSnifferWithMetadata, error) { // nolint: unparam
|
||||
protocolSnifferWithMetadata, error,
|
||||
) { // nolint: unparam
|
||||
// ctx may be used in the future
|
||||
_ = ctx
|
||||
return protocolSnifferWithMetadata{
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol/bittorrent"
|
||||
"github.com/xtls/xray-core/common/protocol/http"
|
||||
"github.com/xtls/xray-core/common/protocol/quic"
|
||||
"github.com/xtls/xray-core/common/protocol/tls"
|
||||
)
|
||||
|
||||
@@ -22,6 +24,7 @@ type protocolSnifferWithMetadata struct {
|
||||
// for both TCP and UDP connections
|
||||
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
|
||||
metadataSniffer bool
|
||||
network net.Network
|
||||
}
|
||||
|
||||
type Sniffer struct {
|
||||
@@ -31,9 +34,11 @@ type Sniffer struct {
|
||||
func NewSniffer(ctx context.Context) *Sniffer {
|
||||
ret := &Sniffer{
|
||||
sniffer: []protocolSnifferWithMetadata{
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
||||
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
|
||||
},
|
||||
}
|
||||
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
|
||||
@@ -49,11 +54,11 @@ func NewSniffer(ctx context.Context) *Sniffer {
|
||||
|
||||
var errUnknownContent = newError("unknown content")
|
||||
|
||||
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
|
||||
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
|
||||
var pendingSniffer []protocolSnifferWithMetadata
|
||||
for _, si := range s.sniffer {
|
||||
s := si.protocolSniffer
|
||||
if si.metadataSniffer {
|
||||
if si.metadataSniffer || si.network != network {
|
||||
continue
|
||||
}
|
||||
result, err := s(c, payload)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/dns/config.proto
|
||||
|
||||
package dns
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/xtls/xray-core/app/dispatcher"
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/app/policy"
|
||||
|
@@ -1,16 +1,19 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/core"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// Fqdn normalizes domain make sure it ends with '.'
|
||||
@@ -226,3 +229,19 @@ L:
|
||||
|
||||
return ipRecord, nil
|
||||
}
|
||||
|
||||
// toDnsContext create a new background context with parent inbound, session and dns log
|
||||
func toDnsContext(ctx context.Context, addr string) context.Context {
|
||||
dnsCtx := core.ToBackgroundDetachedContext(ctx)
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
|
||||
}
|
||||
dnsCtx = session.ContextWithContent(dnsCtx, session.ContentFromContext(ctx))
|
||||
dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
|
||||
From: "DNS",
|
||||
To: addr,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
})
|
||||
return dnsCtx
|
||||
}
|
||||
|
@@ -7,11 +7,10 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
func Test_parseResponse(t *testing.T) {
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
gonet "net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
type Holder struct {
|
||||
domainToIP cache.Lru
|
||||
ipRange *gonet.IPNet
|
||||
mu *sync.Mutex
|
||||
|
||||
config *FakeDnsPool
|
||||
}
|
||||
@@ -49,6 +51,7 @@ func (fkdns *Holder) Start() error {
|
||||
func (fkdns *Holder) Close() error {
|
||||
fkdns.domainToIP = nil
|
||||
fkdns.ipRange = nil
|
||||
fkdns.mu = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,7 +70,7 @@ func NewFakeDNSHolder() (*Holder, error) {
|
||||
}
|
||||
|
||||
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
|
||||
return &Holder{nil, nil, conf}, nil
|
||||
return &Holder{nil, nil, nil, conf}, nil
|
||||
}
|
||||
|
||||
func (fkdns *Holder) initializeFromConfig() error {
|
||||
@@ -89,11 +92,14 @@ func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
|
||||
}
|
||||
fkdns.domainToIP = cache.NewLru(lruSize)
|
||||
fkdns.ipRange = ipRange
|
||||
fkdns.mu = new(sync.Mutex)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFakeIPForDomain checks and generates a fake IP for a domain name
|
||||
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
|
||||
fkdns.mu.Lock()
|
||||
defer fkdns.mu.Unlock()
|
||||
if v, ok := fkdns.domainToIP.Get(domain); ok {
|
||||
return []net.Address{v.(net.Address)}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/dns/fakedns/fakedns.proto
|
||||
|
||||
package fakedns
|
||||
|
@@ -2,14 +2,15 @@ package fakedns
|
||||
|
||||
import (
|
||||
gonet "net"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var ipPrefix = "198.1"
|
||||
@@ -66,6 +67,31 @@ func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) {
|
||||
assert.Equal(t, addr[0].IP().String(), addr2[0].IP().String())
|
||||
}
|
||||
|
||||
func TestGetFakeIPForDomainConcurrently(t *testing.T) {
|
||||
fkdns, err := NewFakeDNSHolder()
|
||||
common.Must(err)
|
||||
|
||||
total := 200
|
||||
addr := make([][]net.Address, total)
|
||||
var errg errgroup.Group
|
||||
for i := 0; i < total; i++ {
|
||||
errg.Go(testGetFakeIP(i, addr, fkdns))
|
||||
}
|
||||
errg.Wait()
|
||||
for i := 0; i < total; i++ {
|
||||
for j := i + 1; j < total; j++ {
|
||||
assert.NotEqual(t, addr[i][0].IP().String(), addr[j][0].IP().String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testGetFakeIP(index int, addr [][]net.Address, fkdns *Holder) func() error {
|
||||
return func() error {
|
||||
addr[index] = fkdns.GetFakeIPForDomain("fakednstest" + strconv.Itoa(index) + ".example.com")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
||||
fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
|
||||
IpPool: dns.FakeIPv4Pool,
|
||||
@@ -147,31 +173,31 @@ func TestFakeDNSMulti(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("allocateTwoAddressForTwoPool", func(t *testing.T) {
|
||||
address := fakeMulti.GetFakeIPForDomain("fakednstest.v2fly.org")
|
||||
address := fakeMulti.GetFakeIPForDomain("fakednstest.example.com")
|
||||
assert.Len(t, address, 2, "should be 2 address one for each pool")
|
||||
t.Run("eachOfThemShouldResolve:0", func(t *testing.T) {
|
||||
domain := fakeMulti.GetDomainFromFakeDNS(address[0])
|
||||
assert.Equal(t, "fakednstest.v2fly.org", domain)
|
||||
assert.Equal(t, "fakednstest.example.com", domain)
|
||||
})
|
||||
t.Run("eachOfThemShouldResolve:1", func(t *testing.T) {
|
||||
domain := fakeMulti.GetDomainFromFakeDNS(address[1])
|
||||
assert.Equal(t, "fakednstest.v2fly.org", domain)
|
||||
assert.Equal(t, "fakednstest.example.com", domain)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("understandIPTypeSelector", func(t *testing.T) {
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.v2fly.org", true, false)
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.example.com", true, false)
|
||||
assert.Len(t, address, 1, "should be 1 address")
|
||||
assert.True(t, address[0].Family().IsIPv4())
|
||||
})
|
||||
t.Run("ipv6", func(t *testing.T) {
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.v2fly.org", false, true)
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.example.com", false, true)
|
||||
assert.Len(t, address, 1, "should be 1 address")
|
||||
assert.True(t, address[0].Family().IsIPv6())
|
||||
})
|
||||
t.Run("ipv46", func(t *testing.T) {
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.v2fly.org", true, true)
|
||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.example.com", true, true)
|
||||
assert.Len(t, address, 2, "should be 2 address")
|
||||
assert.True(t, address[0].Family().IsIPv4())
|
||||
assert.True(t, address[1].Family().IsIPv6())
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -11,8 +11,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -24,6 +22,7 @@ import (
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
|
||||
@@ -53,23 +52,11 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dispatcherCtx := context.Background()
|
||||
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dispatcherCtx = session.ContextWithContent(dispatcherCtx, session.ContentFromContext(ctx))
|
||||
dispatcherCtx = session.ContextWithInbound(dispatcherCtx, session.InboundFromContext(ctx))
|
||||
dispatcherCtx = log.ContextWithAccessMessage(dispatcherCtx, &log.AccessMessage{
|
||||
From: "DoH",
|
||||
To: s.dohURL,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
})
|
||||
|
||||
link, err := s.dispatcher.Dispatch(dispatcherCtx, dest)
|
||||
link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
@@ -116,7 +103,7 @@ func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
||||
}
|
||||
conn, err := internet.DialSystem(ctx, dest, nil)
|
||||
log.Record(&log.AccessMessage{
|
||||
From: "DoH",
|
||||
From: "DNS",
|
||||
To: s.dohURL,
|
||||
Status: log.AccessAccepted,
|
||||
Detour: "local",
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -3,7 +3,9 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/dns/localdns"
|
||||
@@ -18,6 +20,7 @@ const errEmptyResponse = "No address associated with hostname"
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
|
||||
start := time.Now()
|
||||
ips, err = s.client.LookupIP(domain, option)
|
||||
|
||||
if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) {
|
||||
@@ -26,6 +29,7 @@ func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, op
|
||||
|
||||
if len(ips) > 0 {
|
||||
newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
}
|
||||
|
||||
return
|
||||
|
@@ -7,12 +7,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
@@ -20,6 +18,8 @@ import (
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
||||
@@ -37,7 +37,7 @@ type QUICNameServer struct {
|
||||
reqID uint32
|
||||
name string
|
||||
destination *net.Destination
|
||||
session quic.Session
|
||||
connection quic.Connection
|
||||
}
|
||||
|
||||
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
||||
@@ -194,7 +194,7 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
|
||||
|
||||
conn, err := s.openStream(dnsCtx)
|
||||
if err != nil {
|
||||
newError("failed to open quic session").Base(err).AtError().WriteToLog()
|
||||
newError("failed to open quic connection").Base(err).AtError().WriteToLog()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -276,6 +276,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
@@ -307,10 +308,12 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
|
||||
close(done)
|
||||
}()
|
||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, err
|
||||
}
|
||||
|
||||
@@ -322,7 +325,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
|
||||
}
|
||||
}
|
||||
|
||||
func isActive(s quic.Session) bool {
|
||||
func isActive(s quic.Connection) bool {
|
||||
select {
|
||||
case <-s.Context().Done():
|
||||
return false
|
||||
@@ -331,17 +334,17 @@ func isActive(s quic.Session) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *QUICNameServer) getSession() (quic.Session, error) {
|
||||
var session quic.Session
|
||||
func (s *QUICNameServer) getConnection() (quic.Connection, error) {
|
||||
var conn quic.Connection
|
||||
s.RLock()
|
||||
session = s.session
|
||||
if session != nil && isActive(session) {
|
||||
conn = s.connection
|
||||
if conn != nil && isActive(conn) {
|
||||
s.RUnlock()
|
||||
return session, nil
|
||||
return conn, nil
|
||||
}
|
||||
if session != nil {
|
||||
// we're recreating the session, let's create a new one
|
||||
_ = session.CloseWithError(0, "")
|
||||
if conn != nil {
|
||||
// we're recreating the connection, let's create a new one
|
||||
_ = conn.CloseWithError(0, "")
|
||||
}
|
||||
s.RUnlock()
|
||||
|
||||
@@ -349,42 +352,48 @@ func (s *QUICNameServer) getSession() (quic.Session, error) {
|
||||
defer s.Unlock()
|
||||
|
||||
var err error
|
||||
session, err = s.openSession()
|
||||
conn, err = s.openConnection()
|
||||
if err != nil {
|
||||
// This does not look too nice, but QUIC (or maybe quic-go)
|
||||
// doesn't seem stable enough.
|
||||
// Maybe retransmissions aren't fully implemented in quic-go?
|
||||
// Anyways, the simple solution is to make a second try when
|
||||
// it fails to open the QUIC session.
|
||||
session, err = s.openSession()
|
||||
// it fails to open the QUIC connection.
|
||||
conn, err = s.openConnection()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
s.session = session
|
||||
return session, nil
|
||||
s.connection = conn
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *QUICNameServer) openSession() (quic.Session, error) {
|
||||
func (s *QUICNameServer) openConnection() (quic.Connection, error) {
|
||||
tlsConfig := tls.Config{}
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: handshakeTimeout,
|
||||
}
|
||||
|
||||
session, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
||||
conn, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
||||
log.Record(&log.AccessMessage{
|
||||
From: "DNS",
|
||||
To: s.destination,
|
||||
Status: log.AccessAccepted,
|
||||
Detour: "local",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) {
|
||||
session, err := s.getSession()
|
||||
conn, err := s.getConnection()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// open a new stream
|
||||
return session.OpenStreamSync(ctx)
|
||||
return conn.OpenStreamSync(ctx)
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -9,10 +9,9 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// TCPNameServer implemented DNS over TCP (RFC7766).
|
||||
@@ -44,7 +44,7 @@ func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServ
|
||||
}
|
||||
|
||||
s.dial = func(ctx context.Context) (net.Conn, error) {
|
||||
link, err := dispatcher.Dispatch(ctx, *s.destination)
|
||||
link, err := dispatcher.Dispatch(toDnsContext(ctx, s.destination.String()), *s.destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -315,6 +315,7 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
@@ -346,10 +347,12 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
||||
close(done)
|
||||
}()
|
||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, err
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -7,8 +7,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -20,6 +18,7 @@ import (
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/udp"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// ClassicNameServer implemented traditional UDP DNS.
|
||||
@@ -195,21 +194,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
|
||||
for _, req := range reqs {
|
||||
s.addPendingRequest(req)
|
||||
b, _ := dns.PackMessage(req.msg)
|
||||
udpCtx := context.Background()
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
udpCtx = session.ContextWithInbound(udpCtx, inbound)
|
||||
}
|
||||
|
||||
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
||||
Protocol: "dns",
|
||||
})
|
||||
udpCtx = log.ContextWithAccessMessage(udpCtx, &log.AccessMessage{
|
||||
From: "DNS",
|
||||
To: s.address,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
})
|
||||
s.udpServer.Dispatch(udpCtx, *s.address, b)
|
||||
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,11 +5,10 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/app/log"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/core"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type LoggerServer struct {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/log/command/config.proto
|
||||
|
||||
package command
|
||||
|
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.12
|
||||
// source: app/log/command/config.proto
|
||||
|
||||
package command
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/log/config.proto
|
||||
|
||||
package log
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/xtls/xray-core/app/log"
|
||||
"github.com/xtls/xray-core/common"
|
||||
clog "github.com/xtls/xray-core/common/log"
|
||||
|
149
app/metrics/config.pb.go
Normal file
149
app/metrics/config.pb.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/metrics/config.proto
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
// Config is the settings for metrics.
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Tag of the outbound handler that handles metrics http connections.
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_app_metrics_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_app_metrics_config_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && 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_app_metrics_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetTag() string {
|
||||
if x != nil {
|
||||
return x.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_app_metrics_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_app_metrics_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x1a, 0x0a, 0x06,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
|
||||
0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
||||
0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_app_metrics_config_proto_rawDescOnce sync.Once
|
||||
file_app_metrics_config_proto_rawDescData = file_app_metrics_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_app_metrics_config_proto_rawDescGZIP() []byte {
|
||||
file_app_metrics_config_proto_rawDescOnce.Do(func() {
|
||||
file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_metrics_config_proto_rawDescData)
|
||||
})
|
||||
return file_app_metrics_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_metrics_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_app_metrics_config_proto_goTypes = []interface{}{
|
||||
(*Config)(nil), // 0: xray.app.metrics.Config
|
||||
}
|
||||
var file_app_metrics_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_app_metrics_config_proto_init() }
|
||||
func file_app_metrics_config_proto_init() {
|
||||
if File_app_metrics_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_app_metrics_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_app_metrics_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_app_metrics_config_proto_goTypes,
|
||||
DependencyIndexes: file_app_metrics_config_proto_depIdxs,
|
||||
MessageInfos: file_app_metrics_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_app_metrics_config_proto = out.File
|
||||
file_app_metrics_config_proto_rawDesc = nil
|
||||
file_app_metrics_config_proto_goTypes = nil
|
||||
file_app_metrics_config_proto_depIdxs = nil
|
||||
}
|
13
app/metrics/config.proto
Normal file
13
app/metrics/config.proto
Normal file
@@ -0,0 +1,13 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.app.metrics;
|
||||
option csharp_namespace = "Xray.App.Metrics";
|
||||
option go_package = "github.com/xtls/xray-core/app/metrics";
|
||||
option java_package = "com.xray.app.metrics";
|
||||
option java_multiple_files = true;
|
||||
|
||||
// Config is the settings for metrics.
|
||||
message Config {
|
||||
// Tag of the outbound handler that handles metrics http connections.
|
||||
string tag = 1;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package xtls
|
||||
package metrics
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
118
app/metrics/metrics.go
Normal file
118
app/metrics/metrics.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"expvar"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/app/observatory"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/extension"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
feature_stats "github.com/xtls/xray-core/features/stats"
|
||||
)
|
||||
|
||||
type MetricsHandler struct {
|
||||
ohm outbound.Manager
|
||||
statsManager feature_stats.Manager
|
||||
observatory extension.Observatory
|
||||
tag string
|
||||
}
|
||||
|
||||
// NewMetricsHandler creates a new MetricsHandler based on the given config.
|
||||
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
|
||||
c := &MetricsHandler{
|
||||
tag: config.Tag,
|
||||
}
|
||||
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
|
||||
c.statsManager = sm
|
||||
c.ohm = om
|
||||
}))
|
||||
expvar.Publish("stats", expvar.Func(func() interface{} {
|
||||
manager, ok := c.statsManager.(*stats.Manager)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
resp := map[string]map[string]map[string]int64{
|
||||
"inbound": {},
|
||||
"outbound": {},
|
||||
"user": {},
|
||||
}
|
||||
manager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
|
||||
nameSplit := strings.Split(name, ">>>")
|
||||
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
|
||||
if item, found := resp[typeName][tagOrUser]; found {
|
||||
item[direction] = counter.Value()
|
||||
} else {
|
||||
resp[typeName][tagOrUser] = map[string]int64{
|
||||
direction: counter.Value(),
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return resp
|
||||
}))
|
||||
expvar.Publish("observatory", expvar.Func(func() interface{} {
|
||||
if c.observatory == nil {
|
||||
common.Must(core.RequireFeatures(ctx, func(observatory extension.Observatory) error {
|
||||
c.observatory = observatory
|
||||
return nil
|
||||
}))
|
||||
if c.observatory == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
resp := map[string]*observatory.OutboundStatus{}
|
||||
if o, err := c.observatory.GetObservation(context.Background()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
for _, x := range o.(*observatory.ObservationResult).GetStatus() {
|
||||
resp[x.OutboundTag] = x
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}))
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (p *MetricsHandler) Type() interface{} {
|
||||
return (*MetricsHandler)(nil)
|
||||
}
|
||||
|
||||
func (p *MetricsHandler) Start() error {
|
||||
listener := &OutboundListener{
|
||||
buffer: make(chan net.Conn, 4),
|
||||
done: done.New(),
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := http.Serve(listener, http.DefaultServeMux); err != nil {
|
||||
newError("failed to start metrics server").Base(err).AtError().WriteToLog()
|
||||
}
|
||||
}()
|
||||
|
||||
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
|
||||
newError("failed to remove existing handler").WriteToLog()
|
||||
}
|
||||
|
||||
return p.ohm.AddHandler(context.Background(), &Outbound{
|
||||
tag: p.tag,
|
||||
listener: listener,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *MetricsHandler) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||
return NewMetricsHandler(ctx, cfg.(*Config))
|
||||
}))
|
||||
}
|
109
app/metrics/outbound.go
Normal file
109
app/metrics/outbound.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
|
||||
// OutboundListener is a net.Listener for listening metrics http connections.
|
||||
type OutboundListener struct {
|
||||
buffer chan net.Conn
|
||||
done *done.Instance
|
||||
}
|
||||
|
||||
func (l *OutboundListener) add(conn net.Conn) {
|
||||
select {
|
||||
case l.buffer <- conn:
|
||||
case <-l.done.Wait():
|
||||
conn.Close()
|
||||
default:
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Accept implements net.Listener.
|
||||
func (l *OutboundListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case <-l.done.Wait():
|
||||
return nil, newError("listen closed")
|
||||
case c := <-l.buffer:
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Close implement net.Listener.
|
||||
func (l *OutboundListener) Close() error {
|
||||
common.Must(l.done.Close())
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case c := <-l.buffer:
|
||||
c.Close()
|
||||
default:
|
||||
break L
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr implements net.Listener.
|
||||
func (l *OutboundListener) Addr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IP{0, 0, 0, 0},
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Outbound is an outbound.Handler that handles metrics http connections.
|
||||
type Outbound struct {
|
||||
tag string
|
||||
listener *OutboundListener
|
||||
access sync.RWMutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Dispatch implements outbound.Handler.
|
||||
func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
|
||||
co.access.RLock()
|
||||
|
||||
if co.closed {
|
||||
common.Interrupt(link.Reader)
|
||||
common.Interrupt(link.Writer)
|
||||
co.access.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
closeSignal := done.New()
|
||||
c := cnc.NewConnection(cnc.ConnectionInputMulti(link.Writer), cnc.ConnectionOutputMulti(link.Reader), cnc.ConnectionOnClose(closeSignal))
|
||||
co.listener.add(c)
|
||||
co.access.RUnlock()
|
||||
<-closeSignal.Wait()
|
||||
}
|
||||
|
||||
// Tag implements outbound.Handler.
|
||||
func (co *Outbound) Tag() string {
|
||||
return co.tag
|
||||
}
|
||||
|
||||
// Start implements common.Runnable.
|
||||
func (co *Outbound) Start() error {
|
||||
co.access.Lock()
|
||||
co.closed = false
|
||||
co.access.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements common.Closable.
|
||||
func (co *Outbound) Close() error {
|
||||
co.access.Lock()
|
||||
defer co.access.Unlock()
|
||||
|
||||
co.closed = true
|
||||
return co.listener.Close()
|
||||
}
|
@@ -6,12 +6,11 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/app/observatory"
|
||||
"github.com/xtls/xray-core/common"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/extension"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/observatory/command/command.proto
|
||||
|
||||
package command
|
||||
|
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.12
|
||||
// source: app/observatory/command/command.proto
|
||||
|
||||
package command
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/observatory/config.proto
|
||||
|
||||
package observatory
|
||||
@@ -73,23 +73,23 @@ type OutboundStatus struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// @Document Whether this outbound is usable
|
||||
//@Restriction ReadOnlyForUser
|
||||
// @Restriction ReadOnlyForUser
|
||||
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
|
||||
// @Document The time for probe request to finish.
|
||||
//@Type time.ms
|
||||
//@Restriction ReadOnlyForUser
|
||||
// @Type time.ms
|
||||
// @Restriction ReadOnlyForUser
|
||||
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
|
||||
// @Document The last error caused this outbound failed to relay probe request
|
||||
//@Restriction NotMachineReadable
|
||||
// @Restriction NotMachineReadable
|
||||
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
|
||||
// @Document The outbound tag for this Server
|
||||
//@Type id.outboundTag
|
||||
// @Type id.outboundTag
|
||||
OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
||||
// @Document The time this outbound is known to be alive
|
||||
//@Type id.outboundTag
|
||||
// @Type id.outboundTag
|
||||
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"`
|
||||
// @Document The time this outbound is tried
|
||||
//@Type id.outboundTag
|
||||
// @Type id.outboundTag
|
||||
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
|
||||
}
|
||||
|
||||
@@ -173,14 +173,14 @@ type ProbeResult struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// @Document Whether this outbound is usable
|
||||
//@Restriction ReadOnlyForUser
|
||||
// @Restriction ReadOnlyForUser
|
||||
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
|
||||
// @Document The time for probe request to finish.
|
||||
//@Type time.ms
|
||||
//@Restriction ReadOnlyForUser
|
||||
// @Type time.ms
|
||||
// @Restriction ReadOnlyForUser
|
||||
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
|
||||
// @Document The error caused this outbound failed to relay probe request
|
||||
//@Restriction NotMachineReadable
|
||||
// @Restriction NotMachineReadable
|
||||
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ type Intensity struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// @Document The time interval for a probe request in ms.
|
||||
//@Type time.ms
|
||||
// @Type time.ms
|
||||
ProbeInterval uint32 `protobuf:"varint,1,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
v2net "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/policy/config.proto
|
||||
|
||||
package policy
|
||||
|
@@ -3,13 +3,12 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/inbound"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/proxy"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// InboundOperation is the interface for operations that applies to inbound handlers.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/proxyman/command/command.proto
|
||||
|
||||
package command
|
||||
|
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.12
|
||||
// source: app/proxyman/command/command.proto
|
||||
|
||||
package command
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.19.1
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/proxyman/config.proto
|
||||
|
||||
package proxyman
|
||||
@@ -524,6 +524,7 @@ type SenderConfig struct {
|
||||
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
||||
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
||||
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
||||
SmuxSettings *SingMultiplexConfig `protobuf:"bytes,5,opt,name=smux_settings,json=smuxSettings,proto3" json:"smux_settings,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SenderConfig) Reset() {
|
||||
@@ -586,6 +587,13 @@ func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SenderConfig) GetSmuxSettings() *SingMultiplexConfig {
|
||||
if x != nil {
|
||||
return x.SmuxSettings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MultiplexingConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -594,7 +602,11 @@ type MultiplexingConfig struct {
|
||||
// Whether or not Mux is enabled.
|
||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
// Max number of concurrent connections that one Mux connection can handle.
|
||||
Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
||||
Concurrency int32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
||||
// Transport XUDP in another Mux.
|
||||
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
|
||||
// "reject" (default), "allow" or "skip".
|
||||
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) Reset() {
|
||||
@@ -636,13 +648,114 @@ func (x *MultiplexingConfig) GetEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) GetConcurrency() uint32 {
|
||||
func (x *MultiplexingConfig) GetConcurrency() int32 {
|
||||
if x != nil {
|
||||
return x.Concurrency
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) GetXudpConcurrency() int32 {
|
||||
if x != nil {
|
||||
return x.XudpConcurrency
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
|
||||
if x != nil {
|
||||
return x.XudpProxyUDP443
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SingMultiplexConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
Protocol string `protobuf:"bytes,2,opt,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
MaxConnections int32 `protobuf:"varint,3,opt,name=max_connections,json=maxConnections,proto3" json:"max_connections,omitempty"`
|
||||
MinStreams int32 `protobuf:"varint,4,opt,name=min_streams,json=minStreams,proto3" json:"min_streams,omitempty"`
|
||||
MaxStreams int32 `protobuf:"varint,5,opt,name=max_streams,json=maxStreams,proto3" json:"max_streams,omitempty"`
|
||||
Padding bool `protobuf:"varint,6,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) Reset() {
|
||||
*x = SingMultiplexConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SingMultiplexConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SingMultiplexConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SingMultiplexConfig.ProtoReflect.Descriptor instead.
|
||||
func (*SingMultiplexConfig) Descriptor() ([]byte, []int) {
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetEnabled() bool {
|
||||
if x != nil {
|
||||
return x.Enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetProtocol() string {
|
||||
if x != nil {
|
||||
return x.Protocol
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetMaxConnections() int32 {
|
||||
if x != nil {
|
||||
return x.MaxConnections
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetMinStreams() int32 {
|
||||
if x != nil {
|
||||
return x.MinStreams
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetMaxStreams() int32 {
|
||||
if x != nil {
|
||||
return x.MaxStreams
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SingMultiplexConfig) GetPadding() bool {
|
||||
if x != nil {
|
||||
return x.Padding
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type AllocationStrategy_AllocationStrategyConcurrency struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -654,7 +767,7 @@ type AllocationStrategy_AllocationStrategyConcurrency struct {
|
||||
func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() {
|
||||
*x = AllocationStrategy_AllocationStrategyConcurrency{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[8]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -667,7 +780,7 @@ func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string {
|
||||
func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {}
|
||||
|
||||
func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[8]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -701,7 +814,7 @@ type AllocationStrategy_AllocationStrategyRefresh struct {
|
||||
func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() {
|
||||
*x = AllocationStrategy_AllocationStrategyRefresh{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[9]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -714,7 +827,7 @@ func (x *AllocationStrategy_AllocationStrategyRefresh) String() string {
|
||||
func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {}
|
||||
|
||||
func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[9]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -837,7 +950,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65,
|
||||
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53,
|
||||
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
|
||||
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a, 0x0c, 0x53, 0x65,
|
||||
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xfd, 0x02, 0x0a, 0x0c, 0x53, 0x65,
|
||||
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69,
|
||||
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f,
|
||||
@@ -856,21 +969,44 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78,
|
||||
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
|
||||
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12,
|
||||
0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b,
|
||||
0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23,
|
||||
0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73,
|
||||
0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c,
|
||||
0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0d,
|
||||
0x73, 0x6d, 0x75, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x4d, 0x75, 0x6c, 0x74,
|
||||
0x69, 0x70, 0x6c, 0x65, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x73, 0x6d, 0x75,
|
||||
0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75,
|
||||
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
|
||||
0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f,
|
||||
0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75,
|
||||
0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33,
|
||||
0x22, 0xd0, 0x01, 0x0a, 0x13, 0x53, 0x69, 0x6e, 0x67, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
|
||||
0x65, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x27,
|
||||
0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x73,
|
||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x69,
|
||||
0x6e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f,
|
||||
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d,
|
||||
0x61, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64,
|
||||
0x69, 0x6e, 0x67, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12,
|
||||
0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
|
||||
0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
|
||||
0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -886,7 +1022,7 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_app_proxyman_config_proto_goTypes = []interface{}{
|
||||
(KnownProtocols)(0), // 0: xray.app.proxyman.KnownProtocols
|
||||
(AllocationStrategy_Type)(0), // 1: xray.app.proxyman.AllocationStrategy.Type
|
||||
@@ -898,35 +1034,37 @@ var file_app_proxyman_config_proto_goTypes = []interface{}{
|
||||
(*OutboundConfig)(nil), // 7: xray.app.proxyman.OutboundConfig
|
||||
(*SenderConfig)(nil), // 8: xray.app.proxyman.SenderConfig
|
||||
(*MultiplexingConfig)(nil), // 9: xray.app.proxyman.MultiplexingConfig
|
||||
(*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
|
||||
(*AllocationStrategy_AllocationStrategyRefresh)(nil), // 11: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
|
||||
(*net.PortList)(nil), // 12: xray.common.net.PortList
|
||||
(*net.IPOrDomain)(nil), // 13: xray.common.net.IPOrDomain
|
||||
(*internet.StreamConfig)(nil), // 14: xray.transport.internet.StreamConfig
|
||||
(*serial.TypedMessage)(nil), // 15: xray.common.serial.TypedMessage
|
||||
(*internet.ProxyConfig)(nil), // 16: xray.transport.internet.ProxyConfig
|
||||
(*SingMultiplexConfig)(nil), // 10: xray.app.proxyman.SingMultiplexConfig
|
||||
(*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 11: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
|
||||
(*AllocationStrategy_AllocationStrategyRefresh)(nil), // 12: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
|
||||
(*net.PortList)(nil), // 13: xray.common.net.PortList
|
||||
(*net.IPOrDomain)(nil), // 14: xray.common.net.IPOrDomain
|
||||
(*internet.StreamConfig)(nil), // 15: xray.transport.internet.StreamConfig
|
||||
(*serial.TypedMessage)(nil), // 16: xray.common.serial.TypedMessage
|
||||
(*internet.ProxyConfig)(nil), // 17: xray.transport.internet.ProxyConfig
|
||||
}
|
||||
var file_app_proxyman_config_proto_depIdxs = []int32{
|
||||
1, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
|
||||
10, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
|
||||
11, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
|
||||
12, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
|
||||
13, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
|
||||
11, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
|
||||
12, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
|
||||
13, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
|
||||
14, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
|
||||
3, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy
|
||||
14, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||
15, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||
0, // 7: xray.app.proxyman.ReceiverConfig.domain_override:type_name -> xray.app.proxyman.KnownProtocols
|
||||
4, // 8: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
|
||||
15, // 9: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
|
||||
15, // 10: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
13, // 11: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
|
||||
14, // 12: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||
16, // 13: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
|
||||
16, // 9: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
|
||||
16, // 10: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
14, // 11: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
|
||||
15, // 12: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||
17, // 13: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
|
||||
9, // 14: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
|
||||
15, // [15:15] is the sub-list for method output_type
|
||||
15, // [15:15] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
10, // 15: xray.app.proxyman.SenderConfig.smux_settings:type_name -> xray.app.proxyman.SingMultiplexConfig
|
||||
16, // [16:16] is the sub-list for method output_type
|
||||
16, // [16:16] is the sub-list for method input_type
|
||||
16, // [16:16] is the sub-list for extension type_name
|
||||
16, // [16:16] is the sub-list for extension extendee
|
||||
0, // [0:16] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_proxyman_config_proto_init() }
|
||||
@@ -1032,7 +1170,7 @@ func file_app_proxyman_config_proto_init() {
|
||||
}
|
||||
}
|
||||
file_app_proxyman_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AllocationStrategy_AllocationStrategyConcurrency); i {
|
||||
switch v := v.(*SingMultiplexConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1044,6 +1182,18 @@ func file_app_proxyman_config_proto_init() {
|
||||
}
|
||||
}
|
||||
file_app_proxyman_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AllocationStrategy_AllocationStrategyConcurrency); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_app_proxyman_config_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AllocationStrategy_AllocationStrategyRefresh); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1062,7 +1212,7 @@ func file_app_proxyman_config_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_app_proxyman_config_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 10,
|
||||
NumMessages: 11,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
@@ -27,13 +27,13 @@ message AllocationStrategy {
|
||||
|
||||
Type type = 1;
|
||||
|
||||
message AllocationStrategyConcurrency { uint32 value = 1; }
|
||||
message AllocationStrategyConcurrency {uint32 value = 1;}
|
||||
|
||||
// Number of handlers (ports) running in parallel.
|
||||
// Default value is 3 if unset.
|
||||
AllocationStrategyConcurrency concurrency = 2;
|
||||
|
||||
message AllocationStrategyRefresh { uint32 value = 1; }
|
||||
message AllocationStrategyRefresh {uint32 value = 1;}
|
||||
|
||||
// Number of minutes before a handler is regenerated.
|
||||
// Default value is 5 if unset.
|
||||
@@ -73,7 +73,7 @@ message ReceiverConfig {
|
||||
reserved 6;
|
||||
// Override domains for the given protocol.
|
||||
// Deprecated. Use sniffing_settings.
|
||||
repeated KnownProtocols domain_override = 7 [ deprecated = true ];
|
||||
repeated KnownProtocols domain_override = 7 [deprecated = true];
|
||||
SniffingConfig sniffing_settings = 8;
|
||||
}
|
||||
|
||||
@@ -91,11 +91,25 @@ message SenderConfig {
|
||||
xray.transport.internet.StreamConfig stream_settings = 2;
|
||||
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
||||
MultiplexingConfig multiplex_settings = 4;
|
||||
SingMultiplexConfig smux_settings = 5;
|
||||
}
|
||||
|
||||
message MultiplexingConfig {
|
||||
// Whether or not Mux is enabled.
|
||||
bool enabled = 1;
|
||||
// Max number of concurrent connections that one Mux connection can handle.
|
||||
uint32 concurrency = 2;
|
||||
int32 concurrency = 2;
|
||||
// Transport XUDP in another Mux.
|
||||
int32 xudpConcurrency = 3;
|
||||
// "reject" (default), "allow" or "skip".
|
||||
string xudpProxyUDP443 = 4;
|
||||
}
|
||||
|
||||
message SingMultiplexConfig {
|
||||
bool enabled = 1;
|
||||
string protocol = 2;
|
||||
int32 max_connections = 3;
|
||||
int32 min_streams = 4;
|
||||
int32 max_streams = 5;
|
||||
bool padding = 6;
|
||||
}
|
||||
|
@@ -144,7 +144,6 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
@@ -87,7 +87,6 @@ func (h *DynamicInboundHandler) allocatePort() net.Port {
|
||||
return port
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (h *DynamicInboundHandler) closeWorkers(workers []worker) {
|
||||
|
@@ -108,9 +108,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
||||
newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
cancel()
|
||||
if err := conn.Close(); err != nil {
|
||||
newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func (w *tcpWorker) Proxy() proxy.Inbound {
|
||||
|
@@ -2,13 +2,19 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
sing_mux "github.com/sagernet/sing-mux"
|
||||
sing_net "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
@@ -54,6 +60,10 @@ type Handler struct {
|
||||
proxy proxy.Outbound
|
||||
outboundManager outbound.Manager
|
||||
mux *mux.ClientManager
|
||||
xudp *mux.ClientManager
|
||||
smux *sing_mux.Client
|
||||
|
||||
udp443 string
|
||||
uplinkCounter stats.Counter
|
||||
downlinkCounter stats.Counter
|
||||
}
|
||||
@@ -103,22 +113,65 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||
}
|
||||
|
||||
if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
|
||||
config := h.senderSettings.MultiplexSettings
|
||||
if config.Concurrency < 1 || config.Concurrency > 1024 {
|
||||
return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning()
|
||||
}
|
||||
h.mux = &mux.ClientManager{
|
||||
Enabled: h.senderSettings.MultiplexSettings.Enabled,
|
||||
Picker: &mux.IncrementalWorkerPicker{
|
||||
Factory: &mux.DialingWorkerFactory{
|
||||
Proxy: proxyHandler,
|
||||
Dialer: h,
|
||||
Strategy: mux.ClientStrategy{
|
||||
MaxConcurrency: config.Concurrency,
|
||||
MaxConnection: 128,
|
||||
if config := h.senderSettings.MultiplexSettings; config.Enabled {
|
||||
if config.Concurrency < 0 {
|
||||
h.mux = &mux.ClientManager{Enabled: false}
|
||||
}
|
||||
if config.Concurrency == 0 {
|
||||
config.Concurrency = 8 // same as before
|
||||
}
|
||||
if config.Concurrency > 0 {
|
||||
h.mux = &mux.ClientManager{
|
||||
Enabled: true,
|
||||
Picker: &mux.IncrementalWorkerPicker{
|
||||
Factory: &mux.DialingWorkerFactory{
|
||||
Proxy: proxyHandler,
|
||||
Dialer: h,
|
||||
Strategy: mux.ClientStrategy{
|
||||
MaxConcurrency: uint32(config.Concurrency),
|
||||
MaxConnection: 128,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if config.XudpConcurrency < 0 {
|
||||
h.xudp = &mux.ClientManager{Enabled: false}
|
||||
}
|
||||
if config.XudpConcurrency == 0 {
|
||||
h.xudp = nil // same as before
|
||||
}
|
||||
if config.XudpConcurrency > 0 {
|
||||
h.xudp = &mux.ClientManager{
|
||||
Enabled: true,
|
||||
Picker: &mux.IncrementalWorkerPicker{
|
||||
Factory: &mux.DialingWorkerFactory{
|
||||
Proxy: proxyHandler,
|
||||
Dialer: h,
|
||||
Strategy: mux.ClientStrategy{
|
||||
MaxConcurrency: uint32(config.XudpConcurrency),
|
||||
MaxConnection: 128,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
h.udp443 = config.XudpProxyUDP443
|
||||
}
|
||||
}
|
||||
if h.senderSettings != nil && h.senderSettings.SmuxSettings != nil {
|
||||
if config := h.senderSettings.SmuxSettings; config.Enabled {
|
||||
h.smux, err = sing_mux.NewClient(sing_mux.Options{
|
||||
Dialer: singbridge.NewOutboundDialer(proxyHandler, h),
|
||||
Protocol: config.Protocol,
|
||||
MaxConnections: int(config.MaxConnections),
|
||||
MinStreams: int(config.MinStreams),
|
||||
MaxStreams: int(config.MaxStreams),
|
||||
Padding: config.Padding,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, newError("failed to create sing mux client").Base(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,25 +186,82 @@ func (h *Handler) Tag() string {
|
||||
|
||||
// Dispatch implements proxy.Outbound.Dispatch.
|
||||
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
||||
if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
|
||||
if err := h.mux.Dispatch(ctx, link); err != nil {
|
||||
err := newError("failed to process mux outbound traffic").Base(err)
|
||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||
err.WriteToLog(session.ExportIDToError(ctx))
|
||||
common.Interrupt(link.Writer)
|
||||
if h.mux != nil {
|
||||
test := func(err error) {
|
||||
if err != nil {
|
||||
err := newError("failed to process mux outbound traffic").Base(err)
|
||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||
err.WriteToLog(session.ExportIDToError(ctx))
|
||||
common.Interrupt(link.Writer)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := h.proxy.Process(ctx, link, h); err != nil {
|
||||
// Ensure outbound ray is properly closed.
|
||||
err := newError("failed to process outbound traffic").Base(err)
|
||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||
err.WriteToLog(session.ExportIDToError(ctx))
|
||||
common.Interrupt(link.Writer)
|
||||
} else {
|
||||
common.Must(common.Close(link.Writer))
|
||||
outbound := session.OutboundFromContext(ctx)
|
||||
if outbound.Target.Network == net.Network_UDP && outbound.Target.Port == 443 {
|
||||
switch h.udp443 {
|
||||
case "reject":
|
||||
test(newError("XUDP rejected UDP/443 traffic").AtInfo())
|
||||
return
|
||||
case "skip":
|
||||
goto out
|
||||
}
|
||||
}
|
||||
if h.xudp != nil && outbound.Target.Network == net.Network_UDP {
|
||||
if !h.xudp.Enabled {
|
||||
goto out
|
||||
}
|
||||
test(h.xudp.Dispatch(ctx, link))
|
||||
return
|
||||
}
|
||||
if h.mux.Enabled {
|
||||
test(h.mux.Dispatch(ctx, link))
|
||||
return
|
||||
}
|
||||
common.Interrupt(link.Reader)
|
||||
}
|
||||
if h.smux != nil {
|
||||
test := func(err error) {
|
||||
if err != nil {
|
||||
err := newError("failed to process mux outbound traffic").Base(err)
|
||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||
err.WriteToLog(session.ExportIDToError(ctx))
|
||||
common.Interrupt(link.Writer)
|
||||
}
|
||||
}
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
outbound := session.OutboundFromContext(ctx)
|
||||
if outbound.Target.Network == net.Network_TCP {
|
||||
conn, err := h.smux.DialContext(ctx, sing_net.NetworkTCP, singbridge.ToSocksaddr(outbound.Target))
|
||||
if err != nil {
|
||||
test(err)
|
||||
return
|
||||
}
|
||||
test(singbridge.CopyConn(ctx, inbound.Conn, link, conn))
|
||||
} else {
|
||||
packetConn, err := h.smux.ListenPacket(ctx, singbridge.ToSocksaddr(outbound.Target))
|
||||
if err != nil {
|
||||
test(err)
|
||||
return
|
||||
}
|
||||
test(singbridge.CopyPacketConn(ctx, inbound.Conn, link, outbound.Target, packetConn))
|
||||
}
|
||||
return
|
||||
}
|
||||
out:
|
||||
err := h.proxy.Process(ctx, link, h)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Ensure outbound ray is properly closed.
|
||||
err := newError("failed to process outbound traffic").Base(err)
|
||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||
err.WriteToLog(session.ExportIDToError(ctx))
|
||||
common.Interrupt(link.Writer)
|
||||
} else {
|
||||
common.Close(link.Writer)
|
||||
}
|
||||
common.Interrupt(link.Reader)
|
||||
}
|
||||
|
||||
// Address implements internet.Dialer.
|
||||
@@ -202,6 +312,10 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||
}
|
||||
}
|
||||
|
||||
if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid {
|
||||
return conn, err
|
||||
}
|
||||
|
||||
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
||||
return h.getStatCouterConnection(conn), err
|
||||
}
|
||||
|
31
app/proxyman/outbound/uot.go
Normal file
31
app/proxyman/outbound/uot.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
func (h *Handler) getUoTConnection(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
||||
if !dest.Address.Family().IsDomain() {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
var uotVersion int
|
||||
if dest.Address.Domain() == uot.MagicAddress {
|
||||
uotVersion = uot.Version
|
||||
} else if dest.Address.Domain() == uot.LegacyMagicAddress {
|
||||
uotVersion = uot.LegacyVersion
|
||||
} else {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings)
|
||||
if err != nil {
|
||||
return nil, newError("unable to listen socket").Base(err)
|
||||
}
|
||||
conn := uot.NewServerConn(packetConn, uotVersion)
|
||||
return h.getStatCouterConnection(conn), nil
|
||||
}
|
@@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/reverse/config.proto
|
||||
|
||||
package reverse
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
|
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
internalDomain = "reverse.internal.example.com"
|
||||
internalDomain = "reverse.internal.v2fly.org" // make reverse proxy compatible with v2fly
|
||||
)
|
||||
|
||||
func isDomain(dest net.Destination, domain string) bool {
|
||||
|
@@ -44,6 +44,7 @@ func (b *Balancer) PickOutbound() (string, error) {
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
func (b *Balancer) InjectContext(ctx context.Context) {
|
||||
if contextReceiver, ok := b.strategy.(extension.ContextReceiver); ok {
|
||||
contextReceiver.InjectContext(ctx)
|
||||
|
@@ -6,12 +6,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// routingServer is an implementation of RoutingService.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/router/command/command.proto
|
||||
|
||||
package command
|
||||
@@ -163,18 +163,19 @@ func (x *RoutingContext) GetOutboundTag() string {
|
||||
// opened by xray-core.
|
||||
// * FieldSelectors selects a subset of fields in routing statistics to return.
|
||||
// Valid selectors:
|
||||
// - inbound: Selects connection's inbound tag.
|
||||
// - network: Selects connection's network.
|
||||
// - ip: Equivalent as "ip_source" and "ip_target", selects both source and
|
||||
// target IP.
|
||||
// - port: Equivalent as "port_source" and "port_target", selects both source
|
||||
// and target port.
|
||||
// - domain: Selects target domain.
|
||||
// - protocol: Select connection's protocol.
|
||||
// - user: Select connection's inbound user email.
|
||||
// - attributes: Select connection's additional attributes.
|
||||
// - outbound: Equivalent as "outbound" and "outbound_group", select both
|
||||
// outbound tag and outbound group tags.
|
||||
// - inbound: Selects connection's inbound tag.
|
||||
// - network: Selects connection's network.
|
||||
// - ip: Equivalent as "ip_source" and "ip_target", selects both source and
|
||||
// target IP.
|
||||
// - port: Equivalent as "port_source" and "port_target", selects both source
|
||||
// and target port.
|
||||
// - domain: Selects target domain.
|
||||
// - protocol: Select connection's protocol.
|
||||
// - user: Select connection's inbound user email.
|
||||
// - attributes: Select connection's additional attributes.
|
||||
// - outbound: Equivalent as "outbound" and "outbound_group", select both
|
||||
// outbound tag and outbound group tags.
|
||||
//
|
||||
// * If FieldSelectors is left empty, all fields will be returned.
|
||||
type SubscribeRoutingStatsRequest struct {
|
||||
state protoimpl.MessageState
|
||||
|
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.12
|
||||
// source: app/router/command/command.proto
|
||||
|
||||
package command
|
||||
|
||||
|
@@ -8,9 +8,6 @@ import (
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
. "github.com/xtls/xray-core/app/router/command"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
@@ -18,6 +15,8 @@ import (
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/testing/mocks"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
)
|
||||
|
||||
func TestServiceSubscribeRoutingStats(t *testing.T) {
|
||||
|
@@ -3,12 +3,11 @@ package router
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/syntax"
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/strmatcher"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/syntax"
|
||||
)
|
||||
|
||||
type Condition interface {
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
. "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/router/config.proto
|
||||
|
||||
package router
|
||||
@@ -477,6 +477,7 @@ type RoutingRule struct {
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to TargetTag:
|
||||
//
|
||||
// *RoutingRule_Tag
|
||||
// *RoutingRule_BalancingTag
|
||||
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
|
||||
@@ -834,6 +835,7 @@ type Domain_Attribute struct {
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Types that are assignable to TypedValue:
|
||||
//
|
||||
// *Domain_Attribute_BoolValue
|
||||
// *Domain_Attribute_IntValue
|
||||
TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"`
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
. "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@@ -45,7 +45,7 @@ func (l *LeastPingStrategy) PickOutbound(strings []string) string {
|
||||
return selectedOutboundName
|
||||
}
|
||||
|
||||
//No way to understand observeReport
|
||||
// No way to understand observeReport
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@@ -7,13 +7,12 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/strmatcher"
|
||||
"github.com/xtls/xray-core/core"
|
||||
feature_stats "github.com/xtls/xray-core/features/stats"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// statsServer is an implementation of StatsService.
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/stats/command/command.proto
|
||||
|
||||
package command
|
||||
|
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.12
|
||||
// source: app/stats/command/command.proto
|
||||
|
||||
package command
|
||||
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
. "github.com/xtls/xray-core/app/stats/command"
|
||||
"github.com/xtls/xray-core/common"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: app/stats/config.proto
|
||||
|
||||
package stats
|
||||
|
@@ -18,10 +18,11 @@ var pool = bytespool.GetPool(Size)
|
||||
// the buffer into an internal buffer pool, in order to recreate a buffer more
|
||||
// quickly.
|
||||
type Buffer struct {
|
||||
v []byte
|
||||
start int32
|
||||
end int32
|
||||
UDP *net.Destination
|
||||
v []byte
|
||||
start int32
|
||||
end int32
|
||||
unmanaged bool
|
||||
UDP *net.Destination
|
||||
}
|
||||
|
||||
// New creates a Buffer with 0 length and 8K capacity.
|
||||
@@ -38,6 +39,7 @@ func New() *Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// NewExisted creates a managed, standard size Buffer with an existed bytearray
|
||||
func NewExisted(b []byte) *Buffer {
|
||||
if cap(b) < Size {
|
||||
panic("Invalid buffer")
|
||||
@@ -54,6 +56,15 @@ func NewExisted(b []byte) *Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// FromBytes creates a Buffer with an existed bytearray
|
||||
func FromBytes(b []byte) *Buffer {
|
||||
return &Buffer{
|
||||
v: b,
|
||||
end: int32(len(b)),
|
||||
unmanaged: true,
|
||||
}
|
||||
}
|
||||
|
||||
// StackNew creates a new Buffer object on stack.
|
||||
// This method is for buffers that is released in the same function.
|
||||
func StackNew() Buffer {
|
||||
@@ -71,7 +82,7 @@ func StackNew() Buffer {
|
||||
|
||||
// Release recycles the buffer into an internal buffer pool.
|
||||
func (b *Buffer) Release() {
|
||||
if b == nil || b.v == nil {
|
||||
if b == nil || b.v == nil || b.unmanaged {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,6 +160,19 @@ func (b *Buffer) BytesTo(to int32) []byte {
|
||||
return b.v[b.start : b.start+to]
|
||||
}
|
||||
|
||||
// Check makes sure that 0 <= b.start <= b.end.
|
||||
func (b *Buffer) Check() {
|
||||
if b.start < 0 {
|
||||
b.start = 0
|
||||
}
|
||||
if b.end < 0 {
|
||||
b.end = 0
|
||||
}
|
||||
if b.start > b.end {
|
||||
b.start = b.end
|
||||
}
|
||||
}
|
||||
|
||||
// Resize cuts the buffer at the given position.
|
||||
func (b *Buffer) Resize(from, to int32) {
|
||||
if from < 0 {
|
||||
@@ -162,6 +186,7 @@ func (b *Buffer) Resize(from, to int32) {
|
||||
}
|
||||
b.end = b.start + to
|
||||
b.start += from
|
||||
b.Check()
|
||||
}
|
||||
|
||||
// Advance cuts the buffer at the given position.
|
||||
@@ -170,6 +195,7 @@ func (b *Buffer) Advance(from int32) {
|
||||
from += b.Len()
|
||||
}
|
||||
b.start += from
|
||||
b.Check()
|
||||
}
|
||||
|
||||
// Len returns the length of the buffer content.
|
||||
@@ -212,6 +238,28 @@ func (b *Buffer) WriteString(s string) (int, error) {
|
||||
return b.Write([]byte(s))
|
||||
}
|
||||
|
||||
// ReadByte implements io.ByteReader
|
||||
func (b *Buffer) ReadByte() (byte, error) {
|
||||
if b.start == b.end {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
nb := b.v[b.start]
|
||||
b.start++
|
||||
return nb, nil
|
||||
}
|
||||
|
||||
// ReadBytes implements bufio.Reader.ReadBytes
|
||||
func (b *Buffer) ReadBytes(length int32) ([]byte, error) {
|
||||
if b.end-b.start < length {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
nb := b.v[b.start : b.start+length]
|
||||
b.start += length
|
||||
return nb, nil
|
||||
}
|
||||
|
||||
// Read implements io.Reader.Read().
|
||||
func (b *Buffer) Read(data []byte) (int, error) {
|
||||
if b.Len() == 0 {
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/common/buf"
|
||||
)
|
||||
|
@@ -48,7 +48,7 @@ func (e readError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (e readError) Inner() error {
|
||||
func (e readError) Unwrap() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (e writeError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (e writeError) Inner() error {
|
||||
func (e writeError) Unwrap() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/testing/mocks"
|
||||
|
@@ -203,6 +203,19 @@ func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) {
|
||||
return mb, r
|
||||
}
|
||||
|
||||
// SplitMulti splits the beginning of the MultiBuffer into first one, the index i and after into second one
|
||||
func SplitMulti(mb MultiBuffer, i int) (MultiBuffer, MultiBuffer) {
|
||||
mb2 := make(MultiBuffer, 0, len(mb))
|
||||
if i < len(mb) && i >= 0 {
|
||||
mb2 = append(mb2, mb[i:]...)
|
||||
for j := i; j < len(mb); j++ {
|
||||
mb[j] = nil
|
||||
}
|
||||
mb = mb[:i]
|
||||
}
|
||||
return mb, mb2
|
||||
}
|
||||
|
||||
// WriteMultiBuffer writes all buffers from the MultiBuffer to the Writer one by one, and return error if any, with leftover MultiBuffer.
|
||||
func WriteMultiBuffer(writer io.Writer, mb MultiBuffer) (MultiBuffer, error) {
|
||||
for {
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/common/buf"
|
||||
)
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
func readOneUDP(r io.Reader) (*Buffer, error) {
|
||||
func ReadOneUDP(r io.Reader) (*Buffer, error) {
|
||||
b := New()
|
||||
for i := 0; i < 64; i++ {
|
||||
_, err := b.ReadFrom(r)
|
||||
@@ -166,7 +166,7 @@ type PacketReader struct {
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
func (r *PacketReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
b, err := readOneUDP(r.Reader)
|
||||
b, err := ReadOneUDP(r.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -9,11 +9,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/testing/servers/tcp"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func TestReadvReader(t *testing.T) {
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
|
@@ -2,8 +2,8 @@ package crypto
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
@@ -265,7 +265,8 @@ func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) {
|
||||
return nil, err
|
||||
}
|
||||
if paddingSize > 0 {
|
||||
// With size of the chunk and padding length encrypted, the content of padding doesn't matter much.
|
||||
// These paddings will send in clear text.
|
||||
// To avoid leakage of PRNG internal state, a cryptographically secure PRNG should be used.
|
||||
paddingBytes := eb.Extend(paddingSize)
|
||||
common.Must2(rand.Read(paddingBytes))
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
. "github.com/xtls/xray-core/common/crypto"
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
@@ -2,7 +2,6 @@
|
||||
package errors // import "github.com/xtls/xray-core/common/errors"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -13,8 +12,8 @@ import (
|
||||
const trim = len("github.com/xtls/xray-core/")
|
||||
|
||||
type hasInnerError interface {
|
||||
// Inner returns the underlying error of this one.
|
||||
Inner() error
|
||||
// Unwrap returns the underlying error of this one.
|
||||
Unwrap() error
|
||||
}
|
||||
|
||||
type hasSeverity interface {
|
||||
@@ -72,8 +71,8 @@ func (err *Error) Error() string {
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// Inner implements hasInnerError.Inner()
|
||||
func (err *Error) Inner() error {
|
||||
// Unwrap implements hasInnerError.Unwrap()
|
||||
func (err *Error) Unwrap() error {
|
||||
if err.inner == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -171,20 +170,10 @@ L:
|
||||
for {
|
||||
switch inner := err.(type) {
|
||||
case hasInnerError:
|
||||
if inner.Inner() == nil {
|
||||
if inner.Unwrap() == nil {
|
||||
break L
|
||||
}
|
||||
err = inner.Inner()
|
||||
case *os.PathError:
|
||||
if inner.Err == nil {
|
||||
break L
|
||||
}
|
||||
err = inner.Err
|
||||
case *os.SyscallError:
|
||||
if inner.Err == nil {
|
||||
break L
|
||||
}
|
||||
err = inner.Err
|
||||
err = inner.Unwrap()
|
||||
default:
|
||||
break L
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
. "github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.18.0
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: common/log/log.proto
|
||||
|
||||
package log
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/common/xudp"
|
||||
"github.com/xtls/xray-core/proxy"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
@@ -247,22 +248,20 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||
transferType = protocol.TransferTypePacket
|
||||
}
|
||||
s.transferType = transferType
|
||||
writer := NewWriter(s.ID, dest, output, transferType)
|
||||
defer s.Close()
|
||||
writer := NewWriter(s.ID, dest, output, transferType, xudp.GetGlobalID(ctx))
|
||||
defer s.Close(false)
|
||||
defer writer.Close()
|
||||
|
||||
newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
||||
if err := writeFirstPayload(s.input, writer); err != nil {
|
||||
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
writer.hasError = true
|
||||
common.Interrupt(s.input)
|
||||
return
|
||||
}
|
||||
|
||||
if err := buf.Copy(s.input, writer); err != nil {
|
||||
newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
writer.hasError = true
|
||||
common.Interrupt(s.input)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -335,15 +334,8 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
err := buf.Copy(rr, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
||||
|
||||
// Notify remote peer to close this session.
|
||||
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
|
||||
closingWriter.Close()
|
||||
|
||||
drainErr := buf.Copy(rr, buf.Discard)
|
||||
common.Interrupt(s.input)
|
||||
s.Close()
|
||||
return drainErr
|
||||
s.Close(false)
|
||||
return buf.Copy(rr, buf.Discard)
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -351,11 +343,7 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
|
||||
func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
||||
if meta.Option.Has(OptionError) {
|
||||
common.Interrupt(s.input)
|
||||
common.Interrupt(s.output)
|
||||
}
|
||||
s.Close()
|
||||
s.Close(false)
|
||||
}
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
|
@@ -58,6 +58,7 @@ type FrameMetadata struct {
|
||||
SessionID uint16
|
||||
Option bitmask.Byte
|
||||
SessionStatus SessionStatus
|
||||
GlobalID [8]byte
|
||||
}
|
||||
|
||||
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
@@ -81,6 +82,9 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.UDP != nil { // make sure it's user's proxy request
|
||||
b.Write(f.GlobalID[:]) // no need to check whether it's empty
|
||||
}
|
||||
} else if b.UDP != nil {
|
||||
b.WriteByte(byte(TargetNetworkUDP))
|
||||
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
||||
@@ -122,7 +126,8 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
f.Option = bitmask.Byte(b.Byte(3))
|
||||
f.Target.Network = net.Network_Unknown
|
||||
|
||||
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() != 4) {
|
||||
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() > 4 &&
|
||||
TargetNetwork(b.Byte(4)) == TargetNetworkUDP) { // MUST check the flag first
|
||||
if b.Len() < 8 {
|
||||
return newError("insufficient buffer: ", b.Len())
|
||||
}
|
||||
@@ -144,5 +149,11 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Application data is essential, to test whether the pipe is closed.
|
||||
if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) &&
|
||||
f.Target.Network == net.Network_UDP && b.Len() >= 8 {
|
||||
copy(f.GlobalID[:], b.Bytes())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
. "github.com/xtls/xray-core/common/mux"
|
||||
@@ -33,13 +32,13 @@ func TestReaderWriter(t *testing.T) {
|
||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||
|
||||
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream)
|
||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
|
||||
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream)
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
|
||||
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream)
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
|
||||
writePayload := func(writer *Writer, payload ...byte) error {
|
||||
b := buf.New()
|
||||
|
@@ -4,13 +4,17 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
sing_mux "github.com/sagernet/sing-mux"
|
||||
"github.com/sagernet/sing/common/metadata"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
@@ -38,6 +42,14 @@ func (s *Server) Type() interface{} {
|
||||
// Dispatch implements routing.Dispatcher
|
||||
func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
|
||||
if dest.Address != muxCoolAddress {
|
||||
if dest.Address.Family() == net.AddressFamilyDomain && dest.Address.Domain() == sing_mux.Destination.Fqdn {
|
||||
opts := pipe.OptionsFromContext(ctx)
|
||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||
conn := cnc.NewConnection(cnc.ConnectionInputMulti(downlinkWriter), cnc.ConnectionOutputMulti(uplinkReader))
|
||||
go sing_mux.HandleConnection(ctx, singbridge.NewDispatcher(s.dispatcher, newError), singbridge.NewLogger(newError), conn, metadata.Metadata{Destination: singbridge.ToSocksaddr(dest)})
|
||||
return &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil
|
||||
}
|
||||
return s.dispatcher.Dispatch(ctx, dest)
|
||||
}
|
||||
|
||||
@@ -59,6 +71,10 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*transport
|
||||
// DispatchLink implements routing.Dispatcher
|
||||
func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error {
|
||||
if dest.Address != muxCoolAddress {
|
||||
if dest.Address.Family() == net.AddressFamilyDomain && dest.Address.Domain() == sing_mux.Destination.Fqdn {
|
||||
conn := cnc.NewConnection(cnc.ConnectionInputMulti(link.Writer), cnc.ConnectionOutputMulti(link.Reader))
|
||||
return sing_mux.HandleConnection(ctx, singbridge.NewDispatcher(s.dispatcher, newError), singbridge.NewLogger(newError), conn, metadata.Metadata{Destination: singbridge.ToSocksaddr(dest)})
|
||||
}
|
||||
return s.dispatcher.DispatchLink(ctx, dest, link)
|
||||
}
|
||||
_, err := NewServerWorker(ctx, s.dispatcher, link)
|
||||
@@ -99,7 +115,7 @@ func handle(ctx context.Context, s *Session, output buf.Writer) {
|
||||
}
|
||||
|
||||
writer.Close()
|
||||
s.Close()
|
||||
s.Close(false)
|
||||
}
|
||||
|
||||
func (w *ServerWorker) ActiveConnections() uint32 {
|
||||
@@ -131,6 +147,81 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
}
|
||||
ctx = log.ContextWithAccessMessage(ctx, msg)
|
||||
}
|
||||
|
||||
if network := session.AllowedNetworkFromContext(ctx); network != net.Network_Unknown {
|
||||
if meta.Target.Network != network {
|
||||
return newError("unexpected network ", meta.Target.Network) // it will break the whole Mux connection
|
||||
}
|
||||
}
|
||||
|
||||
if meta.GlobalID != [8]byte{} { // MUST ignore empty Global ID
|
||||
mb, err := NewPacketReader(reader, &meta.Target).ReadMultiBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
XUDPManager.Lock()
|
||||
x := XUDPManager.Map[meta.GlobalID]
|
||||
if x == nil {
|
||||
x = &XUDP{GlobalID: meta.GlobalID}
|
||||
XUDPManager.Map[meta.GlobalID] = x
|
||||
XUDPManager.Unlock()
|
||||
} else {
|
||||
if x.Status == Initializing { // nearly impossible
|
||||
XUDPManager.Unlock()
|
||||
newError("XUDP hit ", meta.GlobalID).Base(errors.New("conflict")).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
// It's not a good idea to return an err here, so just let client wait.
|
||||
// Client will receive an End frame after sending a Keep frame.
|
||||
return nil
|
||||
}
|
||||
x.Status = Initializing
|
||||
XUDPManager.Unlock()
|
||||
x.Mux.Close(false) // detach from previous Mux
|
||||
b := buf.New()
|
||||
b.Write(mb[0].Bytes())
|
||||
b.UDP = mb[0].UDP
|
||||
if err = x.Mux.output.WriteMultiBuffer(mb); err != nil {
|
||||
x.Interrupt()
|
||||
mb = buf.MultiBuffer{b}
|
||||
} else {
|
||||
b.Release()
|
||||
mb = nil
|
||||
}
|
||||
newError("XUDP hit ", meta.GlobalID).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
if mb != nil {
|
||||
ctx = session.ContextWithTimeoutOnly(ctx, true)
|
||||
// Actually, it won't return an error in Xray-core's implementations.
|
||||
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||
if err != nil {
|
||||
XUDPManager.Lock()
|
||||
delete(XUDPManager.Map, x.GlobalID)
|
||||
XUDPManager.Unlock()
|
||||
err = newError("XUDP new ", meta.GlobalID).Base(errors.New("failed to dispatch request to ", meta.Target).Base(err))
|
||||
return err // it will break the whole Mux connection
|
||||
}
|
||||
link.Writer.WriteMultiBuffer(mb) // it's meaningless to test a new pipe
|
||||
x.Mux = &Session{
|
||||
input: link.Reader,
|
||||
output: link.Writer,
|
||||
}
|
||||
newError("XUDP new ", meta.GlobalID).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
x.Mux = &Session{
|
||||
input: x.Mux.input,
|
||||
output: x.Mux.output,
|
||||
parent: w.sessionManager,
|
||||
ID: meta.SessionID,
|
||||
transferType: protocol.TransferTypePacket,
|
||||
XUDP: x,
|
||||
}
|
||||
go handle(ctx, x.Mux, w.link.Writer)
|
||||
x.Status = Active
|
||||
if !w.sessionManager.Add(x.Mux) {
|
||||
x.Mux.Close(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||
if err != nil {
|
||||
if meta.Option.Has(OptionData) {
|
||||
@@ -157,8 +248,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
if err := buf.Copy(rr, s.output); err != nil {
|
||||
buf.Copy(rr, buf.Discard)
|
||||
common.Interrupt(s.input)
|
||||
return s.Close()
|
||||
return s.Close(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -182,15 +272,8 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
|
||||
|
||||
// Notify remote peer to close this session.
|
||||
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
|
||||
closingWriter.Close()
|
||||
|
||||
drainErr := buf.Copy(rr, buf.Discard)
|
||||
common.Interrupt(s.input)
|
||||
s.Close()
|
||||
return drainErr
|
||||
s.Close(false)
|
||||
return buf.Copy(rr, buf.Discard)
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -198,11 +281,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
|
||||
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
||||
if meta.Option.Has(OptionError) {
|
||||
common.Interrupt(s.input)
|
||||
common.Interrupt(s.output)
|
||||
}
|
||||
s.Close()
|
||||
s.Close(false)
|
||||
}
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
|
@@ -1,12 +1,16 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
type SessionManager struct {
|
||||
@@ -61,21 +65,25 @@ func (m *SessionManager) Allocate() *Session {
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *SessionManager) Add(s *Session) {
|
||||
func (m *SessionManager) Add(s *Session) bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if m.closed {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
m.count++
|
||||
m.sessions[s.ID] = s
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *SessionManager) Remove(id uint16) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
func (m *SessionManager) Remove(locked bool, id uint16) {
|
||||
if !locked {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
}
|
||||
locked = true
|
||||
|
||||
if m.closed {
|
||||
return
|
||||
@@ -83,9 +91,11 @@ func (m *SessionManager) Remove(id uint16) {
|
||||
|
||||
delete(m.sessions, id)
|
||||
|
||||
if len(m.sessions) == 0 {
|
||||
m.sessions = make(map[uint16]*Session, 16)
|
||||
}
|
||||
/*
|
||||
if len(m.sessions) == 0 {
|
||||
m.sessions = make(map[uint16]*Session, 16)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func (m *SessionManager) Get(id uint16) (*Session, bool) {
|
||||
@@ -127,8 +137,7 @@ func (m *SessionManager) Close() error {
|
||||
m.closed = true
|
||||
|
||||
for _, s := range m.sessions {
|
||||
common.Close(s.input)
|
||||
common.Close(s.output)
|
||||
s.Close(true)
|
||||
}
|
||||
|
||||
m.sessions = nil
|
||||
@@ -142,13 +151,40 @@ type Session struct {
|
||||
parent *SessionManager
|
||||
ID uint16
|
||||
transferType protocol.TransferType
|
||||
closed bool
|
||||
XUDP *XUDP
|
||||
}
|
||||
|
||||
// Close closes all resources associated with this session.
|
||||
func (s *Session) Close() error {
|
||||
common.Close(s.output)
|
||||
common.Close(s.input)
|
||||
s.parent.Remove(s.ID)
|
||||
func (s *Session) Close(locked bool) error {
|
||||
if !locked {
|
||||
s.parent.Lock()
|
||||
defer s.parent.Unlock()
|
||||
}
|
||||
locked = true
|
||||
if s.closed {
|
||||
return nil
|
||||
}
|
||||
s.closed = true
|
||||
if s.XUDP == nil {
|
||||
common.Interrupt(s.input)
|
||||
common.Close(s.output)
|
||||
} else {
|
||||
// Stop existing handle(), then trigger writer.Close().
|
||||
// Note that s.output may be dispatcher.SizeStatWriter.
|
||||
s.input.(*pipe.Reader).ReturnAnError(io.EOF)
|
||||
runtime.Gosched()
|
||||
// If the error set by ReturnAnError still exists, clear it.
|
||||
s.input.(*pipe.Reader).Recover()
|
||||
XUDPManager.Lock()
|
||||
if s.XUDP.Status == Active {
|
||||
s.XUDP.Expire = time.Now().Add(time.Minute)
|
||||
s.XUDP.Status = Expiring
|
||||
newError("XUDP put ", s.XUDP.GlobalID).AtDebug().WriteToLog()
|
||||
}
|
||||
XUDPManager.Unlock()
|
||||
}
|
||||
s.parent.Remove(locked, s.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -159,3 +195,45 @@ func (s *Session) NewReader(reader *buf.BufferedReader, dest *net.Destination) b
|
||||
}
|
||||
return NewPacketReader(reader, dest)
|
||||
}
|
||||
|
||||
const (
|
||||
Initializing = 0
|
||||
Active = 1
|
||||
Expiring = 2
|
||||
)
|
||||
|
||||
type XUDP struct {
|
||||
GlobalID [8]byte
|
||||
Status uint64
|
||||
Expire time.Time
|
||||
Mux *Session
|
||||
}
|
||||
|
||||
func (x *XUDP) Interrupt() {
|
||||
common.Interrupt(x.Mux.input)
|
||||
common.Close(x.Mux.output)
|
||||
}
|
||||
|
||||
var XUDPManager struct {
|
||||
sync.Mutex
|
||||
Map map[[8]byte]*XUDP
|
||||
}
|
||||
|
||||
func init() {
|
||||
XUDPManager.Map = make(map[[8]byte]*XUDP)
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Minute)
|
||||
now := time.Now()
|
||||
XUDPManager.Lock()
|
||||
for id, x := range XUDPManager.Map {
|
||||
if x.Status == Expiring && now.After(x.Expire) {
|
||||
x.Interrupt()
|
||||
delete(XUDPManager.Map, id)
|
||||
newError("XUDP del ", id).AtDebug().WriteToLog()
|
||||
}
|
||||
}
|
||||
XUDPManager.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ func TestSessionManagerClose(t *testing.T) {
|
||||
if m.CloseIfNoSession() {
|
||||
t.Error("able to close")
|
||||
}
|
||||
m.Remove(s.ID)
|
||||
m.Remove(false, s.ID)
|
||||
if !m.CloseIfNoSession() {
|
||||
t.Error("not able to close")
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user