Compare commits

..
10 Commits
Author SHA1 Message Date
yuhan6665 43779f379f Experiment: seed ignore buffer and send 1st fake packet instantly 2025-09-07 09:48:21 -04:00
yuhan6665 082fecf334 Add quick logic for scheduler to send fake packet when no buffer is pending 2025-09-07 09:37:44 -04:00
yuhan6665 7299cfc56f Add VLESS seed tests 2025-09-07 09:36:46 -04:00
yuhan6665 0e206b99bd Add proxy Scheduler 2025-09-07 09:33:58 -04:00
yuhan6665 45f677a538 Fill vless response addon 2025-09-07 09:31:14 -04:00
yuhan6665 b6afe68d84 Add seed padding logic
- Seed is decoupled with XTLS Vision, which means Seed can turn on without flow
- XTLS Vision now use Seed config to configure its padding only mode
2025-09-07 09:28:24 -04:00
yuhan6665 51234fbe53 Collect stats and possible padding (previously Vision Reader Writer) to all traffic 2025-09-07 09:12:48 -04:00
yuhan6665 cba71f8cdc Add more traffic info for future use 2025-09-05 21:49:55 -04:00
yuhan6665 279abd4fc8 Populate Seed (more TBD) and checks 2025-09-05 21:33:31 -04:00
yuhan6665 b1f4d32ef0 Add VLESS seed configurations 2025-09-05 21:29:11 -04:00
705 changed files with 21089 additions and 57808 deletions
+2 -1
View File
@@ -29,5 +29,6 @@
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" }, "openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
"windows-386": { "friendlyName": "windows-32" }, "windows-386": { "friendlyName": "windows-32" },
"windows-amd64": { "friendlyName": "windows-64" }, "windows-amd64": { "friendlyName": "windows-64" },
"windows-arm64": { "friendlyName": "windows-arm64-v8a" } "windows-arm64": { "friendlyName": "windows-arm64-v8a" },
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
} }
-1
View File
@@ -1 +0,0 @@
powershell.exe -ExecutionPolicy Bypass -File ".\xray_no_window.ps1"
-1
View File
@@ -1 +0,0 @@
Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden
-1
View File
@@ -1 +0,0 @@
CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0
+6 -10
View File
@@ -6,7 +6,7 @@ WORKDIR /src
COPY . . COPY . .
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
# Download geodat into a staging directory # Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
@@ -32,13 +32,9 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
# Create log files # Create log files
RUN mkdir -p /tmp/var/log/xray && touch \ RUN mkdir -p /tmp/var/log/xray && touch \
@@ -49,8 +45,8 @@ RUN mkdir -p /tmp/var/log/xray && touch \
FROM gcr.io/distroless/static:nonroot FROM gcr.io/distroless/static:nonroot
COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray
COPY --from=build --chown=65532:65532 --chmod=755 /tmp/empty /usr/local/share/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=65532:65532 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /var/log/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /var/log/xray
+6 -10
View File
@@ -6,7 +6,7 @@ WORKDIR /src
COPY . . COPY . .
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
# Download geodat into a staging directory # Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
@@ -32,13 +32,9 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
# Create log files # Create log files
RUN mkdir -p /tmp/var/log/xray && touch \ RUN mkdir -p /tmp/var/log/xray && touch \
@@ -58,8 +54,8 @@ RUN mkdir -p /tmp/var/log/xray && touch \
FROM --platform=linux/amd64 gcr.io/distroless/static:nonroot FROM --platform=linux/amd64 gcr.io/distroless/static:nonroot
COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray
COPY --from=build --chown=65532:65532 --chmod=755 /tmp/empty /usr/local/share/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=65532:65532 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /var/log/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /var/log/xray
+6 -25
View File
@@ -64,25 +64,17 @@ jobs:
echo "Latest: '$LATEST'." echo "Latest: '$LATEST'."
echo "LATEST=$LATEST" >>${GITHUB_ENV} echo "LATEST=$LATEST" >>${GITHUB_ENV}
NEWEST=false
if [[ "${{ github.event_name }}" == "release" ]]; then
NEWEST=true
fi
echo "Newest: '$NEWEST'."
echo "NEWEST=$NEWEST" >>${GITHUB_ENV}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v5
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -90,7 +82,7 @@ jobs:
- name: Build Docker image (main architectures) - name: Build Docker image (main architectures)
id: build_main_arches id: build_main_arches
uses: docker/build-push-action@v7 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: .github/docker/Dockerfile file: .github/docker/Dockerfile
@@ -105,7 +97,7 @@ jobs:
- name: Build Docker image (additional architectures) - name: Build Docker image (additional architectures)
id: build_additional_arches id: build_additional_arches
uses: docker/build-push-action@v7 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: .github/docker/Dockerfile.usa file: .github/docker/Dockerfile.usa
@@ -132,13 +124,6 @@ jobs:
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi fi
if [[ "${{ env.NEWEST }}" == "true" ]]; then
echo "Adding 'pre-release' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:pre-release'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:pre-release \
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi
- name: Inspect image - name: Inspect image
run: | run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
@@ -146,7 +131,3 @@ jobs:
if [[ "${{ env.LATEST }}" == "true" ]]; then if [[ "${{ env.LATEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
fi fi
if [[ "${{ env.NEWEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:pre-release
fi
+18 -54
View File
@@ -11,20 +11,13 @@ on:
jobs: jobs:
check-assets: check-assets:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Check Assets Existence - name: Check Assets Existence
id: check-assets id: check-assets
run: | run: |
@@ -41,18 +34,6 @@ jobs:
break break
fi fi
done done
LIST=('amd64' 'x86')
for ARCHITECTURE in "${LIST[@]}"
do
echo -e "Checking wintun.dll for ${ARCHITECTURE}..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists."
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Sleep for 90 seconds if Assets Missing - name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true' if: steps.check-assets.outputs.missing == 'true'
@@ -76,14 +57,13 @@ jobs:
fail-fast: false fail-fast: false
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
env: env:
GOOS: ${{ matrix.goos}} GOOS: ${{ matrix.goos}}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v7 uses: actions/checkout@v5
- name: Show workflow information - name: Show workflow information
run: | run: |
@@ -92,7 +72,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v7 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
@@ -115,40 +95,20 @@ jobs:
COMMID=$(git describe --always --dirty) COMMID=$(git describe --always --dirty)
echo 'Building Xray for Windows 7...' echo 'Building Xray for Windows 7...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference. # The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Add additional assets into package
run: |
mv -f resources/geo* build_assets/
if [[ ${GOOS} == 'windows' ]]; then
cp .github/build/windows/* build_assets/
fi
if [[ ${GOOS} == 'windows' ]]; then
echo 'Adding Wintun into packages'
if [[ ${GOARCH} == 'amd64' ]]; then
mv resources/wintun/bin/amd64/wintun.dll build_assets/
fi
if [[ ${GOARCH} == '386' ]]; then
mv resources/wintun/bin/x86/wintun.dll build_assets/
fi
mv resources/wintun/LICENSE.txt build_assets/LICENSE-Wintun
fi
- name: Copy README.md & LICENSE - name: Copy README.md & LICENSE
run: | run: |
mv -f resources/* build_assets
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
@@ -167,6 +127,17 @@ jobs:
openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
done done
- name: Change the name
run: |
mv build_assets Xray-${{ env.ASSET_NAME }}
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./Xray-${{ env.ASSET_NAME }}/*
- name: Upload binaries to release - name: Upload binaries to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
if: github.event_name == 'release' if: github.event_name == 'release'
@@ -175,10 +146,3 @@ jobs:
file: ./Xray-${{ env.ASSET_NAME }}.zip* file: ./Xray-${{ env.ASSET_NAME }}.zip*
tag: ${{ github.ref }} tag: ${{ github.ref }}
file_glob: true file_glob: true
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./build_assets/*
+22 -62
View File
@@ -11,20 +11,13 @@ on:
jobs: jobs:
check-assets: check-assets:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Check Assets Existence - name: Check Assets Existence
id: check-assets id: check-assets
run: | run: |
@@ -41,22 +34,10 @@ jobs:
break break
fi fi
done done
LIST=('amd64' 'x86' 'arm64')
for ARCHITECTURE in "${LIST[@]}"
do
echo -e "Checking wintun.dll for ${ARCHITECTURE}..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists."
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Trigger Asset Update Workflow if Assets Missing - name: Trigger Asset Update Workflow if Assets Missing
if: steps.check-assets.outputs.missing == 'true' if: steps.check-assets.outputs.missing == 'true'
uses: actions/github-script@v9 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
@@ -115,6 +96,9 @@ jobs:
# Windows ARM # Windows ARM
- goos: windows - goos: windows
goarch: arm64 goarch: arm64
- goos: windows
goarch: arm
goarm: 7
# BEGIN Other architectures # BEGIN Other architectures
# BEGIN riscv64 & ARM64 & LOONG64 # BEGIN riscv64 & ARM64 & LOONG64
- goos: linux - goos: linux
@@ -162,7 +146,6 @@ jobs:
fail-fast: false fail-fast: false
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
env: env:
GOOS: ${{ matrix.goos }} GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
@@ -170,7 +153,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v7 uses: actions/checkout@v5
- name: Set up NDK - name: Set up NDK
if: matrix.goos == 'android' if: matrix.goos == 'android'
@@ -193,7 +176,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v7 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
@@ -208,11 +191,10 @@ jobs:
if [[ ${GOOS} == 'windows' ]]; then if [[ ${GOOS} == 'windows' ]]; then
echo 'Building Xray for Windows...' echo 'Building Xray for Windows...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference. # The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
elif [[ ${GOOS} == 'android' ]]; then
echo 'Building Xray for Android...'
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid= -checklinkname=0" -v ./main
else else
echo 'Building Xray...' echo 'Building Xray...'
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
@@ -225,40 +207,14 @@ jobs:
fi fi
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
- name: Restore Wintun Cache
if: matrix.goos == 'windows'
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Add additional assets into package
run: |
mv -f resources/geo* build_assets/
if [[ ${GOOS} == 'windows' ]]; then
cp .github/build/windows/* build_assets/
fi
if [[ ${GOOS} == 'windows' ]]; then
echo 'Adding Wintun into packages'
if [[ ${GOARCH} == 'amd64' ]]; then
mv resources/wintun/bin/amd64/wintun.dll build_assets/
fi
if [[ ${GOARCH} == '386' ]]; then
mv resources/wintun/bin/x86/wintun.dll build_assets/
fi
if [[ ${GOARCH} == 'arm64' ]]; then
mv resources/wintun/bin/arm64/wintun.dll build_assets/
fi
mv resources/wintun/LICENSE.txt build_assets/LICENSE-Wintun
fi
- name: Copy README.md & LICENSE - name: Copy README.md & LICENSE
run: | run: |
mv -f resources/* build_assets
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
@@ -277,6 +233,17 @@ jobs:
openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
done done
- name: Change the name
run: |
mv build_assets Xray-${{ env.ASSET_NAME }}
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./Xray-${{ env.ASSET_NAME }}/*
- name: Upload binaries to release - name: Upload binaries to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
if: github.event_name == 'release' if: github.event_name == 'release'
@@ -285,10 +252,3 @@ jobs:
file: ./Xray-${{ env.ASSET_NAME }}.zip* file: ./Xray-${{ env.ASSET_NAME }}.zip*
tag: ${{ github.ref }} tag: ${{ github.ref }}
file_glob: true file_glob: true
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./build_assets/*
+4 -75
View File
@@ -4,7 +4,6 @@ name: Scheduled assets update
# routine manner, for example: GeoIP/GeoSite. # routine manner, for example: GeoIP/GeoSite.
# Currently updating: # Currently updating:
# - Geodat (GeoIP/Geosite) # - Geodat (GeoIP/Geosite)
# - Wintun (wintun.dll)
on: on:
workflow_dispatch: workflow_dispatch:
@@ -22,18 +21,18 @@ on:
jobs: jobs:
geodat: geodat:
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' if: github.event.schedule == '30 22 * * *' || github.event_name == 'push'|| github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
- name: Update Geodat - name: Update Geodat
id: update id: update
uses: nick-fields/retry@v4 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 60 timeout_minutes: 60
retry_wait_seconds: 60 retry_wait_seconds: 60
@@ -59,78 +58,8 @@ jobs:
done done
- name: Save Geodat Cache - name: Save Geodat Cache
uses: actions/cache/save@v6 uses: actions/cache/save@v4
if: ${{ steps.update.outputs.unhit }} if: ${{ steps.update.outputs.unhit }}
with: with:
path: resources path: resources
key: xray-geodat-${{ github.sha }}-${{ github.run_number }} key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
wintun:
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
env:
ASSETVER: 0.14.1
ASSETHASH: 07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
steps:
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Force downloading if run manually or on file update
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push'
run: |
echo "FORCE_UPDATE=true" >> $GITHUB_ENV
- name: Update Wintun
id: update
uses: nick-fields/retry@v4
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 60
command: |
[ -d 'resources' ] || mkdir resources
LIST=('amd64' 'x86' 'arm64')
for ARCHITECTURE in "${LIST[@]}"
do
FILE_PATH="resources/wintun/bin/${ARCHITECTURE}/wintun.dll"
echo -e "Checking if wintun.dll for ${ARCHITECTURE} exists..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists"
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing"
missing=true
fi
done
if [ -s "./resources/wintun/LICENSE.txt" ]; then
echo -e "LICENSE for Wintun exists"
else
echo -e "LICENSE for Wintun is missing"
missing=true
fi
if [[ -v FORCE_UPDATE ]]; then
missing=true
fi
if [[ "$missing" == true ]]; then
FILENAME=wintun.zip
DOWNLOAD_FILE=wintun-${ASSETVER}.zip
echo -e "Downloading https://www.wintun.net/builds/${DOWNLOAD_FILE}..."
curl -L "https://www.wintun.net/builds/${DOWNLOAD_FILE}" -o "${FILENAME}"
if [[ "$(sha256sum "./${FILENAME}" | awk -F ' ' '{print $1}')" == "${ASSETHASH}" ]]; then
echo -e "Unpacking wintun..."
unzip -u ${FILENAME} -d resources/
echo "unhit=true" >> $GITHUB_OUTPUT
else
echo -e "Digest of ${FILENAME} mismatch."
exit 1
fi
fi
- name: Save Wintun Cache
uses: actions/cache/save@v6
if: ${{ steps.update.outputs.unhit }}
with:
path: resources
key: xray-wintun-${{ github.sha }}-${{ github.run_number }}
+5 -43
View File
@@ -1,4 +1,4 @@
name: Tests and Checkings name: Test
on: on:
push: push:
@@ -8,10 +8,9 @@ on:
jobs: jobs:
check-assets: check-assets:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
@@ -35,45 +34,8 @@ jobs:
if: steps.check-assets.outputs.missing == 'true' if: steps.check-assets.outputs.missing == 'true'
run: sleep 90 run: sleep 90
check-proto:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- name: Checkout codebase
uses: actions/checkout@v7
- name: Check Proto Version Header
run: |
head -n 4 core/config.pb.go > ref.txt
find . -name "*.pb.go" ! -name "*_grpc.pb.go" -print0 | while IFS= read -r -d '' file; do
if ! cmp -s ref.txt <(head -n 4 "$file"); then
echo "Error: Header mismatch in $file"
head -n 4 "$file"
exit 1
fi
done
check-format:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
permissions:
contents: read
steps:
- name: Checkout codebase
uses: actions/checkout@v7
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version-file: go.mod
check-latest: true
cache: false
- name: Check Format
run: |
go install -v mvdan.cc/gofumpt@latest
go run ./infra/vformat/main.go -mode check -pwd ./
test: test:
needs: check-assets needs: check-assets
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
permissions: permissions:
contents: read contents: read
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -83,14 +45,14 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v7 uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v7 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v6 uses: actions/cache/restore@v4
with: with:
path: resources path: resources
key: xray-geodat- key: xray-geodat-
+6 -74
View File
@@ -4,27 +4,12 @@
[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). [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).
## Sponsors
[![Remnawave](https://github.com/user-attachments/assets/a22d34ae-01ee-441c-843a-85356748ed1e)](https://docs.rw)
[![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su)
[![BlancVPN](https://github.com/user-attachments/assets/9145ea7d-5da3-446e-8143-710dba4292c3)](https://blanc.link/VMTSDqW)
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
## Donation & NFTs ## Donation & NFTs
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1) ### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1) [<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
- **TRX(Tron)/USDT/USDC: `TNrDh5VSfwd4RPrwsohr6poyNTfFefNYan`**
- **TON: `UQApeV-u2gm43aC1uP76xAC1m6vCylstaN1gpfBmre_5IyTH`**
- **BTC: `1JpqcziZZuqv3QQJhZGNGBVdCBrGgkL6cT`**
- **XMR: `4ABHQZ3yJZkBnLoqiKvb3f8eqUnX4iMPb6wdant5ZLGQELctcerceSGEfJnoCk6nnyRZm73wrwSgvZ2WmjYLng6R7sR67nq`**
- **SOL/USDT/USDC: `3x5NuXHzB5APG6vRinPZcsUv5ukWUY1tBGRSJiEJWtZa`**
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`** - **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1** - **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **VLESS NFT: https://opensea.io/collection/vless** - **VLESS NFT: https://opensea.io/collection/vless**
@@ -58,22 +43,17 @@
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**) - [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray) - [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
- [wulabing/xray_docker](https://github.com/wulabing/xray_docker) - [wulabing/xray_docker](https://github.com/wulabing/xray_docker)
- Web Panel - Web Panel - **WARNING: Please DO NOT USE plain HTTP panels like 3X-UI**, as they are believed to be bribed by Iran GFW for supporting plain HTTP by default and refused to change (https://github.com/XTLS/Xray-core/pull/3884#issuecomment-2439595331), which has already put many users' data security in danger in the past few years. **If you are already using 3X-UI, please switch to the following panels, which are verified to support HTTPS and SSH port forwarding only:**
- [Remnawave](https://github.com/remnawave/panel) - [Remnawave](https://github.com/remnawave/panel)
- [3X-UI](https://github.com/MHSanaei/3x-ui)
- [PasarGuard](https://github.com/PasarGuard/panel)
- [Xray-UI](https://github.com/qist/xray-ui)
- [X-Panel](https://github.com/xeefei/X-Panel)
- [Marzban](https://github.com/Gozargah/Marzban) - [Marzban](https://github.com/Gozargah/Marzban)
- [Xray-UI](https://github.com/qist/xray-ui)
- [Hiddify](https://github.com/hiddify/Hiddify-Manager) - [Hiddify](https://github.com/hiddify/Hiddify-Manager)
- [TX-UI](https://github.com/AghayeCoder/tx-ui)
- [CELERITY](https://github.com/ClickDevTech/CELERITY-panel)
- One Click - One Click
- [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz) - [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless) - [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU) - [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
- Magisk - Magisk
- [Magic_V2Ray](https://github.com/vincentng295/Magic_V2Ray) - [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk) - [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
- Homebrew - Homebrew
- `brew install xray` - `brew install xray`
@@ -98,65 +78,46 @@
## GUI Clients ## GUI Clients
- OpenWrt - OpenWrt
- [PassWall](https://github.com/Openwrt-Passwall/openwrt-passwall), [PassWall 2](https://github.com/Openwrt-Passwall/openwrt-passwall2) - [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld) - [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)) - [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
- Asuswrt-Merlin - Asuswrt-Merlin
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui) - [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
- [fancyss](https://github.com/hq450/fancyss)
- Windows - Windows
- [v2rayN](https://github.com/2dust/v2rayN) - [v2rayN](https://github.com/2dust/v2rayN)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient) - [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [OneXray](https://github.com/OneXray/OneXray)
- [XrayUI-dev](https://github.com/PhoenixNil/XrayUI-dev)
- Android - Android
- [v2rayNG](https://github.com/2dust/v2rayNG) - [v2rayNG](https://github.com/2dust/v2rayNG)
- [X-flutter](https://github.com/XTLS/X-flutter) - [X-flutter](https://github.com/XTLS/X-flutter)
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray) - [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
- [SimpleXray](https://github.com/lhear/SimpleXray) - [SimpleXray](https://github.com/lhear/SimpleXray)
- [XrayFA](https://github.com/Q7DF1/XrayFA)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [OneXray](https://github.com/OneXray/OneXray)
- [AsteriskNG](https://github.com/Asterisk4Magisk/AsteriskNG)
- iOS & macOS arm64 & tvOS - iOS & macOS arm64 & tvOS
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274) - [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274))
- [Streisand](https://apps.apple.com/app/streisand/id6450534064) - [Streisand](https://apps.apple.com/app/streisand/id6450534064)
- [OneXray](https://github.com/OneXray/OneXray) - [OneXray](https://github.com/OneXray/OneXray)
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
- macOS arm64 & x64 - macOS arm64 & x64
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) - [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
- [V2rayU](https://github.com/yanue/V2rayU) - [V2rayU](https://github.com/yanue/V2rayU)
- [V2RayXS](https://github.com/tzmax/V2RayXS) - [V2RayXS](https://github.com/tzmax/V2RayXS)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [OneXray](https://github.com/OneXray/OneXray) - [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop) - [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [v2rayN](https://github.com/2dust/v2rayN)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
- Linux - Linux
- [v2rayA](https://github.com/v2rayA/v2rayA) - [v2rayA](https://github.com/v2rayA/v2rayA)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [GorzRay](https://github.com/ketetefid/GorzRay) - [GorzRay](https://github.com/ketetefid/GorzRay)
- [GoXRay](https://github.com/goxray/desktop) - [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [v2rayN](https://github.com/2dust/v2rayN)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [OneXray](https://github.com/OneXray/OneXray)
- HarmonyOS
- [Hey](https://github.com/popsiclelmlm/Hey)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX... ## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
- iOS & macOS arm64 & tvOS - iOS & macOS arm64 & tvOS
- [Anywhere](https://github.com/NodePassProject/Anywhere)
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
- [Loon](https://apps.apple.com/us/app/loon/id1373567447) - [Loon](https://apps.apple.com/us/app/loon/id1373567447)
- [Egern](https://apps.apple.com/us/app/egern/id1616105820)
- [Quantumult X](https://apps.apple.com/us/app/quantumult-x/id1443988620)
- Xray Tools - Xray Tools
- [xray-knife](https://github.com/lilendian0x00/xray-knife) - [xray-knife](https://github.com/lilendian0x00/xray-knife)
- [xray-checker](https://github.com/kutovoys/xray-checker) - [xray-checker](https://github.com/kutovoys/xray-checker)
@@ -165,7 +126,6 @@
- [xtls-sdk](https://github.com/remnawave/xtls-sdk) - [xtls-sdk](https://github.com/remnawave/xtls-sdk)
- [xtlsapi](https://github.com/hiddify/xtlsapi) - [xtlsapi](https://github.com/hiddify/xtlsapi)
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite) - [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
- [flutter_vless](https://github.com/XIIIFOX/flutter_vless)
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python) - [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
- [xray-api](https://github.com/XVGuardian/xray-api) - [xray-api](https://github.com/XVGuardian/xray-api)
- [XrayR](https://github.com/XrayR-project/XrayR) - [XrayR](https://github.com/XrayR-project/XrayR)
@@ -187,27 +147,6 @@
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases). - [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
- For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod). - For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod).
### Bundled Third-Party Components Redistribution
**Certain optional features dynamically load third-party components. These optional components are separate works distributed under their own licenses, and are bundled into the ZIP package for ease of use. Users may replace these components under the licenses from these components.**
These components include:
#### Wintun
This distribution contains unmodified official precompiled and pre-signed Wintun binaries.
- Project: Wintun
- Copyright: Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
- Redistribution License: Prebuilt Binaries License (PBL) bundled with official precompiled and pre-signed binaries from wintun.net
- Component(s): wintun.dll
- Source: https://www.wintun.net/
- Included in:
- Windows x86 (windows-32, win7-32)
- Windows x86-64 (windows-64, win7-64)
- Windows AArch64 (windows-arm64)
- Notes: Wintun is an optional runtime-loaded component only used for TUN inbound functionality on supported Windows platforms.
## One-line Compilation ## One-line Compilation
### Windows (PowerShell) ### Windows (PowerShell)
@@ -231,13 +170,6 @@ Make sure that you are using the same Go version, and remember to set the git co
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
``` ```
For Android:
```bash
GOOS=android GOARCH=arm64 CGO_ENABLED=1 CC=/path/to/aarch64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
GOOS=android GOARCH=amd64 CGO_ENABLED=1 CC=/path/to/x86_64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
```
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead: If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
```bash ```bash
-5
View File
@@ -1,5 +0,0 @@
# Security Policy
If you found an issue related to security vulnerability or protocol-identification problem, please report it to us via "[Report a vulnerability](https://github.com/XTLS/Xray-core/security/advisories/new)" privately, instead of publish it publicly before we release the fixed version.
Thanks for your contribution to the FREE Internet!
+5 -19
View File
@@ -3,7 +3,6 @@ package commander
import ( import (
"context" "context"
"net" "net"
"strings"
"sync" "sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
@@ -11,7 +10,6 @@ import (
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
core "github.com/xtls/xray-core/core" core "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@@ -69,32 +67,20 @@ func (c *Commander) Start() error {
} }
c.Unlock() c.Unlock()
listen := func(listener net.Listener) { var listen = func(listener net.Listener) {
if err := c.server.Serve(listener); err != nil { if err := c.server.Serve(listener); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start grpc server") errors.LogErrorInner(context.Background(), err, "failed to start grpc server")
} }
} }
if len(c.listen) > 0 { if len(c.listen) > 0 {
var addr net.Addr if l, err := net.Listen("tcp", c.listen); err != nil {
if strings.HasPrefix(c.listen, "/") || strings.HasPrefix(c.listen, "@") {
addr = &net.UnixAddr{Name: c.listen, Net: "unix"}
} else {
tcpAddr, err := net.ResolveTCPAddr("tcp", c.listen)
if err != nil {
errors.LogErrorInner(context.Background(), err, "API server failed to parse listen address ", c.listen)
return err
}
addr = tcpAddr
}
l, err := internet.ListenSystem(context.Background(), addr, nil)
if err != nil {
errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen) errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen)
return err return err
} else {
errors.LogInfo(context.Background(), "API server listening on ", l.Addr())
go listen(l)
} }
errors.LogInfo(context.Background(), "API server listening on ", l.Addr())
go listen(l)
return nil return nil
} }
+35 -21
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/commander/config.proto // source: app/commander/config.proto
package commander package commander
@@ -12,7 +12,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -24,16 +23,17 @@ const (
// Config is the settings for Commander. // Config is the settings for Commander.
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Tag of the outbound handler that handles grpc connections. // Tag of the outbound handler that handles grpc connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
// Network address of commander grpc service. // Network address of commander grpc service.
Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"` Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
// Services that supported by this server. All services must implement Service // Services that supported by this server. All services must implement Service
// interface. // interface.
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"` Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -89,9 +89,9 @@ func (x *Config) GetService() []*serial.TypedMessage {
// ReflectionConfig is the placeholder config for ReflectionService. // ReflectionConfig is the placeholder config for ReflectionService.
type ReflectionConfig struct { type ReflectionConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *ReflectionConfig) Reset() { func (x *ReflectionConfig) Reset() {
@@ -126,24 +126,37 @@ func (*ReflectionConfig) Descriptor() ([]byte, []int) {
var File_app_commander_config_proto protoreflect.FileDescriptor var File_app_commander_config_proto protoreflect.FileDescriptor
const file_app_commander_config_proto_rawDesc = "" + var file_app_commander_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x1a, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x2f,
"\x1aapp/commander/config.proto\x12\x12xray.app.commander\x1a!common/serial/typed_message.proto\"n\n" + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x78, 0x72,
"\x06Config\x12\x10\n" + 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
"\x06listen\x18\x03 \x01(\tR\x06listen\x12:\n" + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
"\aservice\x18\x02 \x03(\v2 .xray.common.serial.TypedMessageR\aservice\"\x12\n" + 0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
"\x10ReflectionConfigBX\n" + 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
"\x16com.xray.app.commanderP\x01Z'github.com/xtls/xray-core/app/commander\xaa\x02\x12Xray.App.Commanderb\x06proto3" 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x63, 0x6f, 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, 0x07, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
0x72, 0x50, 0x01, 0x5a, 0x27, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_commander_config_proto_rawDescOnce sync.Once file_app_commander_config_proto_rawDescOnce sync.Once
file_app_commander_config_proto_rawDescData []byte file_app_commander_config_proto_rawDescData = file_app_commander_config_proto_rawDesc
) )
func file_app_commander_config_proto_rawDescGZIP() []byte { func file_app_commander_config_proto_rawDescGZIP() []byte {
file_app_commander_config_proto_rawDescOnce.Do(func() { file_app_commander_config_proto_rawDescOnce.Do(func() {
file_app_commander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc))) file_app_commander_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_commander_config_proto_rawDescData)
}) })
return file_app_commander_config_proto_rawDescData return file_app_commander_config_proto_rawDescData
} }
@@ -172,7 +185,7 @@ func file_app_commander_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc)), RawDescriptor: file_app_commander_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
@@ -183,6 +196,7 @@ func file_app_commander_config_proto_init() {
MessageInfos: file_app_commander_config_proto_msgTypes, MessageInfos: file_app_commander_config_proto_msgTypes,
}.Build() }.Build()
File_app_commander_config_proto = out.File File_app_commander_config_proto = out.File
file_app_commander_config_proto_rawDesc = nil
file_app_commander_config_proto_goTypes = nil file_app_commander_config_proto_goTypes = nil
file_app_commander_config_proto_depIdxs = nil file_app_commander_config_proto_depIdxs = nil
} }
+30 -18
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/dispatcher/config.proto // source: app/dispatcher/config.proto
package dispatcher package dispatcher
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,9 +21,9 @@ const (
) )
type SessionConfig struct { type SessionConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *SessionConfig) Reset() { func (x *SessionConfig) Reset() {
@@ -58,10 +57,11 @@ func (*SessionConfig) Descriptor() ([]byte, []int) {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -103,22 +103,33 @@ func (x *Config) GetSettings() *SessionConfig {
var File_app_dispatcher_config_proto protoreflect.FileDescriptor var File_app_dispatcher_config_proto protoreflect.FileDescriptor
const file_app_dispatcher_config_proto_rawDesc = "" + var file_app_dispatcher_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x1b, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
"\x1bapp/dispatcher/config.proto\x12\x13xray.app.dispatcher\"\x15\n" + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x78,
"\rSessionConfigJ\x04\b\x01\x10\x02\"H\n" + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
"\x06Config\x12>\n" + 0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e,
"\bsettings\x18\x01 \x01(\v2\".xray.app.dispatcher.SessionConfigR\bsettingsB[\n" + 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x48, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
"\x17com.xray.app.dispatcherP\x01Z(github.com/xtls/xray-core/app/dispatcher\xaa\x02\x13Xray.App.Dispatcherb\x06proto3" 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x42, 0x5b, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01,
0x5a, 0x28, 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,
0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_dispatcher_config_proto_rawDescOnce sync.Once file_app_dispatcher_config_proto_rawDescOnce sync.Once
file_app_dispatcher_config_proto_rawDescData []byte file_app_dispatcher_config_proto_rawDescData = file_app_dispatcher_config_proto_rawDesc
) )
func file_app_dispatcher_config_proto_rawDescGZIP() []byte { func file_app_dispatcher_config_proto_rawDescGZIP() []byte {
file_app_dispatcher_config_proto_rawDescOnce.Do(func() { file_app_dispatcher_config_proto_rawDescOnce.Do(func() {
file_app_dispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc))) file_app_dispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dispatcher_config_proto_rawDescData)
}) })
return file_app_dispatcher_config_proto_rawDescData return file_app_dispatcher_config_proto_rawDescData
} }
@@ -146,7 +157,7 @@ func file_app_dispatcher_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc)), RawDescriptor: file_app_dispatcher_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
@@ -157,6 +168,7 @@ func file_app_dispatcher_config_proto_init() {
MessageInfos: file_app_dispatcher_config_proto_msgTypes, MessageInfos: file_app_dispatcher_config_proto_msgTypes,
}.Build() }.Build()
File_app_dispatcher_config_proto = out.File File_app_dispatcher_config_proto = out.File
file_app_dispatcher_config_proto_rawDesc = nil
file_app_dispatcher_config_proto_goTypes = nil file_app_dispatcher_config_proto_goTypes = nil
file_app_dispatcher_config_proto_depIdxs = nil file_app_dispatcher_config_proto_depIdxs = nil
} }
+41 -26
View File
@@ -2,6 +2,7 @@ package dispatcher
import ( import (
"context" "context"
"regexp"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -162,7 +163,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
p := d.policy.ForLevel(user.Level) p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink { if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink" name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil { if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
inboundLink.Writer = &SizeStatWriter{ inboundLink.Writer = &SizeStatWriter{
Counter: c, Counter: c,
Writer: inboundLink.Writer, Writer: inboundLink.Writer,
@@ -171,7 +172,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
} }
if p.Stats.UserDownlink { if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink" name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil { if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
outboundLink.Writer = &SizeStatWriter{ outboundLink.Writer = &SizeStatWriter{
Counter: c, Counter: c,
Writer: outboundLink.Writer, Writer: outboundLink.Writer,
@@ -180,14 +181,22 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
} }
if p.Stats.UserOnline { if p.Stats.UserOnline {
trackOnlineIP(ctx, d.stats, user.Email, sessionInbound.Source.Address.String()) name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
} }
} }
return inboundLink, outboundLink return inboundLink, outboundLink
} }
func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager stats.Manager, link *transport.Link) *transport.Link { func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link) *transport.Link {
sessionInbound := session.InboundFromContext(ctx) sessionInbound := session.InboundFromContext(ctx)
var user *protocol.MemoryUser var user *protocol.MemoryUser
if sessionInbound != nil { if sessionInbound != nil {
@@ -197,16 +206,16 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader} link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader}
if user != nil && len(user.Email) > 0 { if user != nil && len(user.Email) > 0 {
p := policyManager.ForLevel(user.Level) p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink { if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink" name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil { if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Reader.(*buf.TimeoutWrapperReader).Counter = c link.Reader.(*buf.TimeoutWrapperReader).Counter = c
} }
} }
if p.Stats.UserDownlink { if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink" name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil { if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Writer = &SizeStatWriter{ link.Writer = &SizeStatWriter{
Counter: c, Counter: c,
Writer: link.Writer, Writer: link.Writer,
@@ -214,31 +223,41 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
} }
} }
if p.Stats.UserOnline { if p.Stats.UserOnline {
trackOnlineIP(ctx, statsManager, user.Email, sessionInbound.Source.Address.String()) name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
} }
} }
return link return link
} }
func trackOnlineIP(ctx context.Context, sm stats.Manager, email, ip string) {
name := "user>>>" + email + ">>>online"
if om, _ := sm.GetOrRegisterOnlineMap(name); om != nil {
om.AddIP(ip)
context.AfterFunc(ctx, func() { om.RemoveIP(ip) })
}
}
func (d *DefaultDispatcher) 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() domain := result.Domain()
if domain == "" { if domain == "" {
return false return false
} }
if request.ExcludeForDomain != nil && request.ExcludeForDomain.MatchAny(strings.ToLower(domain)) { for _, d := range request.ExcludeForDomain {
return false if strings.HasPrefix(d, "regexp:") {
} pattern := d[7:]
if request.ExcludeForIP != nil && destination.Address.Family().IsIP() && request.ExcludeForIP.Match(destination.Address.IP()) { re, err := regexp.Compile(pattern)
return false if err != nil {
errors.LogInfo(ctx, "Unable to compile regex")
continue
}
if re.MatchString(domain) {
return false
}
} else {
if strings.ToLower(domain) == d {
return false
}
}
} }
protocolString := result.Protocol() protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok { if resComp, ok := result.(SnifferResultComposite); ok {
@@ -338,7 +357,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
content = new(session.Content) content = new(session.Content)
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
} }
outbound = WrapLink(ctx, d.policy, d.stats, outbound) outbound = d.WrapLink(ctx, outbound)
sniffingRequest := content.SniffingRequest sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled { if !sniffingRequest.Enabled {
d.routedDispatch(ctx, outbound, destination) d.routedDispatch(ctx, outbound, destination)
@@ -430,7 +449,6 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
} }
return contentResult, contentErr return contentResult, contentErr
} }
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) { func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1] ob := outbounds[len(outbounds)-1]
@@ -465,9 +483,6 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
handler = h handler = h
} else { } else {
errors.LogWarning(ctx, "non existing outTag: ", outTag) errors.LogWarning(ctx, "non existing outTag: ", outTag)
common.Close(link.Writer)
common.Interrupt(link.Reader)
return // DO NOT CHANGE: the traffic shouldn't be processed by default outbound if the specified outbound tag doesn't exist (yet), e.g., VLESS Reverse Proxy
} }
} else { } else {
errors.LogInfo(ctx, "default route for ", destination) errors.LogInfo(ctx, "default route for ", destination)
+106 -258
View File
@@ -3,55 +3,36 @@ package dns
import ( import (
"context" "context"
go_errors "errors" go_errors "errors"
"runtime"
"sync"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/pubsub" "github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
"golang.org/x/sync/singleflight" "sync"
) "time"
const (
minSizeForEmptyRebuild = 512
shrinkAbsoluteThreshold = 10240
shrinkRatioThreshold = 0.65
migrationBatchSize = 4096
) )
type CacheController struct { type CacheController struct {
name string
disableCache bool
serveStale bool
serveExpiredTTL int32
ips map[string]*record
dirtyips map[string]*record
sync.RWMutex sync.RWMutex
pub *pubsub.Service ips map[string]*record
cacheCleanup *task.Periodic pub *pubsub.Service
highWatermark int cacheCleanup *task.Periodic
requestGroup singleflight.Group name string
disableCache bool
} }
func NewCacheController(name string, disableCache bool, serveStale bool, serveExpiredTTL uint32) *CacheController { func NewCacheController(name string, disableCache bool) *CacheController {
c := &CacheController{ c := &CacheController{
name: name, name: name,
disableCache: disableCache, disableCache: disableCache,
serveStale: serveStale, ips: make(map[string]*record),
serveExpiredTTL: -int32(serveExpiredTTL), pub: pubsub.NewService(),
ips: make(map[string]*record),
pub: pubsub.NewService(),
} }
c.cacheCleanup = &task.Periodic{ c.cacheCleanup = &task.Periodic{
Interval: 300 * time.Second, Interval: time.Minute,
Execute: c.CacheCleanup, Execute: c.CacheCleanup,
} }
return c return c
@@ -59,264 +40,131 @@ func NewCacheController(name string, disableCache bool, serveStale bool, serveEx
// CacheCleanup clears expired items from cache // CacheCleanup clears expired items from cache
func (c *CacheController) CacheCleanup() error { func (c *CacheController) CacheCleanup() error {
expiredKeys, err := c.collectExpiredKeys() now := time.Now()
if err != nil { c.Lock()
return err defer c.Unlock()
if len(c.ips) == 0 {
return errors.New("nothing to do. stopping...")
} }
if len(expiredKeys) == 0 {
return nil for domain, record := range c.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), c.name, "cache cleanup ", domain)
delete(c.ips, domain)
} else {
c.ips[domain] = record
}
} }
c.writeAndShrink(expiredKeys)
if len(c.ips) == 0 {
c.ips = make(map[string]*record)
}
return nil return nil
} }
func (c *CacheController) collectExpiredKeys() ([]string, error) { func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
c.RLock() elapsed := time.Since(req.start)
defer c.RUnlock()
if len(c.ips) == 0 {
return nil, errors.New("nothing to do. stopping...")
}
// skip collection if a migration is in progress
if c.dirtyips != nil {
return nil, nil
}
now := time.Now()
if c.serveStale && c.serveExpiredTTL != 0 {
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
}
expiredKeys := make([]string, 0, len(c.ips)/4) // pre-allocate
for domain, rec := range c.ips {
if (rec.A != nil && rec.A.Expire.Before(now)) ||
(rec.AAAA != nil && rec.AAAA.Expire.Before(now)) {
expiredKeys = append(expiredKeys, domain)
}
}
return expiredKeys, nil
}
func (c *CacheController) writeAndShrink(expiredKeys []string) {
c.Lock() c.Lock()
defer c.Unlock() rec, found := c.ips[req.domain]
if !found {
// double check to prevent upper call multiple cleanup tasks rec = &record{}
if c.dirtyips != nil {
return
} }
lenBefore := len(c.ips) switch req.reqType {
if lenBefore > c.highWatermark { case dnsmessage.TypeA:
c.highWatermark = lenBefore rec.A = ipRec
case dnsmessage.TypeAAAA:
rec.AAAA = ipRec
} }
now := time.Now() errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
if c.serveStale && c.serveExpiredTTL != 0 { c.ips[req.domain] = rec
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
}
for _, domain := range expiredKeys { switch req.reqType {
rec := c.ips[domain] case dnsmessage.TypeA:
if rec == nil { c.pub.Publish(req.domain+"4", nil)
continue if !c.disableCache {
_, _, err := rec.AAAA.getIPs()
if !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+"6", nil)
}
} }
if rec.A != nil && rec.A.Expire.Before(now) { case dnsmessage.TypeAAAA:
rec.A = nil c.pub.Publish(req.domain+"6", nil)
} if !c.disableCache {
if rec.AAAA != nil && rec.AAAA.Expire.Before(now) { _, _, err := rec.A.getIPs()
rec.AAAA = nil if !go_errors.Is(err, errRecordNotFound) {
} c.pub.Publish(req.domain+"4", nil)
if rec.A == nil && rec.AAAA == nil { }
delete(c.ips, domain)
} }
} }
lenAfter := len(c.ips) c.Unlock()
common.Must(c.cacheCleanup.Start())
if lenAfter == 0 {
if c.highWatermark >= minSizeForEmptyRebuild {
errors.LogDebug(
context.Background(), c.name,
" rebuilding empty cache map to reclaim memory.",
" size_before_cleanup=", lenBefore,
" peak_size_before_rebuild=", c.highWatermark,
)
c.ips = make(map[string]*record)
c.highWatermark = 0
}
return
}
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold &&
float64(reductionFromPeak) > float64(c.highWatermark)*shrinkRatioThreshold {
errors.LogDebug(
context.Background(), c.name,
" shrinking cache map to reclaim memory.",
" new_size=", lenAfter,
" peak_size_before_shrink=", c.highWatermark,
" reduction_since_peak=", reductionFromPeak,
)
c.dirtyips = c.ips
c.ips = make(map[string]*record, int(float64(lenAfter)*1.1))
c.highWatermark = lenAfter
go c.migrate()
}
} }
type migrationEntry struct { func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
key string
value *record
}
func (c *CacheController) migrate() {
defer func() {
if r := recover(); r != nil {
errors.LogError(context.Background(), c.name, " panic during cache migration: ", r)
c.Lock()
c.dirtyips = nil
// c.ips = make(map[string]*record)
// c.highWatermark = 0
c.Unlock()
}
}()
c.RLock() c.RLock()
dirtyips := c.dirtyips record, found := c.ips[domain]
c.RUnlock() c.RUnlock()
// double check to prevent upper call multiple cleanup tasks if !found {
if dirtyips == nil { return nil, 0, errRecordNotFound
return
} }
errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items") var errs []error
var allIPs []net.IP
var rTTL uint32 = dns_feature.DefaultTTL
batch := make([]migrationEntry, 0, migrationBatchSize) mergeReq := option.IPv4Enable && option.IPv6Enable
for domain, recD := range dirtyips {
batch = append(batch, migrationEntry{domain, recD})
if len(batch) >= migrationBatchSize { if option.IPv4Enable {
c.flush(batch) ips, ttl, err := record.A.getIPs()
batch = batch[:0] if !mergeReq || go_errors.Is(err, errRecordNotFound) {
runtime.Gosched() return ips, ttl, err
} }
} if ttl < rTTL {
if len(batch) > 0 { rTTL = ttl
c.flush(batch) }
} if len(ips) > 0 {
allIPs = append(allIPs, ips...)
c.Lock()
c.dirtyips = nil
c.Unlock()
errors.LogDebug(context.Background(), c.name, " cache migration completed")
}
func (c *CacheController) flush(batch []migrationEntry) {
c.Lock()
defer c.Unlock()
for _, dirty := range batch {
if cur := c.ips[dirty.key]; cur != nil {
merge := &record{}
if cur.A == nil {
merge.A = dirty.value.A
} else {
merge.A = cur.A
}
if cur.AAAA == nil {
merge.AAAA = dirty.value.AAAA
} else {
merge.AAAA = cur.AAAA
}
c.ips[dirty.key] = merge
} else { } else {
c.ips[dirty.key] = dirty.value errs = append(errs, err)
}
}
}
func (c *CacheController) updateRecord(req *dnsRequest, rep *IPRecord) {
rtt := time.Since(req.start)
switch req.reqType {
case dnsmessage.TypeA:
c.pub.Publish(req.domain+"4", rep)
case dnsmessage.TypeAAAA:
c.pub.Publish(req.domain+"6", rep)
}
if c.disableCache {
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt)
return
}
c.Lock()
lockWait := time.Since(req.start) - rtt
newRec := &record{}
oldRec := c.ips[req.domain]
var dirtyRec *record
if c.dirtyips != nil {
dirtyRec = c.dirtyips[req.domain]
}
var pubRecord *IPRecord
var pubSuffix string
switch req.reqType {
case dnsmessage.TypeA:
newRec.A = rep
if oldRec != nil && oldRec.AAAA != nil {
newRec.AAAA = oldRec.AAAA
pubRecord = oldRec.AAAA
} else if dirtyRec != nil && dirtyRec.AAAA != nil {
pubRecord = dirtyRec.AAAA
}
pubSuffix = "6"
case dnsmessage.TypeAAAA:
newRec.AAAA = rep
if oldRec != nil && oldRec.A != nil {
newRec.A = oldRec.A
pubRecord = oldRec.A
} else if dirtyRec != nil && dirtyRec.A != nil {
pubRecord = dirtyRec.A
}
pubSuffix = "4"
}
c.ips[req.domain] = newRec
c.Unlock()
if pubRecord != nil {
_, ttl, err := pubRecord.getIPs()
if ttl > 0 && !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+pubSuffix, pubRecord)
} }
} }
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt, ", lock: ", lockWait) if option.IPv6Enable {
ips, ttl, err := record.AAAA.getIPs()
if !c.serveStale || c.serveExpiredTTL != 0 { if !mergeReq || go_errors.Is(err, errRecordNotFound) {
common.Must(c.cacheCleanup.Start()) return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
} }
}
func (c *CacheController) findRecords(domain string) *record { if len(allIPs) > 0 {
c.RLock() return allIPs, rTTL, nil
defer c.RUnlock()
rec := c.ips[domain]
if rec == nil && c.dirtyips != nil {
rec = c.dirtyips[domain]
} }
return rec if go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
} }
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) { func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
+35 -11
View File
@@ -2,24 +2,48 @@ package dns
import ( import (
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
) )
var typeMap = map[DomainMatchingType]strmatcher.Type{
DomainMatchingType_Full: strmatcher.Full,
DomainMatchingType_Subdomain: strmatcher.Domain,
DomainMatchingType_Keyword: strmatcher.Substr,
DomainMatchingType_Regex: strmatcher.Regex,
}
// References: // References:
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml // https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan // https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
var localTLDsAndDotlessDomainsRules = []*geodata.DomainRule{ var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]+$"}}}, // This will only match domains without any dot {Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "local"}}}, {Type: DomainMatchingType_Subdomain, Domain: "local"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localdomain"}}}, {Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localhost"}}}, {Type: DomainMatchingType_Subdomain, Domain: "localhost"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "lan"}}}, {Type: DomainMatchingType_Subdomain, Domain: "lan"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "home.arpa"}}}, {Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "example"}}}, {Type: DomainMatchingType_Subdomain, Domain: "example"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "invalid"}}}, {Type: DomainMatchingType_Subdomain, Domain: "invalid"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "test"}}}, {Type: DomainMatchingType_Subdomain, Domain: "test"},
}
var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
Rule: "geosite:private",
Size: uint32(len(localTLDsAndDotlessDomains)),
}
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
strMType, f := typeMap[t]
if !f {
return nil, errors.New("unknown mapping type", t).AtWarning()
}
matcher, err := strMType.New(domain)
if err != nil {
return nil, errors.New("failed to create str matcher").Base(err)
}
return matcher, nil
} }
func toNetIP(addrs []net.Address) ([]net.IP, error) { func toNetIP(addrs []net.Address) ([]net.IP, error) {
+355 -175
View File
@@ -1,19 +1,18 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/dns/config.proto // source: app/dns/config.proto
package dns package dns
import ( import (
geodata "github.com/xtls/xray-core/common/geodata" router "github.com/xtls/xray-core/app/router"
net "github.com/xtls/xray-core/common/net" net "github.com/xtls/xray-core/common/net"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -23,6 +22,58 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type DomainMatchingType int32
const (
DomainMatchingType_Full DomainMatchingType = 0
DomainMatchingType_Subdomain DomainMatchingType = 1
DomainMatchingType_Keyword DomainMatchingType = 2
DomainMatchingType_Regex DomainMatchingType = 3
)
// Enum value maps for DomainMatchingType.
var (
DomainMatchingType_name = map[int32]string{
0: "Full",
1: "Subdomain",
2: "Keyword",
3: "Regex",
}
DomainMatchingType_value = map[string]int32{
"Full": 0,
"Subdomain": 1,
"Keyword": 2,
"Regex": 3,
}
)
func (x DomainMatchingType) Enum() *DomainMatchingType {
p := new(DomainMatchingType)
*p = x
return p
}
func (x DomainMatchingType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DomainMatchingType) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[0].Descriptor()
}
func (DomainMatchingType) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[0]
}
func (x DomainMatchingType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DomainMatchingType.Descriptor instead.
func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
}
type QueryStrategy int32 type QueryStrategy int32
const ( const (
@@ -59,11 +110,11 @@ func (x QueryStrategy) String() string {
} }
func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor { func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[0].Descriptor() return file_app_dns_config_proto_enumTypes[1].Descriptor()
} }
func (QueryStrategy) Type() protoreflect.EnumType { func (QueryStrategy) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[0] return &file_app_dns_config_proto_enumTypes[1]
} }
func (x QueryStrategy) Number() protoreflect.EnumNumber { func (x QueryStrategy) Number() protoreflect.EnumNumber {
@@ -72,29 +123,28 @@ func (x QueryStrategy) Number() protoreflect.EnumNumber {
// Deprecated: Use QueryStrategy.Descriptor instead. // Deprecated: Use QueryStrategy.Descriptor instead.
func (QueryStrategy) EnumDescriptor() ([]byte, []int) { func (QueryStrategy) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0} return file_app_dns_config_proto_rawDescGZIP(), []int{1}
} }
type NameServer struct { type NameServer struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` sizeCache protoimpl.SizeCache
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` unknownFields protoimpl.UnknownFields
SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
Domain []*geodata.DomainRule `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
ExpectedIp []*geodata.IPRule `protobuf:"bytes,3,rep,name=expected_ip,json=expectedIp,proto3" json:"expected_ip,omitempty"` ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"` PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"` ExpectedGeoip []*router.GeoIP `protobuf:"bytes,3,rep,name=expected_geoip,json=expectedGeoip,proto3" json:"expected_geoip,omitempty"`
TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"` OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
DisableCache *bool `protobuf:"varint,11,opt,name=disableCache,proto3,oneof" json:"disableCache,omitempty"` QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
ServeStale *bool `protobuf:"varint,15,opt,name=serveStale,proto3,oneof" json:"serveStale,omitempty"` ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"`
ServeExpiredTTL *uint32 `protobuf:"varint,16,opt,name=serveExpiredTTL,proto3,oneof" json:"serveExpiredTTL,omitempty"` Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"` TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
UnexpectedIp []*geodata.IPRule `protobuf:"bytes,13,rep,name=unexpected_ip,json=unexpectedIp,proto3" json:"unexpected_ip,omitempty"` DisableCache bool `protobuf:"varint,11,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"` FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"`
PolicyID uint32 `protobuf:"varint,17,opt,name=policyID,proto3" json:"policyID,omitempty"` UnexpectedGeoip []*router.GeoIP `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"`
unknownFields protoimpl.UnknownFields ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"`
sizeCache protoimpl.SizeCache
} }
func (x *NameServer) Reset() { func (x *NameServer) Reset() {
@@ -148,16 +198,23 @@ func (x *NameServer) GetSkipFallback() bool {
return false return false
} }
func (x *NameServer) GetDomain() []*geodata.DomainRule { func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
if x != nil { if x != nil {
return x.Domain return x.PrioritizedDomain
} }
return nil return nil
} }
func (x *NameServer) GetExpectedIp() []*geodata.IPRule { func (x *NameServer) GetExpectedGeoip() []*router.GeoIP {
if x != nil { if x != nil {
return x.ExpectedIp return x.ExpectedGeoip
}
return nil
}
func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule {
if x != nil {
return x.OriginalRules
} }
return nil return nil
} }
@@ -191,26 +248,12 @@ func (x *NameServer) GetTimeoutMs() uint64 {
} }
func (x *NameServer) GetDisableCache() bool { func (x *NameServer) GetDisableCache() bool {
if x != nil && x.DisableCache != nil { if x != nil {
return *x.DisableCache return x.DisableCache
} }
return false return false
} }
func (x *NameServer) GetServeStale() bool {
if x != nil && x.ServeStale != nil {
return *x.ServeStale
}
return false
}
func (x *NameServer) GetServeExpiredTTL() uint32 {
if x != nil && x.ServeExpiredTTL != nil {
return *x.ServeExpiredTTL
}
return 0
}
func (x *NameServer) GetFinalQuery() bool { func (x *NameServer) GetFinalQuery() bool {
if x != nil { if x != nil {
return x.FinalQuery return x.FinalQuery
@@ -218,9 +261,9 @@ func (x *NameServer) GetFinalQuery() bool {
return false return false
} }
func (x *NameServer) GetUnexpectedIp() []*geodata.IPRule { func (x *NameServer) GetUnexpectedGeoip() []*router.GeoIP {
if x != nil { if x != nil {
return x.UnexpectedIp return x.UnexpectedGeoip
} }
return nil return nil
} }
@@ -232,15 +275,11 @@ func (x *NameServer) GetActUnprior() bool {
return false return false
} }
func (x *NameServer) GetPolicyID() uint32 {
if x != nil {
return x.PolicyID
}
return 0
}
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// NameServer list used by this DNS client. // NameServer list used by this DNS client.
// A special value 'localhost' as a domain address can be set to use DNS on local system. // A special value 'localhost' as a domain address can be set to use DNS on local system.
NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"` NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"`
@@ -252,14 +291,9 @@ type Config struct {
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
// DisableCache disables DNS cache // DisableCache disables DNS cache
DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
ServeStale bool `protobuf:"varint,12,opt,name=serveStale,proto3" json:"serveStale,omitempty"`
ServeExpiredTTL uint32 `protobuf:"varint,13,opt,name=serveExpiredTTL,proto3" json:"serveExpiredTTL,omitempty"`
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"` DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"`
EnableParallelQuery bool `protobuf:"varint,14,opt,name=enableParallelQuery,proto3" json:"enableParallelQuery,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -327,20 +361,6 @@ func (x *Config) GetDisableCache() bool {
return false return false
} }
func (x *Config) GetServeStale() bool {
if x != nil {
return x.ServeStale
}
return false
}
func (x *Config) GetServeExpiredTTL() uint32 {
if x != nil {
return x.ServeExpiredTTL
}
return 0
}
func (x *Config) GetQueryStrategy() QueryStrategy { func (x *Config) GetQueryStrategy() QueryStrategy {
if x != nil { if x != nil {
return x.QueryStrategy return x.QueryStrategy
@@ -362,27 +382,128 @@ func (x *Config) GetDisableFallbackIfMatch() bool {
return false return false
} }
func (x *Config) GetEnableParallelQuery() bool { type NameServer_PriorityDomain struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
}
func (x *NameServer_PriorityDomain) Reset() {
*x = NameServer_PriorityDomain{}
mi := &file_app_dns_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NameServer_PriorityDomain) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NameServer_PriorityDomain) ProtoMessage() {}
func (x *NameServer_PriorityDomain) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[2]
if x != nil { if x != nil {
return x.EnableParallelQuery ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
} }
return false return mi.MessageOf(x)
}
// Deprecated: Use NameServer_PriorityDomain.ProtoReflect.Descriptor instead.
func (*NameServer_PriorityDomain) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 0}
}
func (x *NameServer_PriorityDomain) GetType() DomainMatchingType {
if x != nil {
return x.Type
}
return DomainMatchingType_Full
}
func (x *NameServer_PriorityDomain) GetDomain() string {
if x != nil {
return x.Domain
}
return ""
}
type NameServer_OriginalRule struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Rule string `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"`
Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
}
func (x *NameServer_OriginalRule) Reset() {
*x = NameServer_OriginalRule{}
mi := &file_app_dns_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NameServer_OriginalRule) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NameServer_OriginalRule) ProtoMessage() {}
func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NameServer_OriginalRule.ProtoReflect.Descriptor instead.
func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 1}
}
func (x *NameServer_OriginalRule) GetRule() string {
if x != nil {
return x.Rule
}
return ""
}
func (x *NameServer_OriginalRule) GetSize() uint32 {
if x != nil {
return x.Size
}
return 0
} }
type Config_HostMapping struct { type Config_HostMapping struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Domain *geodata.DomainRule `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` sizeCache protoimpl.SizeCache
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"` unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
// ProxiedDomain indicates the mapped domain has the same IP address on this // ProxiedDomain indicates the mapped domain has the same IP address on this
// domain. Xray will use this domain for IP queries. // domain. Xray will use this domain for IP queries.
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"` ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config_HostMapping) Reset() { func (x *Config_HostMapping) Reset() {
*x = Config_HostMapping{} *x = Config_HostMapping{}
mi := &file_app_dns_config_proto_msgTypes[2] mi := &file_app_dns_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -394,7 +515,7 @@ func (x *Config_HostMapping) String() string {
func (*Config_HostMapping) ProtoMessage() {} func (*Config_HostMapping) ProtoMessage() {}
func (x *Config_HostMapping) ProtoReflect() protoreflect.Message { func (x *Config_HostMapping) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[2] mi := &file_app_dns_config_proto_msgTypes[4]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -410,11 +531,18 @@ func (*Config_HostMapping) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{1, 0} return file_app_dns_config_proto_rawDescGZIP(), []int{1, 0}
} }
func (x *Config_HostMapping) GetDomain() *geodata.DomainRule { func (x *Config_HostMapping) GetType() DomainMatchingType {
if x != nil {
return x.Type
}
return DomainMatchingType_Full
}
func (x *Config_HostMapping) GetDomain() string {
if x != nil { if x != nil {
return x.Domain return x.Domain
} }
return nil return ""
} }
func (x *Config_HostMapping) GetIp() [][]byte { func (x *Config_HostMapping) GetIp() [][]byte {
@@ -433,104 +561,156 @@ func (x *Config_HostMapping) GetProxiedDomain() string {
var File_app_dns_config_proto protoreflect.FileDescriptor var File_app_dns_config_proto protoreflect.FileDescriptor
const file_app_dns_config_proto_rawDesc = "" + var file_app_dns_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
"\x14app/dns/config.proto\x12\fxray.app.dns\x1a\x1ccommon/net/destination.proto\x1a\x1bcommon/geodata/geodat.proto\"\xde\x05\n" + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
"\n" + 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
"NameServer\x123\n" + 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
"\aaddress\x18\x01 \x01(\v2\x19.xray.common.net.EndpointR\aaddress\x12\x1b\n" + 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63,
"\tclient_ip\x18\x05 \x01(\fR\bclientIp\x12\"\n" + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a,
"\fskipFallback\x18\x06 \x01(\bR\fskipFallback\x127\n" + 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64,
"\x06domain\x18\x02 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12<\n" + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72,
"\vexpected_ip\x18\x03 \x03(\v2\x1b.xray.common.geodata.IPRuleR\n" + 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
"expectedIp\x12B\n" + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
"\x0equery_strategy\x18\a \x01(\x0e2\x1b.xray.app.dns.QueryStrategyR\rqueryStrategy\x12\x1a\n" + 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01,
"\bactPrior\x18\b \x01(\bR\bactPrior\x12\x10\n" + 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c,
"\x03tag\x18\t \x01(\tR\x03tag\x12\x1c\n" + 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01,
"\ttimeoutMs\x18\n" + 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
" \x01(\x04R\ttimeoutMs\x12'\n" + 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f,
"\fdisableCache\x18\v \x01(\bH\x00R\fdisableCache\x88\x01\x01\x12#\n" + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78,
"\n" + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65,
"serveStale\x18\x0f \x01(\bH\x01R\n" + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44,
"serveStale\x88\x01\x01\x12-\n" + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a,
"\x0fserveExpiredTTL\x18\x10 \x01(\rH\x02R\x0fserveExpiredTTL\x88\x01\x01\x12\x1e\n" + 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65,
"\n" + 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
"finalQuery\x18\f \x01(\bR\n" + 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
"finalQuery\x12@\n" + 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
"\runexpected_ip\x18\r \x03(\v2\x1b.xray.common.geodata.IPRuleR\funexpectedIp\x12\x1e\n" + 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69,
"\n" + 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
"actUnprior\x18\x0e \x01(\bR\n" + 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e,
"actUnprior\x12\x1a\n" + 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
"\bpolicyID\x18\x11 \x01(\rR\bpolicyIDB\x0f\n" + 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c,
"\r_disableCacheB\r\n" + 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73,
"\v_serveStaleB\x12\n" + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e,
"\x10_serveExpiredTTLJ\x04\b\x04\x10\x05\"\x82\x05\n" + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65,
"\x06Config\x129\n" + 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72,
"\vname_server\x18\x05 \x03(\v2\x18.xray.app.dns.NameServerR\n" + 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74,
"nameServer\x12\x1b\n" + 0x50, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x74,
"\tclient_ip\x18\x03 \x01(\fR\bclientIp\x12C\n" + 0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01,
"\fstatic_hosts\x18\x04 \x03(\v2 .xray.app.dns.Config.HostMappingR\vstaticHosts\x12\x10\n" + 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f,
"\x03tag\x18\x06 \x01(\tR\x03tag\x12\"\n" + 0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
"\fdisableCache\x18\b \x01(\bR\fdisableCache\x12\x1e\n" + 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
"\n" + 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73,
"serveStale\x18\f \x01(\bR\n" + 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e,
"serveStale\x12(\n" + 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66,
"\x0fserveExpiredTTL\x18\r \x01(\rR\x0fserveExpiredTTL\x12B\n" + 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65,
"\x0equery_strategy\x18\t \x01(\x0e2\x1b.xray.app.dns.QueryStrategyR\rqueryStrategy\x12(\n" + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20,
"\x0fdisableFallback\x18\n" + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
" \x01(\bR\x0fdisableFallback\x126\n" + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65,
"\x16disableFallbackIfMatch\x18\v \x01(\bR\x16disableFallbackIfMatch\x120\n" + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a,
"\x13enableParallelQuery\x18\x0e \x01(\bR\x13enableParallelQuery\x1a}\n" + 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
"\vHostMapping\x127\n" + 0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e,
"\x06domain\x18\x02 \x01(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12\x0e\n" + 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34,
"\x02ip\x18\x03 \x03(\fR\x02ip\x12%\n" + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78,
"\x0eproxied_domain\x18\x04 \x01(\tR\rproxiedDomainJ\x04\b\a\x10\b*B\n" + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
"\rQueryStrategy\x12\n" + 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
"\n" + 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02,
"\x06USE_IP\x10\x00\x12\v\n" + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c,
"\aUSE_IP4\x10\x01\x12\v\n" + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
"\aUSE_IP6\x10\x02\x12\v\n" + 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
"\aUSE_SYS\x10\x03BF\n" + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
"\x10com.xray.app.dnsP\x01Z!github.com/xtls/xray-core/app/dns\xaa\x02\fXray.App.Dnsb\x06proto3" 0x73, 0x69, 0x7a, 0x65, 0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a,
0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69,
0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52,
0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22,
0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63,
0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61,
0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63,
0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08,
0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74,
0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12,
0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75,
0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55,
0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10,
0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46,
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 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, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_dns_config_proto_rawDescOnce sync.Once file_app_dns_config_proto_rawDescOnce sync.Once
file_app_dns_config_proto_rawDescData []byte file_app_dns_config_proto_rawDescData = file_app_dns_config_proto_rawDesc
) )
func file_app_dns_config_proto_rawDescGZIP() []byte { func file_app_dns_config_proto_rawDescGZIP() []byte {
file_app_dns_config_proto_rawDescOnce.Do(func() { file_app_dns_config_proto_rawDescOnce.Do(func() {
file_app_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc))) file_app_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_config_proto_rawDescData)
}) })
return file_app_dns_config_proto_rawDescData return file_app_dns_config_proto_rawDescData
} }
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_app_dns_config_proto_goTypes = []any{ var file_app_dns_config_proto_goTypes = []any{
(QueryStrategy)(0), // 0: xray.app.dns.QueryStrategy (DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
(*NameServer)(nil), // 1: xray.app.dns.NameServer (QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy
(*Config)(nil), // 2: xray.app.dns.Config (*NameServer)(nil), // 2: xray.app.dns.NameServer
(*Config_HostMapping)(nil), // 3: xray.app.dns.Config.HostMapping (*Config)(nil), // 3: xray.app.dns.Config
(*net.Endpoint)(nil), // 4: xray.common.net.Endpoint (*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain
(*geodata.DomainRule)(nil), // 5: xray.common.geodata.DomainRule (*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule
(*geodata.IPRule)(nil), // 6: xray.common.geodata.IPRule (*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP
} }
var file_app_dns_config_proto_depIdxs = []int32{ var file_app_dns_config_proto_depIdxs = []int32{
4, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint 7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
5, // 1: xray.app.dns.NameServer.domain:type_name -> xray.common.geodata.DomainRule 4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
6, // 2: xray.app.dns.NameServer.expected_ip:type_name -> xray.common.geodata.IPRule 8, // 2: xray.app.dns.NameServer.expected_geoip:type_name -> xray.app.router.GeoIP
0, // 3: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy 5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
6, // 4: xray.app.dns.NameServer.unexpected_ip:type_name -> xray.common.geodata.IPRule 1, // 4: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy
1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer 8, // 5: xray.app.dns.NameServer.unexpected_geoip:type_name -> xray.app.router.GeoIP
3, // 6: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping 2, // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
0, // 7: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy 6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
5, // 8: xray.app.dns.Config.HostMapping.domain:type_name -> xray.common.geodata.DomainRule 1, // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
9, // [9:9] is the sub-list for method output_type 0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
9, // [9:9] is the sub-list for method input_type 0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
9, // [9:9] is the sub-list for extension type_name 11, // [11:11] is the sub-list for method output_type
9, // [9:9] is the sub-list for extension extendee 11, // [11:11] is the sub-list for method input_type
0, // [0:9] is the sub-list for field type_name 11, // [11:11] is the sub-list for extension type_name
11, // [11:11] is the sub-list for extension extendee
0, // [0:11] is the sub-list for field type_name
} }
func init() { file_app_dns_config_proto_init() } func init() { file_app_dns_config_proto_init() }
@@ -538,14 +718,13 @@ func file_app_dns_config_proto_init() {
if File_app_dns_config_proto != nil { if File_app_dns_config_proto != nil {
return return
} }
file_app_dns_config_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc)), RawDescriptor: file_app_dns_config_proto_rawDesc,
NumEnums: 1, NumEnums: 2,
NumMessages: 3, NumMessages: 5,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
@@ -555,6 +734,7 @@ func file_app_dns_config_proto_init() {
MessageInfos: file_app_dns_config_proto_msgTypes, MessageInfos: file_app_dns_config_proto_msgTypes,
}.Build() }.Build()
File_app_dns_config_proto = out.File File_app_dns_config_proto = out.File
file_app_dns_config_proto_rawDesc = nil
file_app_dns_config_proto_goTypes = nil file_app_dns_config_proto_goTypes = nil
file_app_dns_config_proto_depIdxs = nil file_app_dns_config_proto_depIdxs = nil
} }
+26 -14
View File
@@ -7,26 +7,41 @@ option java_package = "com.xray.app.dns";
option java_multiple_files = true; option java_multiple_files = true;
import "common/net/destination.proto"; import "common/net/destination.proto";
import "common/geodata/geodat.proto"; import "app/router/config.proto";
message NameServer { message NameServer {
xray.common.net.Endpoint address = 1; xray.common.net.Endpoint address = 1;
bytes client_ip = 5; bytes client_ip = 5;
bool skipFallback = 6; bool skipFallback = 6;
repeated xray.common.geodata.DomainRule domain = 2;
repeated xray.common.geodata.IPRule expected_ip = 3; message PriorityDomain {
reserved 4; DomainMatchingType type = 1;
string domain = 2;
}
message OriginalRule {
string rule = 1;
uint32 size = 2;
}
repeated PriorityDomain prioritized_domain = 2;
repeated xray.app.router.GeoIP expected_geoip = 3;
repeated OriginalRule original_rules = 4;
QueryStrategy query_strategy = 7; QueryStrategy query_strategy = 7;
bool actPrior = 8; bool actPrior = 8;
string tag = 9; string tag = 9;
uint64 timeoutMs = 10; uint64 timeoutMs = 10;
optional bool disableCache = 11; bool disableCache = 11;
optional bool serveStale = 15;
optional uint32 serveExpiredTTL = 16;
bool finalQuery = 12; bool finalQuery = 12;
repeated xray.common.geodata.IPRule unexpected_ip = 13; repeated xray.app.router.GeoIP unexpected_geoip = 13;
bool actUnprior = 14; bool actUnprior = 14;
uint32 policyID = 17; }
enum DomainMatchingType {
Full = 0;
Subdomain = 1;
Keyword = 2;
Regex = 3;
} }
enum QueryStrategy { enum QueryStrategy {
@@ -46,7 +61,8 @@ message Config {
bytes client_ip = 3; bytes client_ip = 3;
message HostMapping { message HostMapping {
xray.common.geodata.DomainRule domain = 2; DomainMatchingType type = 1;
string domain = 2;
repeated bytes ip = 3; repeated bytes ip = 3;
@@ -64,13 +80,9 @@ message Config {
// DisableCache disables DNS cache // DisableCache disables DNS cache
bool disableCache = 8; bool disableCache = 8;
bool serveStale = 12;
uint32 serveExpiredTTL = 13;
QueryStrategy query_strategy = 9; QueryStrategy query_strategy = 9;
bool disableFallback = 10; bool disableFallback = 10;
bool disableFallbackIfMatch = 11; bool disableFallbackIfMatch = 11;
bool enableParallelQuery = 14;
} }
+107 -298
View File
@@ -11,10 +11,9 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/utils" "github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
@@ -23,20 +22,19 @@ type DNS struct {
sync.Mutex sync.Mutex
disableFallback bool disableFallback bool
disableFallbackIfMatch bool disableFallbackIfMatch bool
enableParallelQuery bool
ipOption *dns.IPOption ipOption *dns.IPOption
hosts *StaticHosts hosts *StaticHosts
clients []*Client clients []*Client
ctx context.Context ctx context.Context
domainMatcher geodata.DomainMatcher domainMatcher strmatcher.IndexMatcher
matcherInfos []*DomainMatcherInfo matcherInfos []*DomainMatcherInfo
checkSystem bool checkSystem bool
} }
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher. // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
type DomainMatcherInfo struct { type DomainMatcherInfo struct {
clientIdx uint16 clientIdx uint16
domainRule string domainRuleIdx uint16
} }
// New creates a new DNS server with given configuration. // New creates a new DNS server with given configuration.
@@ -86,36 +84,31 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
return nil, errors.New("failed to create hosts").Base(err) return nil, errors.New("failed to create hosts").Base(err)
} }
defaultTag := config.Tag var clients []*Client
domainRuleCount := 0
var defaultTag = config.Tag
if len(config.Tag) == 0 { if len(config.Tag) == 0 {
defaultTag = generateRandomTag() defaultTag = generateRandomTag()
} }
clients := make([]*Client, 0, len(config.NameServer)) for _, ns := range config.NameServer {
matcherInfos := make([]*DomainMatcherInfo, 0) domainRuleCount += len(ns.PrioritizedDomain)
effectiveRules := make([]*geodata.DomainRule, 0) }
// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
matcherInfos := make([]*DomainMatcherInfo, domainRuleCount+1)
domainMatcher := &strmatcher.MatcherGroup{}
for _, ns := range config.NameServer { for _, ns := range config.NameServer {
clientIdx := len(clients) clientIdx := len(clients)
updateRules := func(isLocalNameServer bool) { updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []*DomainMatcherInfo) error {
// Prioritize local domains with specific TLDs or those without any dot for the local DNS midx := domainMatcher.Add(domainRule)
if isLocalNameServer { matcherInfos[midx] = &DomainMatcherInfo{
effectiveRules = append(effectiveRules, localTLDsAndDotlessDomainsRules...) clientIdx: uint16(clientIdx),
for _, rule := range localTLDsAndDotlessDomainsRules { domainRuleIdx: uint16(originalRuleIdx),
matcherInfos = append(matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRule: rule.String(),
})
}
}
effectiveRules = append(effectiveRules, ns.Domain...)
for _, rule := range ns.Domain {
matcherInfos = append(matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRule: rule.String(),
})
} }
return nil
} }
myClientIP := clientIP myClientIP := clientIP
@@ -124,46 +117,24 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
myClientIP = net.IP(ns.ClientIp) myClientIP = net.IP(ns.ClientIp)
} }
disableCache := config.DisableCache disableCache := config.DisableCache || ns.DisableCache
if ns.DisableCache != nil {
disableCache = *ns.DisableCache
}
serveStale := config.ServeStale var tag = defaultTag
if ns.ServeStale != nil {
serveStale = *ns.ServeStale
}
serveExpiredTTL := config.ServeExpiredTTL
if ns.ServeExpiredTTL != nil {
serveExpiredTTL = *ns.ServeExpiredTTL
}
tag := defaultTag
if len(ns.Tag) > 0 { if len(ns.Tag) > 0 {
tag = ns.Tag tag = ns.Tag
} }
clientIPOption := ResolveIpOptionOverride(ns.QueryStrategy, ipOption) clientIPOption := ResolveIpOptionOverride(ns.QueryStrategy, ipOption)
if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable { if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable {
return nil, errors.New("no QueryStrategy available for ", ns.Address) return nil, errors.New("no QueryStrategy available for ", ns.Address)
} }
client, err := NewClient(ctx, ns, myClientIP, disableCache, serveStale, serveExpiredTTL, tag, clientIPOption, updateRules) client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain)
if err != nil { if err != nil {
return nil, errors.New("failed to create client").Base(err) return nil, errors.New("failed to create client").Base(err)
} }
clients = append(clients, client) clients = append(clients, client)
} }
var domainMatcher geodata.DomainMatcher
if len(effectiveRules) > 0 {
domainMatcher, err = geodata.DomainReg.BuildDomainMatcher(effectiveRules)
if err != nil {
return nil, err
}
}
// If there is no DNS client in config, add a `localhost` DNS client // If there is no DNS client in config, add a `localhost` DNS client
if len(clients) == 0 { if len(clients) == 0 {
clients = append(clients, NewLocalDNSClient(ipOption)) clients = append(clients, NewLocalDNSClient(ipOption))
@@ -178,7 +149,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
matcherInfos: matcherInfos, matcherInfos: matcherInfos,
disableFallback: config.DisableFallback, disableFallback: config.DisableFallback,
disableFallbackIfMatch: config.DisableFallbackIfMatch, disableFallbackIfMatch: config.DisableFallbackIfMatch,
enableParallelQuery: config.EnableParallelQuery,
checkSystem: checkSystem, checkSystem: checkSystem,
}, nil }, nil
} }
@@ -221,7 +191,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
} }
if s.checkSystem { if s.checkSystem {
supportIPv4, supportIPv6 := utils.CheckRoutes() supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4 option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6 option.IPv6Enable = option.IPv6Enable && supportIPv6
} else { } else {
@@ -257,11 +227,45 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
} }
// Name servers lookup // Name servers lookup
if s.enableParallelQuery { var errs []error
return s.parallelQuery(domain, option) for _, client := range s.sortClients(domain) {
} else { if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
return s.serialQuery(domain, option) errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
continue
}
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 {
if ttl == 0 {
ttl = 1
}
return ips, ttl, nil
}
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name())
if err == nil {
err = dns.ErrEmptyResponse
}
errs = append(errs, err)
if client.IsFinalQuery() {
break
}
} }
if len(errs) > 0 {
allErrs := errors.Combine(errs...)
err0 := errs[0]
if errors.AllEqual(err0, allErrs) {
if go_errors.Is(err0, dns.ErrEmptyResponse) {
return nil, 0, dns.ErrEmptyResponse
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(err0)
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(allErrs)
}
return nil, 0, dns.ErrEmptyResponse
} }
func (s *DNS) sortClients(domain string) []*Client { func (s *DNS) sortClients(domain string) []*Client {
@@ -272,28 +276,22 @@ func (s *DNS) sortClients(domain string) []*Client {
// Priority domain matching // Priority domain matching
hasMatch := false hasMatch := false
if s.domainMatcher != nil { MatchSlice := s.domainMatcher.Match(domain)
matchSlice := s.domainMatcher.Match(strings.ToLower(domain)) sort.Slice(MatchSlice, func(i, j int) bool {
sort.Slice(matchSlice, func(i, j int) bool { return MatchSlice[i] < MatchSlice[j]
return matchSlice[i] < matchSlice[j] })
}) for _, match := range MatchSlice {
for _, match := range matchSlice { info := s.matcherInfos[match]
info := s.matcherInfos[match] client := s.clients[info.clientIdx]
client := s.clients[info.clientIdx] domainRule := client.domains[info.domainRuleIdx]
domainRule := info.domainRule domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx)) if clientUsed[info.clientIdx] {
if clientUsed[info.clientIdx] { continue
continue
}
clientUsed[info.clientIdx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
hasMatch = true
if client.finalQuery {
logDecision(s.ctx, domain, domainRules, clientNames)
return clients
}
} }
clientUsed[info.clientIdx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
hasMatch = true
} }
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) { if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
@@ -305,235 +303,46 @@ func (s *DNS) sortClients(domain string) []*Client {
clientUsed[idx] = true clientUsed[idx] = true
clients = append(clients, client) clients = append(clients, client)
clientNames = append(clientNames, client.Name()) clientNames = append(clientNames, client.Name())
if client.finalQuery {
logDecision(s.ctx, domain, domainRules, clientNames)
return clients
}
} }
} }
logDecision(s.ctx, domain, domainRules, clientNames) if len(domainRules) > 0 {
errors.LogDebug(s.ctx, "domain ", domain, " matches following rules: ", domainRules)
}
if len(clientNames) > 0 {
errors.LogDebug(s.ctx, "domain ", domain, " will use DNS in order: ", clientNames)
}
if len(clients) == 0 { if len(clients) == 0 {
if len(s.clients) > 0 { clients = append(clients, s.clients[0])
clients = append(clients, s.clients[0]) clientNames = append(clientNames, s.clients[0].Name())
clientNames = append(clientNames, s.clients[0].Name()) errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
} else {
errors.LogError(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured")
}
} }
return clients return clients
} }
func logDecision(ctx context.Context, domain string, domainRules []string, clientNames []string) {
if len(domainRules) > 0 {
errors.LogDebug(ctx, "domain ", domain, " matches following rules: ", domainRules)
}
if len(clientNames) > 0 {
errors.LogDebug(ctx, "domain ", domain, " will use DNS in order: ", clientNames)
}
}
func mergeQueryErrors(domain string, errs []error) error {
if len(errs) == 0 {
return dns.ErrEmptyResponse
}
var noRNF error
for _, err := range errs {
if go_errors.Is(err, errRecordNotFound) {
continue // server no response, ignore
} else if noRNF == nil {
noRNF = err
} else if !go_errors.Is(err, noRNF) {
return errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
}
}
if go_errors.Is(noRNF, dns.ErrEmptyResponse) {
return dns.ErrEmptyResponse
}
if noRNF == nil {
noRNF = errRecordNotFound
}
return errors.New("returning nil for domain ", domain).Base(noRNF)
}
func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
var errs []error
for _, client := range s.sortClients(domain) {
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
continue
}
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 {
return ips, ttl, nil
}
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode")
if err == nil {
err = dns.ErrEmptyResponse
}
errs = append(errs, err)
}
return nil, 0, mergeQueryErrors(domain, errs)
}
func (s *DNS) parallelQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
var errs []error
clients := s.sortClients(domain)
resultsChan := asyncQueryAll(domain, option, clients, s.ctx)
groups, groupOf := makeGroups( /*s.ctx,*/ clients)
results := make([]*queryResult, len(clients))
pending := make([]int, len(groups))
for gi, g := range groups {
pending[gi] = g.end - g.start + 1
}
nextGroup := 0
for range clients {
result := <-resultsChan
results[result.index] = &result
gi := groupOf[result.index]
pending[gi]--
for nextGroup < len(groups) {
g := groups[nextGroup]
// group race, minimum rtt -> return
for j := g.start; j <= g.end; j++ {
r := results[j]
if r != nil && r.err == nil && len(r.ips) > 0 {
return r.ips, r.ttl, nil
}
}
// current group is incomplete and no one success -> continue pending
if pending[nextGroup] > 0 {
break
}
// all failed -> log and continue next group
for j := g.start; j <= g.end; j++ {
r := results[j]
e := r.err
if e == nil {
e = dns.ErrEmptyResponse
}
errors.LogInfoInner(s.ctx, e, "failed to lookup ip for domain ", domain, " at server ", clients[j].Name(), " in parallel query mode")
errs = append(errs, e)
}
nextGroup++
}
}
return nil, 0, mergeQueryErrors(domain, errs)
}
type queryResult struct {
ips []net.IP
ttl uint32
err error
index int
}
func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx context.Context) chan queryResult {
if len(clients) == 0 {
ch := make(chan queryResult)
close(ch)
return ch
}
ch := make(chan queryResult, len(clients))
for i, client := range clients {
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
ch <- queryResult{err: dns.ErrEmptyResponse, index: i}
continue
}
go func(i int, c *Client) {
qctx := ctx
if !c.server.IsDisableCache() {
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeoutMs*2)
qctx = nctx
defer cancel()
}
ips, ttl, err := c.QueryIP(qctx, domain, option)
ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i}
}(i, client)
}
return ch
}
type group struct{ start, end int }
// merge only adjacent and rule-equivalent Client into a single group
func makeGroups( /*ctx context.Context,*/ clients []*Client) ([]group, []int) {
n := len(clients)
if n == 0 {
return nil, nil
}
groups := make([]group, 0, n)
groupOf := make([]int, n)
s, e := 0, 0
for i := 1; i < n; i++ {
if clients[i-1].policyID == clients[i].policyID {
e = i
} else {
for k := s; k <= e; k++ {
groupOf[k] = len(groups)
}
groups = append(groups, group{start: s, end: e})
s, e = i, i
}
}
for k := s; k <= e; k++ {
groupOf[k] = len(groups)
}
groups = append(groups, group{start: s, end: e})
// var b strings.Builder
// b.WriteString("dns grouping: total clients=")
// b.WriteString(strconv.Itoa(n))
// b.WriteString(", groups=")
// b.WriteString(strconv.Itoa(len(groups)))
// for gi, g := range groups {
// b.WriteString("\n [")
// b.WriteString(strconv.Itoa(g.start))
// b.WriteString("..")
// b.WriteString(strconv.Itoa(g.end))
// b.WriteString("] gid=")
// b.WriteString(strconv.Itoa(gi))
// b.WriteString(" pid=")
// b.WriteString(strconv.FormatUint(uint64(clients[g.start].policyID), 10))
// b.WriteString(" members: ")
// for i := g.start; i <= g.end; i++ {
// if i > g.start {
// b.WriteString(", ")
// }
// b.WriteString(strconv.Itoa(i))
// b.WriteByte(':')
// b.WriteString(clients[i].Name())
// }
// }
// errors.LogDebug(ctx, b.String())
return groups, groupOf
}
func init() { func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config)) return New(ctx, config.(*Config))
})) }))
} }
func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
if err4 != nil {
supportIPv4 = false
} else {
supportIPv4 = true
conn4.Close()
}
conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
if err6 != nil {
supportIPv6 = false
} else {
supportIPv6 = true
conn6.Close()
}
return
}
+100 -68
View File
@@ -11,9 +11,9 @@ import (
"github.com/xtls/xray-core/app/policy" "github.com/xtls/xray-core/app/policy"
"github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/proxyman"
_ "github.com/xtls/xray-core/app/proxyman/outbound" _ "github.com/xtls/xray-core/app/proxyman/outbound"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
@@ -147,9 +147,7 @@ func TestUDPServerSubnet(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -209,9 +207,7 @@ func TestUDPServer(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -335,9 +331,10 @@ func TestPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "google.com"}}, Type: DomainMatchingType_Full,
Domain: "google.com",
}, },
}, },
}, },
@@ -349,9 +346,7 @@ func TestPrioritizedDomain(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -420,9 +415,7 @@ func TestUDPServerIPv6(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -478,7 +471,8 @@ func TestStaticHostDomain(t *testing.T) {
}, },
StaticHosts: []*Config_HostMapping{ StaticHosts: []*Config_HostMapping{
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "example.com"}}}, Type: DomainMatchingType_Full,
Domain: "example.com",
ProxiedDomain: "google.com", ProxiedDomain: "google.com",
}, },
}, },
@@ -489,9 +483,7 @@ func TestStaticHostDomain(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -547,9 +539,17 @@ func TestIPMatch(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// inner ip, will not match {
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{192, 168, 11, 1}, Prefix: 32}}}}, CountryCode: "local",
Cidr: []*router.CIDR{
{
// inner ip, will not match
Ip: []byte{192, 168, 11, 1},
Prefix: 32,
},
},
},
}, },
}, },
// second dns, match ip // second dns, match ip
@@ -563,9 +563,25 @@ func TestIPMatch(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32}}}}, {
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 4}, Prefix: 32}}}}, CountryCode: "test",
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 8},
Prefix: 32,
},
},
},
{
CountryCode: "test",
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 4},
Prefix: 32,
},
},
},
}, },
}, },
}, },
@@ -576,9 +592,7 @@ func TestIPMatch(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -649,15 +663,19 @@ func TestLocalDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
// Equivalent of dotless:localhost // Equivalent of dotless:localhost
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]*localhost[^.]*$"}}}, {Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"},
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// Will match localhost, localhost-a and localhost-b, { // Will match localhost, localhost-a and localhost-b,
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 2}, Prefix: 32}}}}, CountryCode: "local",
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 3}, Prefix: 32}}}}, Cidr: []*router.CIDR{
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 4}, Prefix: 32}}}}, {Ip: []byte{127, 0, 0, 2}, Prefix: 32},
{Ip: []byte{127, 0, 0, 3}, Prefix: 32},
{Ip: []byte{127, 0, 0, 4}, Prefix: 32},
},
},
}, },
}, },
{ {
@@ -670,21 +688,23 @@ func TestLocalDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
// Equivalent of dotless: and domain:local // Equivalent of dotless: and domain:local
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]*$"}}}, {Type: DomainMatchingType_Regex, Domain: "^[^.]*$"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "local"}}}, {Type: DomainMatchingType_Subdomain, Domain: "local"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localdomain"}}}, {Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
}, },
}, },
}, },
StaticHosts: []*Config_HostMapping{ StaticHosts: []*Config_HostMapping{
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "hostnamestatic"}}}, Type: DomainMatchingType_Full,
Domain: "hostnamestatic",
Ip: [][]byte{{127, 0, 0, 53}}, Ip: [][]byte{{127, 0, 0, 53}},
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "hostnamealias"}}}, Type: DomainMatchingType_Full,
Domain: "hostnamealias",
ProxiedDomain: "hostname.localdomain", ProxiedDomain: "hostname.localdomain",
}, },
}, },
@@ -695,9 +715,7 @@ func TestLocalDomain(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
@@ -873,15 +891,19 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}}, Type: DomainMatchingType_Subdomain,
Domain: "google.com",
}, },
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// Will only match 8.8.8.8 and 8.8.4.4 { // Will only match 8.8.8.8 and 8.8.4.4
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32}}}}, Cidr: []*router.CIDR{
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 4, 4}, Prefix: 32}}}}, {Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{8, 8, 4, 4}, Prefix: 32},
},
},
}, },
}, },
{ {
@@ -894,14 +916,18 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}}, Type: DomainMatchingType_Subdomain,
Domain: "google.com",
}, },
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// Will match 8.8.8.8 and 8.8.8.7, etc { // Will match 8.8.8.8 and 8.8.8.7, etc
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 7}, Prefix: 24}}}}, Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 8, 7}, Prefix: 24},
},
},
}, },
}, },
{ {
@@ -914,14 +940,18 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "api.google.com"}}, Type: DomainMatchingType_Subdomain,
Domain: "api.google.com",
}, },
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// Will only match 8.8.7.7 (api.google.com) { // Will only match 8.8.7.7 (api.google.com)
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 7, 7}, Prefix: 32}}}}, Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 7, 7}, Prefix: 32},
},
},
}, },
}, },
{ {
@@ -934,14 +964,18 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Port: uint32(port), Port: uint32(port),
}, },
Domain: []*geodata.DomainRule{ PrioritizedDomain: []*NameServer_PriorityDomain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "v2.api.google.com"}}, Type: DomainMatchingType_Full,
Domain: "v2.api.google.com",
}, },
}, },
ExpectedIp: []*geodata.IPRule{ ExpectedGeoip: []*router.GeoIP{
// Will only match 8.8.7.8 (v2.api.google.com) { // Will only match 8.8.7.8 (v2.api.google.com)
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 7, 8}, Prefix: 32}}}}, Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 7, 8}, Prefix: 32},
},
},
}, },
}, },
}, },
@@ -952,9 +986,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}, },
Outbound: []*core.OutboundHandlerConfig{ Outbound: []*core.OutboundHandlerConfig{
{ {
ProxySettings: serial.ToTypedMessage(&freedom.Config{ ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
}, },
}, },
} }
+14 -22
View File
@@ -3,7 +3,6 @@ package dns
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"math"
"strings" "strings"
"time" "time"
@@ -14,12 +13,10 @@ import (
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
) )
// Fqdn normalizes domain make sure it ends with '.' // Fqdn normalizes domain make sure it ends with '.'
// case-sensitive
func Fqdn(domain string) string { func Fqdn(domain string) string {
if len(domain) > 0 && strings.HasSuffix(domain, ".") { if len(domain) > 0 && strings.HasSuffix(domain, ".") {
return domain return domain
@@ -41,14 +38,19 @@ type IPRecord struct {
RawHeader *dnsmessage.Header RawHeader *dnsmessage.Header
} }
func (r *IPRecord) getIPs() ([]net.IP, int32, error) { func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
if r == nil { if r == nil {
return nil, 0, errRecordNotFound return nil, 0, errRecordNotFound
} }
untilExpire := time.Until(r.Expire).Seconds() untilExpire := time.Until(r.Expire).Seconds()
ttl := int32(math.Ceil(untilExpire)) if untilExpire <= 0 {
return nil, 0, errRecordNotFound
}
ttl := uint32(untilExpire) + 1
if ttl == 1 {
r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result
}
if r.RCode != dnsmessage.RCodeSuccess { if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode) return nil, ttl, dns_feature.RCodeError(r.RCode)
} }
@@ -127,20 +129,15 @@ func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource {
return opt return opt
} }
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) ([]*dnsRequest, error) { func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
name, err := dnsmessage.NewName(domain)
if err != nil {
return nil, err
}
qA := dnsmessage.Question{ qA := dnsmessage.Question{
Name: name, Name: dnsmessage.MustNewName(domain),
Type: dnsmessage.TypeA, Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET, Class: dnsmessage.ClassINET,
} }
qAAAA := dnsmessage.Question{ qAAAA := dnsmessage.Question{
Name: name, Name: dnsmessage.MustNewName(domain),
Type: dnsmessage.TypeAAAA, Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET, Class: dnsmessage.ClassINET,
} }
@@ -180,7 +177,7 @@ func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() ui
}) })
} }
return reqs, nil return reqs
} }
// parseResponse parses DNS answers from the returned payload // parseResponse parses DNS answers from the returned payload
@@ -198,14 +195,9 @@ func parseResponse(payload []byte) (*IPRecord, error) {
ipRecord := &IPRecord{ ipRecord := &IPRecord{
ReqID: h.ID, ReqID: h.ID,
RCode: h.RCode, RCode: h.RCode,
Expire: now.Add(time.Second * dns_feature.DefaultTTL),
RawHeader: &h, RawHeader: &h,
} }
defer func() {
// set to default TTL if no valid TTL is found
if ipRecord.Expire.IsZero() {
ipRecord.Expire = now.Add(time.Second * dns_feature.DefaultTTL)
}
}()
L: L:
for { for {
@@ -222,7 +214,7 @@ L:
ttl = 1 ttl = 1
} }
expire := now.Add(time.Duration(ttl) * time.Second) expire := now.Add(time.Duration(ttl) * time.Second)
if ipRecord.Expire.IsZero() || ipRecord.Expire.After(expire) { if ipRecord.Expire.After(expire) {
ipRecord.Expire = expire ipRecord.Expire = expire
} }
+3 -11
View File
@@ -2,7 +2,6 @@ package dns
import ( import (
"math/rand" "math/rand"
"strings"
"testing" "testing"
"time" "time"
@@ -25,8 +24,7 @@ func Test_parseResponse(t *testing.T) {
ans = new(dns.Msg) ans = new(dns.Msg)
ans.Id = 1 ans.Id = 1
ans.Answer = append( ans.Answer = append(ans.Answer,
ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")), common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
@@ -36,8 +34,7 @@ func Test_parseResponse(t *testing.T) {
ans = new(dns.Msg) ans = new(dns.Msg)
ans.Id = 2 ans.Id = 2
ans.Answer = append( ans.Answer = append(ans.Answer,
ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
@@ -134,15 +131,10 @@ func Test_buildReqMsgs(t *testing.T) {
IPv6Enable: false, IPv6Enable: false,
FakeEnable: false, FakeEnable: false,
}, nil}, 0}, }, nil}, 0},
{"name too long", args{strings.Repeat("a", 256), dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}, nil}, 0},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got, _ := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) { if got := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want) t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want)
} }
}) })
+16 -11
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"math" "math"
"math/big" "math/big"
gonet "net"
"sync" "sync"
"time" "time"
@@ -16,8 +17,8 @@ import (
type Holder struct { type Holder struct {
domainToIP cache.Lru domainToIP cache.Lru
ipRange *net.IPNet ipRange *gonet.IPNet
mu sync.Mutex mu *sync.Mutex
config *FakeDnsPool config *FakeDnsPool
} }
@@ -49,7 +50,9 @@ func (fkdns *Holder) Start() error {
} }
func (fkdns *Holder) Close() error { func (fkdns *Holder) Close() error {
// nothing to do for now, just wait GC fkdns.domainToIP = nil
fkdns.ipRange = nil
fkdns.mu = nil
return nil return nil
} }
@@ -68,7 +71,7 @@ func NewFakeDNSHolder() (*Holder, error) {
} }
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) { func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
return &Holder{config: conf}, nil return &Holder{nil, nil, nil, conf}, nil
} }
func (fkdns *Holder) initializeFromConfig() error { func (fkdns *Holder) initializeFromConfig() error {
@@ -76,10 +79,10 @@ func (fkdns *Holder) initializeFromConfig() error {
} }
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error { func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
var ipRange *net.IPNet var ipRange *gonet.IPNet
var err error var err error
if _, ipRange, err = net.ParseCIDR(ipPoolCidr); err != nil { if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
} }
@@ -90,6 +93,7 @@ func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
} }
fkdns.domainToIP = cache.NewLru(lruSize) fkdns.domainToIP = cache.NewLru(lruSize)
fkdns.ipRange = ipRange fkdns.ipRange = ipRange
fkdns.mu = new(sync.Mutex)
return nil return nil
} }
@@ -100,7 +104,7 @@ func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
if v, ok := fkdns.domainToIP.Get(domain); ok { if v, ok := fkdns.domainToIP.Get(domain); ok {
return []net.Address{v.(net.Address)} return []net.Address{v.(net.Address)}
} }
currentTimeMillis := uint64(time.Now().UnixMilli()) currentTimeMillis := uint64(time.Now().UnixNano() / 1e6)
ones, bits := fkdns.ipRange.Mask.Size() ones, bits := fkdns.ipRange.Mask.Size()
rooms := bits - ones rooms := bits - ones
if rooms < 64 { if rooms < 64 {
@@ -199,11 +203,12 @@ func (h *HolderMulti) Start() error {
} }
func (h *HolderMulti) Close() error { func (h *HolderMulti) Close() error {
var errs []error
for _, v := range h.holders { for _, v := range h.holders {
errs = append(errs, v.Close()) if err := v.Close(); err != nil {
return errors.New("Cannot close all fake dns pools").Base(err)
}
} }
return errors.Combine(errs...) return nil
} }
func (h *HolderMulti) createHolderGroups() error { func (h *HolderMulti) createHolderGroups() error {
@@ -218,7 +223,7 @@ func (h *HolderMulti) createHolderGroups() error {
} }
func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) { func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
holderMulti := &HolderMulti{config: conf} holderMulti := &HolderMulti{nil, conf}
if err := holderMulti.createHolderGroups(); err != nil { if err := holderMulti.createHolderGroups(); err != nil {
return nil, err return nil, err
} }
+36 -22
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/dns/fakedns/fakedns.proto // source: app/dns/fakedns/fakedns.proto
package fakedns package fakedns
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,11 +21,12 @@ const (
) )
type FakeDnsPool struct { type FakeDnsPool struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address
} }
func (x *FakeDnsPool) Reset() { func (x *FakeDnsPool) Reset() {
@@ -74,10 +74,11 @@ func (x *FakeDnsPool) GetLruSize() int64 {
} }
type FakeDnsPoolMulti struct { type FakeDnsPoolMulti struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
} }
func (x *FakeDnsPoolMulti) Reset() { func (x *FakeDnsPoolMulti) Reset() {
@@ -119,24 +120,36 @@ func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool {
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
const file_app_dns_fakedns_fakedns_proto_rawDesc = "" + var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
"\n" + 0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
"\x1dapp/dns/fakedns/fakedns.proto\x12\x14xray.app.dns.fakedns\"@\n" + 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
"\vFakeDnsPool\x12\x17\n" + 0x14, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61,
"\aip_pool\x18\x01 \x01(\tR\x06ipPool\x12\x18\n" + 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73,
"\alruSize\x18\x02 \x01(\x03R\alruSize\"K\n" + 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18,
"\x10FakeDnsPoolMulti\x127\n" + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a,
"\x05pools\x18\x01 \x03(\v2!.xray.app.dns.fakedns.FakeDnsPoolR\x05poolsB^\n" + 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07,
"\x18com.xray.app.dns.fakednsP\x01Z)github.com/xtls/xray-core/app/dns/fakedns\xaa\x02\x14Xray.App.Dns.Fakednsb\x06proto3" 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x46, 0x61, 0x6b, 0x65, 0x44,
0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x37, 0x0a, 0x05, 0x70,
0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x70,
0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73,
0x50, 0x01, 0x5a, 0x29, 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, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x14,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b,
0x65, 0x64, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once
file_app_dns_fakedns_fakedns_proto_rawDescData []byte file_app_dns_fakedns_fakedns_proto_rawDescData = file_app_dns_fakedns_fakedns_proto_rawDesc
) )
func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte { func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() { file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() {
file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc))) file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_fakedns_fakedns_proto_rawDescData)
}) })
return file_app_dns_fakedns_fakedns_proto_rawDescData return file_app_dns_fakedns_fakedns_proto_rawDescData
} }
@@ -164,7 +177,7 @@ func file_app_dns_fakedns_fakedns_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc)), RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
@@ -175,6 +188,7 @@ func file_app_dns_fakedns_fakedns_proto_init() {
MessageInfos: file_app_dns_fakedns_fakedns_proto_msgTypes, MessageInfos: file_app_dns_fakedns_fakedns_proto_msgTypes,
}.Build() }.Build()
File_app_dns_fakedns_fakedns_proto = out.File File_app_dns_fakedns_fakedns_proto = out.File
file_app_dns_fakedns_fakedns_proto_rawDesc = nil
file_app_dns_fakedns_fakedns_proto_goTypes = nil file_app_dns_fakedns_fakedns_proto_goTypes = nil
file_app_dns_fakedns_fakedns_proto_depIdxs = nil file_app_dns_fakedns_fakedns_proto_depIdxs = nil
} }
+12 -12
View File
@@ -1,6 +1,7 @@
package fakedns package fakedns
import ( import (
gonet "net"
"strconv" "strconv"
"testing" "testing"
@@ -129,16 +130,15 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
} }
func TestFakeDNSMulti(t *testing.T) { func TestFakeDNSMulti(t *testing.T) {
fakeMulti, err := NewFakeDNSHolderMulti( fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
&FakeDnsPoolMulti{ Pools: []*FakeDnsPool{{
Pools: []*FakeDnsPool{{ IpPool: "240.0.0.0/12",
IpPool: "240.0.0.0/12", LruSize: 256,
LruSize: 256, }, {
}, { IpPool: "fddd:c5b4:ff5f:f4f0::/64",
IpPool: "fddd:c5b4:ff5f:f4f0::/64", LruSize: 256,
LruSize: 256, }},
}}, },
},
) )
common.Must(err) common.Must(err)
@@ -155,7 +155,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.True(t, inPool) assert.True(t, inPool)
}) })
t.Run("ipv6", func(t *testing.T) { t.Run("ipv6", func(t *testing.T) {
ip, err := net.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5") ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err) assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.True(t, inPool) assert.True(t, inPool)
@@ -165,7 +165,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.False(t, inPool) assert.False(t, inPool)
}) })
t.Run("ipv6_inverse", func(t *testing.T) { t.Run("ipv6_inverse", func(t *testing.T) {
ip, err := net.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5") ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err) assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.False(t, inPool) assert.False(t, inPool)
+26 -37
View File
@@ -3,27 +3,34 @@ package dns
import ( import (
"context" "context"
"strconv" "strconv"
"strings"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
// StaticHosts represents static domain-ip mapping in DNS server. // StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct { type StaticHosts struct {
responses [][]net.Address ips [][]net.Address
matcher geodata.DomainMatcher matchers *strmatcher.MatcherGroup
} }
// NewStaticHosts creates a new StaticHosts instance. // NewStaticHosts creates a new StaticHosts instance.
func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) { func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
reps := make([][]net.Address, 0, len(hosts)) g := new(strmatcher.MatcherGroup)
rules := make([]*geodata.DomainRule, 0, len(hosts)) sh := &StaticHosts{
ips: make([][]net.Address, len(hosts)+16),
matchers: g,
}
for _, mapping := range hosts { for _, mapping := range hosts {
rep := make([]net.Address, 0, len(mapping.Ip)) matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
if err != nil {
return nil, errors.New("failed to create domain matcher").Base(err)
}
id := g.Add(matcher)
ips := make([]net.Address, 0, len(mapping.Ip)+1)
switch { switch {
case len(mapping.ProxiedDomain) > 0: case len(mapping.ProxiedDomain) > 0:
if mapping.ProxiedDomain[0] == '#' { if mapping.ProxiedDomain[0] == '#' {
@@ -31,36 +38,24 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rep = append(rep, dns.RCodeError(rcode)) ips = append(ips, dns.RCodeError(rcode))
} else { } else {
rep = append(rep, net.DomainAddress(mapping.ProxiedDomain)) ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
} }
case len(mapping.Ip) > 0: case len(mapping.Ip) > 0:
for _, ip := range mapping.Ip { for _, ip := range mapping.Ip {
addr := net.IPAddress(ip) addr := net.IPAddress(ip)
if addr == nil { if addr == nil {
errors.LogError(context.Background(), "invalid IP address in static hosts: ", ip, ", ignore this ip for rule: ", mapping.Domain) return nil, errors.New("invalid IP address in static hosts: ", ip).AtWarning()
continue
} }
rep = append(rep, addr) ips = append(ips, addr)
} }
} }
reps = append(reps, rep)
rules = append(rules, mapping.Domain) sh.ips[id] = ips
} }
if len(rules) == 0 { return sh, nil
return &StaticHosts{}, nil
}
matcher, err := geodata.DomainReg.BuildDomainMatcher(rules)
if err != nil {
return nil, err
}
return &StaticHosts{
responses: reps,
matcher: matcher,
}, nil
} }
func filterIP(ips []net.Address, option dns.IPOption) []net.Address { func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
@@ -76,16 +71,16 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) { func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
ips := make([]net.Address, 0) ips := make([]net.Address, 0)
found := false found := false
for _, idx := range h.matcher.Match(domain) { for _, id := range h.matchers.Match(domain) {
for _, rep := range h.responses[idx] { for _, v := range h.ips[id] {
if err, ok := rep.(dns.RCodeError); ok { if err, ok := v.(dns.RCodeError); ok {
if uint16(err) == 0 { if uint16(err) == 0 {
return nil, dns.ErrEmptyResponse return nil, dns.ErrEmptyResponse
} }
return nil, err return nil, err
} }
} }
ips = append(ips, h.responses[idx]...) ips = append(ips, h.ips[id]...)
found = true found = true
} }
if !found { if !found {
@@ -95,13 +90,10 @@ func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
} }
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) { func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
domain = strings.ToLower(domain)
switch addrs, err := h.lookupInternal(domain); { switch addrs, err := h.lookupInternal(domain); {
case err != nil: case err != nil:
return nil, err return nil, err
case addrs == nil: // Not recorded in static hosts, return nil case len(addrs) == 0: // Not recorded in static hosts, return nil
return nil, nil
case len(addrs) == 0: // Domain recorded, but no valid IP returned
return addrs, nil return addrs, nil
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it") errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it")
@@ -122,8 +114,5 @@ func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) (
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) { func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) {
if h.matcher == nil {
return nil, nil
}
return h.lookup(domain, option, 5) return h.lookup(domain, option, 5)
} }
+12 -7
View File
@@ -6,7 +6,6 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns" . "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
) )
@@ -14,17 +13,20 @@ import (
func TestStaticHosts(t *testing.T) { func TestStaticHosts(t *testing.T) {
pb := []*Config_HostMapping{ pb := []*Config_HostMapping{
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "lan"}}}, Type: DomainMatchingType_Subdomain,
Domain: "lan",
ProxiedDomain: "#3", ProxiedDomain: "#3",
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "example.com"}}}, Type: DomainMatchingType_Full,
Domain: "example.com",
Ip: [][]byte{ Ip: [][]byte{
{1, 1, 1, 1}, {1, 1, 1, 1},
}, },
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "proxy.xray.com"}}}, Type: DomainMatchingType_Full,
Domain: "proxy.xray.com",
Ip: [][]byte{ Ip: [][]byte{
{1, 2, 3, 4}, {1, 2, 3, 4},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
@@ -32,17 +34,20 @@ func TestStaticHosts(t *testing.T) {
ProxiedDomain: "another-proxy.xray.com", ProxiedDomain: "another-proxy.xray.com",
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "proxy2.xray.com"}}}, Type: DomainMatchingType_Full,
Domain: "proxy2.xray.com",
ProxiedDomain: "proxy.xray.com", ProxiedDomain: "proxy.xray.com",
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "example.cn"}}}, Type: DomainMatchingType_Subdomain,
Domain: "example.cn",
Ip: [][]byte{ Ip: [][]byte{
{2, 2, 2, 2}, {2, 2, 2, 2},
}, },
}, },
{ {
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "baidu.com"}}}, Type: DomainMatchingType_Subdomain,
Domain: "baidu.com",
Ip: [][]byte{ Ip: [][]byte{
{127, 0, 0, 1}, {127, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+96 -45
View File
@@ -6,11 +6,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/utils" "github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
@@ -20,9 +20,6 @@ import (
type Server interface { type Server interface {
// Name of the Client. // Name of the Client.
Name() string Name() string
IsDisableCache() bool
// QueryIP sends IP queries to its configured server. // QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
} }
@@ -31,8 +28,9 @@ type Server interface {
type Client struct { type Client struct {
server Server server Server
skipFallback bool skipFallback bool
expectedIPs geodata.IPMatcher domains []string
unexpectedIPs geodata.IPMatcher expectedIPs []*router.GeoIPMatcher
unexpectedIPs []*router.GeoIPMatcher
actPrior bool actPrior bool
actUnprior bool actUnprior bool
tag string tag string
@@ -40,11 +38,10 @@ type Client struct {
finalQuery bool finalQuery bool
ipOption *dns.IPOption ipOption *dns.IPOption
checkSystem bool checkSystem bool
policyID uint32
} }
// NewServer creates a name server object according to the network destination url. // NewServer creates a name server object according to the network destination url.
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (Server, error) { func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) {
if address := dest.Address; address.Family().IsDomain() { if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain()) u, err := url.Parse(address.Domain())
if err != nil { if err != nil {
@@ -54,19 +51,19 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
case strings.EqualFold(u.String(), "localhost"): case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil return NewLocalNameServer(), nil
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
return NewDoHNameServer(u, dispatcher, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
return NewDoHNameServer(u, dispatcher, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
return NewDoHNameServer(u, nil, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
return NewDoHNameServer(u, nil, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP) return NewQUICNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
return NewTCPNameServer(u, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP) return NewTCPNameServer(u, dispatcher, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
return NewTCPLocalNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP) return NewTCPLocalNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.String(), "fakedns"): case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine var fd dns.FakeDNSEngine
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
@@ -82,7 +79,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
dest.Network = net.Network_UDP dest.Network = net.Network_UDP
} }
if dest.Network == net.Network_UDP { // UDP classic DNS mode if dest.Network == net.Network_UDP { // UDP classic DNS mode
return NewClassicNameServer(dest, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewClassicNameServer(dest, dispatcher, disableCache, clientIP), nil
} }
return nil, errors.New("No available name server could be created from ", dest).AtWarning() return nil, errors.New("No available name server could be created from ", dest).AtWarning()
} }
@@ -92,38 +89,88 @@ func NewClient(
ctx context.Context, ctx context.Context,
ns *NameServer, ns *NameServer,
clientIP net.IP, clientIP net.IP,
disableCache bool, serveStale bool, serveExpiredTTL uint32, disableCache bool,
tag string, tag string,
ipOption dns.IPOption, ipOption dns.IPOption,
updateRules func(bool), matcherInfos *[]*DomainMatcherInfo,
updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error,
) (*Client, error) { ) (*Client, error) {
client := &Client{} client := &Client{}
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
// Create a new server for each client for now // Create a new server for each client for now
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP) server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP)
if err != nil { if err != nil {
return errors.New("failed to create nameserver").Base(err).AtWarning() return errors.New("failed to create nameserver").Base(err).AtWarning()
} }
_, isLocalDNS := server.(*LocalNameServer) // Prioritize local domains with specific TLDs or those without any dot for the local DNS
updateRules(isLocalDNS) if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
// Establish expected IPs ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
var expectedMatcher geodata.IPMatcher // The following lines is a solution to avoid core panicsrule index out of range when setting `localhost` DNS client in config.
if len(ns.ExpectedIp) > 0 { // Because the `localhost` DNS client will append len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
expectedMatcher, err = geodata.IPReg.BuildIPMatcher(ns.ExpectedIp) // But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
if err != nil { // To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
return errors.New("failed to create expected ip matcher").Base(err).AtWarning() // Related issues:
// https://github.com/v2fly/v2ray-core/issues/529
// https://github.com/v2fly/v2ray-core/issues/719
for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
*matcherInfos = append(*matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(0),
domainRuleIdx: uint16(0),
})
} }
} }
// Establish domain rules
var rules []string
ruleCurr := 0
ruleIter := 0
for _, domain := range ns.PrioritizedDomain {
domainRule, err := toStrMatcher(domain.Type, domain.Domain)
if err != nil {
return errors.New("failed to create prioritized domain").Base(err).AtWarning()
}
originalRuleIdx := ruleCurr
if ruleCurr < len(ns.OriginalRules) {
rule := ns.OriginalRules[ruleCurr]
if ruleCurr >= len(rules) {
rules = append(rules, rule.Rule)
}
ruleIter++
if ruleIter >= int(rule.Size) {
ruleIter = 0
ruleCurr++
}
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
rules = append(rules, domainRule.String())
ruleCurr++
}
err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
if err != nil {
return errors.New("failed to create prioritized domain").Base(err).AtWarning()
}
}
// Establish expected IPs
var expectedMatchers []*router.GeoIPMatcher
for _, geoip := range ns.ExpectedGeoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil {
return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
}
expectedMatchers = append(expectedMatchers, matcher)
}
// Establish unexpected IPs // Establish unexpected IPs
var unexpectedMatcher geodata.IPMatcher var unexpectedMatchers []*router.GeoIPMatcher
if len(ns.UnexpectedIp) > 0 { for _, geoip := range ns.UnexpectedGeoip {
unexpectedMatcher, err = geodata.IPReg.BuildIPMatcher(ns.UnexpectedIp) matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil { if err != nil {
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning() return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
} }
unexpectedMatchers = append(unexpectedMatchers, matcher)
} }
if len(clientIP) > 0 { if len(clientIP) > 0 {
@@ -135,7 +182,7 @@ func NewClient(
} }
} }
timeoutMs := 4000 * time.Millisecond var timeoutMs = 4000 * time.Millisecond
if ns.TimeoutMs > 0 { if ns.TimeoutMs > 0 {
timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
} }
@@ -144,8 +191,9 @@ func NewClient(
client.server = server client.server = server
client.skipFallback = ns.SkipFallback client.skipFallback = ns.SkipFallback
client.expectedIPs = expectedMatcher client.domains = rules
client.unexpectedIPs = unexpectedMatcher client.expectedIPs = expectedMatchers
client.unexpectedIPs = unexpectedMatchers
client.actPrior = ns.ActPrior client.actPrior = ns.ActPrior
client.actUnprior = ns.ActUnprior client.actUnprior = ns.ActUnprior
client.tag = tag client.tag = tag
@@ -153,7 +201,6 @@ func NewClient(
client.finalQuery = ns.FinalQuery client.finalQuery = ns.FinalQuery
client.ipOption = &ipOption client.ipOption = &ipOption
client.checkSystem = checkSystem client.checkSystem = checkSystem
client.policyID = ns.PolicyID
return nil return nil
}) })
return client, err return client, err
@@ -164,10 +211,14 @@ func (c *Client) Name() string {
return c.server.Name() return c.server.Name()
} }
func (c *Client) IsFinalQuery() bool {
return c.finalQuery
}
// QueryIP sends DNS query to the name server with the client's IP. // QueryIP sends DNS query to the name server with the client's IP.
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) { func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
if c.checkSystem { if c.checkSystem {
supportIPv4, supportIPv6 := utils.CheckRoutes() supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4 option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6 option.IPv6Enable = option.IPv6Enable && supportIPv6
} else { } else {
@@ -192,32 +243,32 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
if c.expectedIPs != nil && !c.actPrior { if len(c.expectedIPs) > 0 && !c.actPrior {
ips, _ = c.expectedIPs.FilterIPs(ips) ips = router.MatchIPs(c.expectedIPs, ips, false)
errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 { if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
} }
if c.unexpectedIPs != nil && !c.actUnprior { if len(c.unexpectedIPs) > 0 && !c.actUnprior {
_, ips = c.unexpectedIPs.FilterIPs(ips) ips = router.MatchIPs(c.unexpectedIPs, ips, true)
errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 { if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
} }
if c.expectedIPs != nil && c.actPrior { if len(c.expectedIPs) > 0 && c.actPrior {
ipsNew, _ := c.expectedIPs.FilterIPs(ips) ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
if len(ipsNew) > 0 { if len(ipsNew) > 0 {
ips = ipsNew ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
} }
} }
if c.unexpectedIPs != nil && c.actUnprior { if len(c.unexpectedIPs) > 0 && c.actUnprior {
_, ipsNew := c.unexpectedIPs.FilterIPs(ips) ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
if len(ipsNew) > 0 { if len(ipsNew) > 0 {
ips = ipsNew ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
-173
View File
@@ -1,173 +0,0 @@
package dns
import (
"context"
go_errors "errors"
"time"
"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/signal/pubsub"
"github.com/xtls/xray-core/features/dns"
)
type CachedNameserver interface {
getCacheController() *CacheController
sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns.IPOption)
}
// queryIP is called from dns.Server->queryIPTimeout
func queryIP(ctx context.Context, s CachedNameserver, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
cache := s.getCacheController()
if !cache.disableCache {
if rec := cache.findRecords(fqdn); rec != nil {
ips, ttl, err := merge(option, rec.A, rec.AAAA)
if !go_errors.Is(err, errRecordNotFound) {
if ttl > 0 {
errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, uint32(ttl), err
}
if cache.serveStale && (cache.serveExpiredTTL == 0 || cache.serveExpiredTTL < ttl) {
errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheOptimiste, Elapsed: 0, Error: err})
go pull(ctx, s, fqdn, option)
return ips, 1, err
}
}
}
} else {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", fqdn, " at ", cache.name)
}
return fetch(ctx, s, fqdn, option)
}
func pull(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) {
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 8*time.Second)
defer cancel()
fetch(nctx, s, fqdn, option)
}
func fetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) ([]net.IP, uint32, error) {
key := fqdn
switch {
case option.IPv4Enable && option.IPv6Enable:
key = key + "46"
case option.IPv4Enable:
key = key + "4"
case option.IPv6Enable:
key = key + "6"
}
v, _, _ := s.getCacheController().requestGroup.Do(key, func() (any, error) {
return doFetch(ctx, s, fqdn, option), nil
})
ret := v.(result)
return ret.ips, ret.ttl, ret.error
}
type result struct {
ips []net.IP
ttl uint32
error
}
func doFetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) result {
sub4, sub6 := s.getCacheController().registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
noResponseErrCh := make(chan error, 2)
onEvent := func(sub *pubsub.Subscriber) (*IPRecord, error) {
if sub == nil {
return nil, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-noResponseErrCh:
return nil, err
case msg := <-sub.Wait():
sub.Close()
return msg.(*IPRecord), nil // should panic
}
}
start := time.Now()
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
rec4, err4 := onEvent(sub4)
rec6, err6 := onEvent(sub6)
var errs []error
if err4 != nil {
errs = append(errs, err4)
}
if err6 != nil {
errs = append(errs, err6)
}
ips, ttl, err := merge(option, rec4, rec6, errs...)
var rTTL uint32
if ttl > 0 {
rTTL = uint32(ttl)
} else if ttl == 0 && go_errors.Is(err, errRecordNotFound) {
rTTL = 0
} else { // edge case: where a fast rep's ttl expires during the rtt of a slower, parallel query
rTTL = 1
}
log.Record(&log.DNSLog{Server: s.getCacheController().name, Domain: fqdn, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return result{ips, rTTL, err}
}
func merge(option dns.IPOption, rec4 *IPRecord, rec6 *IPRecord, errs ...error) ([]net.IP, int32, error) {
var allIPs []net.IP
var rTTL int32 = dns.DefaultTTL
mergeReq := option.IPv4Enable && option.IPv6Enable
if option.IPv4Enable {
ips, ttl, err := rec4.getIPs() // it's safe
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if option.IPv6Enable {
ips, ttl, err := rec6.getIPs() // it's safe
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if len(allIPs) > 0 {
return allIPs, rTTL, nil
}
if len(errs) == 2 && go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
}
+64 -57
View File
@@ -4,10 +4,12 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
go_errors "errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
utls "github.com/refraction-networking/utls" utls "github.com/refraction-networking/utls"
@@ -19,7 +21,6 @@ import (
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/utils"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
@@ -37,7 +38,7 @@ type DoHNameServer struct {
} }
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving. // NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *DoHNameServer { func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer {
url.Scheme = "https" url.Scheme = "https"
mode := "DOH" mode := "DOH"
if dispatcher == nil { if dispatcher == nil {
@@ -45,7 +46,7 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, dis
} }
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c) errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
s := &DoHNameServer{ s := &DoHNameServer{
cacheController: NewCacheController(mode+"//"+url.Host, disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(mode+"//"+url.Host, disableCache),
dohURL: url.String(), dohURL: url.String(),
clientIP: clientIP, clientIP: clientIP,
} }
@@ -116,53 +117,22 @@ func (s *DoHNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *DoHNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *DoHNameServer) newReqID() uint16 { func (s *DoHNameServer) newReqID() uint16 {
return 0 return 0
} }
// getCacheController implements CachedNameserver. func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *DoHNameServer) getCacheController() *CacheController { errors.LogInfo(ctx, s.Name(), " querying: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. if s.Name()+"." == "DOH//"+domain {
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.")
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn) noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
if s.Name()+"." == "DOH//"+fqdn {
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
if noResponseErrCh != nil {
err := errors.New("tries to resolve itself!", s.Name())
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return return
} }
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467 // As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all // Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300)))) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -196,29 +166,23 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", fqdn) errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain)
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes()) resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", fqdn) errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain)
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(resp) rec, err := parseResponse(resp)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", fqdn) errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain)
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
@@ -232,8 +196,8 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
req.Header.Add("Accept", "application/dns-message") req.Header.Add("Accept", "application/dns-message")
req.Header.Add("Content-Type", "application/dns-message") req.Header.Add("Content-Type", "application/dns-message")
utils.TryDefaultHeadersWith(req.Header, "fetch")
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000))) req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
hc := s.httpClient hc := s.httpClient
@@ -252,6 +216,49 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl
return queryIP(ctx, s, domain, option) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }
+4 -4
View File
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: false, IPv4Enable: false,
-5
View File
@@ -20,11 +20,6 @@ func (FakeDNSServer) Name() string {
return "FakeDNS" return "FakeDNS"
} }
// IsDisableCache implements Server.
func (s *FakeDNSServer) IsDisableCache() bool {
return true
}
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) { func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) {
if f.fakeDNSEngine == nil { if f.fakeDNSEngine == nil {
return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError() return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError()
+1 -5
View File
@@ -18,6 +18,7 @@ type LocalNameServer struct {
// QueryIP implements Server. // QueryIP implements Server.
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) { func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) {
start := time.Now() start := time.Now()
ips, ttl, err = s.client.LookupIP(domain, option) ips, ttl, err = s.client.LookupIP(domain, option)
@@ -34,11 +35,6 @@ func (s *LocalNameServer) Name() string {
return "localhost" return "localhost"
} }
// IsDisableCache implements Server.
func (s *LocalNameServer) IsDisableCache() bool {
return true
}
// NewLocalNameServer creates localdns server object for directly lookup in system DNS. // NewLocalNameServer creates localdns server object for directly lookup in system DNS.
func NewLocalNameServer() *LocalNameServer { func NewLocalNameServer() *LocalNameServer {
errors.LogInfo(context.Background(), "DNS: created localhost client") errors.LogInfo(context.Background(), "DNS: created localhost client")
+66 -60
View File
@@ -4,11 +4,12 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
go_errors "errors"
"net/url" "net/url"
"sync" "sync"
"time" "time"
"github.com/apernet/quic-go" "github.com/quic-go/quic-go"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
@@ -36,7 +37,9 @@ type QUICNameServer struct {
} }
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving // NewQUICNameServer creates DNS-over-QUIC client object for local resolving
func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*QUICNameServer, error) { func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICNameServer, error) {
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
var err error var err error
port := net.Port(853) port := net.Port(853)
if url.Port() != "" { if url.Port() != "" {
@@ -48,49 +51,27 @@ func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveEx
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port) dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
s := &QUICNameServer{ s := &QUICNameServer{
cacheController: NewCacheController(url.String(), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(url.String(), disableCache),
destination: &dest, destination: &dest,
clientIP: clientIP, clientIP: clientIP,
} }
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
return s, nil return s, nil
} }
// Name implements Server. // Name returns client name
func (s *QUICNameServer) Name() string { func (s *QUICNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *QUICNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *QUICNameServer) newReqID() uint16 { func (s *QUICNameServer) newReqID() uint16 {
return 0 return 0
} }
// getCacheController implements CachedNameServer. func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheController } errors.LogInfo(ctx, s.Name(), " querying: ", domain)
// sendQuery implements CachedNameServer. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -122,9 +103,7 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query") errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
@@ -132,17 +111,13 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed") errors.LogErrorInner(ctx, err, "binary write failed")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
_, err = dnsReqBuf.Write(b.Bytes()) _, err = dnsReqBuf.Write(b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed") errors.LogErrorInner(ctx, err, "buffer write failed")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
b.Release() b.Release()
@@ -150,18 +125,14 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
conn, err := s.openStream(dnsCtx) conn, err := s.openStream(dnsCtx)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to open quic connection") errors.LogErrorInner(ctx, err, "failed to open quic connection")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
_, err = conn.Write(dnsReqBuf.Bytes()) _, err = conn.Write(dnsReqBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query") errors.LogErrorInner(ctx, err, "failed to send query")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
@@ -172,46 +143,81 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
n, err := respBuf.ReadFullFrom(conn, 2) n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
var length uint16 var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length") errors.LogErrorInner(ctx, err, "failed to parse response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
respBuf.Clear() respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length)) n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(respBuf.Bytes()) rec, err := parseResponse(respBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle response") errors.LogErrorInner(ctx, err, "failed to handle response")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
// QueryIP implements Server. // QueryIP is called from dns.Server->queryIPTimeout
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }
func isActive(s *quic.Conn) bool { func isActive(s *quic.Conn) bool {
+3 -3
View File
@@ -16,7 +16,7 @@ import (
func TestQUICNameServer(t *testing.T) { func TestQUICNameServer(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -43,7 +43,7 @@ func TestQUICNameServer(t *testing.T) {
func TestQUICNameServerWithIPv4Override(t *testing.T) { func TestQUICNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -66,7 +66,7 @@ func TestQUICNameServerWithIPv4Override(t *testing.T) {
func TestQUICNameServerWithIPv6Override(t *testing.T) { func TestQUICNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
+66 -64
View File
@@ -4,12 +4,14 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
go_errors "errors"
"net/url" "net/url"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "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"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
@@ -32,10 +34,10 @@ type TCPNameServer struct {
func NewTCPNameServer( func NewTCPNameServer(
url *url.URL, url *url.URL,
dispatcher routing.Dispatcher, dispatcher routing.Dispatcher,
disableCache bool, serveStale bool, serveExpiredTTL uint32, disableCache bool,
clientIP net.IP, clientIP net.IP,
) (*TCPNameServer, error) { ) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCP", disableCache, serveStale, serveExpiredTTL, clientIP) s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,13 +54,12 @@ func NewTCPNameServer(
), nil ), nil
} }
errors.LogInfo(context.Background(), "DNS: created TCP client initialized for ", url.String())
return s, nil return s, nil
} }
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving // NewTCPLocalNameServer creates DNS over TCP client object for local resolving
func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) { func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", disableCache, serveStale, serveExpiredTTL, clientIP) s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -67,11 +68,10 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, ser
return internet.DialSystem(ctx, *s.destination, nil) return internet.DialSystem(ctx, *s.destination, nil)
} }
errors.LogInfo(context.Background(), "DNS: created Local TCP client initialized for ", url.String())
return s, nil return s, nil
} }
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) { func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
port := net.Port(53) port := net.Port(53)
if url.Port() != "" { if url.Port() != "" {
var err error var err error
@@ -82,7 +82,7 @@ func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStal
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port) dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
s := &TCPNameServer{ s := &TCPNameServer{
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache),
destination: &dest, destination: &dest,
clientIP: clientIP, clientIP: clientIP,
} }
@@ -95,37 +95,14 @@ func (s *TCPNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *TCPNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *TCPNameServer) newReqID() uint16 { func (s *TCPNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1)) return uint16(atomic.AddUint32(&s.reqID, 1))
} }
// getCacheController implements CachedNameserver. func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *TCPNameServer) getCacheController() *CacheController { errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -154,18 +131,14 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query") errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
conn, err := s.dial(dnsCtx) conn, err := s.dial(dnsCtx)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial namesever") errors.LogErrorInner(ctx, err, "failed to dial namesever")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
defer conn.Close() defer conn.Close()
@@ -173,17 +146,13 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed") errors.LogErrorInner(ctx, err, "binary write failed")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
_, err = dnsReqBuf.Write(b.Bytes()) _, err = dnsReqBuf.Write(b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed") errors.LogErrorInner(ctx, err, "buffer write failed")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
b.Release() b.Release()
@@ -191,9 +160,7 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
_, err = conn.Write(dnsReqBuf.Bytes()) _, err = conn.Write(dnsReqBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query") errors.LogErrorInner(ctx, err, "failed to send query")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
dnsReqBuf.Release() dnsReqBuf.Release()
@@ -203,45 +170,80 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
n, err := respBuf.ReadFullFrom(conn, 2) n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
var length uint16 var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length") errors.LogErrorInner(ctx, err, "failed to parse response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
respBuf.Clear() respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length)) n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(respBuf.Bytes()) rec, err := parseResponse(respBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response") errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
if noResponseErrCh != nil { noResponseErrCh <- err
noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }
+4 -4
View File
@@ -16,7 +16,7 @@ import (
func TestTCPLocalNameServer(t *testing.T) { func TestTCPLocalNameServer(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -33,7 +33,7 @@ func TestTCPLocalNameServer(t *testing.T) {
func TestTCPLocalNameServerWithCache(t *testing.T) { func TestTCPLocalNameServerWithCache(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -61,7 +61,7 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
func TestTCPLocalNameServerWithIPv4Override(t *testing.T) { func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -85,7 +85,7 @@ func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
func TestTCPLocalNameServerWithIPv6Override(t *testing.T) { func TestTCPLocalNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
+59 -41
View File
@@ -2,6 +2,7 @@ package dns
import ( import (
"context" "context"
go_errors "errors"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -9,6 +10,7 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "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"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp" udp_proto "github.com/xtls/xray-core/common/protocol/udp"
@@ -37,14 +39,14 @@ type udpDnsRequest struct {
} }
// NewClassicNameServer creates udp server object for remote resolving. // NewClassicNameServer creates udp server object for remote resolving.
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *ClassicNameServer { func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer {
// default to 53 if unspecific // default to 53 if unspecific
if address.Port == 0 { if address.Port == 0 {
address.Port = net.Port(53) address.Port = net.Port(53)
} }
s := &ClassicNameServer{ s := &ClassicNameServer{
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache),
address: &address, address: &address,
requests: make(map[uint16]*udpDnsRequest), requests: make(map[uint16]*udpDnsRequest),
clientIP: clientIP, clientIP: clientIP,
@@ -54,7 +56,6 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
Execute: s.RequestsCleanup, Execute: s.RequestsCleanup,
} }
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr()) errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
return s return s
} }
@@ -64,11 +65,6 @@ func (s *ClassicNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *ClassicNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
// RequestsCleanup clears expired items from cache // RequestsCleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error { func (s *ClassicNameServer) RequestsCleanup() error {
now := time.Now() now := time.Now()
@@ -98,7 +94,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
ipRec, err := parseResponse(payload.Bytes()) ipRec, err := parseResponse(payload.Bytes())
payload.Release() payload.Release()
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, s.Name(), " fail to parse responded DNS udp") errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
return return
} }
@@ -111,7 +107,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
} }
s.Unlock() s.Unlock()
if !ok { if !ok {
errors.LogErrorInner(ctx, err, s.Name(), " cannot find the pending request") errors.LogError(ctx, s.Name(), " cannot find the pending request")
return return
} }
@@ -131,12 +127,14 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
newReq.msg = &newMsg newReq.msg = &newMsg
s.addPendingRequest(&newReq) s.addPendingRequest(&newReq)
b, _ := dns.PackMessage(newReq.msg) b, _ := dns.PackMessage(newReq.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b) s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
return return
} }
} }
s.cacheController.updateRecord(&req.dnsRequest, ipRec) s.cacheController.updateIP(&req.dnsRequest, ipRec)
} }
func (s *ClassicNameServer) newReqID() uint16 { func (s *ClassicNameServer) newReqID() uint16 {
@@ -152,28 +150,10 @@ func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
common.Must(s.requestsCleanup.Start()) common.Must(s.requestsCleanup.Start())
} }
// getCacheController implements CachedNameserver. func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
func (s *ClassicNameServer) getCacheController() *CacheController { errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
for _, req := range reqs { for _, req := range reqs {
udpReq := &udpDnsRequest{ udpReq := &udpDnsRequest{
@@ -181,19 +161,57 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<
ctx: ctx, ctx: ctx,
} }
s.addPendingRequest(udpReq) s.addPendingRequest(udpReq)
b, err := dns.PackMessage(req.msg) b, _ := dns.PackMessage(req.msg)
if err != nil { copyDest := net.UDPDestination(s.address.Address, s.address.Port)
errors.LogErrorInner(ctx, err, "failed to pack dns query") b.UDP = &copyDest
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b) s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
} }
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
} }
-198
View File
@@ -1,198 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/geodata/config.proto
package geodata
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Asset struct {
state protoimpl.MessageState `protogen:"open.v1"`
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
File string `protobuf:"bytes,2,opt,name=file,proto3" json:"file,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Asset) Reset() {
*x = Asset{}
mi := &file_app_geodata_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Asset) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Asset) ProtoMessage() {}
func (x *Asset) ProtoReflect() protoreflect.Message {
mi := &file_app_geodata_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Asset.ProtoReflect.Descriptor instead.
func (*Asset) Descriptor() ([]byte, []int) {
return file_app_geodata_config_proto_rawDescGZIP(), []int{0}
}
func (x *Asset) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *Asset) GetFile() string {
if x != nil {
return x.File
}
return ""
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
Cron string `protobuf:"bytes,1,opt,name=cron,proto3" json:"cron,omitempty"`
Outbound string `protobuf:"bytes,2,opt,name=outbound,proto3" json:"outbound,omitempty"`
Assets []*Asset `protobuf:"bytes,3,rep,name=assets,proto3" json:"assets,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_geodata_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_geodata_config_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_geodata_config_proto_rawDescGZIP(), []int{1}
}
func (x *Config) GetCron() string {
if x != nil {
return x.Cron
}
return ""
}
func (x *Config) GetOutbound() string {
if x != nil {
return x.Outbound
}
return ""
}
func (x *Config) GetAssets() []*Asset {
if x != nil {
return x.Assets
}
return nil
}
var File_app_geodata_config_proto protoreflect.FileDescriptor
const file_app_geodata_config_proto_rawDesc = "" +
"\n" +
"\x18app/geodata/config.proto\x12\x10xray.app.geodata\"-\n" +
"\x05Asset\x12\x10\n" +
"\x03url\x18\x01 \x01(\tR\x03url\x12\x12\n" +
"\x04file\x18\x02 \x01(\tR\x04file\"i\n" +
"\x06Config\x12\x12\n" +
"\x04cron\x18\x01 \x01(\tR\x04cron\x12\x1a\n" +
"\boutbound\x18\x02 \x01(\tR\boutbound\x12/\n" +
"\x06assets\x18\x03 \x03(\v2\x17.xray.app.geodata.AssetR\x06assetsBR\n" +
"\x14com.xray.app.geodataP\x01Z%github.com/xtls/xray-core/app/geodata\xaa\x02\x10Xray.App.Geodatab\x06proto3"
var (
file_app_geodata_config_proto_rawDescOnce sync.Once
file_app_geodata_config_proto_rawDescData []byte
)
func file_app_geodata_config_proto_rawDescGZIP() []byte {
file_app_geodata_config_proto_rawDescOnce.Do(func() {
file_app_geodata_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_geodata_config_proto_rawDesc), len(file_app_geodata_config_proto_rawDesc)))
})
return file_app_geodata_config_proto_rawDescData
}
var file_app_geodata_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_app_geodata_config_proto_goTypes = []any{
(*Asset)(nil), // 0: xray.app.geodata.Asset
(*Config)(nil), // 1: xray.app.geodata.Config
}
var file_app_geodata_config_proto_depIdxs = []int32{
0, // 0: xray.app.geodata.Config.assets:type_name -> xray.app.geodata.Asset
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_app_geodata_config_proto_init() }
func file_app_geodata_config_proto_init() {
if File_app_geodata_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_geodata_config_proto_rawDesc), len(file_app_geodata_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_geodata_config_proto_goTypes,
DependencyIndexes: file_app_geodata_config_proto_depIdxs,
MessageInfos: file_app_geodata_config_proto_msgTypes,
}.Build()
File_app_geodata_config_proto = out.File
file_app_geodata_config_proto_goTypes = nil
file_app_geodata_config_proto_depIdxs = nil
}
-21
View File
@@ -1,21 +0,0 @@
syntax = "proto3";
package xray.app.geodata;
option csharp_namespace = "Xray.App.Geodata";
option go_package = "github.com/xtls/xray-core/app/geodata";
option java_package = "com.xray.app.geodata";
option java_multiple_files = true;
message Asset {
string url = 1;
string file = 2;
}
message Config {
string cron = 1;
string outbound = 2;
repeated Asset assets = 3;
}
-346
View File
@@ -1,346 +0,0 @@
package geodata
import (
"context"
"crypto/tls"
go_errors "errors"
"io"
"net/http"
"os"
"path/filepath"
"time"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet/tagged"
"golang.org/x/net/http2"
)
const idleTimeout = 30 * time.Second
type stage struct {
target string
temp string
}
type downloader struct {
ctx context.Context
httpClient *http.Client
httpsClient *http.Client
}
type idleConn struct {
net.Conn
}
func (c *idleConn) Read(b []byte) (int, error) {
t := time.AfterFunc(idleTimeout, func() {
_ = c.Close()
})
n, err := c.Conn.Read(b)
if !t.Stop() {
_ = c.Close()
return n, errors.New("connection idle timeout")
}
return n, err
}
func (c *idleConn) Write(b []byte) (int, error) {
return c.Conn.Write(b)
}
func newDownloader(ctx context.Context, dispatcher routing.Dispatcher, outbound string) *downloader {
return &downloader{
ctx: ctx,
httpClient: newClient(ctx, dispatcher, outbound, false),
httpsClient: newClient(ctx, dispatcher, outbound, true),
}
}
func newClient(baseCtx context.Context, dispatcher routing.Dispatcher, outbound string, isHTTPS bool) *http.Client {
dial := func(ctx context.Context, network, address string) (net.Conn, error) {
var conn net.Conn
err := task.Run(ctx, func() error {
if tagged.Dialer == nil {
return errors.New("tagged dialer is not initialized")
}
dest, err := net.ParseDestination(network + ":" + address)
if err != nil {
return errors.New("cannot understand address").Base(err)
}
c, err := tagged.Dialer(baseCtx, dispatcher, dest, outbound)
if err != nil {
return errors.New("cannot dial remote address ", dest).Base(err)
}
conn = c
return nil
})
if err != nil {
return nil, errors.New("cannot finish connection").Base(err)
}
return &idleConn{
Conn: conn,
}, nil
}
if isHTTPS {
return &http.Client{
Transport: &http2.Transport{
DialTLSContext: func(ctx context.Context, network string, address string, cfg *tls.Config) (net.Conn, error) {
conn, err := dial(ctx, network, address)
if err != nil {
return nil, err
}
host, _, _ := net.SplitHostPort(address)
tlsConn := utls.UClient(conn, &utls.Config{ServerName: host}, utls.HelloChrome_Auto)
handshakeCtx, cancel := context.WithTimeout(ctx, idleTimeout)
defer cancel()
if err := tlsConn.HandshakeContext(handshakeCtx); err != nil {
conn.Close()
return nil, err
}
return tlsConn, nil
},
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if req.URL.Scheme != "https" {
return errors.New("redirected to non-https URL: ", req.URL.String())
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
}
} else {
return &http.Client{
Transport: &http.Transport{
Proxy: nil,
DisableKeepAlives: true,
DialContext: dial,
ResponseHeaderTimeout: idleTimeout,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if req.URL.Scheme != "https" {
return errors.New("redirected to non-https URL: ", req.URL.String())
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
}
}
}
func (d *downloader) download(assets []*Asset) ([]stage, error) {
staged := make([]stage, 0, len(assets))
for _, asset := range assets {
stage, err := d.downloadOne(asset)
if err != nil {
clean(staged)
return nil, err
}
staged = append(staged, stage)
}
return staged, nil
}
func (d *downloader) downloadOne(asset *Asset) (stage, error) {
target, err := filesystem.ResolveAsset(asset.File)
if err != nil {
return stage{}, err
}
errors.LogInfo(d.ctx, "downloading geodata asset from ", asset.Url, " to ", target)
temp, err := tempFile(target, ".tmp")
if err != nil {
return stage{}, err
}
tempName := temp.Name()
keepTemp := false
defer func() {
if !keepTemp {
os.Remove(tempName)
}
}()
if err := d.fetch(asset.Url, temp); err != nil {
temp.Close()
return stage{}, err
}
if err := temp.Chmod(0o644); err != nil {
temp.Close()
return stage{}, err
}
if err := temp.Close(); err != nil {
return stage{}, err
}
keepTemp = true
return stage{
target: target,
temp: tempName,
}, nil
}
func (d *downloader) fetch(rawURL string, writer io.Writer) error {
req, err := http.NewRequestWithContext(d.ctx, http.MethodGet, rawURL, nil)
if err != nil {
return err
}
utils.TryDefaultHeadersWith(req.Header, "nav")
var client *http.Client
if req.URL.Scheme == "https" {
client = d.httpsClient
} else {
client = d.httpClient
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
io.Copy(io.Discard, resp.Body)
return errors.New("unexpected status code: ", resp.StatusCode)
}
n, err := io.Copy(writer, resp.Body)
if err != nil {
return err
}
if n == 0 {
return errors.New("empty response body")
}
return nil
}
func clean(assets []stage) {
for _, asset := range assets {
if asset.temp != "" {
os.Remove(asset.temp)
}
}
}
type tx struct {
swaps []swap
}
type swap struct {
target string
backup string
hadOriginal bool
}
func swapAll(assets []stage) (*tx, error) {
t := &tx{}
for _, asset := range assets {
s, err := swapOne(asset)
if err != nil {
return nil, errors.Combine(err, t.rollback())
}
t.swaps = append(t.swaps, s)
}
return t, nil
}
func swapOne(asset stage) (swap, error) {
backup, err := backupFile(asset.target)
if err != nil {
return swap{}, err
}
s := swap{
target: asset.target,
backup: backup,
}
if err := os.Rename(asset.target, backup); err != nil {
if !go_errors.Is(err, os.ErrNotExist) {
return swap{}, err
}
if err := os.Remove(backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
return swap{}, err
}
} else {
s.hadOriginal = true
}
if err := os.Rename(asset.temp, asset.target); err != nil {
if s.hadOriginal {
if restoreErr := os.Rename(backup, asset.target); restoreErr != nil {
return swap{}, errors.Combine(err, restoreErr)
}
}
return swap{}, err
}
return s, nil
}
func (t *tx) rollback() error {
var errs []error
for i := len(t.swaps) - 1; i >= 0; i-- {
if err := t.swaps[i].rollback(); err != nil {
errs = append(errs, err)
}
}
return errors.Combine(errs...)
}
func (s swap) rollback() error {
var errs []error
if err := os.Remove(s.target); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
if s.hadOriginal {
if err := os.Rename(s.backup, s.target); err != nil {
errs = append(errs, err)
}
} else if err := os.Remove(s.backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
return errors.Combine(errs...)
}
func (t *tx) commit() error {
var errs []error
for _, swap := range t.swaps {
if err := os.Remove(swap.backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
}
return errors.Combine(errs...)
}
func tempFile(target string, suffix string) (*os.File, error) {
dir := filepath.Dir(target)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
return os.CreateTemp(dir, "."+filepath.Base(target)+".*"+suffix)
}
func backupFile(target string) (string, error) {
file, err := tempFile(target, ".bak")
if err != nil {
return "", err
}
name := file.Name()
if err := file.Close(); err != nil {
os.Remove(name)
return "", err
}
if err := os.Remove(name); err != nil {
return "", err
}
return name, nil
}
-134
View File
@@ -1,134 +0,0 @@
package geodata
import (
"context"
"sync"
"github.com/robfig/cron/v3"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
commongeodata "github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/routing"
)
type Instance struct {
assets []*Asset
downloader *downloader
tasker *cron.Cron
mu sync.Mutex
running bool
}
func New(ctx context.Context, config *Config) (*Instance, error) {
if config.Cron == "" {
return &Instance{}, nil
}
g := &Instance{
assets: config.Assets,
}
if len(g.assets) > 0 {
var dispatcher routing.Dispatcher
if err := core.RequireFeatures(ctx, func(d routing.Dispatcher) {
dispatcher = d
}); err != nil {
return nil, errors.New("failed to get dispatcher for geodata downloader").Base(err)
}
g.downloader = newDownloader(ctx, dispatcher, config.Outbound)
}
g.tasker = cron.New(
cron.WithChain(cron.SkipIfStillRunning(cron.DiscardLogger)),
cron.WithLogger(cron.DiscardLogger),
)
if _, err := g.tasker.AddFunc(config.Cron, g.execute); err != nil {
return nil, errors.New("invalid geodata cron").Base(err)
}
errors.LogInfo(ctx, "scheduled geodata reload with cron: ", config.Cron)
return g, nil
}
func (g *Instance) execute() {
var err error
if g.downloader != nil {
err = g.reloadWithUpdate()
} else {
err = reload()
}
if err != nil {
errors.LogErrorInner(context.Background(), err, "scheduled geodata reload failed")
}
}
func (g *Instance) reloadWithUpdate() error {
staged, err := g.downloader.download(g.assets)
if err != nil {
return err
}
defer clean(staged)
tx, err := swapAll(staged)
if err != nil {
return err
}
if err := reload(); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to reload geodata after downloading assets, rolling back")
rollbackErr := tx.rollback()
return errors.Combine(err, rollbackErr)
}
return tx.commit()
}
func reload() error {
return errors.Combine(commongeodata.IPReg.Reload(), commongeodata.DomainReg.Reload())
}
func (g *Instance) Type() interface{} {
return (*Instance)(nil)
}
func (g *Instance) Start() error {
g.mu.Lock()
defer g.mu.Unlock()
if g.running {
return nil
}
if g.tasker != nil {
g.tasker.Start()
}
g.running = true
return nil
}
func (g *Instance) Close() error {
g.mu.Lock()
defer g.mu.Unlock()
if !g.running {
return nil
}
if g.tasker != nil {
<-g.tasker.Stop().Done()
}
g.running = false
return nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
return New(ctx, cfg.(*Config))
}))
}
+36 -21
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/log/command/config.proto // source: app/log/command/config.proto
package command package command
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,9 +21,9 @@ const (
) )
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -58,9 +57,9 @@ func (*Config) Descriptor() ([]byte, []int) {
} }
type RestartLoggerRequest struct { type RestartLoggerRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *RestartLoggerRequest) Reset() { func (x *RestartLoggerRequest) Reset() {
@@ -94,9 +93,9 @@ func (*RestartLoggerRequest) Descriptor() ([]byte, []int) {
} }
type RestartLoggerResponse struct { type RestartLoggerResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *RestartLoggerResponse) Reset() { func (x *RestartLoggerResponse) Reset() {
@@ -131,24 +130,39 @@ func (*RestartLoggerResponse) Descriptor() ([]byte, []int) {
var File_app_log_command_config_proto protoreflect.FileDescriptor var File_app_log_command_config_proto protoreflect.FileDescriptor
const file_app_log_command_config_proto_rawDesc = "" + var file_app_log_command_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
"\x1capp/log/command/config.proto\x12\x14xray.app.log.command\"\b\n" + 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
"\x06Config\"\x16\n" + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6d,
"\x14RestartLoggerRequest\"\x17\n" + 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16,
"\x15RestartLoggerResponse2{\n" + 0x0a, 0x14, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52,
"\rLoggerService\x12j\n" + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72,
"\rRestartLogger\x12*.xray.app.log.command.RestartLoggerRequest\x1a+.xray.app.log.command.RestartLoggerResponse\"\x00B^\n" + 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
"\x18com.xray.app.log.commandP\x01Z)github.com/xtls/xray-core/app/log/command\xaa\x02\x14Xray.App.Log.Commandb\x06proto3" 0x7b, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x6a, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65,
0x72, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74,
0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x5e, 0x0a, 0x18,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x29, 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, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_log_command_config_proto_rawDescOnce sync.Once file_app_log_command_config_proto_rawDescOnce sync.Once
file_app_log_command_config_proto_rawDescData []byte file_app_log_command_config_proto_rawDescData = file_app_log_command_config_proto_rawDesc
) )
func file_app_log_command_config_proto_rawDescGZIP() []byte { func file_app_log_command_config_proto_rawDescGZIP() []byte {
file_app_log_command_config_proto_rawDescOnce.Do(func() { file_app_log_command_config_proto_rawDescOnce.Do(func() {
file_app_log_command_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc))) file_app_log_command_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_log_command_config_proto_rawDescData)
}) })
return file_app_log_command_config_proto_rawDescData return file_app_log_command_config_proto_rawDescData
} }
@@ -178,7 +192,7 @@ func file_app_log_command_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc)), RawDescriptor: file_app_log_command_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 3, NumMessages: 3,
NumExtensions: 0, NumExtensions: 0,
@@ -189,6 +203,7 @@ func file_app_log_command_config_proto_init() {
MessageInfos: file_app_log_command_config_proto_msgTypes, MessageInfos: file_app_log_command_config_proto_msgTypes,
}.Build() }.Build()
File_app_log_command_config_proto = out.File File_app_log_command_config_proto = out.File
file_app_log_command_config_proto_rawDesc = nil
file_app_log_command_config_proto_goTypes = nil file_app_log_command_config_proto_goTypes = nil
file_app_log_command_config_proto_depIdxs = nil file_app_log_command_config_proto_depIdxs = nil
} }
+4 -4
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.0 // - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.5 // - protoc v5.28.2
// source: app/log/command/config.proto // source: app/log/command/config.proto
package command package command
@@ -63,7 +63,7 @@ type LoggerServiceServer interface {
type UnimplementedLoggerServiceServer struct{} type UnimplementedLoggerServiceServer struct{}
func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) { func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RestartLogger not implemented") return nil, status.Errorf(codes.Unimplemented, "method RestartLogger not implemented")
} }
func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {} func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {}
func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {} func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {}
@@ -76,7 +76,7 @@ type UnsafeLoggerServiceServer interface {
} }
func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) { func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) {
// If the following call panics, it indicates UnimplementedLoggerServiceServer was // If the following call pancis, it indicates UnimplementedLoggerServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
+53 -32
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/log/config.proto // source: app/log/config.proto
package log package log
@@ -12,7 +12,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -75,16 +74,17 @@ func (LogType) EnumDescriptor() ([]byte, []int) {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
ErrorLogType LogType `protobuf:"varint,1,opt,name=error_log_type,json=errorLogType,proto3,enum=xray.app.log.LogType" json:"error_log_type,omitempty"`
ErrorLogLevel log.Severity `protobuf:"varint,2,opt,name=error_log_level,json=errorLogLevel,proto3,enum=xray.common.log.Severity" json:"error_log_level,omitempty"`
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
MaskAddress string `protobuf:"bytes,7,opt,name=mask_address,json=maskAddress,proto3" json:"mask_address,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ErrorLogType LogType `protobuf:"varint,1,opt,name=error_log_type,json=errorLogType,proto3,enum=xray.app.log.LogType" json:"error_log_type,omitempty"`
ErrorLogLevel log.Severity `protobuf:"varint,2,opt,name=error_log_level,json=errorLogLevel,proto3,enum=xray.common.log.Severity" json:"error_log_level,omitempty"`
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
MaskAddress string `protobuf:"bytes,7,opt,name=mask_address,json=maskAddress,proto3" json:"mask_address,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -168,32 +168,52 @@ func (x *Config) GetMaskAddress() string {
var File_app_log_config_proto protoreflect.FileDescriptor var File_app_log_config_proto protoreflect.FileDescriptor
const file_app_log_config_proto_rawDesc = "" + var file_app_log_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
"\x14app/log/config.proto\x12\fxray.app.log\x1a\x14common/log/log.proto\"\xde\x02\n" + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
"\x06Config\x12;\n" + 0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67,
"\x0eerror_log_type\x18\x01 \x01(\x0e2\x15.xray.app.log.LogTypeR\ferrorLogType\x12A\n" + 0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xde, 0x02, 0x0a, 0x06, 0x43,
"\x0ferror_log_level\x18\x02 \x01(\x0e2\x19.xray.common.log.SeverityR\rerrorLogLevel\x12$\n" + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
"\x0eerror_log_path\x18\x03 \x01(\tR\ferrorLogPath\x12=\n" + 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
"\x0faccess_log_type\x18\x04 \x01(\x0e2\x15.xray.app.log.LogTypeR\raccessLogType\x12&\n" + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
"\x0faccess_log_path\x18\x05 \x01(\tR\raccessLogPath\x12$\n" + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67, 0x54, 0x79,
"\x0eenable_dns_log\x18\x06 \x01(\bR\fenableDnsLog\x12!\n" + 0x70, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f,
"\fmask_address\x18\a \x01(\tR\vmaskAddress*5\n" + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72,
"\aLogType\x12\b\n" + 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x53, 0x65,
"\x04None\x10\x00\x12\v\n" + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67,
"\aConsole\x10\x01\x12\b\n" + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
"\x04File\x10\x02\x12\t\n" + 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65,
"\x05Event\x10\x03BF\n" + 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3d, 0x0a, 0x0f, 0x61,
"\x10com.xray.app.logP\x01Z!github.com/xtls/xray-core/app/log\xaa\x02\fXray.App.Logb\x06proto3" 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, 0x61, 0x63, 0x63,
0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x63,
0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61,
0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x6e, 0x73,
0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x73, 0x6b,
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
0x6d, 0x61, 0x73, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2a, 0x35, 0x0a, 0x07, 0x4c,
0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a, 0x21, 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, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var ( var (
file_app_log_config_proto_rawDescOnce sync.Once file_app_log_config_proto_rawDescOnce sync.Once
file_app_log_config_proto_rawDescData []byte file_app_log_config_proto_rawDescData = file_app_log_config_proto_rawDesc
) )
func file_app_log_config_proto_rawDescGZIP() []byte { func file_app_log_config_proto_rawDescGZIP() []byte {
file_app_log_config_proto_rawDescOnce.Do(func() { file_app_log_config_proto_rawDescOnce.Do(func() {
file_app_log_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc))) file_app_log_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_log_config_proto_rawDescData)
}) })
return file_app_log_config_proto_rawDescData return file_app_log_config_proto_rawDescData
} }
@@ -225,7 +245,7 @@ func file_app_log_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc)), RawDescriptor: file_app_log_config_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 1, NumMessages: 1,
NumExtensions: 0, NumExtensions: 0,
@@ -237,6 +257,7 @@ func file_app_log_config_proto_init() {
MessageInfos: file_app_log_config_proto_msgTypes, MessageInfos: file_app_log_config_proto_msgTypes,
}.Build() }.Build()
File_app_log_config_proto = out.File File_app_log_config_proto = out.File
file_app_log_config_proto_rawDesc = nil
file_app_log_config_proto_goTypes = nil file_app_log_config_proto_goTypes = nil
file_app_log_config_proto_depIdxs = nil file_app_log_config_proto_depIdxs = nil
} }
+33 -83
View File
@@ -2,9 +2,8 @@ package log
import ( import (
"context" "context"
"net" "fmt"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
@@ -21,23 +20,14 @@ type Instance struct {
errorLogger log.Handler errorLogger log.Handler
active bool active bool
dns bool dns bool
mask4 int
mask6 int
} }
// New creates a new log.Instance based on the given config. // New creates a new log.Instance based on the given config.
func New(ctx context.Context, config *Config) (*Instance, error) { func New(ctx context.Context, config *Config) (*Instance, error) {
m4, m6, err := ParseMaskAddress(config.MaskAddress)
if err != nil {
return nil, err
}
g := &Instance{ g := &Instance{
config: config, config: config,
active: false, active: false,
dns: config.EnableDnsLog, dns: config.EnableDnsLog,
mask4: m4,
mask6: m6,
} }
log.RegisterHandler(g) log.RegisterHandler(g)
@@ -114,11 +104,7 @@ func (g *Instance) Handle(msg log.Message) {
var Msg log.Message var Msg log.Message
if g.config.MaskAddress != "" { if g.config.MaskAddress != "" {
Msg = &MaskedMsgWrapper{ Msg = &MaskedMsgWrapper{Message: msg, config: g.config}
Message: msg,
Mask4: g.mask4,
Mask6: g.mask6,
}
} else { } else {
Msg = msg Msg = msg
} }
@@ -163,87 +149,51 @@ func (g *Instance) Close() error {
return nil return nil
} }
func ParseMaskAddress(c string) (int, int, error) {
var m4, m6 int
switch c {
case "half":
m4, m6 = 16, 32
case "quarter":
m4, m6 = 8, 16
case "full":
m4, m6 = 0, 0
case "":
// do nothing
default:
if parts := strings.Split(c, "+"); len(parts) > 0 {
if len(parts) >= 1 && parts[0] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[0], "/"))
if err != nil {
return 32, 128, err
}
m4 = i
}
if len(parts) >= 2 && parts[1] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[1], "/"))
if err != nil {
return 32, 128, err
}
m6 = i
}
}
}
if m4%8 != 0 || m4 > 32 || m4 < 0 {
return 32, 128, errors.New("Log Mask: ipv4 mask must be divisible by 8 and between 0-32")
}
return m4, m6, nil
}
// MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log. // MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log.
type MaskedMsgWrapper struct { type MaskedMsgWrapper struct {
log.Message log.Message
Mask4 int config *Config
Mask6 int
} }
var (
ipv4Regex = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
ipv6Regex = regexp.MustCompile(`(?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7}`)
)
func (m *MaskedMsgWrapper) String() string { func (m *MaskedMsgWrapper) String() string {
str := m.Message.String() str := m.Message.String()
// Process ipv4 ipv4Regex := regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(s string) string { ipv6Regex := regexp.MustCompile(`((?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7})(?:[\/\\%](\d{1,3}))?`)
if m.Mask4 == 32 {
return s
}
if m.Mask4 == 0 {
return "[Masked IPv4]"
}
parts := strings.Split(s, ".") // Process ipv4
for i := m.Mask4 / 8; i < 4; i++ { maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(ip string) string {
parts[i] = "*" parts := strings.Split(ip, ".")
switch m.config.MaskAddress {
case "half":
return fmt.Sprintf("%s.%s.*.*", parts[0], parts[1])
case "quarter":
return fmt.Sprintf("%s.*.*.*", parts[0])
case "full":
return "[Masked IPv4]"
default:
return ip
} }
return strings.Join(parts, ".")
}) })
// process ipv6 // process ipv6
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(s string) string { maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(ip string) string {
if m.Mask6 == 128 { parts := strings.Split(ip, ":")
return s switch m.config.MaskAddress {
case "half":
if len(parts) >= 2 {
return fmt.Sprintf("%s:%s::/32", parts[0], parts[1])
}
case "quarter":
if len(parts) >= 1 {
return fmt.Sprintf("%s::/16", parts[0])
}
case "full":
return "Masked IPv6" // Do not use [Masked IPv6] like ipv4, or you will get "[[Masked IPv6]]" (v6 address already has [])
default:
return ip
} }
if m.Mask6 == 0 { return ip
return "Masked IPv6"
}
ip := net.ParseIP(s)
if ip == nil {
return s
}
return ip.Mask(net.CIDRMask(m.Mask6, 128)).String() + "/" + strconv.Itoa(m.Mask6)
}) })
return maskedMsg return maskedMsg
-37
View File
@@ -2,7 +2,6 @@ package log_test
import ( import (
"context" "context"
"net"
"testing" "testing"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
@@ -51,39 +50,3 @@ func TestCustomLogHandler(t *testing.T) {
common.Must(logger.Close()) common.Must(logger.Close())
} }
func TestMaskAddress(t *testing.T) {
m4, m6, err := log.ParseMaskAddress("half")
if err != nil {
t.Fatal(err)
}
maskedAddr := log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45::/32" {
t.Fatal("expected '11:45::/32', but actually", maskedAddr.String())
}
m4, m6, err = log.ParseMaskAddress("/16+/64")
if err != nil {
t.Fatal(err)
}
maskedAddr = log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45:14:19::/64" {
t.Fatal("expected '11:45:14:19::/64', but actually", maskedAddr.String())
}
}
+26 -18
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/metrics/config.proto // source: app/metrics/config.proto
package metrics package metrics
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -23,12 +22,13 @@ const (
// Config is the settings for metrics. // Config is the settings for metrics.
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
// Tag of the outbound handler that handles metrics http connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache 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"`
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -77,22 +77,29 @@ func (x *Config) GetListen() string {
var File_app_metrics_config_proto protoreflect.FileDescriptor var File_app_metrics_config_proto protoreflect.FileDescriptor
const file_app_metrics_config_proto_rawDesc = "" + var file_app_metrics_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f,
"\x18app/metrics/config.proto\x12\x10xray.app.metrics\"2\n" + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
"\x06Config\x12\x10\n" + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x32, 0x0a, 0x06,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
"\x06listen\x18\x02 \x01(\tR\x06listenBR\n" + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74,
"\x14com.xray.app.metricsP\x01Z%github.com/xtls/xray-core/app/metrics\xaa\x02\x10Xray.App.Metricsb\x06proto3" 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,
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 ( var (
file_app_metrics_config_proto_rawDescOnce sync.Once file_app_metrics_config_proto_rawDescOnce sync.Once
file_app_metrics_config_proto_rawDescData []byte file_app_metrics_config_proto_rawDescData = file_app_metrics_config_proto_rawDesc
) )
func file_app_metrics_config_proto_rawDescGZIP() []byte { func file_app_metrics_config_proto_rawDescGZIP() []byte {
file_app_metrics_config_proto_rawDescOnce.Do(func() { file_app_metrics_config_proto_rawDescOnce.Do(func() {
file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_metrics_config_proto_rawDesc), len(file_app_metrics_config_proto_rawDesc))) file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_metrics_config_proto_rawDescData)
}) })
return file_app_metrics_config_proto_rawDescData return file_app_metrics_config_proto_rawDescData
} }
@@ -118,7 +125,7 @@ func file_app_metrics_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_metrics_config_proto_rawDesc), len(file_app_metrics_config_proto_rawDesc)), RawDescriptor: file_app_metrics_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 1, NumMessages: 1,
NumExtensions: 0, NumExtensions: 0,
@@ -129,6 +136,7 @@ func file_app_metrics_config_proto_init() {
MessageInfos: file_app_metrics_config_proto_msgTypes, MessageInfos: file_app_metrics_config_proto_msgTypes,
}.Build() }.Build()
File_app_metrics_config_proto = out.File 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_goTypes = nil
file_app_metrics_config_proto_depIdxs = nil file_app_metrics_config_proto_depIdxs = nil
} }
+64 -150
View File
@@ -2,18 +2,16 @@ package metrics
import ( import (
"context" "context"
"encoding/json"
stderrors "errors"
"expvar" "expvar"
stdnet "net"
"net/http" "net/http"
"net/http/pprof" _ "net/http/pprof"
"strings" "strings"
"github.com/xtls/xray-core/app/observatory" "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"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
xnet "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension" "github.com/xtls/xray-core/features/extension"
@@ -24,17 +22,15 @@ import (
type MetricsHandler struct { type MetricsHandler struct {
ohm outbound.Manager ohm outbound.Manager
statsManager feature_stats.Manager statsManager feature_stats.Manager
ctx context.Context observatory extension.Observatory
tag string tag string
listen string listen string
tcpListener xnet.Listener tcpListener net.Listener
listener *OutboundListener
} }
// NewMetricsHandler creates a new MetricsHandler based on the given config. // NewMetricsHandler creates a new MetricsHandler based on the given config.
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) { func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
c := &MetricsHandler{ c := &MetricsHandler{
ctx: ctx,
tag: config.Tag, tag: config.Tag,
listen: config.Listen, listen: config.Listen,
} }
@@ -42,6 +38,50 @@ func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, er
c.statsManager = sm c.statsManager = sm
c.ohm = om 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 return c, nil
} }
@@ -50,172 +90,46 @@ func (p *MetricsHandler) Type() interface{} {
} }
func (p *MetricsHandler) Start() error { func (p *MetricsHandler) Start() error {
handler := p.httpHandler()
// direct listen a port if listen is set // direct listen a port if listen is set
if p.listen != "" { if p.listen != "" {
TCPlistener, err := xnet.Listen("tcp", p.listen) TCPlistener, err := net.Listen("tcp", p.listen)
if err != nil { if err != nil {
return err return err
} }
p.tcpListener = TCPlistener p.tcpListener = TCPlistener
errors.LogInfo(context.Background(), "Metrics server listening on ", p.listen) errors.LogInfo(context.Background(), "Metrics server listening on ", p.listen)
go p.serve(TCPlistener, handler) go func() {
} if err := http.Serve(TCPlistener, http.DefaultServeMux); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
if p.tag == "" { }
if p.tcpListener == nil { }()
return errors.New("metrics must have a tag or listen address")
}
return nil
} }
listener := &OutboundListener{ listener := &OutboundListener{
buffer: make(chan xnet.Conn, 4), buffer: make(chan net.Conn, 4),
done: done.New(), done: done.New(),
} }
p.listener = listener
go p.serve(listener, handler) go func() {
if err := http.Serve(listener, http.DefaultServeMux); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}()
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil { if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
errors.LogInfo(context.Background(), "failed to remove existing handler") errors.LogInfo(context.Background(), "failed to remove existing handler")
} }
if err := p.ohm.AddHandler(context.Background(), &Outbound{ return p.ohm.AddHandler(context.Background(), &Outbound{
tag: p.tag, tag: p.tag,
listener: listener, listener: listener,
}); err != nil { })
if closeErr := p.Close(); closeErr != nil {
errors.LogErrorInner(context.Background(), closeErr, "failed to close metrics server after start failure")
}
return err
}
return nil
} }
func (p *MetricsHandler) Close() error { func (p *MetricsHandler) Close() error {
var errs []error return nil
if p.tcpListener != nil {
errs = append(errs, p.tcpListener.Close())
p.tcpListener = nil
}
if p.listener != nil {
errs = append(errs, p.listener.Close())
p.listener = nil
}
if p.ohm != nil && p.tag != "" {
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
errors.LogInfo(context.Background(), "failed to remove metrics handler")
}
}
return errors.Combine(errs...)
}
func (p *MetricsHandler) serve(listener xnet.Listener, handler http.Handler) {
if err := http.Serve(listener, handler); err != nil && !isClosedListenerError(err) {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}
func isClosedListenerError(err error) bool {
if err == nil {
return true
}
if stderrors.Is(err, stdnet.ErrClosed) || stderrors.Is(err, http.ErrServerClosed) {
return true
}
errText := err.Error()
return strings.Contains(errText, "listen closed") ||
strings.Contains(errText, "use of closed network connection")
}
func (p *MetricsHandler) httpHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/debug/vars", p.handleDebugVars)
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
return mux
}
func (p *MetricsHandler) handleDebugVars(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
vars := map[string]json.RawMessage{}
expvar.Do(func(kv expvar.KeyValue) {
value := json.RawMessage(kv.Value.String())
if !json.Valid(value) {
value = json.RawMessage("null")
}
vars[kv.Key] = value
})
vars["stats"] = marshalJSON(p.stats())
vars["observatory"] = marshalJSON(p.observatoryStatus())
payload, err := json.Marshal(vars)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(payload)
}
func marshalJSON(value interface{}) json.RawMessage {
data, err := json.Marshal(value)
if err != nil {
return json.RawMessage("null")
}
return data
}
func (p *MetricsHandler) stats() map[string]map[string]map[string]int64 {
resp := map[string]map[string]map[string]int64{
"inbound": {},
"outbound": {},
"user": {},
}
p.statsManager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
nameSplit := strings.Split(name, ">>>")
if len(nameSplit) < 4 {
return true
}
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
items, found := resp[typeName]
if !found {
items = map[string]map[string]int64{}
resp[typeName] = items
}
if item, found := items[tagOrUser]; found {
item[direction] = counter.Value()
} else {
items[tagOrUser] = map[string]int64{
direction: counter.Value(),
}
}
return true
})
return resp
}
func (p *MetricsHandler) observatoryStatus() interface{} {
feature := core.MustFromContext(p.ctx).GetFeature(extension.ObservatoryType())
if feature == nil {
return nil
}
observatoryFeature := feature.(extension.Observatory)
resp := map[string]*observatory.OutboundStatus{}
if o, err := observatoryFeature.GetObservation(context.Background()); err != nil {
return err
} else {
for _, x := range o.(*observatory.ObservationResult).GetStatus() {
resp[x.OutboundTag] = x
}
}
return resp
} }
func init() { func init() {
-161
View File
@@ -1,161 +0,0 @@
package metrics
import (
"context"
"encoding/json"
stdnet "net"
"net/http"
"net/http/httptest"
"testing"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/app/proxyman"
_ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
appstats "github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
feature_outbound "github.com/xtls/xray-core/features/outbound"
)
func TestMetricsCanRestartInSameProcess(t *testing.T) {
for i := 0; i < 2; i++ {
server := startMetricsTestServer(t)
readMetricsVars(t, server)
readMetricsPprof(t, server)
if err := server.Close(); err != nil {
t.Fatalf("failed to close metrics server: %v", err)
}
}
}
func TestMetricsCanRunMultipleInstancesInSameProcess(t *testing.T) {
server1 := startMetricsTestServer(t)
t.Cleanup(func() {
_ = server1.Close()
})
server2 := startMetricsTestServer(t)
t.Cleanup(func() {
_ = server2.Close()
})
readMetricsVars(t, server1)
readMetricsVars(t, server2)
}
func TestMetricsListenOnlyWithoutTagDoesNotRegisterOutbound(t *testing.T) {
listen := pickMetricsListenAddress(t)
server := startMetricsTestServerWithMetricsConfig(t, &Config{
Listen: listen,
})
t.Cleanup(func() {
_ = server.Close()
})
response, err := http.Get("http://" + listen + "/debug/vars")
if err != nil {
t.Fatalf("failed to read listen-only metrics: %v", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
t.Fatalf("unexpected listen-only metrics status: %d", response.StatusCode)
}
outboundManager := server.GetFeature(feature_outbound.ManagerType()).(feature_outbound.Manager)
if handlers := outboundManager.ListHandlers(context.Background()); len(handlers) != 0 {
t.Fatalf("listen-only metrics registered outbound handlers: got %d, want 0", len(handlers))
}
}
func startMetricsTestServer(t *testing.T) *core.Instance {
return startMetricsTestServerWithMetricsConfig(t, &Config{
Tag: "metrics_out",
})
}
func startMetricsTestServerWithMetricsConfig(t *testing.T, metricsConfig *Config) *core.Instance {
t.Helper()
server, err := core.New(metricsTestConfig(metricsConfig))
if err != nil {
t.Fatalf("failed to create metrics server: %v", err)
}
if err := server.Start(); err != nil {
_ = server.Close()
t.Fatalf("failed to start metrics server: %v", err)
}
return server
}
func metricsTestConfig(metricsConfig *Config) *core.Config {
return &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.InboundConfig{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(&appstats.Config{}),
serial.ToTypedMessage(metricsConfig),
},
}
}
func pickMetricsListenAddress(t *testing.T) string {
t.Helper()
listener, err := stdnet.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to pick metrics listen address: %v", err)
}
defer listener.Close()
return listener.Addr().String()
}
func readMetricsVars(t *testing.T, server *core.Instance) {
t.Helper()
recorder := httptest.NewRecorder()
metricsHandler(t, server).httpHandler().ServeHTTP(
recorder,
httptest.NewRequest(http.MethodGet, "/debug/vars", nil),
)
if recorder.Code != http.StatusOK {
t.Fatalf("unexpected metrics vars status: %d", recorder.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(recorder.Body).Decode(&payload); err != nil {
t.Fatalf("failed to decode metrics vars: %v", err)
}
if _, found := payload["stats"]; !found {
t.Fatal("metrics vars missing stats")
}
if _, found := payload["observatory"]; !found {
t.Fatal("metrics vars missing observatory")
}
}
func readMetricsPprof(t *testing.T, server *core.Instance) {
t.Helper()
recorder := httptest.NewRecorder()
metricsHandler(t, server).httpHandler().ServeHTTP(
recorder,
httptest.NewRequest(http.MethodGet, "/debug/pprof/goroutine?debug=1", nil),
)
if recorder.Code != http.StatusOK {
t.Fatalf("unexpected metrics pprof status: %d", recorder.Code)
}
}
func metricsHandler(t *testing.T, server *core.Instance) *MetricsHandler {
t.Helper()
feature := server.GetFeature((*MetricsHandler)(nil))
handler, ok := feature.(*MetricsHandler)
if !ok || handler == nil {
t.Fatal("metrics handler not registered")
}
return handler
}
+2 -4
View File
@@ -2,6 +2,7 @@ package burst
import ( import (
"context" "context"
"sync" "sync"
"github.com/xtls/xray-core/app/observatory" "github.com/xtls/xray-core/app/observatory"
@@ -31,10 +32,6 @@ func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
return &observatory.ObservationResult{Status: o.createResult()}, nil return &observatory.ObservationResult{Status: o.createResult()}, nil
} }
func (o *Observer) Check(tag []string) {
o.hp.Check(tag)
}
func (o *Observer) createResult() []*observatory.OutboundStatus { func (o *Observer) createResult() []*observatory.OutboundStatus {
var result []*observatory.OutboundStatus var result []*observatory.OutboundStatus
o.hp.access.Lock() o.hp.access.Lock()
@@ -71,6 +68,7 @@ func (o *Observer) Start() error {
o.hp.StartScheduler(func() ([]string, error) { o.hp.StartScheduler(func() ([]string, error) {
hs, ok := o.ohm.(outbound.HandlerSelector) hs, ok := o.ohm.(outbound.HandlerSelector)
if !ok { if !ok {
return nil, errors.New("outbound.Manager is not a HandlerSelector") return nil, errors.New("outbound.Manager is not a HandlerSelector")
} }
+51 -30
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/observatory/burst/config.proto // source: app/observatory/burst/config.proto
package burst package burst
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,12 +21,13 @@ const (
) )
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document The selectors for outbound under observation // @Document The selectors for outbound under observation
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"` SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
PingConfig *HealthPingConfig `protobuf:"bytes,3,opt,name=ping_config,json=pingConfig,proto3" json:"ping_config,omitempty"` PingConfig *HealthPingConfig `protobuf:"bytes,3,opt,name=ping_config,json=pingConfig,proto3" json:"ping_config,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -75,7 +75,10 @@ func (x *Config) GetPingConfig() *HealthPingConfig {
} }
type HealthPingConfig struct { type HealthPingConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// destination url, need 204 for success return // destination url, need 204 for success return
// default https://connectivitycheck.gstatic.com/generate_204 // default https://connectivitycheck.gstatic.com/generate_204
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"`
@@ -88,9 +91,7 @@ type HealthPingConfig struct {
// ping timeout, int64 values of time.Duration // ping timeout, int64 values of time.Duration
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
// http method to make request // http method to make request
HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"` HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *HealthPingConfig) Reset() { func (x *HealthPingConfig) Reset() {
@@ -167,32 +168,51 @@ func (x *HealthPingConfig) GetHttpMethod() string {
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
const file_app_observatory_burst_config_proto_rawDesc = "" + var file_app_observatory_burst_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
"\"app/observatory/burst/config.proto\x12\x1fxray.core.app.observatory.burst\"\x87\x01\n" + 0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
"\x06Config\x12)\n" + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
"\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12R\n" + 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e,
"\vping_config\x18\x03 \x01(\v21.xray.core.app.observatory.burst.HealthPingConfigR\n" + 0x62, 0x75, 0x72, 0x73, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
"pingConfig\"\xd4\x01\n" + 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65,
"\x10HealthPingConfig\x12 \n" + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a,
"\vdestination\x18\x01 \x01(\tR\vdestination\x12\"\n" + 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x52, 0x0a, 0x0b, 0x70,
"\fconnectivity\x18\x02 \x01(\tR\fconnectivity\x12\x1a\n" + 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
"\binterval\x18\x03 \x01(\x03R\binterval\x12$\n" + 0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
"\rsamplingCount\x18\x04 \x01(\x05R\rsamplingCount\x12\x18\n" + 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72,
"\atimeout\x18\x05 \x01(\x03R\atimeout\x12\x1e\n" + 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
"\n" + 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
"httpMethod\x18\x06 \x01(\tR\n" + 0xd4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f,
"httpMethodBp\n" + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
"\x1ecom.xray.app.observatory.burstP\x01Z/github.com/xtls/xray-core/app/observatory/burst\xaa\x02\x1aXray.App.Observatory.Burstb\x06proto3" 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73,
0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07,
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74,
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76,
0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0xaa, 0x02, 0x1a, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x42, 0x75, 0x72, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_observatory_burst_config_proto_rawDescOnce sync.Once file_app_observatory_burst_config_proto_rawDescOnce sync.Once
file_app_observatory_burst_config_proto_rawDescData []byte file_app_observatory_burst_config_proto_rawDescData = file_app_observatory_burst_config_proto_rawDesc
) )
func file_app_observatory_burst_config_proto_rawDescGZIP() []byte { func file_app_observatory_burst_config_proto_rawDescGZIP() []byte {
file_app_observatory_burst_config_proto_rawDescOnce.Do(func() { file_app_observatory_burst_config_proto_rawDescOnce.Do(func() {
file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc))) file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_burst_config_proto_rawDescData)
}) })
return file_app_observatory_burst_config_proto_rawDescData return file_app_observatory_burst_config_proto_rawDescData
} }
@@ -220,7 +240,7 @@ func file_app_observatory_burst_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc)), RawDescriptor: file_app_observatory_burst_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
@@ -231,6 +251,7 @@ func file_app_observatory_burst_config_proto_init() {
MessageInfos: file_app_observatory_burst_config_proto_msgTypes, MessageInfos: file_app_observatory_burst_config_proto_msgTypes,
}.Build() }.Build()
File_app_observatory_burst_config_proto = out.File File_app_observatory_burst_config_proto = out.File
file_app_observatory_burst_config_proto_rawDesc = nil
file_app_observatory_burst_config_proto_goTypes = nil file_app_observatory_burst_config_proto_goTypes = nil
file_app_observatory_burst_config_proto_depIdxs = nil file_app_observatory_burst_config_proto_depIdxs = nil
} }
+26 -44
View File
@@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/dice"
@@ -25,12 +24,11 @@ type HealthPingSettings struct {
// HealthPing is the health checker for balancers // HealthPing is the health checker for balancers
type HealthPing struct { type HealthPing struct {
ctx context.Context ctx context.Context
cancelCtx context.CancelFunc dispatcher routing.Dispatcher
cancelPending atomic.Pointer[context.CancelFunc] access sync.Mutex
dispatcher routing.Dispatcher ticker *time.Ticker
access sync.Mutex tickerClose chan struct{}
ticker *time.Ticker
Settings *HealthPingSettings Settings *HealthPingSettings
Results map[string]*HealthPingRTTS Results map[string]*HealthPingRTTS
@@ -64,10 +62,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
settings.Destination = "https://connectivitycheck.gstatic.com/generate_204" settings.Destination = "https://connectivitycheck.gstatic.com/generate_204"
} }
if settings.Interval == 0 { if settings.Interval == 0 {
settings.Interval = 1 * time.Minute settings.Interval = time.Duration(1) * time.Minute
} else if settings.Interval < 10*time.Second { } else if settings.Interval < 10 {
errors.LogWarning(ctx, "health check interval is too small, 10s is applied") errors.LogWarning(ctx, "health check interval is too small, 10s is applied")
settings.Interval = 10 * time.Second settings.Interval = time.Duration(10) * time.Second
} }
if settings.SamplingCount <= 0 { if settings.SamplingCount <= 0 {
settings.SamplingCount = 10 settings.SamplingCount = 10
@@ -75,12 +73,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
if settings.Timeout <= 0 { if settings.Timeout <= 0 {
// results are saved after all health pings finish, // results are saved after all health pings finish,
// a larger timeout could possibly makes checks run longer // a larger timeout could possibly makes checks run longer
settings.Timeout = 5 * time.Second settings.Timeout = time.Duration(5) * time.Second
} }
ctx, cancel := context.WithCancel(ctx)
return &HealthPing{ return &HealthPing{
ctx: ctx, ctx: ctx,
cancelCtx: cancel,
dispatcher: dispatcher, dispatcher: dispatcher,
Settings: settings, Settings: settings,
Results: nil, Results: nil,
@@ -94,9 +90,9 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
} }
interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount)
ticker := time.NewTicker(interval) ticker := time.NewTicker(interval)
tickerClose := make(chan struct{})
h.ticker = ticker h.ticker = ticker
h.tickerClose = tickerClose
// init run to get a fast check result
go func() { go func() {
tags, err := selector() tags, err := selector()
if err != nil { if err != nil {
@@ -114,20 +110,13 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err) errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err)
return return
} }
subCtx, cancel := context.WithCancel(h.ctx) h.doCheck(tags, interval, h.Settings.SamplingCount)
old := h.cancelPending.Swap(&cancel)
if old != nil {
errors.LogDebug(h.ctx, "scheduled health check not finished before next round, canceling previous one")
(*old)()
}
h.doCheck(subCtx, tags, interval, h.Settings.SamplingCount)
h.cancelPending.CompareAndSwap(&cancel, nil)
h.Cleanup(tags) h.Cleanup(tags)
}() }()
select { select {
case <-ticker.C: case <-ticker.C:
continue continue
case <-h.ctx.Done(): case <-tickerClose:
return return
} }
} }
@@ -141,7 +130,8 @@ func (h *HealthPing) StopScheduler() {
} }
h.ticker.Stop() h.ticker.Stop()
h.ticker = nil h.ticker = nil
h.cancelCtx() close(h.tickerClose)
h.tickerClose = nil
} }
// Check implements the HealthChecker // Check implements the HealthChecker
@@ -150,7 +140,7 @@ func (h *HealthPing) Check(tags []string) error {
return nil return nil
} }
errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags) errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags)
h.doCheck(h.ctx, tags, 0, 1) h.doCheck(tags, 0, 1)
return nil return nil
} }
@@ -161,29 +151,28 @@ type rtt struct {
// doCheck performs the 'rounds' amount checks in given 'duration'. You should make // doCheck performs the 'rounds' amount checks in given 'duration'. You should make
// sure all tags are valid for current balancer // sure all tags are valid for current balancer
// cancel ctx will stop all pending checks func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) {
func (h *HealthPing) doCheck(ctx context.Context, tags []string, duration time.Duration, rounds int) {
count := len(tags) * rounds count := len(tags) * rounds
if count == 0 { if count == 0 {
return return
} }
ch := make(chan *rtt, count) ch := make(chan *rtt, count)
timers := make([]*time.Timer, 0, count)
for _, tag := range tags { for _, tag := range tags {
handler := tag handler := tag
client := newPingClient( client := newPingClient(
ctx, h.ctx,
h.dispatcher, h.dispatcher,
h.Settings.Destination, h.Settings.Destination,
h.Settings.Timeout, h.Settings.Timeout,
handler, handler,
) )
for range rounds { for i := 0; i < rounds; i++ {
delay := time.Duration(0) delay := time.Duration(0)
if duration > 0 { if duration > 0 {
delay = time.Duration(dice.RollInt63n(int64(duration))) delay = time.Duration(dice.RollInt63n(int64(duration)))
} }
timers = append(timers, time.AfterFunc(delay, func() { time.AfterFunc(delay, func() {
errors.LogDebug(h.ctx, "checking ", handler) errors.LogDebug(h.ctx, "checking ", handler)
delay, err := client.MeasureDelay(h.Settings.HttpMethod) delay, err := client.MeasureDelay(h.Settings.HttpMethod)
if err == nil { if err == nil {
@@ -211,21 +200,14 @@ func (h *HealthPing) doCheck(ctx context.Context, tags []string, duration time.D
handler: handler, handler: handler,
value: rttFailed, value: rttFailed,
} }
})) })
} }
} }
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
select { rtt := <-ch
case rtt := <-ch: if rtt.value > 0 {
if rtt.value > 0 { // should not put results when network is down
// should not put results when network is down h.PutResult(rtt.handler, rtt.value)
h.PutResult(rtt.handler, rtt.value)
}
case <-ctx.Done():
for _, timer := range timers {
timer.Stop()
}
return
} }
} }
} }
+2 -2
View File
@@ -59,7 +59,7 @@ func (h *HealthPingRTTS) Put(d time.Duration) {
if h.rtts == nil { if h.rtts == nil {
h.rtts = make([]*pingRTT, h.cap) h.rtts = make([]*pingRTT, h.cap)
for i := 0; i < h.cap; i++ { for i := 0; i < h.cap; i++ {
h.rtts[i] = &pingRTT{value: rttUntested} h.rtts[i] = &pingRTT{}
} }
h.idx = -1 h.idx = -1
} }
@@ -88,7 +88,7 @@ func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
validRTTs := make([]time.Duration, 0) validRTTs := make([]time.Duration, 0)
for _, rtt := range h.rtts { for _, rtt := range h.rtts {
switch { switch {
case rtt.value == rttUntested || time.Since(rtt.time) > h.validity: case rtt.value == 0 || time.Since(rtt.time) > h.validity:
continue continue
case rtt.value == rttFailed: case rtt.value == rttFailed:
stats.Fail++ stats.Fail++
-2
View File
@@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet/tagged" "github.com/xtls/xray-core/transport/internet/tagged"
) )
@@ -62,7 +61,6 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
if err != nil { if err != nil {
return rttFailed, err return rttFailed, err
} }
utils.TryDefaultHeadersWith(req.Header, "nav")
start := time.Now() start := time.Now()
resp, err := s.httpClient.Do(req) resp, err := s.httpClient.Do(req)
+51 -23
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/observatory/command/command.proto // source: app/observatory/command/command.proto
package command package command
@@ -12,7 +12,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -23,9 +22,9 @@ const (
) )
type GetOutboundStatusRequest struct { type GetOutboundStatusRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *GetOutboundStatusRequest) Reset() { func (x *GetOutboundStatusRequest) Reset() {
@@ -59,10 +58,11 @@ func (*GetOutboundStatusRequest) Descriptor() ([]byte, []int) {
} }
type GetOutboundStatusResponse struct { type GetOutboundStatusResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
} }
func (x *GetOutboundStatusResponse) Reset() { func (x *GetOutboundStatusResponse) Reset() {
@@ -103,9 +103,9 @@ func (x *GetOutboundStatusResponse) GetStatus() *observatory.ObservationResult {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -140,25 +140,52 @@ func (*Config) Descriptor() ([]byte, []int) {
var File_app_observatory_command_command_proto protoreflect.FileDescriptor var File_app_observatory_command_command_proto protoreflect.FileDescriptor
const file_app_observatory_command_command_proto_rawDesc = "" + var file_app_observatory_command_command_proto_rawDesc = []byte{
"\n" + 0x0a, 0x25, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
"%app/observatory/command/command.proto\x12!xray.core.app.observatory.command\x1a\x1capp/observatory/config.proto\"\x1a\n" + 0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
"\x18GetOutboundStatusRequest\"a\n" + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
"\x19GetOutboundStatusResponse\x12D\n" + 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
"\x06status\x18\x01 \x01(\v2,.xray.core.app.observatory.ObservationResultR\x06status\"\b\n" + 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x1c, 0x61, 0x70, 0x70, 0x2f,
"\x06Config2\xa7\x01\n" + 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
"\x12ObservatoryService\x12\x90\x01\n" + 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4f,
"\x11GetOutboundStatus\x12;.xray.core.app.observatory.command.GetOutboundStatusRequest\x1a<.xray.core.app.observatory.command.GetOutboundStatusResponse\"\x00B\x80\x01\n" + 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
"%com.xray.core.app.observatory.commandP\x01Z1github.com/xtls/xray-core/app/observatory/command\xaa\x02!Xray.Core.App.Observatory.Commandb\x06proto3" 0x75, 0x65, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x32, 0xa7, 0x01, 0x0a, 0x12, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x90, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65,
0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x80, 0x01, 0x0a, 0x25,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x31, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72,
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_observatory_command_command_proto_rawDescOnce sync.Once file_app_observatory_command_command_proto_rawDescOnce sync.Once
file_app_observatory_command_command_proto_rawDescData []byte file_app_observatory_command_command_proto_rawDescData = file_app_observatory_command_command_proto_rawDesc
) )
func file_app_observatory_command_command_proto_rawDescGZIP() []byte { func file_app_observatory_command_command_proto_rawDescGZIP() []byte {
file_app_observatory_command_command_proto_rawDescOnce.Do(func() { file_app_observatory_command_command_proto_rawDescOnce.Do(func() {
file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc))) file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_command_command_proto_rawDescData)
}) })
return file_app_observatory_command_command_proto_rawDescData return file_app_observatory_command_command_proto_rawDescData
} }
@@ -190,7 +217,7 @@ func file_app_observatory_command_command_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc)), RawDescriptor: file_app_observatory_command_command_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 3, NumMessages: 3,
NumExtensions: 0, NumExtensions: 0,
@@ -201,6 +228,7 @@ func file_app_observatory_command_command_proto_init() {
MessageInfos: file_app_observatory_command_command_proto_msgTypes, MessageInfos: file_app_observatory_command_command_proto_msgTypes,
}.Build() }.Build()
File_app_observatory_command_command_proto = out.File File_app_observatory_command_command_proto = out.File
file_app_observatory_command_command_proto_rawDesc = nil
file_app_observatory_command_command_proto_goTypes = nil file_app_observatory_command_command_proto_goTypes = nil
file_app_observatory_command_command_proto_depIdxs = nil file_app_observatory_command_command_proto_depIdxs = nil
} }
+4 -4
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.0 // - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.5 // - protoc v5.28.2
// source: app/observatory/command/command.proto // source: app/observatory/command/command.proto
package command package command
@@ -63,7 +63,7 @@ type ObservatoryServiceServer interface {
type UnimplementedObservatoryServiceServer struct{} type UnimplementedObservatoryServiceServer struct{}
func (UnimplementedObservatoryServiceServer) GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) { func (UnimplementedObservatoryServiceServer) GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetOutboundStatus not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetOutboundStatus not implemented")
} }
func (UnimplementedObservatoryServiceServer) mustEmbedUnimplementedObservatoryServiceServer() {} func (UnimplementedObservatoryServiceServer) mustEmbedUnimplementedObservatoryServiceServer() {}
func (UnimplementedObservatoryServiceServer) testEmbeddedByValue() {} func (UnimplementedObservatoryServiceServer) testEmbeddedByValue() {}
@@ -76,7 +76,7 @@ type UnsafeObservatoryServiceServer interface {
} }
func RegisterObservatoryServiceServer(s grpc.ServiceRegistrar, srv ObservatoryServiceServer) { func RegisterObservatoryServiceServer(s grpc.ServiceRegistrar, srv ObservatoryServiceServer) {
// If the following call panics, it indicates UnimplementedObservatoryServiceServer was // If the following call pancis, it indicates UnimplementedObservatoryServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
+104 -64
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/observatory/config.proto // source: app/observatory/config.proto
package observatory package observatory
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,10 +21,11 @@ const (
) )
type ObservationResult struct { type ObservationResult struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
} }
func (x *ObservationResult) Reset() { func (x *ObservationResult) Reset() {
@@ -66,15 +66,16 @@ func (x *ObservationResult) GetStatus() []*OutboundStatus {
} }
type HealthPingMeasurementResult struct { type HealthPingMeasurementResult struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"`
Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"`
Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"`
Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"`
Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"`
Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"`
Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"`
Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"`
Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"`
} }
func (x *HealthPingMeasurementResult) Reset() { func (x *HealthPingMeasurementResult) Reset() {
@@ -150,7 +151,10 @@ func (x *HealthPingMeasurementResult) GetMin() int64 {
} }
type OutboundStatus struct { type OutboundStatus struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document Whether this outbound is usable // @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser // @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"` Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
@@ -169,10 +173,8 @@ type OutboundStatus struct {
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` 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 // @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"` LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"` HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *OutboundStatus) Reset() { func (x *OutboundStatus) Reset() {
@@ -255,7 +257,10 @@ func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult {
} }
type ProbeResult struct { type ProbeResult struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document Whether this outbound is usable // @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser // @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"` Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
@@ -266,8 +271,6 @@ type ProbeResult struct {
// @Document The error caused this outbound failed to relay probe request // @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"` LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *ProbeResult) Reset() { func (x *ProbeResult) Reset() {
@@ -322,12 +325,13 @@ func (x *ProbeResult) GetLastErrorReason() string {
} }
type Intensity struct { type Intensity struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document The time interval for a probe request in ms. // @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"` ProbeInterval uint32 `protobuf:"varint,1,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Intensity) Reset() { func (x *Intensity) Reset() {
@@ -368,14 +372,15 @@ func (x *Intensity) GetProbeInterval() uint32 {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @Document The selectors for outbound under observation // @Document The selectors for outbound under observation
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"` SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"` ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"` ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
EnableConcurrency bool `protobuf:"varint,5,opt,name=enable_concurrency,json=enableConcurrency,proto3" json:"enable_concurrency,omitempty"` EnableConcurrency bool `protobuf:"varint,5,opt,name=enable_concurrency,json=enableConcurrency,proto3" json:"enable_concurrency,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -438,48 +443,82 @@ func (x *Config) GetEnableConcurrency() bool {
var File_app_observatory_config_proto protoreflect.FileDescriptor var File_app_observatory_config_proto protoreflect.FileDescriptor
const file_app_observatory_config_proto_rawDesc = "" + var file_app_observatory_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
"\x1capp/observatory/config.proto\x12\x19xray.core.app.observatory\"V\n" + 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19,
"\x11ObservationResult\x12A\n" + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62,
"\x06status\x18\x01 \x03(\v2).xray.core.app.observatory.OutboundStatusR\x06status\"\x9f\x01\n" + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x56, 0x0a, 0x11, 0x4f, 0x62, 0x73,
"\x1bHealthPingMeasurementResult\x12\x10\n" + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x41,
"\x03all\x18\x01 \x01(\x03R\x03all\x12\x12\n" + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
"\x04fail\x18\x02 \x01(\x03R\x04fail\x12\x1c\n" + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
"\tdeviation\x18\x03 \x01(\x03R\tdeviation\x12\x18\n" + 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
"\aaverage\x18\x04 \x01(\x03R\aaverage\x12\x10\n" + 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
"\x03max\x18\x05 \x01(\x03R\x03max\x12\x10\n" + 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x1b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67,
"\x03min\x18\x06 \x01(\x03R\x03min\"\xae\x02\n" + 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
"\x0eOutboundStatus\x12\x14\n" + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
"\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" + 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
"\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" + 0x03, 0x52, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x61,
"\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\x12!\n" + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69,
"\foutbound_tag\x18\x04 \x01(\tR\voutboundTag\x12$\n" + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65,
"\x0elast_seen_time\x18\x05 \x01(\x03R\flastSeenTime\x12\"\n" + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12,
"\rlast_try_time\x18\x06 \x01(\x03R\vlastTryTime\x12W\n" + 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6d, 0x61,
"\vhealth_ping\x18\a \x01(\v26.xray.core.app.observatory.HealthPingMeasurementResultR\n" + 0x78, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
"healthPing\"e\n" + 0x6d, 0x69, 0x6e, 0x22, 0xae, 0x02, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
"\vProbeResult\x12\x14\n" + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18,
"\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05,
"\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c,
"\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\"2\n" + 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
"\tIntensity\x12%\n" + 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c,
"\x0eprobe_interval\x18\x01 \x01(\rR\rprobeInterval\"\xa6\x01\n" + 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21,
"\x06Config\x12)\n" + 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04,
"\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12\x1b\n" + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61,
"\tprobe_url\x18\x03 \x01(\tR\bprobeUrl\x12%\n" + 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74,
"\x0eprobe_interval\x18\x04 \x01(\x03R\rprobeInterval\x12-\n" + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53,
"\x12enable_concurrency\x18\x05 \x01(\bR\x11enableConcurrencyB^\n" + 0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f,
"\x18com.xray.app.observatoryP\x01Z)github.com/xtls/xray-core/app/observatory\xaa\x02\x14Xray.App.Observatoryb\x06proto3" 0x74, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x68,
0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x48, 0x65, 0x61,
0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
0x50, 0x69, 0x6e, 0x67, 0x22, 0x65, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c,
0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12,
0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65,
0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x49,
0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62,
0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22,
0xa6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x75,
0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x55,
0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62,
0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x29, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_observatory_config_proto_rawDescOnce sync.Once file_app_observatory_config_proto_rawDescOnce sync.Once
file_app_observatory_config_proto_rawDescData []byte file_app_observatory_config_proto_rawDescData = file_app_observatory_config_proto_rawDesc
) )
func file_app_observatory_config_proto_rawDescGZIP() []byte { func file_app_observatory_config_proto_rawDescGZIP() []byte {
file_app_observatory_config_proto_rawDescOnce.Do(func() { file_app_observatory_config_proto_rawDescOnce.Do(func() {
file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc))) file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_config_proto_rawDescData)
}) })
return file_app_observatory_config_proto_rawDescData return file_app_observatory_config_proto_rawDescData
} }
@@ -512,7 +551,7 @@ func file_app_observatory_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc)), RawDescriptor: file_app_observatory_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 6, NumMessages: 6,
NumExtensions: 0, NumExtensions: 0,
@@ -523,6 +562,7 @@ func file_app_observatory_config_proto_init() {
MessageInfos: file_app_observatory_config_proto_msgTypes, MessageInfos: file_app_observatory_config_proto_msgTypes,
}.Build() }.Build()
File_app_observatory_config_proto = out.File File_app_observatory_config_proto = out.File
file_app_observatory_config_proto_rawDesc = nil
file_app_observatory_config_proto_goTypes = nil file_app_observatory_config_proto_goTypes = nil
file_app_observatory_config_proto_depIdxs = nil file_app_observatory_config_proto_depIdxs = nil
} }
+6 -18
View File
@@ -5,7 +5,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"slices"
"sort" "sort"
"sync" "sync"
"time" "time"
@@ -16,7 +15,6 @@ import (
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension" "github.com/xtls/xray-core/features/extension"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
@@ -71,7 +69,7 @@ func (o *Observer) background() {
outbounds := hs.Select(o.config.SubjectSelector) outbounds := hs.Select(o.config.SubjectSelector)
o.clearRemovedOutbounds(outbounds) o.updateStatus(outbounds)
sleepTime := time.Second * 10 sleepTime := time.Second * 10
if o.config.ProbeInterval != 0 { if o.config.ProbeInterval != 0 {
@@ -112,19 +110,11 @@ func (o *Observer) background() {
} }
} }
func (o *Observer) clearRemovedOutbounds(outbounds []string) { func (o *Observer) updateStatus(outbounds []string) {
o.statusLock.Lock() o.statusLock.Lock()
defer o.statusLock.Unlock() defer o.statusLock.Unlock()
if len(o.status) == 0 { // TODO should remove old inbound that is removed
return _ = outbounds
}
var pruned []*OutboundStatus
for _, status := range o.status {
if slices.Contains(outbounds, status.OutboundTag) {
pruned = append(pruned, status)
}
}
o.status = pruned
} }
func (o *Observer) probe(outbound string) ProbeResult { func (o *Observer) probe(outbound string) ProbeResult {
@@ -172,9 +162,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
if o.config.ProbeUrl != "" { if o.config.ProbeUrl != "" {
probeURL = o.config.ProbeUrl probeURL = o.config.ProbeUrl
} }
req, _ := http.NewRequest(http.MethodGet, probeURL, nil) response, err := httpClient.Get(probeURL)
utils.TryDefaultHeadersWith(req.Header, "nav")
response, err := httpClient.Do(req)
if err != nil { if err != nil {
return errors.New("outbound failed to relay connection").Base(err) return errors.New("outbound failed to relay connection").Base(err)
} }
@@ -186,7 +174,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
return nil return nil
}) })
if err != nil { if err != nil {
errorMessage := "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed" var errorMessage = "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed"
errors.LogInfoInner(o.ctx, errorCollectorForRequest.UnderlyingError(), errorMessage) errors.LogInfoInner(o.ctx, errorCollectorForRequest.UnderlyingError(), errorMessage)
return ProbeResult{Alive: false, LastErrorReason: errorMessage} return ProbeResult{Alive: false, LastErrorReason: errorMessage}
} }
-64
View File
@@ -1,64 +0,0 @@
package observatory
import "testing"
func TestObserverUpdateStatusPrunesStaleOutbounds(t *testing.T) {
observer := &Observer{
status: []*OutboundStatus{
{
OutboundTag: "keep",
Alive: true,
Delay: 42,
LastErrorReason: "",
LastSeenTime: 111,
LastTryTime: 222,
},
{
OutboundTag: "drop",
Alive: false,
Delay: 99999999,
LastErrorReason: "probe failed",
LastSeenTime: 333,
LastTryTime: 444,
},
},
}
observer.clearRemovedOutbounds([]string{"keep"})
if len(observer.status) != 1 {
t.Fatalf("expected 1 status after pruning, got %d", len(observer.status))
}
got := observer.status[0]
if got.OutboundTag != "keep" {
t.Fatalf("expected remaining status for keep, got %q", got.OutboundTag)
}
if !got.Alive {
t.Fatal("expected remaining status to preserve Alive field")
}
if got.Delay != 42 {
t.Fatalf("expected remaining status to preserve Delay, got %d", got.Delay)
}
if got.LastSeenTime != 111 {
t.Fatalf("expected remaining status to preserve LastSeenTime, got %d", got.LastSeenTime)
}
if got.LastTryTime != 222 {
t.Fatalf("expected remaining status to preserve LastTryTime, got %d", got.LastTryTime)
}
}
func TestObserverUpdateStatusClearsWhenNoOutboundsRemain(t *testing.T) {
observer := &Observer{
status: []*OutboundStatus{
{OutboundTag: "drop-1"},
{OutboundTag: "drop-2"},
},
}
observer.clearRemovedOutbounds(nil)
if len(observer.status) != 0 {
t.Fatalf("expected all statuses to be removed, got %d", len(observer.status))
}
}
+130 -84
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/policy/config.proto // source: app/policy/config.proto
package policy package policy
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -22,10 +21,11 @@ const (
) )
type Second struct { type Second struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
} }
func (x *Second) Reset() { func (x *Second) Reset() {
@@ -66,12 +66,13 @@ func (x *Second) GetValue() uint32 {
} }
type Policy struct { type Policy struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"`
Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"`
Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"`
Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"`
Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"`
} }
func (x *Policy) Reset() { func (x *Policy) Reset() {
@@ -126,10 +127,11 @@ func (x *Policy) GetBuffer() *Policy_Buffer {
} }
type SystemPolicy struct { type SystemPolicy struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Stats *SystemPolicy_Stats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Stats *SystemPolicy_Stats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"`
} }
func (x *SystemPolicy) Reset() { func (x *SystemPolicy) Reset() {
@@ -170,11 +172,12 @@ func (x *SystemPolicy) GetStats() *SystemPolicy_Stats {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level,proto3" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
System *SystemPolicy `protobuf:"bytes,2,opt,name=system,proto3" json:"system,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level,proto3" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
System *SystemPolicy `protobuf:"bytes,2,opt,name=system,proto3" json:"system,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -223,13 +226,14 @@ func (x *Config) GetSystem() *SystemPolicy {
// Timeout is a message for timeout settings in various stages, in seconds. // Timeout is a message for timeout settings in various stages, in seconds.
type Policy_Timeout struct { type Policy_Timeout struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Handshake *Second `protobuf:"bytes,1,opt,name=handshake,proto3" json:"handshake,omitempty"` sizeCache protoimpl.SizeCache
ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle,proto3" json:"connection_idle,omitempty"` unknownFields protoimpl.UnknownFields
UplinkOnly *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly,proto3" json:"uplink_only,omitempty"`
DownlinkOnly *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly,proto3" json:"downlink_only,omitempty"` Handshake *Second `protobuf:"bytes,1,opt,name=handshake,proto3" json:"handshake,omitempty"`
unknownFields protoimpl.UnknownFields ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle,proto3" json:"connection_idle,omitempty"`
sizeCache protoimpl.SizeCache UplinkOnly *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly,proto3" json:"uplink_only,omitempty"`
DownlinkOnly *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly,proto3" json:"downlink_only,omitempty"`
} }
func (x *Policy_Timeout) Reset() { func (x *Policy_Timeout) Reset() {
@@ -291,12 +295,13 @@ func (x *Policy_Timeout) GetDownlinkOnly() *Second {
} }
type Policy_Stats struct { type Policy_Stats struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"`
UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"`
UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"`
UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"`
UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"`
} }
func (x *Policy_Stats) Reset() { func (x *Policy_Stats) Reset() {
@@ -351,11 +356,12 @@ func (x *Policy_Stats) GetUserOnline() bool {
} }
type Policy_Buffer struct { type Policy_Buffer struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
// Buffer size per connection, in bytes. -1 for unlimited buffer.
Connection int32 `protobuf:"varint,1,opt,name=connection,proto3" json:"connection,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Buffer size per connection, in bytes. -1 for unlimited buffer.
Connection int32 `protobuf:"varint,1,opt,name=connection,proto3" json:"connection,omitempty"`
} }
func (x *Policy_Buffer) Reset() { func (x *Policy_Buffer) Reset() {
@@ -396,13 +402,14 @@ func (x *Policy_Buffer) GetConnection() int32 {
} }
type SystemPolicy_Stats struct { type SystemPolicy_Stats struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
InboundUplink bool `protobuf:"varint,1,opt,name=inbound_uplink,json=inboundUplink,proto3" json:"inbound_uplink,omitempty"` sizeCache protoimpl.SizeCache
InboundDownlink bool `protobuf:"varint,2,opt,name=inbound_downlink,json=inboundDownlink,proto3" json:"inbound_downlink,omitempty"` unknownFields protoimpl.UnknownFields
OutboundUplink bool `protobuf:"varint,3,opt,name=outbound_uplink,json=outboundUplink,proto3" json:"outbound_uplink,omitempty"`
OutboundDownlink bool `protobuf:"varint,4,opt,name=outbound_downlink,json=outboundDownlink,proto3" json:"outbound_downlink,omitempty"` InboundUplink bool `protobuf:"varint,1,opt,name=inbound_uplink,json=inboundUplink,proto3" json:"inbound_uplink,omitempty"`
unknownFields protoimpl.UnknownFields InboundDownlink bool `protobuf:"varint,2,opt,name=inbound_downlink,json=inboundDownlink,proto3" json:"inbound_downlink,omitempty"`
sizeCache protoimpl.SizeCache OutboundUplink bool `protobuf:"varint,3,opt,name=outbound_uplink,json=outboundUplink,proto3" json:"outbound_uplink,omitempty"`
OutboundDownlink bool `protobuf:"varint,4,opt,name=outbound_downlink,json=outboundDownlink,proto3" json:"outbound_downlink,omitempty"`
} }
func (x *SystemPolicy_Stats) Reset() { func (x *SystemPolicy_Stats) Reset() {
@@ -465,55 +472,93 @@ func (x *SystemPolicy_Stats) GetOutboundDownlink() bool {
var File_app_policy_config_proto protoreflect.FileDescriptor var File_app_policy_config_proto protoreflect.FileDescriptor
const file_app_policy_config_proto_rawDesc = "" + var file_app_policy_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
"\x17app/policy/config.proto\x12\x0fxray.app.policy\"\x1e\n" + 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e,
"\x06Second\x12\x14\n" + 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x1e, 0x0a, 0x06, 0x53, 0x65,
"\x05value\x18\x01 \x01(\rR\x05value\"\xc7\x04\n" + 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
"\x06Policy\x129\n" + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc7, 0x04, 0x0a, 0x06, 0x50,
"\atimeout\x18\x01 \x01(\v2\x1f.xray.app.policy.Policy.TimeoutR\atimeout\x123\n" + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
"\x05stats\x18\x02 \x01(\v2\x1d.xray.app.policy.Policy.StatsR\x05stats\x126\n" + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
"\x06buffer\x18\x03 \x01(\v2\x1e.xray.app.policy.Policy.BufferR\x06buffer\x1a\xfa\x01\n" + 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
"\aTimeout\x125\n" + 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
"\thandshake\x18\x01 \x01(\v2\x17.xray.app.policy.SecondR\thandshake\x12@\n" + 0x12, 0x33, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
"\x0fconnection_idle\x18\x02 \x01(\v2\x17.xray.app.policy.SecondR\x0econnectionIdle\x128\n" + 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
"\vuplink_only\x18\x03 \x01(\v2\x17.xray.app.policy.SecondR\n" + 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05,
"uplinkOnly\x12<\n" + 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18,
"\rdownlink_only\x18\x04 \x01(\v2\x17.xray.app.policy.SecondR\fdownlinkOnly\x1an\n" + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
"\x05Stats\x12\x1f\n" + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42,
"\vuser_uplink\x18\x01 \x01(\bR\n" + 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a, 0xfa, 0x01,
"userUplink\x12#\n" + 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x35, 0x0a, 0x09, 0x68, 0x61, 0x6e,
"\ruser_downlink\x18\x02 \x01(\bR\fuserDownlink\x12\x1f\n" + 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78,
"\vuser_online\x18\x03 \x01(\bR\n" + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53,
"userOnline\x1a(\n" + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65,
"\x06Buffer\x12\x1e\n" + 0x12, 0x40, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69,
"\n" + 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79,
"connection\x18\x01 \x01(\x05R\n" + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f,
"connection\"\xfb\x01\n" + 0x6e, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64,
"\fSystemPolicy\x129\n" + 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c,
"\x05stats\x18\x01 \x01(\v2#.xray.app.policy.SystemPolicy.StatsR\x05stats\x1a\xaf\x01\n" + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
"\x05Stats\x12%\n" + 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
"\x0einbound_uplink\x18\x01 \x01(\bR\rinboundUplink\x12)\n" + 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0d,
"\x10inbound_downlink\x18\x02 \x01(\bR\x0finboundDownlink\x12'\n" + 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20,
"\x0foutbound_uplink\x18\x03 \x01(\bR\x0eoutboundUplink\x12+\n" + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
"\x11outbound_downlink\x18\x04 \x01(\bR\x10outboundDownlink\"\xcc\x01\n" + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f,
"\x06Config\x128\n" + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x6e, 0x0a, 0x05, 0x53, 0x74,
"\x05level\x18\x01 \x03(\v2\".xray.app.policy.Config.LevelEntryR\x05level\x125\n" + 0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69,
"\x06system\x18\x02 \x01(\v2\x1d.xray.app.policy.SystemPolicyR\x06system\x1aQ\n" + 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70,
"\n" + 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77,
"LevelEntry\x12\x10\n" + 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65,
"\x03key\x18\x01 \x01(\rR\x03key\x12-\n" + 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65,
"\x05value\x18\x02 \x01(\v2\x17.xray.app.policy.PolicyR\x05value:\x028\x01BO\n" + 0x72, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
"\x13com.xray.app.policyP\x01Z$github.com/xtls/xray-core/app/policy\xaa\x02\x0fXray.App.Policyb\x06proto3" 0x75, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75,
0x66, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73,
0x1a, 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e,
0x6b, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77,
0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f,
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69,
0x6e, 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a,
0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51,
0x0a, 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 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, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_policy_config_proto_rawDescOnce sync.Once file_app_policy_config_proto_rawDescOnce sync.Once
file_app_policy_config_proto_rawDescData []byte file_app_policy_config_proto_rawDescData = file_app_policy_config_proto_rawDesc
) )
func file_app_policy_config_proto_rawDescGZIP() []byte { func file_app_policy_config_proto_rawDescGZIP() []byte {
file_app_policy_config_proto_rawDescOnce.Do(func() { file_app_policy_config_proto_rawDescOnce.Do(func() {
file_app_policy_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc))) file_app_policy_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_policy_config_proto_rawDescData)
}) })
return file_app_policy_config_proto_rawDescData return file_app_policy_config_proto_rawDescData
} }
@@ -558,7 +603,7 @@ func file_app_policy_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc)), RawDescriptor: file_app_policy_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 9, NumMessages: 9,
NumExtensions: 0, NumExtensions: 0,
@@ -569,6 +614,7 @@ func file_app_policy_config_proto_init() {
MessageInfos: file_app_policy_config_proto_msgTypes, MessageInfos: file_app_policy_config_proto_msgTypes,
}.Build() }.Build()
File_app_policy_config_proto = out.File File_app_policy_config_proto = out.File
file_app_policy_config_proto_rawDesc = nil
file_app_policy_config_proto_goTypes = nil file_app_policy_config_proto_goTypes = nil
file_app_policy_config_proto_depIdxs = nil file_app_policy_config_proto_depIdxs = nil
} }
+1 -1
View File
@@ -138,7 +138,7 @@ func (s *handlerServer) GetInboundUsers(ctx context.Context, request *GetInbound
if len(request.Email) > 0 { if len(request.Email) > 0 {
return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil
} }
result := make([]*protocol.User, 0, 100) var result = make([]*protocol.User, 0, 100)
users := um.GetUsers(ctx) users := um.GetUsers(ctx)
for _, u := range users { for _, u := range users {
result = append(result, protocol.ToProtoUser(u)) result = append(result, protocol.ToProtoUser(u))
+249 -124
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/proxyman/command/command.proto // source: app/proxyman/command/command.proto
package command package command
@@ -14,7 +14,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -25,10 +24,11 @@ const (
) )
type AddUserOperation struct { type AddUserOperation struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
} }
func (x *AddUserOperation) Reset() { func (x *AddUserOperation) Reset() {
@@ -69,10 +69,11 @@ func (x *AddUserOperation) GetUser() *protocol.User {
} }
type RemoveUserOperation struct { type RemoveUserOperation struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
} }
func (x *RemoveUserOperation) Reset() { func (x *RemoveUserOperation) Reset() {
@@ -113,10 +114,11 @@ func (x *RemoveUserOperation) GetEmail() string {
} }
type AddInboundRequest struct { type AddInboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Inbound *core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound,proto3" json:"inbound,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Inbound *core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound,proto3" json:"inbound,omitempty"`
} }
func (x *AddInboundRequest) Reset() { func (x *AddInboundRequest) Reset() {
@@ -157,9 +159,9 @@ func (x *AddInboundRequest) GetInbound() *core.InboundHandlerConfig {
} }
type AddInboundResponse struct { type AddInboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *AddInboundResponse) Reset() { func (x *AddInboundResponse) Reset() {
@@ -193,10 +195,11 @@ func (*AddInboundResponse) Descriptor() ([]byte, []int) {
} }
type RemoveInboundRequest struct { type RemoveInboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
} }
func (x *RemoveInboundRequest) Reset() { func (x *RemoveInboundRequest) Reset() {
@@ -237,9 +240,9 @@ func (x *RemoveInboundRequest) GetTag() string {
} }
type RemoveInboundResponse struct { type RemoveInboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *RemoveInboundResponse) Reset() { func (x *RemoveInboundResponse) Reset() {
@@ -273,11 +276,12 @@ func (*RemoveInboundResponse) Descriptor() ([]byte, []int) {
} }
type AlterInboundRequest struct { type AlterInboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
} }
func (x *AlterInboundRequest) Reset() { func (x *AlterInboundRequest) Reset() {
@@ -325,9 +329,9 @@ func (x *AlterInboundRequest) GetOperation() *serial.TypedMessage {
} }
type AlterInboundResponse struct { type AlterInboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *AlterInboundResponse) Reset() { func (x *AlterInboundResponse) Reset() {
@@ -361,10 +365,11 @@ func (*AlterInboundResponse) Descriptor() ([]byte, []int) {
} }
type ListInboundsRequest struct { type ListInboundsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
} }
func (x *ListInboundsRequest) Reset() { func (x *ListInboundsRequest) Reset() {
@@ -405,10 +410,11 @@ func (x *ListInboundsRequest) GetIsOnlyTags() bool {
} }
type ListInboundsResponse struct { type ListInboundsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"`
} }
func (x *ListInboundsResponse) Reset() { func (x *ListInboundsResponse) Reset() {
@@ -449,11 +455,12 @@ func (x *ListInboundsResponse) GetInbounds() []*core.InboundHandlerConfig {
} }
type GetInboundUserRequest struct { type GetInboundUserRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
} }
func (x *GetInboundUserRequest) Reset() { func (x *GetInboundUserRequest) Reset() {
@@ -501,10 +508,11 @@ func (x *GetInboundUserRequest) GetEmail() string {
} }
type GetInboundUserResponse struct { type GetInboundUserResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
} }
func (x *GetInboundUserResponse) Reset() { func (x *GetInboundUserResponse) Reset() {
@@ -545,10 +553,11 @@ func (x *GetInboundUserResponse) GetUsers() []*protocol.User {
} }
type GetInboundUsersCountResponse struct { type GetInboundUsersCountResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
} }
func (x *GetInboundUsersCountResponse) Reset() { func (x *GetInboundUsersCountResponse) Reset() {
@@ -589,10 +598,11 @@ func (x *GetInboundUsersCountResponse) GetCount() int64 {
} }
type AddOutboundRequest struct { type AddOutboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Outbound *core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Outbound *core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"`
} }
func (x *AddOutboundRequest) Reset() { func (x *AddOutboundRequest) Reset() {
@@ -633,9 +643,9 @@ func (x *AddOutboundRequest) GetOutbound() *core.OutboundHandlerConfig {
} }
type AddOutboundResponse struct { type AddOutboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *AddOutboundResponse) Reset() { func (x *AddOutboundResponse) Reset() {
@@ -669,10 +679,11 @@ func (*AddOutboundResponse) Descriptor() ([]byte, []int) {
} }
type RemoveOutboundRequest struct { type RemoveOutboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
} }
func (x *RemoveOutboundRequest) Reset() { func (x *RemoveOutboundRequest) Reset() {
@@ -713,9 +724,9 @@ func (x *RemoveOutboundRequest) GetTag() string {
} }
type RemoveOutboundResponse struct { type RemoveOutboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *RemoveOutboundResponse) Reset() { func (x *RemoveOutboundResponse) Reset() {
@@ -749,11 +760,12 @@ func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) {
} }
type AlterOutboundRequest struct { type AlterOutboundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
} }
func (x *AlterOutboundRequest) Reset() { func (x *AlterOutboundRequest) Reset() {
@@ -801,9 +813,9 @@ func (x *AlterOutboundRequest) GetOperation() *serial.TypedMessage {
} }
type AlterOutboundResponse struct { type AlterOutboundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *AlterOutboundResponse) Reset() { func (x *AlterOutboundResponse) Reset() {
@@ -837,9 +849,9 @@ func (*AlterOutboundResponse) Descriptor() ([]byte, []int) {
} }
type ListOutboundsRequest struct { type ListOutboundsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *ListOutboundsRequest) Reset() { func (x *ListOutboundsRequest) Reset() {
@@ -873,10 +885,11 @@ func (*ListOutboundsRequest) Descriptor() ([]byte, []int) {
} }
type ListOutboundsResponse struct { type ListOutboundsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"`
} }
func (x *ListOutboundsResponse) Reset() { func (x *ListOutboundsResponse) Reset() {
@@ -917,9 +930,9 @@ func (x *ListOutboundsResponse) GetOutbounds() []*core.OutboundHandlerConfig {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -954,72 +967,183 @@ func (*Config) Descriptor() ([]byte, []int) {
var File_app_proxyman_command_command_proto protoreflect.FileDescriptor var File_app_proxyman_command_command_proto protoreflect.FileDescriptor
const file_app_proxyman_command_command_proto_rawDesc = "" + var file_app_proxyman_command_command_proto_rawDesc = []byte{
"\n" + 0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63,
"\"app/proxyman/command/command.proto\x12\x19xray.app.proxyman.command\x1a\x1acommon/protocol/user.proto\x1a!common/serial/typed_message.proto\x1a\x11core/config.proto\"B\n" + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70,
"\x10AddUserOperation\x12.\n" + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
"\x04user\x18\x01 \x01(\v2\x1a.xray.common.protocol.UserR\x04user\"+\n" + 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a,
"\x13RemoveUserOperation\x12\x14\n" + 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
"\x05email\x18\x01 \x01(\tR\x05email\"N\n" + 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d,
"\x11AddInboundRequest\x129\n" + 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64,
"\ainbound\x18\x01 \x01(\v2\x1f.xray.core.InboundHandlerConfigR\ainbound\"\x14\n" + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11,
"\x12AddInboundResponse\"(\n" + 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
"\x14RemoveInboundRequest\x12\x10\n" + 0x6f, 0x22, 0x42, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72,
"\x03tag\x18\x01 \x01(\tR\x03tag\"\x17\n" + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20,
"\x15RemoveInboundResponse\"g\n" + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
"\x13AlterInboundRequest\x12\x10\n" + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12>\n" + 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2b, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55,
"\toperation\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\toperation\"\x16\n" + 0x73, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05,
"\x14AlterInboundResponse\"5\n" + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61,
"\x13ListInboundsRequest\x12\x1e\n" + 0x69, 0x6c, 0x22, 0x4e, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
"\n" + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75,
"isOnlyTags\x18\x01 \x01(\bR\n" + 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
"isOnlyTags\"S\n" + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64,
"\x14ListInboundsResponse\x12;\n" + 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75,
"\binbounds\x18\x01 \x03(\v2\x1f.xray.core.InboundHandlerConfigR\binbounds\"?\n" + 0x6e, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
"\x15GetInboundUserRequest\x12\x10\n" + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x14\n" + 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
"\x05email\x18\x02 \x01(\tR\x05email\"J\n" + 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74,
"\x16GetInboundUserResponse\x120\n" + 0x61, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f,
"\x05users\x18\x01 \x03(\v2\x1a.xray.common.protocol.UserR\x05users\"4\n" + 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x13, 0x41,
"\x1cGetInboundUsersCountResponse\x12\x14\n" + 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
"\x05count\x18\x01 \x01(\x03R\x05count\"R\n" + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
"\x12AddOutboundRequest\x12<\n" + 0x03, 0x74, 0x61, 0x67, 0x12, 0x3e, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
"\boutbound\x18\x01 \x01(\v2 .xray.core.OutboundHandlerConfigR\boutbound\"\x15\n" + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
"\x13AddOutboundResponse\")\n" + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
"\x15RemoveOutboundRequest\x12\x10\n" + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
"\x03tag\x18\x01 \x01(\tR\x03tag\"\x18\n" + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62,
"\x16RemoveOutboundResponse\"h\n" + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x13,
"\x14AlterOutboundRequest\x12\x10\n" + 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12>\n" + 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54, 0x61, 0x67,
"\toperation\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\toperation\"\x17\n" + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54,
"\x15AlterOutboundResponse\"\x16\n" + 0x61, 0x67, 0x73, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75,
"\x14ListOutboundsRequest\"W\n" + 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69,
"\x15ListOutboundsResponse\x12>\n" + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
"\toutbounds\x18\x01 \x03(\v2 .xray.core.OutboundHandlerConfigR\toutbounds\"\b\n" + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
"\x06Config2\xae\t\n" + 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08,
"\x0eHandlerService\x12k\n" + 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x49,
"\n" + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
"AddInbound\x12,.xray.app.proxyman.command.AddInboundRequest\x1a-.xray.app.proxyman.command.AddInboundResponse\"\x00\x12t\n" + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
"\rRemoveInbound\x12/.xray.app.proxyman.command.RemoveInboundRequest\x1a0.xray.app.proxyman.command.RemoveInboundResponse\"\x00\x12q\n" + 0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01,
"\fAlterInbound\x12..xray.app.proxyman.command.AlterInboundRequest\x1a/.xray.app.proxyman.command.AlterInboundResponse\"\x00\x12q\n" + 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74,
"\fListInbounds\x12..xray.app.proxyman.command.ListInboundsRequest\x1a/.xray.app.proxyman.command.ListInboundsResponse\"\x00\x12x\n" + 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
"\x0fGetInboundUsers\x120.xray.app.proxyman.command.GetInboundUserRequest\x1a1.xray.app.proxyman.command.GetInboundUserResponse\"\x00\x12\x83\x01\n" + 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
"\x14GetInboundUsersCount\x120.xray.app.proxyman.command.GetInboundUserRequest\x1a7.xray.app.proxyman.command.GetInboundUsersCountResponse\"\x00\x12n\n" + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
"\vAddOutbound\x12-.xray.app.proxyman.command.AddOutboundRequest\x1a..xray.app.proxyman.command.AddOutboundResponse\"\x00\x12w\n" + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05,
"\x0eRemoveOutbound\x120.xray.app.proxyman.command.RemoveOutboundRequest\x1a1.xray.app.proxyman.command.RemoveOutboundResponse\"\x00\x12t\n" + 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x34, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f,
"\rAlterOutbound\x12/.xray.app.proxyman.command.AlterOutboundRequest\x1a0.xray.app.proxyman.command.AlterOutboundResponse\"\x00\x12t\n" + 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73,
"\rListOutbounds\x12/.xray.app.proxyman.command.ListOutboundsRequest\x1a0.xray.app.proxyman.command.ListOutboundsResponse\"\x00Bm\n" + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01,
"\x1dcom.xray.app.proxyman.commandP\x01Z.github.com/xtls/xray-core/app/proxyman/command\xaa\x02\x19Xray.App.Proxyman.Commandb\x06proto3" 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x52, 0x0a, 0x12, 0x41,
0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22,
0x15, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
0x67, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x68, 0x0a, 0x14, 0x41,
0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3e, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x63, 0x6f, 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, 0x09, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x17, 0x0a, 0x15, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75,
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16,
0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75,
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x3e, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22,
0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xae, 0x09, 0x0a, 0x0e, 0x48, 0x61,
0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6b, 0x0a, 0x0a,
0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2c, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x0d, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x71, 0x0a, 0x0c, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x71, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x78, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73,
0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74,
0x0a, 0x0d, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74,
0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2e, 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, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x19,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var ( var (
file_app_proxyman_command_command_proto_rawDescOnce sync.Once file_app_proxyman_command_command_proto_rawDescOnce sync.Once
file_app_proxyman_command_command_proto_rawDescData []byte file_app_proxyman_command_command_proto_rawDescData = file_app_proxyman_command_command_proto_rawDesc
) )
func file_app_proxyman_command_command_proto_rawDescGZIP() []byte { func file_app_proxyman_command_command_proto_rawDescGZIP() []byte {
file_app_proxyman_command_command_proto_rawDescOnce.Do(func() { file_app_proxyman_command_command_proto_rawDescOnce.Do(func() {
file_app_proxyman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc))) file_app_proxyman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_proxyman_command_command_proto_rawDescData)
}) })
return file_app_proxyman_command_command_proto_rawDescData return file_app_proxyman_command_command_proto_rawDescData
} }
@@ -1098,7 +1222,7 @@ func file_app_proxyman_command_command_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc)), RawDescriptor: file_app_proxyman_command_command_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 22, NumMessages: 22,
NumExtensions: 0, NumExtensions: 0,
@@ -1109,6 +1233,7 @@ func file_app_proxyman_command_command_proto_init() {
MessageInfos: file_app_proxyman_command_command_proto_msgTypes, MessageInfos: file_app_proxyman_command_command_proto_msgTypes,
}.Build() }.Build()
File_app_proxyman_command_command_proto = out.File File_app_proxyman_command_command_proto = out.File
file_app_proxyman_command_command_proto_rawDesc = nil
file_app_proxyman_command_command_proto_goTypes = nil file_app_proxyman_command_command_proto_goTypes = nil
file_app_proxyman_command_command_proto_depIdxs = nil file_app_proxyman_command_command_proto_depIdxs = nil
} }
+13 -13
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.0 // - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.5 // - protoc v5.28.2
// source: app/proxyman/command/command.proto // source: app/proxyman/command/command.proto
package command package command
@@ -180,34 +180,34 @@ type HandlerServiceServer interface {
type UnimplementedHandlerServiceServer struct{} type UnimplementedHandlerServiceServer struct{}
func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) { func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddInbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented")
} }
func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) { func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveInbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method RemoveInbound not implemented")
} }
func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) { func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AlterInbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented")
} }
func (UnimplementedHandlerServiceServer) ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) { func (UnimplementedHandlerServiceServer) ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListInbounds not implemented") return nil, status.Errorf(codes.Unimplemented, "method ListInbounds not implemented")
} }
func (UnimplementedHandlerServiceServer) GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) { func (UnimplementedHandlerServiceServer) GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetInboundUsers not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsers not implemented")
} }
func (UnimplementedHandlerServiceServer) GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) { func (UnimplementedHandlerServiceServer) GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetInboundUsersCount not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsersCount not implemented")
} }
func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) { func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddOutbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented")
} }
func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) { func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveOutbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method RemoveOutbound not implemented")
} }
func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) { func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AlterOutbound not implemented") return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented")
} }
func (UnimplementedHandlerServiceServer) ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) { func (UnimplementedHandlerServiceServer) ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListOutbounds not implemented") return nil, status.Errorf(codes.Unimplemented, "method ListOutbounds not implemented")
} }
func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {} func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {}
func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {} func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {}
@@ -220,7 +220,7 @@ type UnsafeHandlerServiceServer interface {
} }
func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) { func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) {
// If the following call panics, it indicates UnimplementedHandlerServiceServer was // If the following call pancis, it indicates UnimplementedHandlerServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
+19 -30
View File
@@ -1,34 +1,23 @@
package proxyman package proxyman
import ( func (s *AllocationStrategy) GetConcurrencyValue() uint32 {
"github.com/xtls/xray-core/common/geodata" if s == nil || s.Concurrency == nil {
"github.com/xtls/xray-core/common/session" return 3
)
func BuildSniffingRequest(config *SniffingConfig) (session.SniffingRequest, error) {
if config == nil {
return session.SniffingRequest{}, nil
} }
return s.Concurrency.Value
request := session.SniffingRequest{ }
Enabled: config.Enabled,
OverrideDestinationForProtocol: config.DestinationOverride, func (s *AllocationStrategy) GetRefreshValue() uint32 {
MetadataOnly: config.MetadataOnly, if s == nil || s.Refresh == nil {
RouteOnly: config.RouteOnly, return 5
} }
if len(config.DomainsExcluded) > 0 { return s.Refresh.Value
excludeForDomain, err := geodata.DomainReg.BuildDomainMatcher(config.DomainsExcluded) }
if err != nil {
return session.SniffingRequest{}, err func (c *ReceiverConfig) GetEffectiveSniffingSettings() *SniffingConfig {
} if c.SniffingSettings != nil {
request.ExcludeForDomain = excludeForDomain return c.SniffingSettings
} }
if len(config.IpsExcluded) > 0 {
excludeForIP, err := geodata.IPReg.BuildIPMatcher(config.IpsExcluded) return nil
if err != nil {
return session.SniffingRequest{}, err
}
request.ExcludeForIP = excludeForIP
}
return request, nil
} }
+453 -135
View File
@@ -1,13 +1,12 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/proxyman/config.proto // source: app/proxyman/config.proto
package proxyman package proxyman
import ( import (
geodata "github.com/xtls/xray-core/common/geodata"
net "github.com/xtls/xray-core/common/net" net "github.com/xtls/xray-core/common/net"
serial "github.com/xtls/xray-core/common/serial" serial "github.com/xtls/xray-core/common/serial"
internet "github.com/xtls/xray-core/transport/internet" internet "github.com/xtls/xray-core/transport/internet"
@@ -15,7 +14,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -25,10 +23,62 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type AllocationStrategy_Type int32
const (
// Always allocate all connection handlers.
AllocationStrategy_Always AllocationStrategy_Type = 0
// Randomly allocate specific range of handlers.
AllocationStrategy_Random AllocationStrategy_Type = 1
// External. Not supported yet.
AllocationStrategy_External AllocationStrategy_Type = 2
)
// Enum value maps for AllocationStrategy_Type.
var (
AllocationStrategy_Type_name = map[int32]string{
0: "Always",
1: "Random",
2: "External",
}
AllocationStrategy_Type_value = map[string]int32{
"Always": 0,
"Random": 1,
"External": 2,
}
)
func (x AllocationStrategy_Type) Enum() *AllocationStrategy_Type {
p := new(AllocationStrategy_Type)
*p = x
return p
}
func (x AllocationStrategy_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AllocationStrategy_Type) Descriptor() protoreflect.EnumDescriptor {
return file_app_proxyman_config_proto_enumTypes[0].Descriptor()
}
func (AllocationStrategy_Type) Type() protoreflect.EnumType {
return &file_app_proxyman_config_proto_enumTypes[0]
}
func (x AllocationStrategy_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AllocationStrategy_Type.Descriptor instead.
func (AllocationStrategy_Type) EnumDescriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0}
}
type InboundConfig struct { type InboundConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *InboundConfig) Reset() { func (x *InboundConfig) Reset() {
@@ -61,27 +111,92 @@ func (*InboundConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{0} return file_app_proxyman_config_proto_rawDescGZIP(), []int{0}
} }
type AllocationStrategy struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type AllocationStrategy_Type `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.proxyman.AllocationStrategy_Type" json:"type,omitempty"`
// Number of handlers (ports) running in parallel.
// Default value is 3 if unset.
Concurrency *AllocationStrategy_AllocationStrategyConcurrency `protobuf:"bytes,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
// Number of minutes before a handler is regenerated.
// Default value is 5 if unset.
Refresh *AllocationStrategy_AllocationStrategyRefresh `protobuf:"bytes,3,opt,name=refresh,proto3" json:"refresh,omitempty"`
}
func (x *AllocationStrategy) Reset() {
*x = AllocationStrategy{}
mi := &file_app_proxyman_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy) ProtoMessage() {}
func (x *AllocationStrategy) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AllocationStrategy.ProtoReflect.Descriptor instead.
func (*AllocationStrategy) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1}
}
func (x *AllocationStrategy) GetType() AllocationStrategy_Type {
if x != nil {
return x.Type
}
return AllocationStrategy_Always
}
func (x *AllocationStrategy) GetConcurrency() *AllocationStrategy_AllocationStrategyConcurrency {
if x != nil {
return x.Concurrency
}
return nil
}
func (x *AllocationStrategy) GetRefresh() *AllocationStrategy_AllocationStrategyRefresh {
if x != nil {
return x.Refresh
}
return nil
}
type SniffingConfig struct { type SniffingConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Whether or not to enable content sniffing on an inbound connection. // Whether or not to enable content sniffing on an inbound connection.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Override target destination if sniff'ed protocol is in the given list. // Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns". // Supported values are "http", "tls", "fakedns".
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
DomainsExcluded []*geodata.DomainRule `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"` DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
IpsExcluded []*geodata.IPRule `protobuf:"bytes,6,rep,name=ips_excluded,json=ipsExcluded,proto3" json:"ips_excluded,omitempty"`
// Whether should only try to sniff metadata without waiting for client input. // Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first // Can be used to support SMTP like protocol where server send the first
// message. // message.
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"` MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"` RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *SniffingConfig) Reset() { func (x *SniffingConfig) Reset() {
*x = SniffingConfig{} *x = SniffingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[1] mi := &file_app_proxyman_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -93,7 +208,7 @@ func (x *SniffingConfig) String() string {
func (*SniffingConfig) ProtoMessage() {} func (*SniffingConfig) ProtoMessage() {}
func (x *SniffingConfig) ProtoReflect() protoreflect.Message { func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[1] mi := &file_app_proxyman_config_proto_msgTypes[2]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -106,7 +221,7 @@ func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use SniffingConfig.ProtoReflect.Descriptor instead. // Deprecated: Use SniffingConfig.ProtoReflect.Descriptor instead.
func (*SniffingConfig) Descriptor() ([]byte, []int) { func (*SniffingConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1} return file_app_proxyman_config_proto_rawDescGZIP(), []int{2}
} }
func (x *SniffingConfig) GetEnabled() bool { func (x *SniffingConfig) GetEnabled() bool {
@@ -123,20 +238,13 @@ func (x *SniffingConfig) GetDestinationOverride() []string {
return nil return nil
} }
func (x *SniffingConfig) GetDomainsExcluded() []*geodata.DomainRule { func (x *SniffingConfig) GetDomainsExcluded() []string {
if x != nil { if x != nil {
return x.DomainsExcluded return x.DomainsExcluded
} }
return nil return nil
} }
func (x *SniffingConfig) GetIpsExcluded() []*geodata.IPRule {
if x != nil {
return x.IpsExcluded
}
return nil
}
func (x *SniffingConfig) GetMetadataOnly() bool { func (x *SniffingConfig) GetMetadataOnly() bool {
if x != nil { if x != nil {
return x.MetadataOnly return x.MetadataOnly
@@ -152,21 +260,23 @@ func (x *SniffingConfig) GetRouteOnly() bool {
} }
type ReceiverConfig struct { type ReceiverConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// PortList specifies the ports which the Receiver should listen on. // PortList specifies the ports which the Receiver should listen on.
PortList *net.PortList `protobuf:"bytes,1,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"` PortList *net.PortList `protobuf:"bytes,1,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
// Listen specifies the IP address that the Receiver should listen on. // Listen specifies the IP address that the Receiver should listen on.
Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"` Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,3,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy,proto3" json:"allocation_strategy,omitempty"`
ReceiveOriginalDestination bool `protobuf:"varint,4,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
SniffingSettings *SniffingConfig `protobuf:"bytes,6,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"` ReceiveOriginalDestination bool `protobuf:"varint,5,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"`
unknownFields protoimpl.UnknownFields SniffingSettings *SniffingConfig `protobuf:"bytes,7,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"`
sizeCache protoimpl.SizeCache
} }
func (x *ReceiverConfig) Reset() { func (x *ReceiverConfig) Reset() {
*x = ReceiverConfig{} *x = ReceiverConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[2] mi := &file_app_proxyman_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -178,7 +288,7 @@ func (x *ReceiverConfig) String() string {
func (*ReceiverConfig) ProtoMessage() {} func (*ReceiverConfig) ProtoMessage() {}
func (x *ReceiverConfig) ProtoReflect() protoreflect.Message { func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[2] mi := &file_app_proxyman_config_proto_msgTypes[3]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -191,7 +301,7 @@ func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use ReceiverConfig.ProtoReflect.Descriptor instead. // Deprecated: Use ReceiverConfig.ProtoReflect.Descriptor instead.
func (*ReceiverConfig) Descriptor() ([]byte, []int) { func (*ReceiverConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{2} return file_app_proxyman_config_proto_rawDescGZIP(), []int{3}
} }
func (x *ReceiverConfig) GetPortList() *net.PortList { func (x *ReceiverConfig) GetPortList() *net.PortList {
@@ -208,6 +318,13 @@ func (x *ReceiverConfig) GetListen() *net.IPOrDomain {
return nil return nil
} }
func (x *ReceiverConfig) GetAllocationStrategy() *AllocationStrategy {
if x != nil {
return x.AllocationStrategy
}
return nil
}
func (x *ReceiverConfig) GetStreamSettings() *internet.StreamConfig { func (x *ReceiverConfig) GetStreamSettings() *internet.StreamConfig {
if x != nil { if x != nil {
return x.StreamSettings return x.StreamSettings
@@ -230,17 +347,18 @@ func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig {
} }
type InboundHandlerConfig struct { type InboundHandlerConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` sizeCache protoimpl.SizeCache
ReceiverSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"` unknownFields protoimpl.UnknownFields
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
unknownFields protoimpl.UnknownFields Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
sizeCache protoimpl.SizeCache ReceiverSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"`
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
} }
func (x *InboundHandlerConfig) Reset() { func (x *InboundHandlerConfig) Reset() {
*x = InboundHandlerConfig{} *x = InboundHandlerConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[3] mi := &file_app_proxyman_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -252,7 +370,7 @@ func (x *InboundHandlerConfig) String() string {
func (*InboundHandlerConfig) ProtoMessage() {} func (*InboundHandlerConfig) ProtoMessage() {}
func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[3] mi := &file_app_proxyman_config_proto_msgTypes[4]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -265,7 +383,7 @@ func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead. // Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead.
func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { func (*InboundHandlerConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{3} return file_app_proxyman_config_proto_rawDescGZIP(), []int{4}
} }
func (x *InboundHandlerConfig) GetTag() string { func (x *InboundHandlerConfig) GetTag() string {
@@ -290,14 +408,14 @@ func (x *InboundHandlerConfig) GetProxySettings() *serial.TypedMessage {
} }
type OutboundConfig struct { type OutboundConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *OutboundConfig) Reset() { func (x *OutboundConfig) Reset() {
*x = OutboundConfig{} *x = OutboundConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[4] mi := &file_app_proxyman_config_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -309,7 +427,7 @@ func (x *OutboundConfig) String() string {
func (*OutboundConfig) ProtoMessage() {} func (*OutboundConfig) ProtoMessage() {}
func (x *OutboundConfig) ProtoReflect() protoreflect.Message { func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[4] mi := &file_app_proxyman_config_proto_msgTypes[5]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -322,11 +440,14 @@ func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use OutboundConfig.ProtoReflect.Descriptor instead. // Deprecated: Use OutboundConfig.ProtoReflect.Descriptor instead.
func (*OutboundConfig) Descriptor() ([]byte, []int) { func (*OutboundConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{4} return file_app_proxyman_config_proto_rawDescGZIP(), []int{5}
} }
type SenderConfig struct { type SenderConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Send traffic through the given IP. Only IP is allowed. // Send traffic through the given IP. Only IP is allowed.
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"` Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
@@ -334,13 +455,11 @@ type SenderConfig struct {
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"` ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"` TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *SenderConfig) Reset() { func (x *SenderConfig) Reset() {
*x = SenderConfig{} *x = SenderConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[5] mi := &file_app_proxyman_config_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -352,7 +471,7 @@ func (x *SenderConfig) String() string {
func (*SenderConfig) ProtoMessage() {} func (*SenderConfig) ProtoMessage() {}
func (x *SenderConfig) ProtoReflect() protoreflect.Message { func (x *SenderConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[5] mi := &file_app_proxyman_config_proto_msgTypes[6]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -365,7 +484,7 @@ func (x *SenderConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use SenderConfig.ProtoReflect.Descriptor instead. // Deprecated: Use SenderConfig.ProtoReflect.Descriptor instead.
func (*SenderConfig) Descriptor() ([]byte, []int) { func (*SenderConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{5} return file_app_proxyman_config_proto_rawDescGZIP(), []int{6}
} }
func (x *SenderConfig) GetVia() *net.IPOrDomain { func (x *SenderConfig) GetVia() *net.IPOrDomain {
@@ -411,7 +530,10 @@ func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
} }
type MultiplexingConfig struct { type MultiplexingConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Whether or not Mux is enabled. // Whether or not Mux is enabled.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Max number of concurrent connections that one Mux connection can handle. // Max number of concurrent connections that one Mux connection can handle.
@@ -420,13 +542,11 @@ type MultiplexingConfig struct {
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"` XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
// "reject" (default), "allow" or "skip". // "reject" (default), "allow" or "skip".
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"` XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *MultiplexingConfig) Reset() { func (x *MultiplexingConfig) Reset() {
*x = MultiplexingConfig{} *x = MultiplexingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[6] mi := &file_app_proxyman_config_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -438,7 +558,7 @@ func (x *MultiplexingConfig) String() string {
func (*MultiplexingConfig) ProtoMessage() {} func (*MultiplexingConfig) ProtoMessage() {}
func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message { func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[6] mi := &file_app_proxyman_config_proto_msgTypes[7]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -451,7 +571,7 @@ func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use MultiplexingConfig.ProtoReflect.Descriptor instead. // Deprecated: Use MultiplexingConfig.ProtoReflect.Descriptor instead.
func (*MultiplexingConfig) Descriptor() ([]byte, []int) { func (*MultiplexingConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{6} return file_app_proxyman_config_proto_rawDescGZIP(), []int{7}
} }
func (x *MultiplexingConfig) GetEnabled() bool { func (x *MultiplexingConfig) GetEnabled() bool {
@@ -482,94 +602,290 @@ func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
return "" return ""
} }
type AllocationStrategy_AllocationStrategyConcurrency struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() {
*x = AllocationStrategy_AllocationStrategyConcurrency{}
mi := &file_app_proxyman_config_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AllocationStrategy_AllocationStrategyConcurrency.ProtoReflect.Descriptor instead.
func (*AllocationStrategy_AllocationStrategyConcurrency) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0}
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
type AllocationStrategy_AllocationStrategyRefresh struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() {
*x = AllocationStrategy_AllocationStrategyRefresh{}
mi := &file_app_proxyman_config_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy_AllocationStrategyRefresh) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AllocationStrategy_AllocationStrategyRefresh.ProtoReflect.Descriptor instead.
func (*AllocationStrategy_AllocationStrategyRefresh) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 1}
}
func (x *AllocationStrategy_AllocationStrategyRefresh) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
var File_app_proxyman_config_proto protoreflect.FileDescriptor var File_app_proxyman_config_proto protoreflect.FileDescriptor
const file_app_proxyman_config_proto_rawDesc = "" + var file_app_proxyman_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x19, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63,
"\x19app/proxyman/config.proto\x12\x11xray.app.proxyman\x1a\x18common/net/address.proto\x1a\x15common/net/port.proto\x1a\x1ftransport/internet/config.proto\x1a!common/serial/typed_message.proto\x1a\x1bcommon/geodata/geodat.proto\"\x0f\n" + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x78, 0x72, 0x61,
"\rInboundConfig\"\xad\x02\n" + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x1a, 0x18,
"\x0eSniffingConfig\x12\x18\n" + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65,
"\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" + 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
"\x14destination_override\x18\x02 \x03(\tR\x13destinationOverride\x12J\n" + 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
"\x10domains_excluded\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x0fdomainsExcluded\x12>\n" + 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
"\fips_excluded\x18\x06 \x03(\v2\x1b.xray.common.geodata.IPRuleR\vipsExcluded\x12#\n" + 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
"\rmetadata_only\x18\x04 \x01(\bR\fmetadataOnly\x12\x1d\n" + 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
"\n" + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
"route_only\x18\x05 \x01(\bR\trouteOnly\"\xe5\x02\n" + 0x6f, 0x74, 0x6f, 0x22, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f,
"\x0eReceiverConfig\x126\n" + 0x6e, 0x66, 0x69, 0x67, 0x22, 0xae, 0x03, 0x0a, 0x12, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
"\tport_list\x18\x01 \x01(\v2\x19.xray.common.net.PortListR\bportList\x123\n" + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x3e, 0x0a, 0x04, 0x74,
"\x06listen\x18\x02 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x06listen\x12N\n" + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
"\x0fstream_settings\x18\x03 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12@\n" + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c,
"\x1creceive_original_destination\x18\x04 \x01(\bR\x1areceiveOriginalDestination\x12N\n" + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
"\x11sniffing_settings\x18\x06 \x01(\v2!.xray.app.proxyman.SniffingConfigR\x10sniffingSettingsJ\x04\b\x05\x10\x06\"\xc0\x01\n" + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x63,
"\x14InboundHandlerConfig\x12\x10\n" + 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12M\n" + 0x32, 0x43, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
"\x11receiver_settings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\x10receiverSettings\x12G\n" + 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,
"\x0eproxy_settings\x18\x03 \x01(\v2 .xray.common.serial.TypedMessageR\rproxySettings\"\x10\n" + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
"\x0eOutboundConfig\"\x9d\x03\n" + 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72,
"\fSenderConfig\x12-\n" + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
"\x03via\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x03via\x12N\n" + 0x63, 0x79, 0x12, 0x59, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20,
"\x0fstream_settings\x18\x02 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12K\n" + 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
"\x0eproxy_settings\x18\x03 \x01(\v2$.xray.transport.internet.ProxyConfigR\rproxySettings\x12T\n" + 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
"\x12multiplex_settings\x18\x04 \x01(\v2%.xray.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12\x19\n" + 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
"\bvia_cidr\x18\x05 \x01(\tR\aviaCidr\x12P\n" + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66,
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategy\"\xa4\x01\n" + 0x72, 0x65, 0x73, 0x68, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x1a, 0x35, 0x0a,
"\x12MultiplexingConfig\x12\x18\n" + 0x1d, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74,
"\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" + 0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14,
"\vconcurrency\x18\x02 \x01(\x05R\vconcurrency\x12(\n" + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76,
"\x0fxudpConcurrency\x18\x03 \x01(\x05R\x0fxudpConcurrency\x12(\n" + 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x31, 0x0a, 0x19, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
"\x0fxudpProxyUDP443\x18\x04 \x01(\tR\x0fxudpProxyUDP443BU\n" + 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73,
"\x15com.xray.app.proxymanP\x01Z&github.com/xtls/xray-core/app/proxyman\xaa\x02\x11Xray.App.Proxymanb\x06proto3" 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 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, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65,
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6f,
0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x4f, 0x6e, 0x6c, 0x79, 0x22, 0xbd, 0x03, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f,
0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72,
0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12,
0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 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, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69,
0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f,
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c,
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c,
0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01,
0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69,
0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e,
0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e,
0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e,
0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04,
0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 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, 0x10, 0x72, 0x65,
0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47,
0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
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, 0x9d, 0x03, 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,
0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01,
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, 0x12, 0x19, 0x0a, 0x08,
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 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,
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 ( var (
file_app_proxyman_config_proto_rawDescOnce sync.Once file_app_proxyman_config_proto_rawDescOnce sync.Once
file_app_proxyman_config_proto_rawDescData []byte file_app_proxyman_config_proto_rawDescData = file_app_proxyman_config_proto_rawDesc
) )
func file_app_proxyman_config_proto_rawDescGZIP() []byte { func file_app_proxyman_config_proto_rawDescGZIP() []byte {
file_app_proxyman_config_proto_rawDescOnce.Do(func() { file_app_proxyman_config_proto_rawDescOnce.Do(func() {
file_app_proxyman_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc))) file_app_proxyman_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_proxyman_config_proto_rawDescData)
}) })
return file_app_proxyman_config_proto_rawDescData return file_app_proxyman_config_proto_rawDescData
} }
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_app_proxyman_config_proto_goTypes = []any{ var file_app_proxyman_config_proto_goTypes = []any{
(*InboundConfig)(nil), // 0: xray.app.proxyman.InboundConfig (AllocationStrategy_Type)(0), // 0: xray.app.proxyman.AllocationStrategy.Type
(*SniffingConfig)(nil), // 1: xray.app.proxyman.SniffingConfig (*InboundConfig)(nil), // 1: xray.app.proxyman.InboundConfig
(*ReceiverConfig)(nil), // 2: xray.app.proxyman.ReceiverConfig (*AllocationStrategy)(nil), // 2: xray.app.proxyman.AllocationStrategy
(*InboundHandlerConfig)(nil), // 3: xray.app.proxyman.InboundHandlerConfig (*SniffingConfig)(nil), // 3: xray.app.proxyman.SniffingConfig
(*OutboundConfig)(nil), // 4: xray.app.proxyman.OutboundConfig (*ReceiverConfig)(nil), // 4: xray.app.proxyman.ReceiverConfig
(*SenderConfig)(nil), // 5: xray.app.proxyman.SenderConfig (*InboundHandlerConfig)(nil), // 5: xray.app.proxyman.InboundHandlerConfig
(*MultiplexingConfig)(nil), // 6: xray.app.proxyman.MultiplexingConfig (*OutboundConfig)(nil), // 6: xray.app.proxyman.OutboundConfig
(*geodata.DomainRule)(nil), // 7: xray.common.geodata.DomainRule (*SenderConfig)(nil), // 7: xray.app.proxyman.SenderConfig
(*geodata.IPRule)(nil), // 8: xray.common.geodata.IPRule (*MultiplexingConfig)(nil), // 8: xray.app.proxyman.MultiplexingConfig
(*net.PortList)(nil), // 9: xray.common.net.PortList (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 9: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
(*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig (*net.PortList)(nil), // 11: xray.common.net.PortList
(*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage (*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain
(*internet.ProxyConfig)(nil), // 13: xray.transport.internet.ProxyConfig (*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
(internet.DomainStrategy)(0), // 14: xray.transport.internet.DomainStrategy (*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy
} }
var file_app_proxyman_config_proto_depIdxs = []int32{ var file_app_proxyman_config_proto_depIdxs = []int32{
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule 0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
8, // 1: xray.app.proxyman.SniffingConfig.ips_excluded:type_name -> xray.common.geodata.IPRule 9, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
9, // 2: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList 10, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
10, // 3: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain 11, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
11, // 4: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 12, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
1, // 5: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig 2, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy
12, // 6: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage 13, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage 3, // 7: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain 14, // 8: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 14, // 9: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
13, // 10: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig 12, // 10: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
6, // 11: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig 13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
14, // 12: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy 15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
13, // [13:13] is the sub-list for method output_type 8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
13, // [13:13] is the sub-list for method input_type 16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
13, // [13:13] is the sub-list for extension type_name 15, // [15:15] is the sub-list for method output_type
13, // [13:13] is the sub-list for extension extendee 15, // [15:15] is the sub-list for method input_type
0, // [0:13] is the sub-list for field type_name 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
} }
func init() { file_app_proxyman_config_proto_init() } func init() { file_app_proxyman_config_proto_init() }
@@ -581,17 +897,19 @@ func file_app_proxyman_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc)), RawDescriptor: file_app_proxyman_config_proto_rawDesc,
NumEnums: 0, NumEnums: 1,
NumMessages: 7, NumMessages: 10,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_app_proxyman_config_proto_goTypes, GoTypes: file_app_proxyman_config_proto_goTypes,
DependencyIndexes: file_app_proxyman_config_proto_depIdxs, DependencyIndexes: file_app_proxyman_config_proto_depIdxs,
EnumInfos: file_app_proxyman_config_proto_enumTypes,
MessageInfos: file_app_proxyman_config_proto_msgTypes, MessageInfos: file_app_proxyman_config_proto_msgTypes,
}.Build() }.Build()
File_app_proxyman_config_proto = out.File File_app_proxyman_config_proto = out.File
file_app_proxyman_config_proto_rawDesc = nil
file_app_proxyman_config_proto_goTypes = nil file_app_proxyman_config_proto_goTypes = nil
file_app_proxyman_config_proto_depIdxs = nil file_app_proxyman_config_proto_depIdxs = nil
} }
+33 -9
View File
@@ -10,10 +10,36 @@ import "common/net/address.proto";
import "common/net/port.proto"; import "common/net/port.proto";
import "transport/internet/config.proto"; import "transport/internet/config.proto";
import "common/serial/typed_message.proto"; import "common/serial/typed_message.proto";
import "common/geodata/geodat.proto";
message InboundConfig {} message InboundConfig {}
message AllocationStrategy {
enum Type {
// Always allocate all connection handlers.
Always = 0;
// Randomly allocate specific range of handlers.
Random = 1;
// External. Not supported yet.
External = 2;
}
Type type = 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; }
// Number of minutes before a handler is regenerated.
// Default value is 5 if unset.
AllocationStrategyRefresh refresh = 3;
}
message SniffingConfig { message SniffingConfig {
// Whether or not to enable content sniffing on an inbound connection. // Whether or not to enable content sniffing on an inbound connection.
bool enabled = 1; bool enabled = 1;
@@ -21,10 +47,7 @@ message SniffingConfig {
// Override target destination if sniff'ed protocol is in the given list. // Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns". // Supported values are "http", "tls", "fakedns".
repeated string destination_override = 2; repeated string destination_override = 2;
repeated string domains_excluded = 3;
repeated xray.common.geodata.DomainRule domains_excluded = 3;
repeated xray.common.geodata.IPRule ips_excluded = 6;
// Whether should only try to sniff metadata without waiting for client input. // Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first // Can be used to support SMTP like protocol where server send the first
@@ -39,10 +62,11 @@ message ReceiverConfig {
xray.common.net.PortList port_list = 1; xray.common.net.PortList port_list = 1;
// Listen specifies the IP address that the Receiver should listen on. // Listen specifies the IP address that the Receiver should listen on.
xray.common.net.IPOrDomain listen = 2; xray.common.net.IPOrDomain listen = 2;
xray.transport.internet.StreamConfig stream_settings = 3; AllocationStrategy allocation_strategy = 3;
bool receive_original_destination = 4; xray.transport.internet.StreamConfig stream_settings = 4;
reserved 5; bool receive_original_destination = 5;
SniffingConfig sniffing_settings = 6; reserved 6;
SniffingConfig sniffing_settings = 7;
} }
message InboundHandlerConfig { message InboundHandlerConfig {
+20 -34
View File
@@ -5,11 +5,11 @@ import (
"github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
@@ -26,7 +26,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink { if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "inbound>>>" + tag + ">>>traffic>>>uplink" name := "inbound>>>" + tag + ">>>traffic>>>uplink"
c, _ := statsManager.GetOrRegisterCounter(name) c, _ := stats.GetOrRegisterCounter(statsManager, name)
if c != nil { if c != nil {
uplinkCounter = c uplinkCounter = c
} }
@@ -34,7 +34,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink { if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "inbound>>>" + tag + ">>>traffic>>>downlink" name := "inbound>>>" + tag + ">>>traffic>>>downlink"
c, _ := statsManager.GetOrRegisterCounter(name) c, _ := stats.GetOrRegisterCounter(statsManager, name)
if c != nil { if c != nil {
downlinkCounter = c downlinkCounter = c
} }
@@ -53,27 +53,7 @@ type AlwaysOnInboundHandler struct {
} }
func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) {
sniffingRequest, err := proxyman.BuildSniffingRequest(receiverConfig.SniffingSettings) rawProxy, err := common.CreateObject(ctx, proxyConfig)
if err != nil {
return nil, err
}
src := net.TCPDestination(net.AnyIP, 0)
if receiverConfig.Listen != nil {
src.Address = receiverConfig.Listen.AsAddress()
}
if receiverConfig.PortList != nil && len(receiverConfig.PortList.Range) > 0 {
src.Port = net.Port(receiverConfig.PortList.Range[0].From)
}
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream config").Base(err).AtWarning()
}
newCtx := session.ContextWithInbound(ctx, &session.Inbound{Tag: tag, Source: src})
newCtx = session.ContextWithContent(newCtx, &session.Content{SniffingRequest: sniffingRequest})
newCtx = session.ContextWithStreamSettings(newCtx, mss)
rawProxy, err := common.CreateObject(newCtx, proxyConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -99,6 +79,11 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
address = net.AnyIP address = net.AnyIP
} }
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream config").Base(err).AtWarning()
}
if receiverConfig.ReceiveOriginalDestination { if receiverConfig.ReceiveOriginalDestination {
if mss.SocketSettings == nil { if mss.SocketSettings == nil {
mss.SocketSettings = &internet.SocketConfig{} mss.SocketSettings = &internet.SocketConfig{}
@@ -118,7 +103,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
stream: mss, stream: mss,
tag: tag, tag: tag,
dispatcher: h.mux, dispatcher: h.mux,
sniffingRequest: sniffingRequest, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter, uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter, downlinkCounter: downlinkCounter,
ctx: ctx, ctx: ctx,
@@ -140,7 +125,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
recvOrigDest: receiverConfig.ReceiveOriginalDestination, recvOrigDest: receiverConfig.ReceiveOriginalDestination,
tag: tag, tag: tag,
dispatcher: h.mux, dispatcher: h.mux,
sniffingRequest: sniffingRequest, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter, uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter, downlinkCounter: downlinkCounter,
ctx: ctx, ctx: ctx,
@@ -155,7 +140,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
address: address, address: address,
port: net.Port(port), port: net.Port(port),
dispatcher: h.mux, dispatcher: h.mux,
sniffingRequest: sniffingRequest, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter, uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter, downlinkCounter: downlinkCounter,
stream: mss, stream: mss,
@@ -172,12 +157,6 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
// Start implements common.Runnable. // Start implements common.Runnable.
func (h *AlwaysOnInboundHandler) Start() error { func (h *AlwaysOnInboundHandler) Start() error {
// for inbound without worker (TUN)
if run, ok := h.proxy.(common.Runnable); ok {
if err := run.Start(); err != nil {
return errors.New("failed to start proxy").Base(err)
}
}
for _, worker := range h.workers { for _, worker := range h.workers {
if err := worker.Start(); err != nil { if err := worker.Start(); err != nil {
return err return err
@@ -193,13 +172,20 @@ func (h *AlwaysOnInboundHandler) Close() error {
errs = append(errs, worker.Close()) errs = append(errs, worker.Close())
} }
errs = append(errs, h.mux.Close()) errs = append(errs, h.mux.Close())
errs = append(errs, common.Close(h.proxy))
if err := errors.Combine(errs...); err != nil { if err := errors.Combine(errs...); err != nil {
return errors.New("failed to close all resources").Base(err) return errors.New("failed to close all resources").Base(err)
} }
return nil return nil
} }
func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
if len(h.workers) == 0 {
return nil, 0, 0
}
w := h.workers[dice.Roll(len(h.workers))]
return w.Proxy(), w.Port(), 9999
}
func (h *AlwaysOnInboundHandler) Tag() string { func (h *AlwaysOnInboundHandler) Tag() string {
return h.tag return h.tag
} }
+222
View File
@@ -0,0 +1,222 @@
package inbound
import (
"context"
"sync"
"time"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/protobuf/proto"
)
type DynamicInboundHandler struct {
tag string
v *core.Instance
proxyConfig interface{}
receiverConfig *proxyman.ReceiverConfig
streamSettings *internet.MemoryStreamConfig
portMutex sync.Mutex
portsInUse map[net.Port]struct{}
workerMutex sync.RWMutex
worker []worker
lastRefresh time.Time
mux *mux.Server
task *task.Periodic
ctx context.Context
}
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
v := core.MustFromContext(ctx)
h := &DynamicInboundHandler{
tag: tag,
proxyConfig: proxyConfig,
receiverConfig: receiverConfig,
portsInUse: make(map[net.Port]struct{}),
mux: mux.NewServer(ctx),
v: v,
ctx: ctx,
}
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream settings").Base(err).AtWarning()
}
if receiverConfig.ReceiveOriginalDestination {
if mss.SocketSettings == nil {
mss.SocketSettings = &internet.SocketConfig{}
}
if mss.SocketSettings.Tproxy == internet.SocketConfig_Off {
mss.SocketSettings.Tproxy = internet.SocketConfig_Redirect
}
mss.SocketSettings.ReceiveOriginalDestAddress = true
}
h.streamSettings = mss
h.task = &task.Periodic{
Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()),
Execute: h.refresh,
}
return h, nil
}
func (h *DynamicInboundHandler) allocatePort() net.Port {
allPorts := []int32{}
for _, pr := range h.receiverConfig.PortList.Range {
for i := pr.From; i <= pr.To; i++ {
allPorts = append(allPorts, int32(i))
}
}
h.portMutex.Lock()
defer h.portMutex.Unlock()
for {
r := dice.Roll(len(allPorts))
port := net.Port(allPorts[r])
_, used := h.portsInUse[port]
if !used {
h.portsInUse[port] = struct{}{}
return port
}
}
}
func (h *DynamicInboundHandler) closeWorkers(workers []worker) {
ports2Del := make([]net.Port, len(workers))
for idx, worker := range workers {
ports2Del[idx] = worker.Port()
if err := worker.Close(); err != nil {
errors.LogInfoInner(h.ctx, err, "failed to close worker")
}
}
h.portMutex.Lock()
for _, port := range ports2Del {
delete(h.portsInUse, port)
}
h.portMutex.Unlock()
}
func (h *DynamicInboundHandler) refresh() error {
h.lastRefresh = time.Now()
timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2
concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue()
workers := make([]worker, 0, concurrency)
address := h.receiverConfig.Listen.AsAddress()
if address == nil {
address = net.AnyIP
}
uplinkCounter, downlinkCounter := getStatCounter(h.v, h.tag)
for i := uint32(0); i < concurrency; i++ {
port := h.allocatePort()
rawProxy, err := core.CreateObject(h.v, h.proxyConfig)
if err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create proxy instance")
continue
}
p := rawProxy.(proxy.Inbound)
nl := p.Network()
if net.HasNetwork(nl, net.Network_TCP) {
worker := &tcpWorker{
tag: h.tag,
address: address,
port: port,
proxy: p,
stream: h.streamSettings,
recvOrigDest: h.receiverConfig.ReceiveOriginalDestination,
dispatcher: h.mux,
sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: h.ctx,
}
if err := worker.Start(); err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create TCP worker")
continue
}
workers = append(workers, worker)
}
if net.HasNetwork(nl, net.Network_UDP) {
worker := &udpWorker{
tag: h.tag,
proxy: p,
address: address,
port: port,
dispatcher: h.mux,
sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
stream: h.streamSettings,
ctx: h.ctx,
}
if err := worker.Start(); err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create UDP worker")
continue
}
workers = append(workers, worker)
}
}
h.workerMutex.Lock()
h.worker = workers
h.workerMutex.Unlock()
time.AfterFunc(timeout, func() {
h.closeWorkers(workers)
})
return nil
}
func (h *DynamicInboundHandler) Start() error {
return h.task.Start()
}
func (h *DynamicInboundHandler) Close() error {
return h.task.Close()
}
func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
h.workerMutex.RLock()
defer h.workerMutex.RUnlock()
if len(h.worker) == 0 {
return nil, 0, 0
}
w := h.worker[dice.Roll(len(h.worker))]
expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute)
return w.Proxy(), w.Port(), int(expire)
}
func (h *DynamicInboundHandler) Tag() string {
return h.tag
}
// ReceiverSettings implements inbound.Handler.
func (h *DynamicInboundHandler) ReceiverSettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.receiverConfig)
}
// ProxySettings implements inbound.Handler.
func (h *DynamicInboundHandler) ProxySettings() *serial.TypedMessage {
if v, ok := h.proxyConfig.(proto.Message); ok {
return serial.ToTypedMessage(v)
}
return nil
}
+12 -4
View File
@@ -16,10 +16,10 @@ import (
// Manager manages all inbound handlers. // Manager manages all inbound handlers.
type Manager struct { type Manager struct {
access sync.RWMutex access sync.RWMutex
untaggedHandlers []inbound.Handler untaggedHandlers []inbound.Handler
taggedHandlers map[string]inbound.Handler taggedHandlers map[string]inbound.Handler
running bool running bool
} }
// New returns a new Manager for inbound handlers. // New returns a new Manager for inbound handlers.
@@ -178,7 +178,15 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound
ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP) ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP)
} }
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings) allocStrategy := receiverSettings.AllocationStrategy
if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always {
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
if allocStrategy.Type == proxyman.AllocationStrategy_Random {
return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
return nil, errors.New("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError()
} }
func init() { func init() {
+25 -46
View File
@@ -6,6 +6,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
c "github.com/xtls/xray-core/common/ctx" c "github.com/xtls/xray-core/common/ctx"
@@ -18,9 +19,7 @@ import (
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy"
hysteria_proxy "github.com/xtls/xray-core/proxy/hysteria"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/hysteria"
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tcp" "github.com/xtls/xray-core/transport/internet/tcp"
"github.com/xtls/xray-core/transport/internet/udp" "github.com/xtls/xray-core/transport/internet/udp"
@@ -42,7 +41,7 @@ type tcpWorker struct {
recvOrigDest bool recvOrigDest bool
tag string tag string
dispatcher routing.Dispatcher dispatcher routing.Dispatcher
sniffingRequest session.SniffingRequest sniffingConfig *proxyman.SniffingConfig
uplinkCounter stats.Counter uplinkCounter stats.Counter
downlinkCounter stats.Counter downlinkCounter stats.Counter
@@ -77,25 +76,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
case internet.SocketConfig_TProxy: case internet.SocketConfig_TProxy:
dest = net.DestinationFromAddr(conn.LocalAddr()) dest = net.DestinationFromAddr(conn.LocalAddr())
} }
if dest.IsValid() { if dest.IsValid() {
// Check if try to connect to this inbound itself (can cause loopback)
var isLoopBack bool
if w.address == net.AnyIP || w.address == net.AnyIPv6 {
if dest.Port.Value() == w.port.Value() && IsLocal(dest.Address.IP()) {
isLoopBack = true
}
} else {
if w.hub.Addr().String() == dest.NetAddr() {
isLoopBack = true
}
}
if isLoopBack {
cancel()
conn.Close()
errors.LogError(ctx, errors.New("loopback connection detected"))
return
}
outbounds[0].Target = dest outbounds[0].Target = dest
} }
} }
@@ -117,7 +98,13 @@ func (w *tcpWorker) callback(conn stat.Connection) {
}) })
content := new(session.Content) content := new(session.Content)
content.SniffingRequest = w.sniffingRequest if w.sniffingConfig != nil {
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
}
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil { if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
@@ -133,11 +120,6 @@ func (w *tcpWorker) Proxy() proxy.Inbound {
func (w *tcpWorker) Start() error { func (w *tcpWorker) Start() error {
ctx := context.Background() ctx := context.Background()
if v, ok := w.proxy.(*hysteria_proxy.Server); ok {
ctx = hysteria.ContextWithValidator(ctx, v.HysteriaInboundValidator())
}
hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) { hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) {
go w.callback(conn) go w.callback(conn)
}) })
@@ -266,7 +248,7 @@ type udpWorker struct {
tag string tag string
stream *internet.MemoryStreamConfig stream *internet.MemoryStreamConfig
dispatcher routing.Dispatcher dispatcher routing.Dispatcher
sniffingRequest session.SniffingRequest sniffingConfig *proxyman.SniffingConfig
uplinkCounter stats.Counter uplinkCounter stats.Counter
downlinkCounter stats.Counter downlinkCounter stats.Counter
@@ -356,7 +338,13 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
Tag: w.tag, Tag: w.tag,
}) })
content := new(session.Content) content := new(session.Content)
content.SniffingRequest = w.sniffingRequest if w.sniffingConfig != nil {
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
}
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil { if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
errors.LogInfoInner(ctx, err, "connection ends") errors.LogInfoInner(ctx, err, "connection ends")
@@ -472,7 +460,7 @@ type dsWorker struct {
stream *internet.MemoryStreamConfig stream *internet.MemoryStreamConfig
tag string tag string
dispatcher routing.Dispatcher dispatcher routing.Dispatcher
sniffingRequest session.SniffingRequest sniffingConfig *proxyman.SniffingConfig
uplinkCounter stats.Counter uplinkCounter stats.Counter
downlinkCounter stats.Counter downlinkCounter stats.Counter
@@ -502,7 +490,13 @@ func (w *dsWorker) callback(conn stat.Connection) {
}) })
content := new(session.Content) content := new(session.Content)
content.SniffingRequest = w.sniffingRequest if w.sniffingConfig != nil {
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
}
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil { if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil {
@@ -550,18 +544,3 @@ func (w *dsWorker) Close() error {
return nil return nil
} }
func IsLocal(ip net.IP) bool {
addrs, err := net.InterfaceAddrs()
if err != nil {
return false
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(ip) {
return true
}
}
}
return false
}
+16 -16
View File
@@ -6,6 +6,8 @@ import (
goerrors "errors" goerrors "errors"
"io" "io"
"math/big" "math/big"
gonet "net"
"os"
"github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/dice"
@@ -39,7 +41,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink { if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "outbound>>>" + tag + ">>>traffic>>>uplink" name := "outbound>>>" + tag + ">>>traffic>>>uplink"
c, _ := statsManager.GetOrRegisterCounter(name) c, _ := stats.GetOrRegisterCounter(statsManager, name)
if c != nil { if c != nil {
uplinkCounter = c uplinkCounter = c
} }
@@ -47,7 +49,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink { if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "outbound>>>" + tag + ">>>traffic>>>downlink" name := "outbound>>>" + tag + ">>>traffic>>>downlink"
c, _ := statsManager.GetOrRegisterCounter(name) c, _ := stats.GetOrRegisterCounter(statsManager, name)
if c != nil { if c != nil {
downlinkCounter = c downlinkCounter = c
} }
@@ -106,11 +108,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
} }
h.proxyConfig = proxyConfig h.proxyConfig = proxyConfig
ctx = session.ContextWithFullHandler(ctx, h) rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
newCtx := session.ContextWithStreamSettings(ctx, h.streamSettings)
rawProxyHandler, err := common.CreateObject(newCtx, proxyConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -307,17 +305,18 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
ob := outbounds[len(outbounds)-1] ob := outbounds[len(outbounds)-1]
h.SetOutboundGateway(ctx, ob) h.SetOutboundGateway(ctx, ob)
} }
}
if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid {
return conn, err
} }
conn, err := internet.Dial(ctx, dest, h.streamSettings) conn, err := internet.Dial(ctx, dest, h.streamSettings)
conn = h.getStatCouterConnection(conn) conn = h.getStatCouterConnection(conn)
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
if outbounds != nil { ob := outbounds[len(outbounds)-1]
ob := outbounds[len(outbounds)-1] ob.Conn = conn
ob.Conn = conn
} else {
// for Vision's pre-connect
}
return conn, err return conn, err
} }
@@ -344,7 +343,7 @@ func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound)
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String()) errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
} }
} }
// case addr.Family().IsDomain(): //case addr.Family().IsDomain():
default: default:
ob.Gateway = addr ob.Gateway = addr
@@ -392,7 +391,8 @@ func (h *Handler) ProxySettings() *serial.TypedMessage {
} }
func ParseRandomIP(addr net.Address, prefix string) net.Address { func ParseRandomIP(addr net.Address, prefix string) net.Address {
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix)
_, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix)
ones, bits := ipnet.Mask.Size() ones, bits := ipnet.Mask.Size()
subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones)) subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones))
@@ -406,5 +406,5 @@ func ParseRandomIP(addr net.Address, prefix string) net.Address {
padded := make([]byte, len(ipnet.IP)) padded := make([]byte, len(ipnet.IP))
copy(padded[len(padded)-len(rndBytes):], rndBytes) copy(padded[len(padded)-len(rndBytes):], rndBytes)
return net.ParseAddress(net.IP(padded).String()) return net.ParseAddress(gonet.IP(padded).String())
} }
+8 -7
View File
@@ -22,8 +22,8 @@ import (
) )
func TestInterfaces(t *testing.T) { func TestInterfaces(t *testing.T) {
_ = outbound.Handler(new(Handler)) _ = (outbound.Handler)(new(Handler))
_ = outbound.Manager(new(Manager)) _ = (outbound.Manager)(new(Manager))
} }
const xrayKey core.XrayKey = 1 const xrayKey core.XrayKey = 1
@@ -43,12 +43,12 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
} }
v, _ := core.New(config) v, _ := core.New(config)
v.AddFeature(outbound.Manager(new(Manager))) v.AddFeature((outbound.Manager)(new(Manager)))
ctx := context.WithValue(context.Background(), xrayKey, v) ctx := context.WithValue(context.Background(), xrayKey, v)
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}}) ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
Tag: "tag", Tag: "tag",
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}), ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
}) })
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146)) conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
_, ok := conn.(*stat.CounterConnection) _, ok := conn.(*stat.CounterConnection)
@@ -73,12 +73,12 @@ func TestOutboundWithStatCounter(t *testing.T) {
} }
v, _ := core.New(config) v, _ := core.New(config)
v.AddFeature(outbound.Manager(new(Manager))) v.AddFeature((outbound.Manager)(new(Manager)))
ctx := context.WithValue(context.Background(), xrayKey, v) ctx := context.WithValue(context.Background(), xrayKey, v)
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}}) ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
Tag: "tag", Tag: "tag",
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}), ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
}) })
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146)) conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
_, ok := conn.(*stat.CounterConnection) _, ok := conn.(*stat.CounterConnection)
@@ -88,6 +88,7 @@ func TestOutboundWithStatCounter(t *testing.T) {
} }
func TestTagsCache(t *testing.T) { func TestTagsCache(t *testing.T) {
test_duration := 10 * time.Second test_duration := 10 * time.Second
threads_num := 50 threads_num := 50
delay := 10 * time.Millisecond delay := 10 * time.Millisecond
@@ -117,7 +118,7 @@ func TestTagsCache(t *testing.T) {
tag := fmt.Sprintf("%s%d", tags_prefix, idx) tag := fmt.Sprintf("%s%d", tags_prefix, idx)
cfg := &core.OutboundHandlerConfig{ cfg := &core.OutboundHandlerConfig{
Tag: tag, Tag: tag,
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}), ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
} }
if h, err := NewHandler(ctx, cfg); err == nil { if h, err := NewHandler(ctx, cfg); err == nil {
if err := ohm.AddHandler(ctx, h); err == nil { if err := ohm.AddHandler(ctx, h); err == nil {
+1
View File
@@ -162,6 +162,7 @@ func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
// Select implements outbound.HandlerSelector. // Select implements outbound.HandlerSelector.
func (m *Manager) Select(selectors []string) []string { func (m *Manager) Select(selectors []string) []string {
key := strings.Join(selectors, ",") key := strings.Join(selectors, ",")
if cache, ok := m.tagsCache.Load(key); ok { if cache, ok := m.tagsCache.Load(key); ok {
return cache.([]string) return cache.([]string)
+35
View File
@@ -0,0 +1,35 @@
package outbound
import (
"context"
"os"
"github.com/sagernet/sing/common/uot"
"github.com/xtls/xray-core/common/errors"
"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 == nil {
return nil, errors.New("nil destination address")
}
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, errors.New("unable to listen socket").Base(err)
}
conn := uot.NewServerConn(packetConn, uotVersion)
return h.getStatCouterConnection(conn), nil
}
+24 -53
View File
@@ -4,11 +4,11 @@ import (
"context" "context"
"time" "time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
@@ -53,11 +53,6 @@ func (b *Bridge) cleanup() {
if w.IsActive() { if w.IsActive() {
activeWorkers = append(activeWorkers, w) activeWorkers = append(activeWorkers, w)
} }
if w.Closed() {
if w.Timer != nil {
w.Timer.SetTimeout(0)
}
}
} }
if len(activeWorkers) != len(b.workers) { if len(activeWorkers) != len(b.workers) {
@@ -99,11 +94,10 @@ func (b *Bridge) Close() error {
} }
type BridgeWorker struct { type BridgeWorker struct {
Tag string tag string
Worker *mux.ServerWorker worker *mux.ServerWorker
Dispatcher routing.Dispatcher dispatcher routing.Dispatcher
State Control_State state Control_State
Timer *signal.ActivityTimer
} }
func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) { func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
@@ -121,20 +115,16 @@ func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWo
} }
w := &BridgeWorker{ w := &BridgeWorker{
Dispatcher: d, dispatcher: d,
Tag: tag, tag: tag,
} }
worker, err := mux.NewServerWorker(context.Background(), w, link) worker, err := mux.NewServerWorker(context.Background(), w, link)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.Worker = worker w.worker = worker
terminate := func() {
worker.Close()
}
w.Timer = signal.CancelAfterInactivity(ctx, terminate, 60*time.Second)
return w, nil return w, nil
} }
@@ -151,15 +141,11 @@ func (w *BridgeWorker) Close() error {
} }
func (w *BridgeWorker) IsActive() bool { func (w *BridgeWorker) IsActive() bool {
return w.State == Control_ACTIVE && !w.Worker.Closed() return w.state == Control_ACTIVE && !w.worker.Closed()
}
func (w *BridgeWorker) Closed() bool {
return w.Worker.Closed()
} }
func (w *BridgeWorker) Connections() uint32 { func (w *BridgeWorker) Connections() uint32 {
return w.Worker.ActiveConnections() return w.worker.ActiveConnections()
} }
func (w *BridgeWorker) handleInternalConn(link *transport.Link) { func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
@@ -167,29 +153,16 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
for { for {
mb, err := reader.ReadMultiBuffer() mb, err := reader.ReadMultiBuffer()
if err != nil { if err != nil {
if w.Timer != nil { break
if w.Closed() {
w.Timer.SetTimeout(0)
} else {
w.Timer.SetTimeout(24 * time.Hour)
}
}
return
}
if w.Timer != nil {
w.Timer.Update()
} }
for _, b := range mb { for _, b := range mb {
var ctl Control var ctl Control
if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil { if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil {
errors.LogInfoInner(context.Background(), err, "failed to parse proto message") errors.LogInfoInner(context.Background(), err, "failed to parse proto message")
if w.Timer != nil { break
w.Timer.SetTimeout(0)
}
return
} }
if ctl.State != w.State { if ctl.State != w.state {
w.State = ctl.State w.state = ctl.State
} }
} }
} }
@@ -197,12 +170,10 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
if !isInternalDomain(dest) { if !isInternalDomain(dest) {
if session.InboundFromContext(ctx) == nil { ctx = session.ContextWithInbound(ctx, &session.Inbound{
ctx = session.ContextWithInbound(ctx, &session.Inbound{ Tag: w.tag,
Tag: w.Tag, })
}) return w.dispatcher.Dispatch(ctx, dest)
}
return w.Dispatcher.Dispatch(ctx, dest)
} }
opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)} opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
@@ -222,13 +193,13 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error { func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error {
if !isInternalDomain(dest) { if !isInternalDomain(dest) {
if session.InboundFromContext(ctx) == nil { ctx = session.ContextWithInbound(ctx, &session.Inbound{
ctx = session.ContextWithInbound(ctx, &session.Inbound{ Tag: w.tag,
Tag: w.Tag, })
}) return w.dispatcher.DispatchLink(ctx, dest, link)
}
return w.Dispatcher.DispatchLink(ctx, dest, link)
} }
link = w.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
w.handleInternalConn(link) w.handleInternalConn(link)
return nil return nil
+61 -42
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/reverse/config.proto // source: app/reverse/config.proto
package reverse package reverse
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -68,11 +67,12 @@ func (Control_State) EnumDescriptor() ([]byte, []int) {
} }
type Control struct { type Control struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
State Control_State `protobuf:"varint,1,opt,name=state,proto3,enum=xray.app.reverse.Control_State" json:"state,omitempty"`
Random []byte `protobuf:"bytes,99,opt,name=random,proto3" json:"random,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
State Control_State `protobuf:"varint,1,opt,name=state,proto3,enum=xray.app.reverse.Control_State" json:"state,omitempty"`
Random []byte `protobuf:"bytes,99,opt,name=random,proto3" json:"random,omitempty"`
} }
func (x *Control) Reset() { func (x *Control) Reset() {
@@ -120,11 +120,12 @@ func (x *Control) GetRandom() []byte {
} }
type BridgeConfig struct { type BridgeConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
} }
func (x *BridgeConfig) Reset() { func (x *BridgeConfig) Reset() {
@@ -172,11 +173,12 @@ func (x *BridgeConfig) GetDomain() string {
} }
type PortalConfig struct { type PortalConfig struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
} }
func (x *PortalConfig) Reset() { func (x *PortalConfig) Reset() {
@@ -224,11 +226,12 @@ func (x *PortalConfig) GetDomain() string {
} }
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
BridgeConfig []*BridgeConfig `protobuf:"bytes,1,rep,name=bridge_config,json=bridgeConfig,proto3" json:"bridge_config,omitempty"`
PortalConfig []*PortalConfig `protobuf:"bytes,2,rep,name=portal_config,json=portalConfig,proto3" json:"portal_config,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BridgeConfig []*BridgeConfig `protobuf:"bytes,1,rep,name=bridge_config,json=bridgeConfig,proto3" json:"bridge_config,omitempty"`
PortalConfig []*PortalConfig `protobuf:"bytes,2,rep,name=portal_config,json=portalConfig,proto3" json:"portal_config,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -277,35 +280,50 @@ func (x *Config) GetPortalConfig() []*PortalConfig {
var File_app_reverse_config_proto protoreflect.FileDescriptor var File_app_reverse_config_proto protoreflect.FileDescriptor
const file_app_reverse_config_proto_rawDesc = "" + var file_app_reverse_config_proto_rawDesc = []byte{
"\n" + 0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2f, 0x63, 0x6f,
"\x18app/reverse/config.proto\x12\x10xray.app.reverse\"x\n" + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
"\aControl\x125\n" + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x07,
"\x05state\x18\x01 \x01(\x0e2\x1f.xray.app.reverse.Control.StateR\x05state\x12\x16\n" + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
"\x06random\x18c \x01(\fR\x06random\"\x1e\n" + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
"\x05State\x12\n" + 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
"\n" + 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16,
"\x06ACTIVE\x10\x00\x12\t\n" + 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06,
"\x05DRAIN\x10\x01\"8\n" + 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
"\fBridgeConfig\x12\x10\n" + 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + 0x52, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x22, 0x38, 0x0a, 0x0c, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65,
"\x06domain\x18\x02 \x01(\tR\x06domain\"8\n" + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
"\fPortalConfig\x12\x10\n" + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
"\x06domain\x18\x02 \x01(\tR\x06domain\"\x92\x01\n" + 0x22, 0x38, 0x0a, 0x0c, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
"\x06Config\x12C\n" + 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74,
"\rbridge_config\x18\x01 \x03(\v2\x1e.xray.app.reverse.BridgeConfigR\fbridgeConfig\x12C\n" + 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01,
"\rportal_config\x18\x02 \x03(\v2\x1e.xray.app.reverse.PortalConfigR\fportalConfigBV\n" + 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x92, 0x01, 0x0a, 0x06, 0x43,
"\x16com.xray.proxy.reverseP\x01Z%github.com/xtls/xray-core/app/reverse\xaa\x02\x12Xray.Proxy.Reverseb\x06proto3" 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e,
0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x62, 0x72,
0x69, 0x64, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x70, 0x6f,
0x72, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76,
0x65, 0x72, 0x73, 0x65, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x0c, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
0x56, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 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, 0x72, 0x65, 0x76, 0x65, 0x72,
0x73, 0x65, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_reverse_config_proto_rawDescOnce sync.Once file_app_reverse_config_proto_rawDescOnce sync.Once
file_app_reverse_config_proto_rawDescData []byte file_app_reverse_config_proto_rawDescData = file_app_reverse_config_proto_rawDesc
) )
func file_app_reverse_config_proto_rawDescGZIP() []byte { func file_app_reverse_config_proto_rawDescGZIP() []byte {
file_app_reverse_config_proto_rawDescOnce.Do(func() { file_app_reverse_config_proto_rawDescOnce.Do(func() {
file_app_reverse_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc))) file_app_reverse_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_reverse_config_proto_rawDescData)
}) })
return file_app_reverse_config_proto_rawDescData return file_app_reverse_config_proto_rawDescData
} }
@@ -339,7 +357,7 @@ func file_app_reverse_config_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc)), RawDescriptor: file_app_reverse_config_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 4, NumMessages: 4,
NumExtensions: 0, NumExtensions: 0,
@@ -351,6 +369,7 @@ func file_app_reverse_config_proto_init() {
MessageInfos: file_app_reverse_config_proto_msgTypes, MessageInfos: file_app_reverse_config_proto_msgTypes,
}.Build() }.Build()
File_app_reverse_config_proto = out.File File_app_reverse_config_proto = out.File
file_app_reverse_config_proto_rawDesc = nil
file_app_reverse_config_proto_goTypes = nil file_app_reverse_config_proto_goTypes = nil
file_app_reverse_config_proto_depIdxs = nil file_app_reverse_config_proto_depIdxs = nil
} }
+1 -22
View File
@@ -12,7 +12,6 @@ import (
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
@@ -83,21 +82,9 @@ func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) err
} }
p.picker.AddWorker(worker) p.picker.AddWorker(worker)
if _, ok := link.Reader.(*pipe.Reader); !ok {
select {
case <-ctx.Done():
case <-muxClient.WaitClosed():
}
}
return nil return nil
} }
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
}
return p.client.Dispatch(ctx, link) return p.client.Dispatch(ctx, link)
} }
@@ -114,7 +101,6 @@ func (o *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
if err := o.portal.HandleConnection(ctx, link); err != nil { if err := o.portal.HandleConnection(ctx, link); err != nil {
errors.LogInfoInner(ctx, err, "failed to process reverse connection") errors.LogInfoInner(ctx, err, "failed to process reverse connection")
common.Interrupt(link.Writer) common.Interrupt(link.Writer)
common.Interrupt(link.Reader)
} }
} }
@@ -160,8 +146,6 @@ func (p *StaticMuxPicker) cleanup() error {
for _, w := range p.workers { for _, w := range p.workers {
if !w.Closed() { if !w.Closed() {
activeWorkers = append(activeWorkers, w) activeWorkers = append(activeWorkers, w)
} else {
w.timer.SetTimeout(0)
} }
} }
@@ -228,7 +212,6 @@ type PortalWorker struct {
reader buf.Reader reader buf.Reader
draining bool draining bool
counter uint32 counter uint32
timer *signal.ActivityTimer
} }
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) { func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
@@ -248,14 +231,10 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
if !f { if !f {
return nil, errors.New("unable to dispatch control connection") return nil, errors.New("unable to dispatch control connection")
} }
terminate := func() {
client.Close()
}
w := &PortalWorker{ w := &PortalWorker{
client: client, client: client,
reader: downlinkReader, reader: downlinkReader,
writer: uplinkWriter, writer: uplinkWriter,
timer: signal.CancelAfterInactivity(ctx, terminate, 24*time.Hour), // // prevent leak
} }
w.control = &task.Periodic{ w.control = &task.Periodic{
Execute: w.heartbeat, Execute: w.heartbeat,
@@ -282,6 +261,7 @@ func (w *PortalWorker) heartbeat() error {
msg.State = Control_DRAIN msg.State = Control_DRAIN
defer func() { defer func() {
w.client.GetTimer().Reset(time.Second * 16)
common.Close(w.writer) common.Close(w.writer)
common.Interrupt(w.reader) common.Interrupt(w.reader)
w.writer = nil w.writer = nil
@@ -293,7 +273,6 @@ func (w *PortalWorker) heartbeat() error {
b, err := proto.Marshal(msg) b, err := proto.Marshal(msg)
common.Must(err) common.Must(err)
mb := buf.MergeBytes(nil, b) mb := buf.MergeBytes(nil, b)
w.timer.Update()
return w.writer.WriteMultiBuffer(mb) return w.writer.WriteMultiBuffer(mb)
} }
return nil return nil
+1 -1
View File
@@ -12,7 +12,7 @@ import (
) )
const ( const (
internalDomain = "reverse" internalDomain = "reverse.internal.v2fly.org" // make reverse proxy compatible with v2fly
) )
func isDomain(dest net.Destination, domain string) bool { func isDomain(dest net.Destination, domain string) bool {
+1 -15
View File
@@ -58,8 +58,8 @@ func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*
return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend) return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend)
} }
return nil, errors.New("unsupported router implementation") return nil, errors.New("unsupported router implementation")
}
}
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) { func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok { if bo, ok := s.router.(routing.Router); ok {
return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag) return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag)
@@ -67,20 +67,6 @@ func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleReque
return nil, errors.New("unsupported router implementation") return nil, errors.New("unsupported router implementation")
} }
func (s *routingServer) ListRule(ctx context.Context, request *ListRuleRequest) (*ListRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok {
response := &ListRuleResponse{}
for _, v := range bo.ListRule() {
response.Rules = append(response.Rules, &ListRuleItem{
Tag: v.GetOutboundTag(),
RuleTag: v.GetRuleTag(),
})
}
return response, nil
}
return nil, errors.New("unsupported router implementation")
}
// NewRoutingServer creates a statistics service with statistics manager. // NewRoutingServer creates a statistics service with statistics manager.
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer { func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
return &routingServer{ return &routingServer{
+272 -315
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.35.1
// protoc v6.33.5 // protoc v5.28.2
// source: app/router/command/command.proto // source: app/router/command/command.proto
package command package command
@@ -13,7 +13,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@@ -27,24 +26,25 @@ const (
// It conforms to the structure of xray.features.routing.Context and // It conforms to the structure of xray.features.routing.Context and
// xray.features.routing.Route. // xray.features.routing.Route.
type RoutingContext struct { type RoutingContext struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"` sizeCache protoimpl.SizeCache
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"` unknownFields protoimpl.UnknownFields
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"` InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"` Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"` SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"` TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"` SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"` TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"` Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"` User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"` Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"` OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"` OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
unknownFields protoimpl.UnknownFields LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
sizeCache protoimpl.SizeCache LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
} }
func (x *RoutingContext) Reset() { func (x *RoutingContext) Reset() {
@@ -201,10 +201,11 @@ func (x *RoutingContext) GetVlessRoute() uint32 {
// //
// * If FieldSelectors is left empty, all fields will be returned. // * If FieldSelectors is left empty, all fields will be returned.
type SubscribeRoutingStatsRequest struct { type SubscribeRoutingStatsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"` sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
} }
func (x *SubscribeRoutingStatsRequest) Reset() { func (x *SubscribeRoutingStatsRequest) Reset() {
@@ -252,12 +253,13 @@ func (x *SubscribeRoutingStatsRequest) GetFieldSelectors() []string {
// * PublishResult broadcasts the routing result to routing statistics channel // * PublishResult broadcasts the routing result to routing statistics channel
// if set true. // if set true.
type TestRouteRequest struct { type TestRouteRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"` sizeCache protoimpl.SizeCache
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"` unknownFields protoimpl.UnknownFields
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
unknownFields protoimpl.UnknownFields RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
sizeCache protoimpl.SizeCache FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
} }
func (x *TestRouteRequest) Reset() { func (x *TestRouteRequest) Reset() {
@@ -312,10 +314,11 @@ func (x *TestRouteRequest) GetPublishResult() bool {
} }
type PrincipleTargetInfo struct { type PrincipleTargetInfo struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
} }
func (x *PrincipleTargetInfo) Reset() { func (x *PrincipleTargetInfo) Reset() {
@@ -356,10 +359,11 @@ func (x *PrincipleTargetInfo) GetTag() []string {
} }
type OverrideInfo struct { type OverrideInfo struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
} }
func (x *OverrideInfo) Reset() { func (x *OverrideInfo) Reset() {
@@ -400,11 +404,12 @@ func (x *OverrideInfo) GetTarget() string {
} }
type BalancerMsg struct { type BalancerMsg struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"` sizeCache protoimpl.SizeCache
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"` unknownFields protoimpl.UnknownFields
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
} }
func (x *BalancerMsg) Reset() { func (x *BalancerMsg) Reset() {
@@ -452,10 +457,11 @@ func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo {
} }
type GetBalancerInfoRequest struct { type GetBalancerInfoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
} }
func (x *GetBalancerInfoRequest) Reset() { func (x *GetBalancerInfoRequest) Reset() {
@@ -496,10 +502,11 @@ func (x *GetBalancerInfoRequest) GetTag() string {
} }
type GetBalancerInfoResponse struct { type GetBalancerInfoResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
} }
func (x *GetBalancerInfoResponse) Reset() { func (x *GetBalancerInfoResponse) Reset() {
@@ -540,11 +547,12 @@ func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg {
} }
type OverrideBalancerTargetRequest struct { type OverrideBalancerTargetRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
} }
func (x *OverrideBalancerTargetRequest) Reset() { func (x *OverrideBalancerTargetRequest) Reset() {
@@ -592,9 +600,9 @@ func (x *OverrideBalancerTargetRequest) GetTarget() string {
} }
type OverrideBalancerTargetResponse struct { type OverrideBalancerTargetResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *OverrideBalancerTargetResponse) Reset() { func (x *OverrideBalancerTargetResponse) Reset() {
@@ -628,11 +636,12 @@ func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) {
} }
type AddRuleRequest struct { type AddRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
} }
func (x *AddRuleRequest) Reset() { func (x *AddRuleRequest) Reset() {
@@ -680,9 +689,9 @@ func (x *AddRuleRequest) GetShouldAppend() bool {
} }
type AddRuleResponse struct { type AddRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *AddRuleResponse) Reset() { func (x *AddRuleResponse) Reset() {
@@ -716,10 +725,11 @@ func (*AddRuleResponse) Descriptor() ([]byte, []int) {
} }
type RemoveRuleRequest struct { type RemoveRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
} }
func (x *RemoveRuleRequest) Reset() { func (x *RemoveRuleRequest) Reset() {
@@ -760,9 +770,9 @@ func (x *RemoveRuleRequest) GetRuleTag() string {
} }
type RemoveRuleResponse struct { type RemoveRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *RemoveRuleResponse) Reset() { func (x *RemoveRuleResponse) Reset() {
@@ -795,147 +805,15 @@ func (*RemoveRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{13} return file_app_router_command_command_proto_rawDescGZIP(), []int{13}
} }
type ListRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleRequest) Reset() {
*x = ListRuleRequest{}
mi := &file_app_router_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleRequest) ProtoMessage() {}
func (x *ListRuleRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListRuleRequest.ProtoReflect.Descriptor instead.
func (*ListRuleRequest) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
}
type ListRuleItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
RuleTag string `protobuf:"bytes,2,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleItem) Reset() {
*x = ListRuleItem{}
mi := &file_app_router_command_command_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleItem) ProtoMessage() {}
func (x *ListRuleItem) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListRuleItem.ProtoReflect.Descriptor instead.
func (*ListRuleItem) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{15}
}
func (x *ListRuleItem) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
func (x *ListRuleItem) GetRuleTag() string {
if x != nil {
return x.RuleTag
}
return ""
}
type ListRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Rules []*ListRuleItem `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleResponse) Reset() {
*x = ListRuleResponse{}
mi := &file_app_router_command_command_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleResponse) ProtoMessage() {}
func (x *ListRuleResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListRuleResponse.ProtoReflect.Descriptor instead.
func (*ListRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{16}
}
func (x *ListRuleResponse) GetRules() []*ListRuleItem {
if x != nil {
return x.Rules
}
return nil
}
type Config struct { type Config struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
} }
func (x *Config) Reset() { func (x *Config) Reset() {
*x = Config{} *x = Config{}
mi := &file_app_router_command_command_proto_msgTypes[17] mi := &file_app_router_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -947,7 +825,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {} func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message { func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[17] mi := &file_app_router_command_command_proto_msgTypes[14]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -960,103 +838,187 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead. // Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) { func (*Config) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{17} return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
} }
var File_app_router_command_command_proto protoreflect.FileDescriptor var File_app_router_command_command_proto protoreflect.FileDescriptor
const file_app_router_command_command_proto_rawDesc = "" + var file_app_router_command_command_proto_rawDesc = []byte{
"\n" + 0x0a, 0x20, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d,
" app/router/command/command.proto\x12\x17xray.app.router.command\x1a\x18common/net/network.proto\x1a!common/serial/typed_message.proto\"\xf6\x04\n" + 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f,
"\x0eRoutingContext\x12\x1e\n" + 0x74, 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
"\n" + 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x63, 0x6f, 0x6d,
"InboundTag\x18\x01 \x01(\tR\n" + 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e,
"InboundTag\x122\n" + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65,
"\aNetwork\x18\x02 \x01(\x0e2\x18.xray.common.net.NetworkR\aNetwork\x12\x1c\n" + 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
"\tSourceIPs\x18\x03 \x03(\fR\tSourceIPs\x12\x1c\n" + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75,
"\tTargetIPs\x18\x04 \x03(\fR\tTargetIPs\x12\x1e\n" + 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49,
"\n" + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
"SourcePort\x18\x05 \x01(\rR\n" + 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e,
"SourcePort\x12\x1e\n" + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78,
"\n" + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e,
"TargetPort\x18\x06 \x01(\rR\n" + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12,
"TargetPort\x12\"\n" + 0x1c, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03,
"\fTargetDomain\x18\a \x01(\tR\fTargetDomain\x12\x1a\n" + 0x28, 0x0c, 0x52, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a,
"\bProtocol\x18\b \x01(\tR\bProtocol\x12\x12\n" + 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c,
"\x04User\x18\t \x01(\tR\x04User\x12W\n" + 0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53,
"\n" + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
"Attributes\x18\n" + 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54,
" \x03(\v27.xray.app.router.command.RoutingContext.AttributesEntryR\n" + 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
"Attributes\x12,\n" + 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54,
"\x11OutboundGroupTags\x18\v \x03(\tR\x11OutboundGroupTags\x12 \n" + 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
"\vOutboundTag\x18\f \x01(\tR\vOutboundTag\x12\x1a\n" + 0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
"\bLocalIPs\x18\r \x03(\fR\bLocalIPs\x12\x1c\n" + 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28,
"\tLocalPort\x18\x0e \x01(\rR\tLocalPort\x12\x1e\n" + 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55,
"\n" + 0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
"VlessRoute\x18\x0f \x01(\rR\n" + 0x57, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
"VlessRoute\x1a=\n" + 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
"\x0fAttributesEntry\x12\x10\n" + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f,
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74,
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"F\n" + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74,
"\x1cSubscribeRoutingStatsRequest\x12&\n" + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62,
"\x0eFieldSelectors\x18\x01 \x03(\tR\x0eFieldSelectors\"\xb1\x01\n" + 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20,
"\x10TestRouteRequest\x12O\n" + 0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f,
"\x0eRoutingContext\x18\x01 \x01(\v2'.xray.app.router.command.RoutingContextR\x0eRoutingContext\x12&\n" + 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
"\x0eFieldSelectors\x18\x02 \x03(\tR\x0eFieldSelectors\x12$\n" + 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74,
"\rPublishResult\x18\x03 \x01(\bR\rPublishResult\"'\n" + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61,
"\x13PrincipleTargetInfo\x12\x10\n" + 0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61,
"\x03tag\x18\x01 \x03(\tR\x03tag\"&\n" + 0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72,
"\fOverrideInfo\x12\x16\n" + 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
"\x06target\x18\x02 \x01(\tR\x06target\"\xa9\x01\n" + 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65,
"\vBalancerMsg\x12A\n" + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75,
"\boverride\x18\x05 \x01(\v2%.xray.app.router.command.OverrideInfoR\boverride\x12W\n" + 0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
"\x10principle_target\x18\x06 \x01(\v2,.xray.app.router.command.PrincipleTargetInfoR\x0fprincipleTarget\"*\n" + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
"\x16GetBalancerInfoRequest\x12\x10\n" + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
"\x03tag\x18\x01 \x01(\tR\x03tag\"[\n" + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
"\x17GetBalancerInfoResponse\x12@\n" + 0x01, 0x22, 0x46, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f,
"\bbalancer\x18\x01 \x01(\v2$.xray.app.router.command.BalancerMsgR\bbalancer\"Y\n" + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
"\x1dOverrideBalancerTargetRequest\x12 \n" + 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
"\vbalancerTag\x18\x01 \x01(\tR\vbalancerTag\x12\x16\n" + 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
"\x06target\x18\x02 \x01(\tR\x06target\" \n" + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65,
"\x1eOverrideBalancerTargetResponse\"n\n" + 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
"\x0eAddRuleRequest\x128\n" + 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
"\x06config\x18\x01 \x01(\v2 .xray.common.serial.TypedMessageR\x06config\x12\"\n" + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
"\fshouldAppend\x18\x02 \x01(\bR\fshouldAppend\"\x11\n" + 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
"\x0fAddRuleResponse\"-\n" + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52,
"\x11RemoveRuleRequest\x12\x18\n" + 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12,
"\aruleTag\x18\x01 \x01(\tR\aruleTag\"\x14\n" + 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
"\x12RemoveRuleResponse\"\x11\n" + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
"\x0fListRuleRequest\":\n" + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69,
"\fListRuleItem\x12\x10\n" + 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x18\n" + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a,
"\aruleTag\x18\x02 \x01(\tR\aruleTag\"O\n" + 0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
"\x10ListRuleResponse\x12;\n" + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28,
"\x05rules\x18\x01 \x03(\v2%.xray.app.router.command.ListRuleItemR\x05rules\"\b\n" + 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
"\x06Config2\xa2\x06\n" + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
"\x0eRoutingService\x12{\n" + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xa9,
"\x15SubscribeRoutingStats\x125.xray.app.router.command.SubscribeRoutingStatsRequest\x1a'.xray.app.router.command.RoutingContext\"\x000\x01\x12a\n" + 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x41,
"\tTestRoute\x12).xray.app.router.command.TestRouteRequest\x1a'.xray.app.router.command.RoutingContext\"\x00\x12v\n" + 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
"\x0fGetBalancerInfo\x12/.xray.app.router.command.GetBalancerInfoRequest\x1a0.xray.app.router.command.GetBalancerInfoResponse\"\x00\x12\x8b\x01\n" + 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
"\x16OverrideBalancerTarget\x126.xray.app.router.command.OverrideBalancerTargetRequest\x1a7.xray.app.router.command.OverrideBalancerTargetResponse\"\x00\x12^\n" + 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
"\aAddRule\x12'.xray.app.router.command.AddRuleRequest\x1a(.xray.app.router.command.AddRuleResponse\"\x00\x12g\n" + 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
"\n" + 0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74,
"RemoveRule\x12*.xray.app.router.command.RemoveRuleRequest\x1a+.xray.app.router.command.RemoveRuleResponse\"\x00\x12a\n" + 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
"\bListRule\x12(.xray.app.router.command.ListRuleRequest\x1a).xray.app.router.command.ListRuleResponse\"\x00Bg\n" + 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f,
"\x1bcom.xray.app.router.commandP\x01Z,github.com/xtls/xray-core/app/router/command\xaa\x02\x17Xray.App.Router.Commandb\x06proto3" 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65,
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x42, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x1d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x20,
0x0a, 0x1e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x6e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 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, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c,
0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64,
0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54,
0x61, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x32, 0xbf, 0x05, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x35,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00,
0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f,
0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74,
0x65, 0x78, 0x74, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e,
0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49,
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8b, 0x01,
0x0a, 0x16, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x07, 0x41,
0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0a, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2c, 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, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var ( var (
file_app_router_command_command_proto_rawDescOnce sync.Once file_app_router_command_command_proto_rawDescOnce sync.Once
file_app_router_command_command_proto_rawDescData []byte file_app_router_command_command_proto_rawDescData = file_app_router_command_command_proto_rawDesc
) )
func file_app_router_command_command_proto_rawDescGZIP() []byte { func file_app_router_command_command_proto_rawDescGZIP() []byte {
file_app_router_command_command_proto_rawDescOnce.Do(func() { file_app_router_command_command_proto_rawDescOnce.Do(func() {
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc))) file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_router_command_command_proto_rawDescData)
}) })
return file_app_router_command_command_proto_rawDescData return file_app_router_command_command_proto_rawDescData
} }
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_app_router_command_command_proto_goTypes = []any{ var file_app_router_command_command_proto_goTypes = []any{
(*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext (*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext
(*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest (*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest
@@ -1072,42 +1034,36 @@ var file_app_router_command_command_proto_goTypes = []any{
(*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse (*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse
(*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest (*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest
(*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse (*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse
(*ListRuleRequest)(nil), // 14: xray.app.router.command.ListRuleRequest (*Config)(nil), // 14: xray.app.router.command.Config
(*ListRuleItem)(nil), // 15: xray.app.router.command.ListRuleItem nil, // 15: xray.app.router.command.RoutingContext.AttributesEntry
(*ListRuleResponse)(nil), // 16: xray.app.router.command.ListRuleResponse (net.Network)(0), // 16: xray.common.net.Network
(*Config)(nil), // 17: xray.app.router.command.Config (*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
nil, // 18: xray.app.router.command.RoutingContext.AttributesEntry
(net.Network)(0), // 19: xray.common.net.Network
(*serial.TypedMessage)(nil), // 20: xray.common.serial.TypedMessage
} }
var file_app_router_command_command_proto_depIdxs = []int32{ var file_app_router_command_command_proto_depIdxs = []int32{
19, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network 16, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
18, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry 15, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
0, // 2: xray.app.router.command.TestRouteRequest.RoutingContext:type_name -> xray.app.router.command.RoutingContext 0, // 2: xray.app.router.command.TestRouteRequest.RoutingContext:type_name -> xray.app.router.command.RoutingContext
4, // 3: xray.app.router.command.BalancerMsg.override:type_name -> xray.app.router.command.OverrideInfo 4, // 3: xray.app.router.command.BalancerMsg.override:type_name -> xray.app.router.command.OverrideInfo
3, // 4: xray.app.router.command.BalancerMsg.principle_target:type_name -> xray.app.router.command.PrincipleTargetInfo 3, // 4: xray.app.router.command.BalancerMsg.principle_target:type_name -> xray.app.router.command.PrincipleTargetInfo
5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg 5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg
20, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage 17, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
15, // 7: xray.app.router.command.ListRuleResponse.rules:type_name -> xray.app.router.command.ListRuleItem 1, // 7: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
1, // 8: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest 2, // 8: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
2, // 9: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest 6, // 9: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
6, // 10: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest 8, // 10: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
8, // 11: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest 10, // 11: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
10, // 12: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest 12, // 12: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
12, // 13: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest 0, // 13: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
14, // 14: xray.app.router.command.RoutingService.ListRule:input_type -> xray.app.router.command.ListRuleRequest 0, // 14: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
0, // 15: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext 7, // 15: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
0, // 16: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext 9, // 16: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
7, // 17: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse 11, // 17: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
9, // 18: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse 13, // 18: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
11, // 19: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse 13, // [13:19] is the sub-list for method output_type
13, // 20: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse 7, // [7:13] is the sub-list for method input_type
16, // 21: xray.app.router.command.RoutingService.ListRule:output_type -> xray.app.router.command.ListRuleResponse 7, // [7:7] is the sub-list for extension type_name
15, // [15:22] is the sub-list for method output_type 7, // [7:7] is the sub-list for extension extendee
8, // [8:15] is the sub-list for method input_type 0, // [0:7] is the sub-list for field type_name
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
} }
func init() { file_app_router_command_command_proto_init() } func init() { file_app_router_command_command_proto_init() }
@@ -1119,9 +1075,9 @@ func file_app_router_command_command_proto_init() {
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)), RawDescriptor: file_app_router_command_command_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 19, NumMessages: 16,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
@@ -1130,6 +1086,7 @@ func file_app_router_command_command_proto_init() {
MessageInfos: file_app_router_command_command_proto_msgTypes, MessageInfos: file_app_router_command_command_proto_msgTypes,
}.Build() }.Build()
File_app_router_command_command_proto = out.File File_app_router_command_command_proto = out.File
file_app_router_command_command_proto_rawDesc = nil
file_app_router_command_command_proto_goTypes = nil file_app_router_command_command_proto_goTypes = nil
file_app_router_command_command_proto_depIdxs = nil file_app_router_command_command_proto_depIdxs = nil
} }
-13
View File
@@ -104,17 +104,6 @@ message RemoveRuleRequest {
message RemoveRuleResponse {} message RemoveRuleResponse {}
message ListRuleRequest {}
message ListRuleItem {
string tag = 1;
string ruleTag = 2;
}
message ListRuleResponse{
repeated ListRuleItem rules = 1;
}
service RoutingService { service RoutingService {
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest) rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
returns (stream RoutingContext) {} returns (stream RoutingContext) {}
@@ -125,8 +114,6 @@ service RoutingService {
rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {} rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {}
rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {} rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {}
rpc ListRule(ListRuleRequest) returns (ListRuleResponse) {}
} }
message Config {} message Config {}
+9 -47
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.0 // - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.5 // - protoc v5.28.2
// source: app/router/command/command.proto // source: app/router/command/command.proto
package command package command
@@ -25,7 +25,6 @@ const (
RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget" RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget"
RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule" RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule"
RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule" RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule"
RoutingService_ListRule_FullMethodName = "/xray.app.router.command.RoutingService/ListRule"
) )
// RoutingServiceClient is the client API for RoutingService service. // RoutingServiceClient is the client API for RoutingService service.
@@ -38,7 +37,6 @@ type RoutingServiceClient interface {
OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error)
AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error) AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error)
RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error) RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error)
ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error)
} }
type routingServiceClient struct { type routingServiceClient struct {
@@ -118,16 +116,6 @@ func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleReq
return out, nil return out, nil
} }
func (c *routingServiceClient) ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRuleResponse)
err := c.cc.Invoke(ctx, RoutingService_ListRule_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RoutingServiceServer is the server API for RoutingService service. // RoutingServiceServer is the server API for RoutingService service.
// All implementations must embed UnimplementedRoutingServiceServer // All implementations must embed UnimplementedRoutingServiceServer
// for forward compatibility. // for forward compatibility.
@@ -138,7 +126,6 @@ type RoutingServiceServer interface {
OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error)
AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error)
RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error)
ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error)
mustEmbedUnimplementedRoutingServiceServer() mustEmbedUnimplementedRoutingServiceServer()
} }
@@ -150,25 +137,22 @@ type RoutingServiceServer interface {
type UnimplementedRoutingServiceServer struct{} type UnimplementedRoutingServiceServer struct{}
func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error { func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error {
return status.Error(codes.Unimplemented, "method SubscribeRoutingStats not implemented") return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
} }
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) { func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
return nil, status.Error(codes.Unimplemented, "method TestRoute not implemented") return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
} }
func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) { func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetBalancerInfo not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented")
} }
func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) { func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
return nil, status.Error(codes.Unimplemented, "method OverrideBalancerTarget not implemented") return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
} }
func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) { func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddRule not implemented") return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented")
} }
func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) { func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveRule not implemented") return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented")
}
func (UnimplementedRoutingServiceServer) ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListRule not implemented")
} }
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {} func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {} func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {}
@@ -181,7 +165,7 @@ type UnsafeRoutingServiceServer interface {
} }
func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) { func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) {
// If the following call panics, it indicates UnimplementedRoutingServiceServer was // If the following call pancis, it indicates UnimplementedRoutingServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
@@ -292,24 +276,6 @@ func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _RoutingService_ListRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRuleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoutingServiceServer).ListRule(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoutingService_ListRule_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoutingServiceServer).ListRule(ctx, req.(*ListRuleRequest))
}
return interceptor(ctx, in, info, handler)
}
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service. // RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@@ -337,10 +303,6 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{
MethodName: "RemoveRule", MethodName: "RemoveRule",
Handler: _RoutingService_RemoveRule_Handler, Handler: _RoutingService_RemoveRule_Handler,
}, },
{
MethodName: "ListRule",
Handler: _RoutingService_ListRule_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
+3 -4
View File
@@ -12,7 +12,6 @@ import (
. "github.com/xtls/xray-core/app/router/command" . "github.com/xtls/xray-core/app/router/command"
"github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/testing/mocks" "github.com/xtls/xray-core/testing/mocks"
@@ -304,12 +303,12 @@ func TestServiceTestRoute(t *testing.T) {
TargetTag: &router.RoutingRule_Tag{Tag: "out"}, TargetTag: &router.RoutingRule_Tag{Tag: "out"},
}, },
{ {
Domain: []*geodata.DomainRule{{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "com"}}}}, Domain: []*router.Domain{{Type: router.Domain_Domain, Value: "com"}},
TargetTag: &router.RoutingRule_Tag{Tag: "out"}, TargetTag: &router.RoutingRule_Tag{Tag: "out"},
}, },
{ {
SourceIp: []*geodata.IPRule{{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}}}, SourceGeoip: []*router.GeoIP{{CountryCode: "private", Cidr: []*router.CIDR{{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}},
TargetTag: &router.RoutingRule_Tag{Tag: "out"}, TargetTag: &router.RoutingRule_Tag{Tag: "out"},
}, },
{ {
UserEmail: []string{"example@example.com"}, UserEmail: []string{"example@example.com"},
+81 -149
View File
@@ -1,18 +1,13 @@
package router package router
import ( import (
"context"
"os"
"path/filepath"
"regexp" "regexp"
"slices"
"strings" "strings"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/routing/dns"
) )
type Condition interface { type Condition interface {
@@ -45,18 +40,51 @@ func (v *ConditionChan) Len() int {
return len(*v) return len(*v)
} }
type DomainMatcher struct{ geodata.DomainMatcher } var matcherTypeMap = map[Domain_Type]strmatcher.Type{
Domain_Plain: strmatcher.Substr,
Domain_Regex: strmatcher.Regex,
Domain_Domain: strmatcher.Domain,
Domain_Full: strmatcher.Full,
}
func NewDomainMatcher(rules []*geodata.DomainRule) (*DomainMatcher, error) { func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
m, err := geodata.DomainReg.BuildDomainMatcher(rules) matcherType, f := matcherTypeMap[domain.Type]
if err != nil { if !f {
return nil, err return nil, errors.New("unsupported domain type", domain.Type)
} }
return &DomainMatcher{DomainMatcher: m}, nil
matcher, err := matcherType.New(domain.Value)
if err != nil {
return nil, errors.New("failed to create domain matcher").Base(err)
}
return matcher, nil
}
type DomainMatcher struct {
matchers strmatcher.IndexMatcher
}
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
g := strmatcher.NewMphMatcherGroup()
for _, d := range domains {
matcherType, f := matcherTypeMap[d.Type]
if !f {
return nil, errors.New("unsupported domain type", d.Type)
}
_, err := g.AddPattern(d.Value, matcherType)
if err != nil {
return nil, err
}
}
g.Build()
return &DomainMatcher{
matchers: g,
}, nil
} }
func (m *DomainMatcher) ApplyDomain(domain string) bool { func (m *DomainMatcher) ApplyDomain(domain string) bool {
return m.DomainMatcher.MatchAny(strings.ToLower(domain)) return len(m.matchers.Match(strings.ToLower(domain))) > 0
} }
// Apply implements Condition. // Apply implements Condition.
@@ -65,56 +93,64 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
if len(domain) == 0 { if len(domain) == 0 {
return false return false
} }
return m.DomainMatcher.MatchAny(strings.ToLower(domain)) return m.ApplyDomain(domain)
} }
type MatcherAsType byte type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher
const ( asType string // local, source, target
MatcherAsType_Local MatcherAsType = iota
MatcherAsType_Source
MatcherAsType_Target
MatcherAsType_VlessRoute // for port
)
type IPMatcher struct {
matcher geodata.IPMatcher
asType MatcherAsType
} }
func NewIPMatcher(rules []*geodata.IPRule, asType MatcherAsType) (*IPMatcher, error) { func NewMultiGeoIPMatcher(geoips []*GeoIP, asType string) (*MultiGeoIPMatcher, error) {
m, err := geodata.IPReg.BuildIPMatcher(rules) var matchers []*GeoIPMatcher
if err != nil { for _, geoip := range geoips {
return nil, err matcher, err := GlobalGeoIPContainer.Add(geoip)
if err != nil {
return nil, err
}
matchers = append(matchers, matcher)
} }
return &IPMatcher{matcher: m, asType: asType}, nil
matcher := &MultiGeoIPMatcher{
matchers: matchers,
asType: asType,
}
return matcher, nil
} }
// Apply implements Condition. // Apply implements Condition.
func (m *IPMatcher) Apply(ctx routing.Context) bool { func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
var ips []net.IP var ips []net.IP
switch m.asType { switch m.asType {
case MatcherAsType_Local: case "local":
ips = ctx.GetLocalIPs() ips = ctx.GetLocalIPs()
case MatcherAsType_Source: case "source":
ips = ctx.GetSourceIPs() ips = ctx.GetSourceIPs()
case MatcherAsType_Target: case "target":
ips = ctx.GetTargetIPs() ips = ctx.GetTargetIPs()
default: default:
panic("unk asType") panic("unreachable, asType should be local or source or target")
} }
return m.matcher.AnyMatch(ips) for _, ip := range ips {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
return true
}
}
}
return false
} }
type PortMatcher struct { type PortMatcher struct {
port net.MemoryPortList port net.MemoryPortList
asType MatcherAsType asType string // local, source, target
} }
// NewPortMatcher create a new port matcher that can match source or local or destination port // NewPortMatcher create a new port matcher that can match source or local or destination port
func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher { func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
return &PortMatcher{ return &PortMatcher{
port: net.PortListFromProto(list), port: net.PortListFromProto(list),
asType: asType, asType: asType,
@@ -124,17 +160,18 @@ func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher {
// Apply implements Condition. // Apply implements Condition.
func (v *PortMatcher) Apply(ctx routing.Context) bool { func (v *PortMatcher) Apply(ctx routing.Context) bool {
switch v.asType { switch v.asType {
case MatcherAsType_Local: case "local":
return v.port.Contains(ctx.GetLocalPort()) return v.port.Contains(ctx.GetLocalPort())
case MatcherAsType_Source: case "source":
return v.port.Contains(ctx.GetSourcePort()) return v.port.Contains(ctx.GetSourcePort())
case MatcherAsType_Target: case "target":
return v.port.Contains(ctx.GetTargetPort()) return v.port.Contains(ctx.GetTargetPort())
case MatcherAsType_VlessRoute: case "vlessRoute":
return v.port.Contains(ctx.GetVlessRoute()) return v.port.Contains(ctx.GetVlessRoute())
default: default:
panic("unk asType") panic("unreachable, asType should be local or source or target")
} }
} }
type NetworkMatcher struct { type NetworkMatcher struct {
@@ -288,108 +325,3 @@ func (m *AttributeMatcher) Apply(ctx routing.Context) bool {
} }
return m.Match(attributes) return m.Match(attributes)
} }
type ProcessNameMatcher struct {
ProcessNames []string
AbsPaths []string
Folders []string
MatchXraySelf bool
}
func NewProcessNameMatcher(names []string) *ProcessNameMatcher {
processNames := []string{}
folders := []string{}
absPaths := []string{}
matchXraySelf := false
for _, name := range names {
if name == "self/" {
matchXraySelf = true
continue
}
// replace xray/ with self executable path
if name == "xray/" {
xrayPath, err := os.Executable()
if err != nil {
errors.LogError(context.Background(), "Failed to get xray executable path: ", err)
continue
}
name = xrayPath
}
name := filepath.ToSlash(name)
// /usr/bin/
if strings.HasSuffix(name, "/") {
folders = append(folders, name)
continue
}
// /usr/bin/curl
if strings.Contains(name, "/") {
absPaths = append(absPaths, name)
continue
}
// curl.exe or curl
processNames = append(processNames, strings.TrimSuffix(name, ".exe"))
}
return &ProcessNameMatcher{
ProcessNames: processNames,
AbsPaths: absPaths,
Folders: folders,
MatchXraySelf: matchXraySelf,
}
}
func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
if len(ctx.GetSourceIPs()) == 0 {
return false
}
srcPort := uint16(ctx.GetSourcePort())
srcIP := ctx.GetSourceIPs()[0].String()
var network string
switch ctx.GetNetwork() {
case net.Network_TCP:
network = "tcp"
case net.Network_UDP:
network = "udp"
default:
return false
}
var dstIP string
var dstPort uint16 = 0
// do not use resolved IP because Android process lookup needs original dst ip
resolvableContext, ok := ctx.(*dns.ResolvableContext)
if ok && len(resolvableContext.Context.GetTargetIPs()) > 0 {
dstIP = resolvableContext.Context.GetTargetIPs()[0].String()
dstPort = uint16(resolvableContext.Context.GetTargetPort())
} else if len(ctx.GetTargetIPs()) > 0 {
dstIP = ctx.GetTargetIPs()[0].String()
dstPort = uint16(ctx.GetTargetPort())
}
pid, name, absPath, err := net.FindProcess(network, srcIP, uint16(srcPort), dstIP, uint16(dstPort))
if err != nil {
if err != net.ErrNotLocal {
errors.LogError(context.Background(), "Unables to find local process name: ", err)
}
return false
}
if m.MatchXraySelf {
if pid == os.Getpid() {
return true
}
}
if slices.Contains(m.ProcessNames, name) {
return true
}
if slices.Contains(m.AbsPaths, absPath) {
return true
}
for _, f := range m.Folders {
if strings.HasPrefix(absPath, f) {
return true
}
}
return false
}
+144
View File
@@ -0,0 +1,144 @@
package router
import (
"net/netip"
"strconv"
"github.com/xtls/xray-core/common/net"
"go4.org/netipx"
)
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
ip4 *netipx.IPSet
ip6 *netipx.IPSet
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
var builder4, builder6 netipx.IPSetBuilder
for _, cidr := range cidrs {
ip := net.IP(cidr.GetIp())
ipPrefixString := ip.String() + "/" + strconv.Itoa(int(cidr.GetPrefix()))
ipPrefix, err := netip.ParsePrefix(ipPrefixString)
if err != nil {
return err
}
switch len(ip) {
case net.IPv4len:
builder4.AddPrefix(ipPrefix)
case net.IPv6len:
builder6.AddPrefix(ipPrefix)
}
}
if ip4, err := builder4.IPSet(); err != nil {
return err
} else {
m.ip4 = ip4
}
if ip6, err := builder6.IPSet(); err != nil {
return err
} else {
m.ip6 = ip6
}
return nil
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
func (m *GeoIPMatcher) match4(ip net.IP) bool {
nip, ok := netipx.FromStdIP(ip)
if !ok {
return false
}
return m.ip4.Contains(nip)
}
func (m *GeoIPMatcher) match6(ip net.IP) bool {
nip, ok := netipx.FromStdIP(ip)
if !ok {
return false
}
return m.ip6.Contains(nip)
}
// Match returns true if the given ip is included by the GeoIP.
func (m *GeoIPMatcher) Match(ip net.IP) bool {
isMatched := false
switch len(ip) {
case net.IPv4len:
isMatched = m.match4(ip)
case net.IPv6len:
isMatched = m.match6(ip)
}
if m.reverseMatch {
return !isMatched
}
return isMatched
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
type GeoIPMatcherContainer struct {
matchers []*GeoIPMatcher
}
// Add adds a new GeoIP set into the container.
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
if len(geoip.CountryCode) > 0 {
for _, m := range c.matchers {
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
return m, nil
}
}
}
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
}
if len(geoip.CountryCode) > 0 {
c.matchers = append(c.matchers, m)
}
return m, nil
}
var GlobalGeoIPContainer GeoIPMatcherContainer
func MatchIPs(matchers []*GeoIPMatcher, ips []net.IP, reverse bool) []net.IP {
if len(matchers) == 0 {
panic("GeoIP matchers should not be empty to avoid ambiguity")
}
newIPs := make([]net.IP, 0, len(ips))
var isFound bool
for _, ip := range ips {
isFound = false
for _, matcher := range matchers {
if matcher.Match(ip) {
isFound = true
break
}
}
if isFound && !reverse {
newIPs = append(newIPs, ip)
continue
}
if !isFound && reverse {
newIPs = append(newIPs, ip)
continue
}
}
return newIPs
}
+279
View File
@@ -0,0 +1,279 @@
package router_test
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/platform/filesystem"
"google.golang.org/protobuf/proto"
)
func getAssetPath(file string) (string, error) {
path := platform.GetAssetLocation(file)
_, err := os.Stat(path)
if os.IsNotExist(err) {
path := filepath.Join("..", "..", "resources", file)
_, err := os.Stat(path)
if os.IsNotExist(err) {
return "", fmt.Errorf("can't find %s in standard asset locations or {project_root}/resources", file)
}
if err != nil {
return "", fmt.Errorf("can't stat %s: %v", path, err)
}
return path, nil
}
if err != nil {
return "", fmt.Errorf("can't stat %s: %v", path, err)
}
return path, nil
}
func TestGeoIPMatcherContainer(t *testing.T) {
container := &router.GeoIPMatcherContainer{}
m1, err := container.Add(&router.GeoIP{
CountryCode: "CN",
})
common.Must(err)
m2, err := container.Add(&router.GeoIP{
CountryCode: "US",
})
common.Must(err)
m3, err := container.Add(&router.GeoIP{
CountryCode: "CN",
})
common.Must(err)
if m1 != m3 {
t.Error("expect same matcher for same geoip, but not")
}
if m1 == m2 {
t.Error("expect different matcher for different geoip, but actually same")
}
}
func TestGeoIPMatcher(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
}
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "192.168.1.1",
Output: true,
},
{
Input: "192.0.0.0",
Output: true,
},
{
Input: "192.0.1.0",
Output: false,
},
{
Input: "0.1.0.0",
Output: true,
},
{
Input: "1.0.0.1",
Output: false,
},
{
Input: "8.8.8.7",
Output: false,
},
{
Input: "8.8.8.8",
Output: true,
},
{
Input: "2001:cdba::3257:9652",
Output: false,
},
{
Input: "91.108.255.254",
Output: true,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPMatcherRegression(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{98, 108, 20, 0}, Prefix: 22},
{Ip: []byte{98, 108, 20, 0}, Prefix: 23},
}
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "98.108.22.11",
Output: true,
},
{
Input: "98.108.25.0",
Output: false,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPReverseMatcher(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
}
matcher := &router.GeoIPMatcher{}
matcher.SetReverseMatch(true) // Reverse match
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "8.8.8.8",
Output: false,
},
{
Input: "2001:cdba::3257:9652",
Output: true,
},
{
Input: "91.108.255.254",
Output: false,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPMatcher4CN(t *testing.T) {
ips, err := loadGeoIP("CN")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
if matcher.Match([]byte{8, 8, 8, 8}) {
t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
}
}
func TestGeoIPMatcher6US(t *testing.T) {
ips, err := loadGeoIP("US")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
}
}
func loadGeoIP(country string) ([]*router.CIDR, error) {
path, err := getAssetPath("geoip.dat")
if err != nil {
return nil, err
}
geoipBytes, err := filesystem.ReadFile(path)
if err != nil {
return nil, err
}
var geoipList router.GeoIPList
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
return nil, err
}
for _, geoip := range geoipList.Entry {
if geoip.CountryCode == country {
return geoip.Cidr, nil
}
}
panic("country not found: " + country)
}
func BenchmarkGeoIPMatcher4CN(b *testing.B) {
ips, err := loadGeoIP("CN")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = matcher.Match([]byte{8, 8, 8, 8})
}
}
func BenchmarkGeoIPMatcher6US(b *testing.B) {
ips, err := loadGeoIP("US")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
}
}
+94 -38
View File
@@ -1,19 +1,20 @@
package router_test package router_test
import ( import (
"path/filepath"
"strconv" "strconv"
"testing" "testing"
. "github.com/xtls/xray-core/app/router" . "github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session" routing_session "github.com/xtls/xray-core/features/routing/session"
"google.golang.org/protobuf/proto"
) )
func withBackground() routing.Context { func withBackground() routing.Context {
@@ -44,15 +45,18 @@ func TestRoutingRule(t *testing.T) {
}{ }{
{ {
rule: &RoutingRule{ rule: &RoutingRule{
Domain: []*geodata.DomainRule{ Domain: []*Domain{
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Substr, Value: "example.com"}}, Value: "example.com",
Type: Domain_Plain,
}, },
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}}, Value: "google.com",
Type: Domain_Domain,
}, },
{ {
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^facebook\\.com$"}}, Value: "^facebook\\.com$",
Type: Domain_Regex,
}, },
}, },
}, },
@@ -89,25 +93,20 @@ func TestRoutingRule(t *testing.T) {
}, },
{ {
rule: &RoutingRule{ rule: &RoutingRule{
Ip: []*geodata.IPRule{ Geoip: []*GeoIP{
{ {
Value: &geodata.IPRule_Custom{ Cidr: []*CIDR{
Custom: &geodata.CIDRRule{ {
Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32}, Ip: []byte{8, 8, 8, 8},
Prefix: 32,
}, },
}, {
}, Ip: []byte{8, 8, 8, 8},
{ Prefix: 32,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
}, },
}, {
}, Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(),
{ Prefix: 128,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(), Prefix: 128},
}, },
}, },
}, },
@@ -134,11 +133,12 @@ func TestRoutingRule(t *testing.T) {
}, },
{ {
rule: &RoutingRule{ rule: &RoutingRule{
SourceIp: []*geodata.IPRule{ SourceGeoip: []*GeoIP{
{ {
Value: &geodata.IPRule_Custom{ Cidr: []*CIDR{
Custom: &geodata.CIDRRule{ {
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16}, Ip: []byte{192, 168, 0, 0},
Prefix: 16,
}, },
}, },
}, },
@@ -300,12 +300,35 @@ func TestRoutingRule(t *testing.T) {
} }
} }
func loadGeoSite(country string) ([]*Domain, error) {
path, err := getAssetPath("geosite.dat")
if err != nil {
return nil, err
}
geositeBytes, err := filesystem.ReadFile(path)
if err != nil {
return nil, err
}
var geositeList GeoSiteList
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
return nil, err
}
for _, site := range geositeList.Entry {
if site.CountryCode == country {
return site.Domain, nil
}
}
return nil, errors.New("country not found: " + country)
}
func TestChinaSites(t *testing.T) { func TestChinaSites(t *testing.T) {
t.Setenv("xray.location.asset", filepath.Join("..", "..", "resources")) domains, err := loadGeoSite("CN")
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
common.Must(err) common.Must(err)
matcher, err := NewDomainMatcher(rules) acMatcher, err := NewMphMatcherGroup(domains)
common.Must(err) common.Must(err)
type TestCase struct { type TestCase struct {
@@ -336,19 +359,18 @@ func TestChinaSites(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
r := matcher.ApplyDomain(testCase.Domain) r := acMatcher.ApplyDomain(testCase.Domain)
if r != testCase.Output { if r != testCase.Output {
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r) t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
} }
} }
} }
func BenchmarkMphDomainMatcher(b *testing.B) { func BenchmarkMphDomainMatcher(b *testing.B) {
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources")) domains, err := loadGeoSite("CN")
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
common.Must(err) common.Must(err)
matcher, err := NewDomainMatcher(rules) matcher, err := NewMphMatcherGroup(domains)
common.Must(err) common.Must(err)
type TestCase struct { type TestCase struct {
@@ -387,11 +409,45 @@ func BenchmarkMphDomainMatcher(b *testing.B) {
} }
func BenchmarkMultiGeoIPMatcher(b *testing.B) { func BenchmarkMultiGeoIPMatcher(b *testing.B) {
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources")) var geoips []*GeoIP
rules, err := geodata.ParseIPRules([]string{"geoip:cn", "geoip:jp", "geoip:ca", "geoip:us"})
common.Must(err)
matcher, err := NewIPMatcher(rules, MatcherAsType_Target) {
ips, err := loadGeoIP("CN")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CN",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("JP")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "JP",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("CA")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CA",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("US")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "US",
Cidr: ips,
})
}
matcher, err := NewMultiGeoIPMatcher(geoips, "target")
common.Must(err) common.Must(err)
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}) ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
+54 -58
View File
@@ -15,7 +15,6 @@ type Rule struct {
RuleTag string RuleTag string
Balancer *Balancer Balancer *Balancer
Condition Condition Condition Condition
Webhook *WebhookNotifier
} }
func (r *Rule) GetTag() (string, error) { func (r *Rule) GetTag() (string, error) {
@@ -33,38 +32,72 @@ func (r *Rule) Apply(ctx routing.Context) bool {
func (rr *RoutingRule) BuildCondition() (Condition, error) { func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan() conds := NewConditionChan()
if len(rr.Domain) > 0 {
matcher, err := NewMphMatcherGroup(rr.Domain)
if err != nil {
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
}
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
conds.Add(matcher)
}
if len(rr.UserEmail) > 0 {
conds.Add(NewUserMatcher(rr.UserEmail))
}
if rr.VlessRouteList != nil {
conds.Add(NewPortMatcher(rr.VlessRouteList, "vlessRoute"))
}
if len(rr.InboundTag) > 0 { if len(rr.InboundTag) > 0 {
conds.Add(NewInboundTagMatcher(rr.InboundTag)) conds.Add(NewInboundTagMatcher(rr.InboundTag))
} }
if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, "target"))
}
if rr.SourcePortList != nil {
conds.Add(NewPortMatcher(rr.SourcePortList, "source"))
}
if rr.LocalPortList != nil {
conds.Add(NewPortMatcher(rr.LocalPortList, "local"))
}
if len(rr.Networks) > 0 { if len(rr.Networks) > 0 {
conds.Add(NewNetworkMatcher(rr.Networks)) conds.Add(NewNetworkMatcher(rr.Networks))
} }
if len(rr.Geoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.Geoip, "target")
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.SourceGeoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, "source")
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.LocalGeoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.LocalGeoip, "local")
if err != nil {
return nil, err
}
conds.Add(cond)
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
}
if len(rr.Protocol) > 0 { if len(rr.Protocol) > 0 {
conds.Add(NewProtocolMatcher(rr.Protocol)) conds.Add(NewProtocolMatcher(rr.Protocol))
} }
if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, MatcherAsType_Target))
}
if rr.SourcePortList != nil {
conds.Add(NewPortMatcher(rr.SourcePortList, MatcherAsType_Source))
}
if rr.LocalPortList != nil {
conds.Add(NewPortMatcher(rr.LocalPortList, MatcherAsType_Local))
}
if rr.VlessRouteList != nil {
conds.Add(NewPortMatcher(rr.VlessRouteList, MatcherAsType_VlessRoute))
}
if len(rr.UserEmail) > 0 {
conds.Add(NewUserMatcher(rr.UserEmail))
}
if len(rr.Attributes) > 0 { if len(rr.Attributes) > 0 {
configuredKeys := make(map[string]*regexp.Regexp) configuredKeys := make(map[string]*regexp.Regexp)
for key, value := range rr.Attributes { for key, value := range rr.Attributes {
@@ -73,43 +106,6 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(&AttributeMatcher{configuredKeys}) conds.Add(&AttributeMatcher{configuredKeys})
} }
if len(rr.Ip) > 0 {
cond, err := NewIPMatcher(rr.Ip, MatcherAsType_Target)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.SourceIp) > 0 {
cond, err := NewIPMatcher(rr.SourceIp, MatcherAsType_Source)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.LocalIp) > 0 {
cond, err := NewIPMatcher(rr.LocalIp, MatcherAsType_Local)
if err != nil {
return nil, err
}
conds.Add(cond)
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
}
if len(rr.Domain) > 0 {
cond, err := NewDomainMatcher(rr.Domain)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.Process) > 0 {
conds.Add(NewProcessNameMatcher(rr.Process))
}
if conds.Len() == 0 { if conds.Len() == 0 {
return nil, errors.New("this rule has no effective fields").AtWarning() return nil, errors.New("this rule has no effective fields").AtWarning()
} }
+774 -264
View File
File diff suppressed because it is too large Load Diff
+75 -23
View File
@@ -9,7 +9,67 @@ option java_multiple_files = true;
import "common/serial/typed_message.proto"; import "common/serial/typed_message.proto";
import "common/net/port.proto"; import "common/net/port.proto";
import "common/net/network.proto"; import "common/net/network.proto";
import "common/geodata/geodat.proto";
// Domain for routing decision.
message Domain {
// Type of domain value.
enum Type {
// The value is used as is.
Plain = 0;
// The value is used as a regular expression.
Regex = 1;
// The value is a root domain.
Domain = 2;
// The value is a domain.
Full = 3;
}
// Domain matching type.
Type type = 1;
// Domain value.
string value = 2;
message Attribute {
string key = 1;
oneof typed_value {
bool bool_value = 2;
int64 int_value = 3;
}
}
// Attributes of this domain. May be used for filtering.
repeated Attribute attribute = 3;
}
// IP for routing decision, in CIDR form.
message CIDR {
// IP address, should be either 4 or 16 bytes.
bytes ip = 1;
// Number of leading ones in the network mask.
uint32 prefix = 2;
}
message GeoIP {
string country_code = 1;
repeated CIDR cidr = 2;
bool reverse_match = 3;
}
message GeoIPList {
repeated GeoIP entry = 1;
}
message GeoSite {
string country_code = 1;
repeated Domain domain = 2;
}
message GeoSiteList {
repeated GeoSite entry = 1;
}
message RoutingRule { message RoutingRule {
oneof target_tag { oneof target_tag {
@@ -19,23 +79,26 @@ message RoutingRule {
// Tag of routing balancer. // Tag of routing balancer.
string balancing_tag = 12; string balancing_tag = 12;
} }
string rule_tag = 19;
string rule_tag = 19;
// List of domains for target domain matching. // List of domains for target domain matching.
repeated xray.common.geodata.DomainRule domain = 2; repeated Domain domain = 2;
// List of IPs for target IP address matching. // List of GeoIPs for target IP address matching. If this entry exists, the
repeated xray.common.geodata.IPRule ip = 10; // cidr above will have no effect. GeoIP fields with the same country code are
// supposed to contain exactly same content. They will be merged during
// runtime. For customized GeoIPs, please leave country code empty.
repeated GeoIP geoip = 10;
// List of ports for target port matching. // List of ports.
xray.common.net.PortList port_list = 14; xray.common.net.PortList port_list = 14;
// List of networks for matching. // List of networks for matching.
repeated xray.common.net.Network networks = 13; repeated xray.common.net.Network networks = 13;
// List of IPs for source IP address matching. // List of GeoIPs for source IP address matching. If this entry exists, the
repeated xray.common.geodata.IPRule source_ip = 11; // source_cidr above will have no effect.
repeated GeoIP source_geoip = 11;
// List of ports for source port matching. // List of ports for source port matching.
xray.common.net.PortList source_port_list = 16; xray.common.net.PortList source_port_list = 16;
@@ -46,22 +109,10 @@ message RoutingRule {
map<string, string> attributes = 15; map<string, string> attributes = 15;
// List of IPs for local IP address matching. repeated GeoIP local_geoip = 17;
repeated xray.common.geodata.IPRule local_ip = 17;
// List of ports for local port matching.
xray.common.net.PortList local_port_list = 18; xray.common.net.PortList local_port_list = 18;
xray.common.net.PortList vless_route_list = 20; xray.common.net.PortList vless_route_list = 20;
repeated string process = 21;
WebhookConfig webhook = 22;
}
message WebhookConfig {
string url = 1;
uint32 deduplication = 2;
map<string, string> headers = 3;
} }
message BalancingRule { message BalancingRule {
@@ -96,7 +147,8 @@ message Config {
// Use domain as is. // Use domain as is.
AsIs = 0; AsIs = 0;
reserved 1; // Always resolve IP for domains.
UseIp = 1;
// Resolve to IP if the domain doesn't match any rules. // Resolve to IP if the domain doesn't match any rules.
IpIfNonMatch = 2; IpIfNonMatch = 2;
+2 -75
View File
@@ -2,7 +2,7 @@ package router
import ( import (
"context" "context"
"sync" sync "sync"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
@@ -57,7 +57,6 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
for _, rule := range config.Rule { for _, rule := range config.Rule {
cond, err := rule.BuildCondition() cond, err := rule.BuildCondition()
if err != nil { if err != nil {
r.closeWebhooks()
return err return err
} }
rr := &Rule{ rr := &Rule{
@@ -65,22 +64,10 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
Tag: rule.GetTag(), Tag: rule.GetTag(),
RuleTag: rule.GetRuleTag(), RuleTag: rule.GetRuleTag(),
} }
if wh := rule.GetWebhook(); wh != nil {
notifier, err := NewWebhookNotifier(wh)
if err != nil {
r.closeWebhooks()
return err
}
rr.Webhook = notifier
}
btag := rule.GetBalancingTag() btag := rule.GetBalancingTag()
if len(btag) > 0 { if len(btag) > 0 {
brule, found := r.balancers[btag] brule, found := r.balancers[btag]
if !found { if !found {
if rr.Webhook != nil {
rr.Webhook.Close()
}
r.closeWebhooks()
return errors.New("balancer ", btag, " not found") return errors.New("balancer ", btag, " not found")
} }
rr.Balancer = brule rr.Balancer = brule
@@ -93,7 +80,6 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
// PickRoute implements routing.Router. // PickRoute implements routing.Router.
func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) { func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
originalCtx := ctx
rule, ctx, err := r.pickRouteInternal(ctx) rule, ctx, err := r.pickRouteInternal(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -102,14 +88,12 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if rule.Webhook != nil {
rule.Webhook.Fire(originalCtx, tag)
}
return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil
} }
// AddRule implements routing.Router. // AddRule implements routing.Router.
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error { func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
inst, err := config.GetInstance() inst, err := config.GetInstance()
if err != nil { if err != nil {
return err return err
@@ -125,11 +109,6 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
defer r.mu.Unlock() defer r.mu.Unlock()
if !shouldAppend { if !shouldAppend {
for _, rule := range r.rules {
if rule.Webhook != nil {
rule.Webhook.Close()
}
}
r.balancers = make(map[string]*Balancer, len(config.BalancingRule)) r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
r.rules = make([]*Rule, 0, len(config.Rule)) r.rules = make([]*Rule, 0, len(config.Rule))
} }
@@ -146,24 +125,12 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
r.balancers[rule.Tag] = balancer r.balancers[rule.Tag] = balancer
} }
startIdx := len(r.rules)
closeNewWebhooks := func() {
for i := startIdx; i < len(r.rules); i++ {
if r.rules[i].Webhook != nil {
r.rules[i].Webhook.Close()
}
}
r.rules = r.rules[:startIdx]
}
for _, rule := range config.Rule { for _, rule := range config.Rule {
if r.RuleExists(rule.GetRuleTag()) { if r.RuleExists(rule.GetRuleTag()) {
closeNewWebhooks()
return errors.New("duplicate ruleTag ", rule.GetRuleTag()) return errors.New("duplicate ruleTag ", rule.GetRuleTag())
} }
cond, err := rule.BuildCondition() cond, err := rule.BuildCondition()
if err != nil { if err != nil {
closeNewWebhooks()
return err return err
} }
rr := &Rule{ rr := &Rule{
@@ -171,22 +138,10 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
Tag: rule.GetTag(), Tag: rule.GetTag(),
RuleTag: rule.GetRuleTag(), RuleTag: rule.GetRuleTag(),
} }
if wh := rule.GetWebhook(); wh != nil {
notifier, err := NewWebhookNotifier(wh)
if err != nil {
closeNewWebhooks()
return err
}
rr.Webhook = notifier
}
btag := rule.GetBalancingTag() btag := rule.GetBalancingTag()
if len(btag) > 0 { if len(btag) > 0 {
brule, found := r.balancers[btag] brule, found := r.balancers[btag]
if !found { if !found {
if rr.Webhook != nil {
rr.Webhook.Close()
}
closeNewWebhooks()
return errors.New("balancer ", btag, " not found") return errors.New("balancer ", btag, " not found")
} }
rr.Balancer = brule rr.Balancer = brule
@@ -218,30 +173,14 @@ func (r *Router) RemoveRule(tag string) error {
for _, rule := range r.rules { for _, rule := range r.rules {
if rule.RuleTag != tag { if rule.RuleTag != tag {
newRules = append(newRules, rule) newRules = append(newRules, rule)
} else if rule.Webhook != nil {
rule.Webhook.Close()
} }
} }
r.rules = newRules r.rules = newRules
return nil return nil
} }
return errors.New("empty tag name!") return errors.New("empty tag name!")
}
// ListRule implements routing.Router
func (r *Router) ListRule() []routing.Route {
r.mu.Lock()
defer r.mu.Unlock()
ruleList := make([]routing.Route, 0)
for _, rule := range r.rules {
ruleList = append(ruleList, &Route{
outboundTag: rule.Tag,
ruleTag: rule.RuleTag,
})
}
return ruleList
} }
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) { func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
// SkipDNSResolve is set from DNS module. // SkipDNSResolve is set from DNS module.
// the DOH remote server maybe a domain name, // the DOH remote server maybe a domain name,
@@ -279,20 +218,8 @@ func (r *Router) Start() error {
return nil return nil
} }
// closeWebhooks closes all webhook notifiers in the current rule set.
func (r *Router) closeWebhooks() {
for _, rule := range r.rules {
if rule.Webhook != nil {
rule.Webhook.Close()
}
}
}
// Close implements common.Closable. // Close implements common.Closable.
func (r *Router) Close() error { func (r *Router) Close() error {
r.mu.Lock()
defer r.mu.Unlock()
r.closeWebhooks()
return nil return nil
} }
+15 -13
View File
@@ -7,7 +7,6 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
. "github.com/xtls/xray-core/app/router" . "github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
@@ -156,11 +155,12 @@ func TestIPOnDemand(t *testing.T) {
TargetTag: &RoutingRule_Tag{ TargetTag: &RoutingRule_Tag{
Tag: "test", Tag: "test",
}, },
Ip: []*geodata.IPRule{ Geoip: []*GeoIP{
{ {
Value: &geodata.IPRule_Custom{ Cidr: []*CIDR{
Custom: &geodata.CIDRRule{ {
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16}, Ip: []byte{192, 168, 0, 0},
Prefix: 16,
}, },
}, },
}, },
@@ -200,11 +200,12 @@ func TestIPIfNonMatchDomain(t *testing.T) {
TargetTag: &RoutingRule_Tag{ TargetTag: &RoutingRule_Tag{
Tag: "test", Tag: "test",
}, },
Ip: []*geodata.IPRule{ Geoip: []*GeoIP{
{ {
Value: &geodata.IPRule_Custom{ Cidr: []*CIDR{
Custom: &geodata.CIDRRule{ {
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16}, Ip: []byte{192, 168, 0, 0},
Prefix: 16,
}, },
}, },
}, },
@@ -244,11 +245,12 @@ func TestIPIfNonMatchIP(t *testing.T) {
TargetTag: &RoutingRule_Tag{ TargetTag: &RoutingRule_Tag{
Tag: "test", Tag: "test",
}, },
Ip: []*geodata.IPRule{ Geoip: []*GeoIP{
{ {
Value: &geodata.IPRule_Custom{ Cidr: []*CIDR{
Custom: &geodata.CIDRRule{ {
Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 0}, Prefix: 8}, Ip: []byte{127, 0, 0, 0},
Prefix: 8,
}, },
}, },
}, },
+6 -22
View File
@@ -3,7 +3,6 @@ package router
import ( import (
"context" "context"
"math" "math"
"slices"
"sort" "sort"
"time" "time"
@@ -78,7 +77,7 @@ func (s *LeastLoadStrategy) PickOutbound(candidates []string) string {
} }
func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node { func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node {
qualified := s.getNodes(candidates) qualified := s.getNodes(candidates, time.Duration(s.settings.MaxRTT))
selects := s.selectLeastLoad(qualified) selects := s.selectLeastLoad(qualified)
return selects return selects
} }
@@ -139,7 +138,7 @@ func (s *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node {
return nodes[:count] return nodes[:count]
} }
func (s *LeastLoadStrategy) getNodes(candidates []string) []*node { func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node {
if s.observer == nil { if s.observer == nil {
errors.LogError(s.ctx, "observer is nil") errors.LogError(s.ctx, "observer is nil")
return make([]*node, 0) return make([]*node, 0)
@@ -152,10 +151,12 @@ func (s *LeastLoadStrategy) getNodes(candidates []string) []*node {
results := observeResult.(*observatory.ObservationResult) results := observeResult.(*observatory.ObservationResult)
outboundlist := outboundList(candidates)
var ret []*node var ret []*node
for _, v := range results.Status { for _, v := range results.Status {
if s.shouldSelectNode(v, candidates) { if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) {
record := &node{ record := &node{
Tag: v.OutboundTag, Tag: v.OutboundTag,
CountAll: 1, CountAll: 1,
@@ -171,8 +172,8 @@ func (s *LeastLoadStrategy) getNodes(candidates []string) []*node {
record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation))) record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation)))
record.CountAll = int(v.HealthPing.All) record.CountAll = int(v.HealthPing.All)
record.CountFail = int(v.HealthPing.Fail) record.CountFail = int(v.HealthPing.Fail)
}
}
ret = append(ret, record) ret = append(ret, record)
} }
} }
@@ -181,23 +182,6 @@ func (s *LeastLoadStrategy) getNodes(candidates []string) []*node {
return ret return ret
} }
func (s *LeastLoadStrategy) shouldSelectNode(v *observatory.OutboundStatus, candidates []string) bool {
maxRTT := time.Duration(s.settings.MaxRTT)
if !v.Alive {
return false
}
if maxRTT != 0 && v.Delay >= maxRTT.Milliseconds() {
return false
}
if !slices.Contains(candidates, v.OutboundTag) {
return false
}
if v.HealthPing != nil && v.HealthPing.All > 0 && s.settings.Tolerance > 0 && float64(v.HealthPing.Fail)/float64(v.HealthPing.All) > float64(s.settings.Tolerance) {
return false
}
return true
}
func leastloadSort(nodes []*node) { func leastloadSort(nodes []*node) {
sort.Slice(nodes, func(i, j int) bool { sort.Slice(nodes, func(i, j int) bool {
left := nodes[i] left := nodes[i]
-5
View File
@@ -85,7 +85,6 @@ func TestSelectLeastExpected(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns)) t.Errorf("expected: %v, actual: %v", expected, len(ns))
} }
} }
func TestSelectLeastExpected2(t *testing.T) { func TestSelectLeastExpected2(t *testing.T) {
strategy := &LeastLoadStrategy{ strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{ settings: &StrategyLeastLoadConfig{
@@ -103,7 +102,6 @@ func TestSelectLeastExpected2(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns)) t.Errorf("expected: %v, actual: %v", expected, len(ns))
} }
} }
func TestSelectLeastExpectedAndBaselines(t *testing.T) { func TestSelectLeastExpectedAndBaselines(t *testing.T) {
strategy := &LeastLoadStrategy{ strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{ settings: &StrategyLeastLoadConfig{
@@ -124,7 +122,6 @@ func TestSelectLeastExpectedAndBaselines(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns)) t.Errorf("expected: %v, actual: %v", expected, len(ns))
} }
} }
func TestSelectLeastExpectedAndBaselines2(t *testing.T) { func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
strategy := &LeastLoadStrategy{ strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{ settings: &StrategyLeastLoadConfig{
@@ -145,7 +142,6 @@ func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns)) t.Errorf("expected: %v, actual: %v", expected, len(ns))
} }
} }
func TestSelectLeastLoadBaselines(t *testing.T) { func TestSelectLeastLoadBaselines(t *testing.T) {
strategy := &LeastLoadStrategy{ strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{ settings: &StrategyLeastLoadConfig{
@@ -164,7 +160,6 @@ func TestSelectLeastLoadBaselines(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns)) t.Errorf("expected: %v, actual: %v", expected, len(ns))
} }
} }
func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) { func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) {
strategy := &LeastLoadStrategy{ strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{ settings: &StrategyLeastLoadConfig{
-241
View File
@@ -1,241 +0,0 @@
package router
import (
"bytes"
"context"
"encoding/json"
"io"
"net"
"net/http"
"sync"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session"
)
func ptr[T any](v T) *T { return &v }
type event struct {
Email *string `json:"email"`
Level *uint32 `json:"level"`
Protocol *string `json:"protocol"`
Network *string `json:"network"`
Source *string `json:"source"`
Destination *string `json:"destination"`
OriginalTarget *string `json:"originalTarget"`
RouteTarget *string `json:"routeTarget"`
InboundTag *string `json:"inboundTag"`
InboundName *string `json:"inboundName"`
InboundLocal *string `json:"inboundLocal"`
OutboundTag *string `json:"outboundTag"`
Timestamp int64 `json:"ts"`
}
type WebhookNotifier struct {
url string
headers map[string]string
deduplication uint32
client *http.Client
seen sync.Map
done chan struct{}
wg sync.WaitGroup
closeOnce sync.Once
}
func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
if cfg == nil || cfg.Url == "" {
return nil, nil
}
httpURL, socketPath := utils.SplitHTTPUnixURL(cfg.Url)
h := &WebhookNotifier{
url: httpURL,
deduplication: cfg.Deduplication,
client: &http.Client{
Timeout: 5 * time.Second,
},
done: make(chan struct{}),
}
if socketPath != "" {
dialAddr := utils.ResolveSocketPath(socketPath)
h.client.Transport = &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", dialAddr)
},
}
}
if len(cfg.Headers) > 0 {
h.headers = make(map[string]string, len(cfg.Headers))
for k, v := range cfg.Headers {
h.headers[k] = v
}
}
if h.deduplication > 0 {
h.wg.Add(1)
go h.cleanupLoop()
}
return h, nil
}
func (h *WebhookNotifier) Fire(ctx routing.Context, outboundTag string) {
ev := buildEvent(ctx, outboundTag)
email := ""
if ev.Email != nil {
email = *ev.Email
}
if h.isDuplicate(email) {
return
}
h.wg.Add(1)
select {
case <-h.done:
h.wg.Done()
return
default:
}
go func() {
defer h.wg.Done()
h.post(ev)
}()
}
func buildEvent(ctx routing.Context, outboundTag string) *event {
ev := &event{
Timestamp: time.Now().Unix(),
OutboundTag: ptr(outboundTag),
InboundTag: ptr(ctx.GetInboundTag()),
Protocol: ptr(ctx.GetProtocol()),
Network: ptr(ctx.GetNetwork().SystemString()),
}
if user := ctx.GetUser(); user != "" {
ev.Email = ptr(user)
}
if srcIPs := ctx.GetSourceIPs(); len(srcIPs) > 0 {
srcPort := ctx.GetSourcePort()
ev.Source = ptr(net.JoinHostPort(srcIPs[0].String(), srcPort.String()))
}
targetPort := ctx.GetTargetPort()
if domain := ctx.GetTargetDomain(); domain != "" {
ev.Destination = ptr(net.JoinHostPort(domain, targetPort.String()))
} else if targetIPs := ctx.GetTargetIPs(); len(targetIPs) > 0 {
ev.Destination = ptr(net.JoinHostPort(targetIPs[0].String(), targetPort.String()))
}
if localIPs := ctx.GetLocalIPs(); len(localIPs) > 0 {
localPort := ctx.GetLocalPort()
ev.InboundLocal = ptr(net.JoinHostPort(localIPs[0].String(), localPort.String()))
}
if sctx, ok := ctx.(*routing_session.Context); ok {
enrichFromSession(ev, sctx)
}
return ev
}
func enrichFromSession(ev *event, sctx *routing_session.Context) {
if sctx.Inbound != nil {
ev.InboundName = ptr(sctx.Inbound.Name)
if sctx.Inbound.User != nil {
ev.Level = ptr(sctx.Inbound.User.Level)
}
}
if sctx.Outbound != nil {
if sctx.Outbound.OriginalTarget.Address != nil {
ev.OriginalTarget = ptr(sctx.Outbound.OriginalTarget.String())
}
if sctx.Outbound.RouteTarget.Address != nil {
ev.RouteTarget = ptr(sctx.Outbound.RouteTarget.String())
}
}
}
func (h *WebhookNotifier) post(ev *event) {
body, err := json.Marshal(ev)
if err != nil {
errors.LogWarning(context.Background(), "webhook: marshal failed: ", err)
return
}
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, h.url, bytes.NewReader(body))
if err != nil {
errors.LogWarning(context.Background(), "webhook: request build failed: ", err)
return
}
req.Header.Set("Content-Type", "application/json")
for k, v := range h.headers {
req.Header.Set(k, v)
}
resp, err := h.client.Do(req)
if err != nil {
errors.LogInfo(context.Background(), "webhook: POST failed: ", err)
return
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
if resp.StatusCode >= 400 {
errors.LogWarning(context.Background(), "webhook: POST returned status ", resp.StatusCode)
}
}
func (h *WebhookNotifier) isDuplicate(email string) bool {
if h.deduplication == 0 || email == "" {
return false
}
ttl := time.Duration(h.deduplication) * time.Second
now := time.Now()
if v, loaded := h.seen.LoadOrStore(email, now); loaded {
if now.Sub(v.(time.Time)) < ttl {
return true
}
h.seen.Store(email, now)
}
return false
}
func (h *WebhookNotifier) cleanupLoop() {
defer h.wg.Done()
ttl := time.Duration(h.deduplication) * time.Second
ticker := time.NewTicker(ttl)
defer ticker.Stop()
for {
select {
case <-h.done:
return
case <-ticker.C:
now := time.Now()
h.seen.Range(func(key, value any) bool {
if now.Sub(value.(time.Time)) >= ttl {
h.seen.Delete(key)
}
return true
})
}
}
}
func (h *WebhookNotifier) Close() error {
h.closeOnce.Do(func() {
close(h.done)
})
h.wg.Wait()
h.client.CloseIdleConnections()
return nil
}
+18 -89
View File
@@ -3,10 +3,12 @@ package command
import ( import (
"context" "context"
"runtime" "runtime"
"strings"
"time" "time"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
feature_stats "github.com/xtls/xray-core/features/stats" feature_stats "github.com/xtls/xray-core/features/stats"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
@@ -68,10 +70,9 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
} }
ips := make(map[string]int64) ips := make(map[string]int64)
c.ForEach(func(ip string, lastSeen int64) bool { for ip, t := range c.IpTimeMap() {
ips[ip] = lastSeen ips[ip] = t.Unix()
return true }
})
return &GetStatsOnlineIpListResponse{ return &GetStatsOnlineIpListResponse{
Name: request.Name, Name: request.Name,
@@ -79,93 +80,21 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
}, nil }, nil
} }
func (s *statsServer) GetAllOnlineUsers(ctx context.Context, request *GetAllOnlineUsersRequest) (*GetAllOnlineUsersResponse, error) {
return &GetAllOnlineUsersResponse{
Users: s.stats.GetAllOnlineUsers(),
}, nil
}
func (s *statsServer) GetUsersStats(ctx context.Context, request *GetUsersStatsRequest) (*GetUsersStatsResponse, error) {
userMap := make(map[string]*UserStat)
s.stats.VisitOnlineMaps(func(name string, om feature_stats.OnlineMap) bool {
if om.Count() == 0 {
return true
}
_, rest, _ := strings.Cut(name, ">>>")
email, _, _ := strings.Cut(rest, ">>>")
user := &UserStat{Email: email}
om.ForEach(func(ip string, lastSeen int64) bool {
user.Ips = append(user.Ips, &OnlineIPEntry{
Ip: ip,
LastSeen: lastSeen,
})
return true
})
if len(user.Ips) > 0 {
userMap[email] = user
}
return true
})
if request.IncludeTraffic {
for _, u := range userMap {
u.Traffic = &TrafficUserStat{}
}
const (
prefixUser = "user>>>"
suffixUplink = ">>>traffic>>>uplink"
suffixDownlink = ">>>traffic>>>downlink"
)
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool {
var email string
var isUplink bool
if strings.HasSuffix(name, suffixUplink) {
email = name[len(prefixUser) : len(name)-len(suffixUplink)]
isUplink = true
} else if strings.HasSuffix(name, suffixDownlink) {
email = name[len(prefixUser) : len(name)-len(suffixDownlink)]
} else {
return true
}
u, ok := userMap[email]
if !ok {
return true
}
var value int64
if request.Reset_ {
value = c.Set(0)
} else {
value = c.Value()
}
if isUplink {
u.Traffic.Uplink = value
} else {
u.Traffic.Downlink = value
}
return true
})
}
resp := &GetUsersStatsResponse{}
for _, u := range userMap {
resp.Users = append(resp.Users, u)
}
return resp, nil
}
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) { func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
matcher, err := strmatcher.Substr.New(request.Pattern)
if err != nil {
return nil, err
}
response := &QueryStatsResponse{} response := &QueryStatsResponse{}
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool { manager, ok := s.stats.(*stats.Manager)
if strings.Contains(name, request.Pattern) { if !ok {
return nil, errors.New("QueryStats only works its own stats.Manager.")
}
manager.VisitCounters(func(name string, c feature_stats.Counter) bool {
if matcher.Match(name) {
var value int64 var value int64
if request.Reset_ { if request.Reset_ {
value = c.Set(0) value = c.Set(0)

Some files were not shown because too many files have changed in this diff Show More